Friday, August 23, 2013

What would the average of all dogs look like?

To be more specific, if suddenly no one had any interest in maintaining purebred breeds and all dogs  interbred, would certain common features begin to take hold across the species after, say 500 years. Would we eventually have pockets of new, naturally-evolved breeds in certain areas of the world? At what point would the dogs in the US start to become so different from their Icelandic counterparts that they would not be able to produce viable offspring together?

Thursday, April 4, 2013

3D Tracking: An Idea for a Design Project

I think I'll publish this here and then expand on it later.

The concept I have here is to track an object in three dimensional space with the use of a stand-alone transmitter and four wireless mesh routers (two would create a line, three a plane and four a three dimensional object) which will communicate with a hub connected to an Arduino board. The transmitter will continually send out a signal, the strength of that signal will be monitored by the routers and sent to the hub and the hub will parlay the data into six triangulation calculations in order to determine the current position of the transmitter.

If this can be achieved, maybe I can even create some code to take the real-time results, transfer them into my computer and display the object at its actual position in the three-dimensional box created by the sensors. Going further still, I could introduce a virtual ball into the environment that will "ricochet" off the sensor should the sensor "touch" the ball. Basically an expansion of Microsoft's Kinect into the third dimension.

Thursday, March 7, 2013

The Cat Door

