Let's Make Robots!

SDT (Stationary Defense Turret)

Detects humans and fires a laser at them
I2Cmaster.zip16.6 KB

Most geeks will be aware of the 50th Anniversary of Doctor Who this weekend.  For that occasion, I wanted to have my version of K-9 up and running.  I hope to have a simple mobile security bot version operational Saturday, but this was a logical separate function point on the way.

(Note: Editing video now.  Will add shortly.)

"SDT" originally comes from the phrase "Ignore the Shooty Dog Thingy" used by Anthony Head's character in the episode "School Reunion."  The fact that it is a Stationary Defense Turret was a happy accident.

As a proof of concept, SDT exhibits the function that it set out to-simple, one-sensor human detection and targeting.  It is limited in scope though.  The range of effectiveness at the moment is at greatest a meter and a half.  Reliably it is closer to .7 m.  

The heart of the system is the Melexis 90614 which you see in place of K-9's red "probe" on the unit:

This is an I2C thermopile unit.  This means it measures temperature at a distance by comparing the infrared wavelengths coming through its window with the measured ambient temperature of the unit and sends the result over 2-wire.  To use it with an Arduino the regular I2C library doesn't work though, so you need to use the I2Cmaster.h library, which I've attached.  Most of the heavy programming to get this unit to work as a thermometer was done by bildr.blog.  Once you have a threshold for human temperature you can use programming similar to the distance sensor on an SHR "head" for targeting.

As mentioned, this is limited in range.  Part of that is because the field of view (FOV) of this unit is 90°.  That means that a) at a distance, human body temperature gets averaged into the ambient temperature pretty quickly, and b) at closer ranges, the unit fires the laser at more "targets" than it should.  

There is a version of the sensor that has a much more limited (10°-12°) field of view, which would probably increase the accuracy and range.  Thermopile sensors the likes of which are put in commercially available point-and-read thermometers are reasonably accurate at quite a distance.  The narrow-angle sensor costs about twice as much as the one I got.  When I'm working again I may be able to justify the expense but not unemployed just before the holidays.  Also, there are non-I2C thermopile sensors available for about a third of what this unit costs with a more limited (45°,55° and 80°) FOVs but the output is analog and even with the more limited field, chances are sensitivity and accuracy wouldn't make it worth the immediate investment (I do plan on testing them eventually though.)

For triggering, I'm using the PIR sensors in the ears.  This is a solution to the human detection problem-motion within range of the unit tells it to look for the human, body temperature tells it when to fire.  (No sense in continuously sweeping the field of view-especially with a battery-powered unit.)  The bi-directional nature of the PIR sensors probably would allow a more efficient seek-and-sweep (ie, if the left ear is triggered and not the right then the head would start the sweep on the left.)

The laser is a stock eBay Chinese Green pen-pointer that I cut in half with a pipe cutter.  The Arduino doesn't give you enough power to run it off of a Dpin, so there's a 2N2222 triggered by the Arduino pin across the full 2.1Amps from the power supply.

Sketch is below.  Keep in mind that the UNO uses A4 and A5 for I2C, and that there are pull-up resistors to the I2C lines.  See the bildr link above for pin-out.  This is the third iteration of the code. I never cleaned up a lot of the dropped variables or commented lines.  Keep in mind that using PIRs on a moving platform is a trade-off.  I let the neck stabilze for 10 seconds after a targeting sweep to make sure any motion detected isn't self generated.  Also, the zap function is triggered at anything above body temperature (mine is 97.1° F) but conceivably this could leave the dog firing at a furnace, so I had an upper limit option as well (commented out.)  In the video, you can see it almost always shoots at the soldering iron and the furnace wall (behind the TV.)

#include <i2cmaster.h>
#include <Servo.h> 
Servo neckServo;
#define laserPin 8
#define rightEarPIR 11//green
#define leftEarPIR 12//yellow
int leftNeckMax=15;
int rightNeckMax=165;
int neckMiddle=95;
int tempSweep,neckPos, sweepIndex, targetPosition, maxTempInSweep;
/*heat value read at current neck position, neck position, array index related to neck position, maximum
read heat value position in array and maximum read temp in sweep*/
int minimumBodyTemp=295;//Most human bodies within 2 meters will read between these two temps
int maximumBodyTemp=310;
int right, left;

void setup(){
	neckServo.attach(9);//only use 9 for UNO testing
        pinMode(laserPin, OUTPUT);
        pinMode (leftEarPIR, INPUT);
        pinMode (rightEarPIR, INPUT);
	i2c_init(); //Initialise the i2c bus
	PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
        digitalWrite(laserPin, LOW);

void loop(){
  if ((digitalRead(rightEarPIR)==HIGH) || (digitalRead(leftEarPIR)==HIGH)){
    for (neckPos=15; neckPos<166; neckPos +=10)
      delay(100);//give it a moment to stop moving
     /* if (((tempSweep>minimumBodyTemp)&&(tempSweep<maximumBodyTemp))&&(tempSweep>maxTempInSweep))*/
     if (tempSweep>minimumBodyTemp)
    for (neckPos=166; neckPos>15; neckPos -=10)
      delay(100);//give it a moment to stop moving
     /* if (((tempSweep>minimumBodyTemp)&&(tempSweep<maximumBodyTemp))&&(tempSweep>maxTempInSweep))*/
     if (tempSweep>minimumBodyTemp)

void zap(){
  for (int i=0;i<3;i++){
  digitalWrite (laserPin, HIGH);
  digitalWrite (laserPin, LOW);

int myTempData(){//borrowed from bildr:http://bildr.org/2011/02/mlx90614-arduino/
    int dev = 0x5A<<1;
    int data_low = 0;
    int data_high = 0;
    int pec = 0;
    // read
    data_low = i2c_readAck(); //Read 1 byte and then send ack
    data_high = i2c_readAck(); //Read 1 byte and then send ack
    pec = i2c_readNak();
    //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
    double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
    double tempData = 0x0000; // zero out the data
    int frac; // data past the decimal point
    // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
    tempData = (double)(((data_high & 0x007F) << 8) + data_low);
    tempData = (tempData * tempFactor)-0.01;
    return (tempData);

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Woah! This is awesome Max!!

Your K-9's head shape came out looking very much like the original. Only thing I saw was K-9's single eye versus your. but yours looks better, actually. Now for the body.


I love this so much! :D

Nice work so far. Now that I know you built the head, I know I am really behind. :)

Jelly baby?


Love your K9 Max!

Very interesting project. I love that you have a K9 robot. I'm old and have been a Doctor Who fan since before dirt was invented! I would really like to see more Pics of the bot.

More pictures when it's the K-9 MK1.  I don't consider this the full "K-9" since it's stationary. 

Damn. Now all I have to do is catch up on all the episodes leading to this guys inception. Regardless, sir, what you do with three ICs and 30 lines of code takes me 12 SoCs and 3,000 lines. I'm proud to know you. :)

Welcome to a new addiction then.  Like Jscottb, I've been a fan since Tom Baker

(I only like a few others though-3, 5, 10 and 11 to be honest)

but there's more than enough material among that set of Doctors to waste a life.  Incidentally, if you run out of "First Series" episodes on Netflix, I'd suggest going over to dailymotion.  Almost everything that has been released on DVD is there.