I live in a.....tepid (if we're being generous) basement apartment, so I need to conserve all the heat I can. A major hurdle in this goal is my cat (Figures 1 through 4).

Figure 1

Figure 2

Figure 3

Figure 4

This cat -- who I will henceforth refer to as "Ollie" (being his actual name) -- cherishes his time outdoors and spends much of his waking time roaming the neighbourhood, determining which variety of vole he'll attempt to leave on my bed that night. Unfortunately there is only one way for him to exit and enter my apartment during the day: an open window. Even more unfortunate is that the only window is positioned so that its top-most edge abuts the ceiling, effectively nullifying the meager offering of heat that my landlords are so generous enough to provide me. I had a plan though, and it wasn't to sit around complaining.

I knew the basic idea of Arduino and have a decent enough programming knowledge, so I decided to give it a shot. My wallet took a sizable hit in order to buy the microcontroller board, soldering station, multimeter, helping hands, solder, cordless drill, hand saw...*INHAAAALE*... wires, measuring tape, electrical tape, two sided tape, tape tape, breadboards, resistors, beer and particle board, but I made the executive decision that all of this would be needed anyways once I became a full-fledged engineer and additionally crossed that ambiguous line into manhood.

With all my tools purchased, I was ready and set off to have a nervous breakdown -- I mean, build an automatic window opener. The first step was to relearn the trivial parts of programming that had atrophied in my brain long ago. After a few days of getting up to speed with the great knowledge emporium that Arduino has set up and after a few more of making several programs (detecting a switch and turning on an LED, detecting the presence or absence of light with a photoresistor), I drew up the outline of how I wanted to complete the project.

The outline of how the product would work goes as follows:

  • Ollie steps on a pad
  • Pad depresses one of 5 switches directly beneath it on a breadboard
  • Signal is passed through the switch and back to Arduino
  • Arduino -- via motor shield add-on -- turns on DC motor(s) which fling the window open
  • Ollie waits for 5 minutes, half his body inside, half outside in order to maximize the amount of hot air being let out
  • Ollie finally goes outside, letting his weight off the pad in the process
  • Arduino waits for 10 seconds just to be sure not to inadvertently create a kitty guillotine
  • Arduino reverses the direction of the motors and sends full power through them once more
  • Window slams shut
  • Al has peace of mind at work
Here's why this was a terrible idea (again, in point form):
  • DC motors are meant for high speeds and extremely low torque, making them as suitable for this job as a clown is for real estate
  • Ollie is apparently deathly afraid of clicking noises and/or strange pads
So it was back to the drawing board. I immediately recognized (read: figured out after a month) that DC motors were not the way to go. I needed the opposite end of the spectrum: slow and steady with high torque. That solved problem 1. Problem 2 was a bit different; I needed a solution that Ollie could not see or feel had altered his environment, being the prissy little lad he is. Two options came to mind in a proximity sensor or an RFID setup, and I went with the former but may eventually update the system for the latter (I'll talk about why later).

Problems determined, solutions found, I proceeded on with my new plan, which went like so:
  • Ollie jumps up to window
  • Sensor picks up object
  • Sensor is continually feeding raw data back to Arduino
  • Arduino interprets data as either above or below threshold determined in previous testing
  • If above, cat wants out/in, so Arduino tells the servo to change its angular position to "open"
  • If below, nothing there, so Arduino keeps window closed
(of note here is that there is extra code to determine what to do when the window is open and an object is sensed or if it is closed with no object sensed)
  • Ollie heads in or out unmolested
Now that I had a plan and the equipment, I needed to fabricate a mount for the servo as well as an arm to screw into the servo (Figure 5), such that this arm could pull or push the window.

Figure 5
It worked like a charm. Then the engineering gods once again decided that things were running far too smoothly at this point, and so they decided to obliterate the 5 V regulator on the board (Figure 6).


Figure 6
What they weren't counting on was that I would have access to a vast repository of ICs at work, and my employers were more than happy to donate one of the 18 million adjustable regulators they had on hand. With a few resistors, some solder and a bit of through-hole board I found in a scrap pile, my monstrosity was complete (Figure 7).

Figure 7
Notice the melted DC jack. This happened at about the 5 hour mark of trying to get all three leads to remain in place (I should have soldered the wires in first, then twisted, but hind sight is 20-20). Just when it seemed as though a solar flare itself could not heat up the pad and lead enough to make them unite, everything came into place and, frustration finally eased, I collapsed.

Finally... finally, I had everything that I needed. I had my servo setup, I had my proximity sensor, I had my sticky tack and push pins for securing, and I had my code (which I will append to the end of this post).

After much fine tuning and more tack and pins than a kindergarten classroom, I had completed the set up and was ready for a live demonstration with a member of the audience.


A success for the ages.


"Why would you need an RFID setup, Al?" you might ask, adding "The proximity sensor seems to work fine."

Right you are. The problem now is that it works TOO well, as in any animal that would like to take a peek in my room can help themselves. Story time:

I was fast asleep at around 3 in the morning on a blustery February evening when I was awoken by the window opening and slamming shut, opening and slamming shut (this was before I updated the program to monitor the current status of proximities and instead just close 10 seconds after opening). So I clicked on my bedside lamp, thinking that I would see young Oliver in a confused state at the window. I was wrong, he was  resting on my bed.

My eyes wandered up to the opening near my ceiling and happened upon our friend, the raccoon, just as the window slid into his head with another "THUNK."

"......I need to make an update" was all I thought as I shooed him away with a broomstick.


As promised, here is the code:


/* Sketch that reads a proximity value from a sensor and
 opens or closes a servo attached to a window based on the reading.
 The VCNL4000 communication section is taken from Adafruit's
 public-use code found here https://github.com/adafruit/VCNL4000
 */

// Include the requisite libraries to
// talk to the servo and talk to the sensor,
// respectively
#include <Servo.h>
#include <Wire.h>

Servo myservo;    // the servo object to be used

/* The motor control board offered by Arduino
 has a section for motors that uses several parameters
 (PWM, direction, brake) for two, unique motors.
 The board appears to have a junction for servos, the
 proble -- at least for my project -- is that the pin
 locations for the junction do not align with the
 pin locations on the servo's input. So I've used the
 motor connections to supply full, continuous 5 volts
 and changed the position of the servo with functions
 from the servo library
 */
const int dirPin = 12;
const int pwm = 3;
const int brake = 9;

int velocity = 0;  // make sure the servo won't move when program starts
int pos = 0;    // give the position variable a default value
unsigned long startTime = 0;    // initialize time variables
unsigned long currentTime = 0;

// Adafruit coding section

// the i2c address
#define VCNL4000_ADDRESS 0x13

// commands and constants
#define VCNL4000_COMMAND 0x80
#define VCNL4000_PRODUCTID 0x81
#define VCNL4000_IRLED 0x83
#define VCNL4000_AMBIENTPARAMETER 0x84
#define VCNL4000_AMBIENTDATA 0x85
#define VCNL4000_PROXIMITYDATA 0x87
#define VCNL4000_SIGNALFREQ 0x89
#define VCNL4000_PROXINITYADJUST 0x8A

#define VCNL4000_3M125 0
#define VCNL4000_1M5625 1
#define VCNL4000_781K25 2
#define VCNL4000_390K625 3

#define VCNL4000_MEASUREAMBIENT 0x10
#define VCNL4000_MEASUREPROXIMITY 0x08
#define VCNL4000_AMBIENTREADY 0x40
#define VCNL4000_PROXIMITYREADY 0x20

// end of Adafruit coding

void setup() {
  Serial.begin(9600);  // Begin sending information to the screen

  Serial.println("VCNL");
  Wire.begin();

  // digital pin two on the Arduino board will be used
  // for sending information to the servo
  myservo.attach(2);

  // initialize the motor pins as outputs
  pinMode(dirPin, OUTPUT);
  pinMode(pwm, OUTPUT);
  pinMode(brake, OUTPUT);

  // Adafruit coding section

  // check to see if the board has recognized that
  // the proximity sensor is connected/capable of
  //communicating
  uint8_t rev = read8(VCNL4000_PRODUCTID);

  if ((rev & 0xF0) != 0x10) {
    Serial.println("Sensor not found :(");
    while (1);
  }

  // The following section is meant to provide the programmer
  // with some sort of idea of the signal being sent to and received
  // from the sensor
  write8(VCNL4000_IRLED, 20);        // sending 20 * 10mA = 200mA of current to IR light
  Serial.print("IR LED current = ");
  Serial.print(read8(VCNL4000_IRLED) * 10, DEC);
  Serial.println(" mA");

  //write8(VCNL4000_SIGNALFREQ, 3);
  Serial.print("Proximity measurement frequency = ");
  uint8_t freq = read8(VCNL4000_SIGNALFREQ);
  if (freq == VCNL4000_3M125) Serial.println("3.125 MHz");
  if (freq == VCNL4000_1M5625) Serial.println("1.5625 MHz");
  if (freq == VCNL4000_781K25) Serial.println("781.25 KHz");
  if (freq == VCNL4000_390K625) Serial.println("390.625 KHz");

  write8(VCNL4000_PROXINITYADJUST, 0x81);
  Serial.print("Proximity adjustment register = ");
  Serial.println(read8(VCNL4000_PROXINITYADJUST), HEX);

  // arrange for continuous conversion
  //write8(VCNL4000_AMBIENTPARAMETER, 0x89);

  // end of Adafruit section

}

// Adafruit function to let the sensor know we'll be retrieving
// a proximity value from it and then returning that value
uint16_t readProximity() {
  write8(VCNL4000_COMMAND, VCNL4000_MEASUREPROXIMITY);
  while (1) {
    uint8_t result = read8(VCNL4000_COMMAND);
    //Serial.print("Ready = 0x"); Serial.println(result, HEX);
    if (result & VCNL4000_PROXIMITYREADY) {
      return read16(VCNL4000_PROXIMITYDATA);  // the value we're after
    }
    delay(1);
  }
}

void loop(){

  // Supply +5 V to the servo
  digitalWrite(dirPin,HIGH);
  digitalWrite(brake,LOW);
  velocity = 255;
  analogWrite(pwm,velocity);
   
  // close the window just in case it was open prior
  // to running this program
  for(pos = 105; pos < 145; pos += 1)
    {
      myservo.write(pos);            
      delay(15);                    
    }

  // Adafruit code section

  // read ambient light. This isn't entirely necessary for this project
  // but it could become relevant during later design iterations
  write8(VCNL4000_COMMAND, VCNL4000_MEASUREAMBIENT | VCNL4000_MEASUREPROXIMITY);

  while (1) {
    uint8_t result = read8(VCNL4000_COMMAND);
    //Serial.print("Ready = 0x"); Serial.println(result, HEX);
    if ((result & VCNL4000_AMBIENTREADY)&&(result & VCNL4000_PROXIMITYREADY)) {
   
 
      // supply the servo with 0 V to prevent needless movement
      velocity = 0;
      analogWrite(pwm,velocity);

      // output the proximity and ambient light data to the screen
      Serial.print("Ambient = ");
      Serial.print(read16(VCNL4000_AMBIENTDATA));
      Serial.print("\t\tProximity = ");
      Serial.println(read16(VCNL4000_PROXIMITYDATA));

  // End of Adafruit code

      // The proximity sensor has an accurate detection range of about 3 to 4 inches
      // when something gets inside this distance and occupies a large enough angular
      // size, the proximity value returned from the sensor will be above 2600
   
   
      if (read16(VCNL4000_PROXIMITYDATA) > 2600 && (myservo.read() >= 105)) {
      // this section of code only runs when something is in range
      // and the window is closed or the closing sequence has been
      // initiated
   
        // Supply +5 V to the servo
        digitalWrite(dirPin,HIGH);
        digitalWrite(brake,LOW);
        velocity = 255;
        analogWrite(pwm,velocity);

        // move the servo to 80 degrees in one movement to give
        // the servo arm inertia and overcome static friction
        // and then decrease the degrees in one degree incriments
        // until the servo arm is at 40 degrees
        for(pos = 80; pos > 40; pos -= 1)
        {                                
          myservo.write(pos);            
          delay(15);                      
        }

        delay(100);

        // supply the servo with 0 V to prevent needless movement
        velocity = 0;
        analogWrite(pwm,velocity);

      }
      else if(read16(VCNL4000_PROXIMITYDATA) <= 2600 && (myservo.read() < 105)) {
      // here we have the window closing code. It is stepped into when
      // the sensor is not reading any nearby objects and the window is open
   
        // supply the servo with 5 V to close
        velocity = 255;
        analogWrite(pwm,velocity);
     
        // start the count to 10 seconds of no object sensed
        startTime = millis();
        currentTime = millis();
     
        // continue looping until either an object is sensed or
        // ten seconds have passed without sensing an object
        while (currentTime - startTime < 10000){
       
          if (read16(VCNL4000_PROXIMITYDATA) > 2600){
             break;
          }
       
          currentTime = millis();        
        }
     
        // breaking from the previous loop means that
        // the ten second mark was not reached
        // because an object was sensed. If this is the case
        // we want to go to the beginning and sense again
        if (currentTime - startTime < 10000){
          continue;
        }
     
        // At this point, the requisite ten seconds have passed and
        // we need to close the window      

        // just like opening, we want to overcome static friction
        // with one, big position change (40 deg --> 105 deg)
        // and then ease into the closed position
        for(pos = 105; pos < 145; pos += 1)
        {
          // at any point in the closing process, an object might come
          // into proximity, and since we don't want to squish it
          // we have to hault the closing process and start over
          if (read16(VCNL4000_PROXIMITYDATA) > 2600){
            break;
          }
       
          // no object? okay, move to the next position
          myservo.write(pos);            
          delay(15);                    
        }

        // stop the servo from being able to move
        velocity = 0;
        analogWrite(pwm,velocity);
      }
    }
  }
}

// Read 1 byte from the VCNL4000 at 'address'
uint8_t read8(uint8_t address)
{
  uint8_t data;

  Wire.beginTransmission(VCNL4000_ADDRESS);
#if ARDUINO >= 100
  Wire.write(address);
#else
  Wire.send(address);
#endif
  Wire.endTransmission();

  delayMicroseconds(170);  // delay required

  Wire.requestFrom(VCNL4000_ADDRESS, 1);
  while(!Wire.available());

#if ARDUINO >= 100
  return Wire.read();
#else
  return Wire.receive();
#endif
}


// Read 2 byte from the VCNL4000 at 'address'
uint16_t read16(uint8_t address)
{
  uint16_t data;

  Wire.beginTransmission(VCNL4000_ADDRESS);
#if ARDUINO >= 100
  Wire.write(address);
#else
  Wire.send(address);
#endif
  Wire.endTransmission();

  Wire.requestFrom(VCNL4000_ADDRESS, 2);
  while(!Wire.available());
#if ARDUINO >= 100
  data = Wire.read();
  data <<= 8;
  while(!Wire.available());
  data |= Wire.read();
#else
  data = Wire.receive();
  data <<= 8;
  while(!Wire.available());
  data |= Wire.receive();
#endif

  return data;
}

// write 1 byte
void write8(uint8_t address, uint8_t data)
{
  Wire.beginTransmission(VCNL4000_ADDRESS);
#if ARDUINO >= 100
  Wire.write(address);
  Wire.write(data);
#else
  Wire.send(address);
  Wire.send(data);
#endif
  Wire.endTransmission();
}