Sunday, May 11, 2014

Android / Arduino Remote Control Car

In this post we build a remote control car using Arduino, Bluetooth, and an Android device. The Android device serves as the controller. One cool factor of this project is our Android app uses the position of the Android device to control the car!



RC Car Schematic
/* This sketch is for a remote controlled car with four electric motors that uses the Arduino Uno, RN42 Bluetooth module, and an
Adafruit Motorshield. This code is free for anybody to use or modify
*/

#include <Wire.h> //needed for motors and motor shield
#include <Adafruit_MotorShield.h> //needed for motors and motor shield
#include "utility/Adafruit_PWMServoDriver.h" //needed for motors and motor shield
#include <ctype.h>

int con = 0; //global variable to track connection status
// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
// create an object for each motor and assign it to a port on the shield 
Adafruit_DCMotor *M1 = AFMS.getMotor(1);
Adafruit_DCMotor *M2 = AFMS.getMotor(2);
Adafruit_DCMotor *M3 = AFMS.getMotor(3);
Adafruit_DCMotor *M4 = AFMS.getMotor(4);
int count = 0; //counts how long its been since comms from joystick
 String uDSpeed = "500"; //create global variables to hold speed and direction info
 String lRSpeed = "500"; //defulat is 500 because that is stop condition

//setup code only executed once
void setup() {
  Serial.begin(115200); //start serial commm
  
  //This loop runs until a connection from another RN42 is complete and a "#" is recieved from the car
  //The joystick RN42 is the slave
  while(!con) { 
    if((char)Serial.read() == '#') { con = 1; }//once connected change "con" to true
    delay(5);
  }

 AFMS.begin();  //Start motor shield object, create with the default frequency 1.6KHz



void loop() {
  
  //check if a full speed / direction frame is ready to be read
  if(Serial.available() >= 6) {
    String temp; //temperary string to hold incoming data
    char c = (char)Serial.read();
    if(c == 'u') { //If a 'u' was read this is start of an up / down data frame
      for(int i=0; i<5; i++) { //loop to read 5 other bytes of frame
        if(i < 4) { //reads the speed portion of frame into string 
          temp += (char)Serial.read();
        }
        else { //look for end of frame 'd' character, if it is there save this reading as new speed
          if((char)Serial.read() == 'd') { 
           uDSpeed = temp; 
           count = 0; //just got speed so reset count
          }
        }
      }
    }
    else if(c == 'l') { //If a 'l' was read this is start of an left / right data frame
      for(int i=0; i<5; i++) { //following code is the same as above except for direction frame
        if(i < 4) {
          temp += (char)Serial.read();
        }
        else {
          if((char)Serial.read() == 'r') { 
           lRSpeed = temp; 
           count = 0; //just got speed so reset count
          }
        }
      }
    }
  }
  
  delay(1);
  //the following code will stop the car if no comms with joystick for 150ms
  count++;
  if(count > 20) {
    setMotorSpeed(500,500);
  }
  
  //function call to set motor speeds
  setMotorSpeed(uDSpeed.toInt(),lRSpeed.toInt());
}

//This function clears all bytes out of arduino serial read buffer
void clearSerialBuf() {
 while(Serial.available()) { Serial.read(); }
}

//This function uses the ADC values from the joystick and turns them into motor speeds for going 
//forware, right, left, and reverse. Inputs are the left/right and up/down joystick axis
void setMotorSpeed(int upDown, int leftRight) {
  int lR = 0;
  int bF = 0;
  
  //If left/right is 500 no turn 
  if(leftRight == 500) {
    lR = 0;
  }
   else if(leftRight > 500) { //If greater than 500 this is a right turn
     lR = 1;
     leftRight = leftRight - 500;
   }
   else { //less than 500 this is a left turn
     lR = 2;
     leftRight = 500 - leftRight;
   }
   
   if(upDown == 500) { //500 no up/down direction
      bF = 0;
   }
   else if(upDown > 500) {//more than 500 go forward
     bF = 1;
     upDown = upDown - 500;
   }
   else { //less than 500 go backward
     bF = 2;
     upDown = 500 - upDown;
   }
   
   //If direction variables are both 0 the car is stopped
   if(lR == 0 && bF == 0) {
     motorStop();
   }
   else if (bF==1) { //if forward variable is true
     if(lR == 0) { //no turn so go straight forward
       goForward(scaleSpeed(upDown));
     }
     else if(lR == 1) { //go forward and right
       goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 1);
     }
     else { //go forward and left
       goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 1);
     }
   }
   else if (bF==2) { //if backwards variable is true
     if(lR == 0) { //go straight backwards
       goBackward(scaleSpeed(upDown));
     }
     else if(lR == 1) { //go backward and right
       goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 0);
     }
     else { //go backward and left
       goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 0);
     }
   }
   else { //if no forward or back then just turn
     if(lR==1) { //Right turn, left wheels forward and right wheels backwards
       goRight(scaleSpeed(leftRight));
     }
     else { //left turn, right wheels forward and left wheels backwards
       goLeft(scaleSpeed(leftRight));
     }
   }
}

//function to stop the motors
void motorStop() {
  M2->run(RELEASE);
  M4->run(RELEASE);
  M1->run(RELEASE);
  M3->run(RELEASE);
}

//function to tell motors to go forward, input is speed
void goForward(int mSpeed) {
  M1->setSpeed(mSpeed);
  M2->setSpeed(mSpeed);
  M3->setSpeed(mSpeed);
  M4->setSpeed(mSpeed);
  M2->run(FORWARD);
  M4->run(FORWARD);
  M1->run(FORWARD);
  M3->run(FORWARD);
}

//function to tell motors to go backward, input is speed
void goBackward(int mSpeed) {
  M1->setSpeed(mSpeed);
  M2->setSpeed(mSpeed);
  M3->setSpeed(mSpeed);
  M4->setSpeed(mSpeed);
  M2->run(BACKWARD);
  M4->run(BACKWARD);
  M1->run(BACKWARD);
  M3->run(BACKWARD);
}


//function for left or right turn. inputs are speed for left tires and speed for right tires
//and whether we are going forward or backwards
void goTurn(int rTire, int lTire, int forward) {
  
  M1->setSpeed(rTire);
  M2->setSpeed(lTire);
  M3->setSpeed(rTire);
  M4->setSpeed(lTire);
   //code to turn Right
  if(forward) {
    M2->run(FORWARD); //M2 and M4 are left tires
    M4->run(FORWARD);
    M1->run(FORWARD); //M1 and M3 are right tires
    M3->run(FORWARD);
  }
  else {
    M2->run(BACKWARD);
    M4->run(BACKWARD);
    M1->run(BACKWARD);
    M3->run(BACKWARD);
  }
}

//right turn function, no forward or backwards motion
void goRight(int tSpeed) {
  tSpeed = tSpeed - (tSpeed*.2); //reduce speed by 20%
  M1->setSpeed(tSpeed);
  M2->setSpeed(tSpeed);
  M3->setSpeed(tSpeed);
  M4->setSpeed(tSpeed);
   //code to turn Right
  M2->run(FORWARD); //left tires
  M4->run(FORWARD);
  M1->run(BACKWARD); //right tires
  M3->run(BACKWARD);
}

//left turn function, no forward or backwards motion
void goLeft(int tSpeed) {
  tSpeed = tSpeed - (tSpeed*.2); //reduce speed by 20%
  M1->setSpeed(tSpeed);
  M2->setSpeed(tSpeed);
  M3->setSpeed(tSpeed);
  M4->setSpeed(tSpeed);
   //code to turn Right
  M2->run(BACKWARD); //left tires
  M4->run(BACKWARD);
  M1->run(FORWARD); //right tires
  M3->run(FORWARD);
}

//This function scales the speed values from the joystick ADCs to the speed values of the motors
int scaleSpeed(int scale) {
  float r = ((float)scale/500)*250;
  return int(r);
}

//This scales the turns based on the forward / backward speeds
int scaleTurn(int fBSp, int lRSp) {
  float r =(float)fBSp*(1 - (float)lRSp/500);
  return int(r);
}


Wednesday, April 30, 2014

Building a Bluetooth Remote Control Car

In this post we build a remote control car and controller / joystick using Bluetooth for communication and Arduino for control. A fun project for all ages! At the end of the post you will find the code and schematics shown in the video.



Bluetooth RC Car Schematics
/* This sketch is for a remote controlled car with four electric motors that uses the Arduino Uno, RN42 Bluetooth module, and an
Adafruit Motorshield. This code is free for anybody to use or modify
*/

#include <Wire.h> //needed for motors and motor shield
#include <Adafruit_MotorShield.h> //needed for motors and motor shield
#include "utility/Adafruit_PWMServoDriver.h" //needed for motors and motor shield
#include <ctype.h>

int con = 0; //global variable to track connection status
// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
// create an object for each motor and assign it to a port on the shield 
Adafruit_DCMotor *M1 = AFMS.getMotor(1);
Adafruit_DCMotor *M2 = AFMS.getMotor(2);
Adafruit_DCMotor *M3 = AFMS.getMotor(3);
Adafruit_DCMotor *M4 = AFMS.getMotor(4);
int count = 0; //counts how long its been since comms from joystick
 String uDSpeed = "500"; //create global variables to hold speed and direction info
 String lRSpeed = "500"; //defulat is 500 because that is stop condition

//setup code only executed once
void setup() {
  Serial.begin(115200); //start serial comm
  delay(1000); //delay for serial comm to start up
  do //do while loop for putting RN42 module into command mode
  {
   clearSerialBuf(); //empty serial read buffer
   Serial.print("$$$"); //but BT module in command mode
   delay(1000); //Wait for module to enter command mode
  } while ((char)Serial.read() != 'C'); //look for C from CMD response to confirm in command mode
  
  clearSerialBuf(); //empty serial read buffer

 while(!con) { //Connect to bluetooth device and keep trying until successful
  connectBT("0006666741AD"); //call function to connect, address is hardcoded, if connection is succesful "con" is set
  clearSerialBuf(); //empty serial buffer
  delay(4000); //delay after connect
 }

 Serial.print('#'); //Send this to tell joystick that we are connected, this will cause the joystick to exit setup code

 AFMS.begin();  //Start motor shield object, create with the default frequency 1.6KHz



void loop() {
  
  //check if a full speed / direction frame is ready to be read
  if(Serial.available() >= 6) {
    String temp; //temperary string to hold incoming data
    char c = (char)Serial.read();
    if(c == 'u') { //If a 'u' was read this is start of an up / down data frame
      for(int i=0; i<5; i++) { //loop to read 5 other bytes of frame
        if(i < 4) { //reads the speed portion of frame into string 
          temp += (char)Serial.read();
        }
        else { //look for end of frame 'd' character, if it is there save this reading as new speed
          if((char)Serial.read() == 'd') { 
           uDSpeed = temp; 
           count = 0; //just got speed so reset count
          }
        }
      }
    }
    else if(c == 'l') { //If a 'l' was read this is start of an left / right data frame
      for(int i=0; i<5; i++) { //following code is the same as above except for direction frame
        if(i < 4) {
          temp += (char)Serial.read();
        }
        else {
          if((char)Serial.read() == 'r') { 
           lRSpeed = temp; 
           count = 0; //just got speed so reset count
          }
        }
      }
    }
  }
  
  delay(1);
  //the following code will stop the car if no comms with joystick for 150ms
  count++;
  if(count > 20) {
    setMotorSpeed(500,500);
  }
  
  //function call to set motor speeds
  setMotorSpeed(uDSpeed.toInt(),lRSpeed.toInt());
}

//This function connects with a BT module. Input is the device's address
//If the connection is successful
void connectBT(String address) {
  //module is in command mode send action command to connect with to address
  Serial.print("C," + address + "\r");
  //successful response to connection command
  //TRYING
  //%CONNECT,0006666741AD,0
  int done = 0; //variable to track when connection response is recieved
  
  while(!done) { //wait for reply, read it and set connection variable accordingly
    if(Serial.available()) { //if data is ready to be read
      char c = (char)Serial.read();
      if(c=='%') { //this is variable set in RN42 firmware for connection response
        if((char)Serial.read() == 'C') { //if followed by 'C' connection was successful
          con = 1; //set connection variable
          done = 1; //we can exit loop
        }
        else { //if not a C then connection failed
         con = 0; //not connected
         done = 1; //exit loop
        }
      }
      else if(c == 'f') { //if this is an 'f' connection failed
        con = 0;
        done = 1;
      }
      else { } //do nothing
    }
    delay(50); //delay before running loop again
  }
}

//This function clears all bytes out of arduino serial read buffer
void clearSerialBuf() {
 while(Serial.available()) { Serial.read(); }
}

//This function uses the ADC values from the joystick and turns them into motor speeds for going 
//forware, right, left, and reverse. Inputs are the left/right and up/down joystick axis
void setMotorSpeed(int upDown, int leftRight) {
  int lR = 0;
  int bF = 0;
  
  //If left/right is 500 no turn 
  if(leftRight == 500) {
    lR = 0;
  }
   else if(leftRight > 500) { //If greater than 500 this is a right turn
     lR = 1;
     leftRight = leftRight - 500;
   }
   else { //less than 500 this is a left turn
     lR = 2;
     leftRight = 500 - leftRight;
   }
   
   if(upDown == 500) { //500 no up/down direction
      bF = 0;
   }
   else if(upDown > 500) {//more than 500 go forward
     bF = 1;
     upDown = upDown - 500;
   }
   else { //less than 500 go backward
     bF = 2;
     upDown = 500 - upDown;
   }
   
   //If direction variables are both 0 the car is stopped
   if(lR == 0 && bF == 0) {
     motorStop();
   }
   else if (bF==1) { //if forward variable is true
     if(lR == 0) { //no turn so go straight forward
       goForward(scaleSpeed(upDown));
     }
     else if(lR == 1) { //go forward and right
       goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 1);
     }
     else { //go forward and left
       goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 1);
     }
   }
   else if (bF==2) { //if backwards variable is true
     if(lR == 0) { //go straight backwards
       goBackward(scaleSpeed(upDown));
     }
     else if(lR == 1) { //go backward and right
       goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 0);
     }
     else { //go backward and left
       goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 0);
     }
   }
   else { //if no forward or back then just turn
     if(lR==1) { //Right turn, left wheels forward and right wheels backwards
       goRight(scaleSpeed(leftRight));
     }
     else { //left turn, right wheels forward and left wheels backwards
       goLeft(scaleSpeed(leftRight));
     }
   }
}

//function to stop the motors
void motorStop() {
  M2->run(RELEASE);
  M4->run(RELEASE);
  M1->run(RELEASE);
  M3->run(RELEASE);
}

//function to tell motors to go forward, input is speed
void goForward(int mSpeed) {
  M1->setSpeed(mSpeed);
  M2->setSpeed(mSpeed);
  M3->setSpeed(mSpeed);
  M4->setSpeed(mSpeed);
  M2->run(FORWARD);
  M4->run(FORWARD);
  M1->run(FORWARD);
  M3->run(FORWARD);
}

//function to tell motors to go backward, input is speed
void goBackward(int mSpeed) {
  M1->setSpeed(mSpeed);
  M2->setSpeed(mSpeed);
  M3->setSpeed(mSpeed);
  M4->setSpeed(mSpeed);
  M2->run(BACKWARD);
  M4->run(BACKWARD);
  M1->run(BACKWARD);
  M3->run(BACKWARD);
}


//function for left or right turn. inputs are speed for left tires and speed for right tires
//and whether we are going forward or backwards
void goTurn(int rTire, int lTire, int forward) {
  
  M1->setSpeed(rTire);
  M2->setSpeed(lTire);
  M3->setSpeed(rTire);
  M4->setSpeed(lTire);
   //code to turn Right
  if(forward) {
    M2->run(FORWARD); //M2 and M4 are left tires
    M4->run(FORWARD);
    M1->run(FORWARD); //M1 and M3 are right tires
    M3->run(FORWARD);
  }
  else {
    M2->run(BACKWARD);
    M4->run(BACKWARD);
    M1->run(BACKWARD);
    M3->run(BACKWARD);
  }
}

//right turn function, no forward or backwards motion
void goRight(int tSpeed) {
  M1->setSpeed(tSpeed);
  M2->setSpeed(tSpeed);
  M3->setSpeed(tSpeed);
  M4->setSpeed(tSpeed);
   //code to turn Right
  M2->run(FORWARD); //left tires
  M4->run(FORWARD);
  M1->run(BACKWARD); //right tires
  M3->run(BACKWARD);
}

//left turn function, no forward or backwards motion
void goLeft(int tSpeed) {
  M1->setSpeed(tSpeed);
  M2->setSpeed(tSpeed);
  M3->setSpeed(tSpeed);
  M4->setSpeed(tSpeed);
   //code to turn Right
  M2->run(BACKWARD); //left tires
  M4->run(BACKWARD);
  M1->run(FORWARD); //right tires
  M3->run(FORWARD);
}

//This function scales the speed values from the joystick ADCs to the speed values of the motors
int scaleSpeed(int scale) {
  float r = ((float)scale/500)*250;
  return int(r);
}

//This scales the turns based on the forward / backward speeds
int scaleTurn(int fBSp, int lRSp) {
  float r =(float)fBSp*(1 - (float)lRSp/500);
  return int(r);
}

Bluetooth Joystick

/*This arduino sketch is for a joystick for controller an RC car. The joystick is Parallax 2 axis
joystick. The RN42 Bluetooth module is used to communicate with the RC car. This code is free for 
anybody to use or modify*/

int UD = 500; //Variable for storing up / down joystick axis for forward / reverse speed
int LR = 500; //Variable for storing left / right joystick axis reading for direction
int con = 0; //Variable to track if RN42 is connected

void setup() {
  Serial.begin(115200); //start serial commm
  
  //This loop runs until a connection from another RN42 is complete and a "#" is recieved from the car
  //The joystick RN42 is the slave
  while(!con) { 
    if((char)Serial.read() == '#') { con = 1; }//once connected change "con" to true
    delay(5);
  }
}

void loop() {
   UD = filter(analogRead(A0)); //Read up / down joystick axis value, apply filter, and store result
   LR = filter(analogRead(A1)); //Read left / right joystick axis value, apply filter, and store result
   Serial.print(formatValue(UD,1)); //format up / down axis value into packet and send it to RC car
   delay(7);
   Serial.print(formatValue(LR,0)); //format left / right axis value into packet and send it to RC car
   delay(7);
}

//This function sets joystick resting axis values to a consistent value (500) for both axis. It also
//keeps extreme values in a consistent range
int filter(int jRead) {
  if(jRead > 485 && jRead < 540) { return 500; }
  else if(jRead < 20) { return 0; }
  else if(jRead > 1000) {return 1000; }
  else { return jRead; }
}

//This function creates the up / down and left / right packets for RC car to read.
//It makes every value four digits and adds a starting and ending character for each packet
//The input is the speed or direction value and packet type (speed or direction)
String formatValue(int val, int udlr) {
 String temp;

  if(val < 10) { //if below 10 add three leading zeros
   temp = "000" + (String)val;
  } 
  else if (val < 100) { //if below 100 add two leading zeros
   temp = "00" + (String)val; 
  }
  else if (val < 1000) { //if below 1000 add one leading zero
   temp = "0" + (String)val; 
  }
  else { temp = (String)val; } //if 1000 add no zeros
  
  if(udlr) { //for speed packet add 'u' to front and 'd' to back
    temp = 'u' + temp + 'd';
  }
  else { //for direction packet add 'l' to front and 'r' to back
    temp = 'l' + temp + 'r';
  }
  
  return temp;
}

Monday, March 24, 2014

Getting Started with the RN42 Bluetooth Module

In this video post tutorial we go over the basics of using the RN42 Bluetooth module. This tiny but capable Bluetooth module makes it easy to add wireless capability to any project or design. Topics covered include:
  • Connecting to and communicating with the RN42 wirelessly 
  • Using the RN42 in command mode to change settings
  • Wireless communication with an Arduino Uno using the RN42


RN42 Tutorial Schematics
Basic Setup with Serial Pins Shorted Together
RN42 Connected to Arduino Uno
Communicating with an Arduino wirelessly using the RN42 Bluetooth module
/*This Arduino Uno sketch was used to communicate with an Arduino Uno wirelessly using a serial terminal and the RN42 Bluetooth module. This code is free and open for anyone to use*/

void setup() {
  //set baud rate to match BT module
  Serial.begin(115200);
}

void loop() {

  String t; //string to hold data from BT module
  while(Serial.available()) { //keep reading bytes while they are still more in the buffer
    t += (char)Serial.read(); //read byte, convert to char, and append it to string
  }

  if(t.length()) { //if string is not empty do the following

    if(t == "Hi Uno\r\n") { Serial.print("Hello Neil\n"); } //say hello
    else if(t == "Meaning of life?\r\n") { //find out the meaning of life
      delay(1000);
      Serial.print("Money. ");
      delay(1000);
      Serial.print("Guns. ");
      delay(1000);
      Serial.print("Hoes.\n");
   }
    else { Serial.print("Syntax Error\n"); } //send this for any other string
   }
   delay(20);
}

Sunday, March 23, 2014

Voltage Level Shifting Tutorial

In this tutorial we look at three methods for shifting or converting a digital logic level from one voltage level to another voltage level. For instance how to convert a 3.3 V serial signal to a 5 V serial signal or vice versa. Voltage level shifting or voltage translation is needed for serial communication between an Arduino Uno (5 V) and an XBee Zigbee module or an RN42 Bluetooth module (3.3 V).


Level Shifting Tutorial Schematics
Level Shifting with a Voltage Divider
Level Shifting with a NPN Transistor
Level Shifting TX Arduino Sketch
/*The code was used for the Arduino Uno and Duo, whichever was acting as the serial communication transmitter in the tutorial at the time. This code is free and open to all to use. */

void setup() {
//Want to use fast baud rate for this example
Serial.begin(115200);
}

void loop() {
// Just transmit same message over and over
Serial.write("forcetronics\n");
delay(10);
}

Level Shifting RX Arduino Sketch
/*The code was used for the Arduino Uno and Duo, whichever was acting as the serial communication reciever in the tutorial at the time. This code is free and open to all to use. */

void setup() {
//Want to use a fast baud rat
Serial.begin(115200);
}

void loop() {
//look for a serial data and print it
while(Serial.available()) {
  char c = (char)Serial.read();
  Serial.print(c);
}

delay(2);
}

Sunday, March 9, 2014

Building a Motion Coded Light

In this project we build a motion coded light. What is a motion coded light? A light that is turned on or off using a certain sequence of hand or body movements (like a secret handshake). To build a motion coded light you need an Arduino Uno, a high power relay, and an infrared switch. Check out the video below to learn more....



Motion Coded Light Schematic

Motion Coded Light Arduino Sketch
//The following Arduino code for the motion coded light is totally open for anybody to use for anything
int tog = LOW; //variable for toggling the light on or off
const int val = 350; //value for tracking if IR switch has been tripped

void setup() {//only setup code is to set digital pin to output
  pinMode(9, OUTPUT); //Using digital pin 9 to control relay
}

void loop() {
  
  if (analogRead(A5) < val) { //look for object in front of sensor (switch is tripped)

    for (int i=0; i<14; i++) { //check for object to move out of sensor range within 420 ms
       delay(30);

       if (analogRead(A5) >= val) { //look for object to move from sensor

         for (int j=0; j<14; j++) { //give the object 420 ms to move back in front of sensor
           delay(30);
          
           if (analogRead(A5) < val) { //look for object in front of sensor
             int activate = HIGH; //variable to track if the light should be turned off or on
             
             for (int c=0; c<100; c++) { //ensure the object stays in front of sensor for 1 second
               delay(10); //delay 10 ms 10o time for 1 sec total
              
               if (analogRead(A5) > val) { //if object is not in front of sensor for 1 sec break out of loop and do not toogle switch
                 activate = LOW; //set variable to not toogle switch
                 break; //break out of loop
               }
             }
             
             if(activate == HIGH) { //if variable is high toogle light switch
               tog = toggleSwitch(tog); //toggle switch value so opposite action is taken next time
               digitalWrite(9,tog); //write value to digital pin to open or close relay
               delay(1500); //delay 1.5 seconds so switch is not unintentionally toogled again
             }
             break;
           }
         }
         break;
       }
    }
  }
  
  delay(100);
}

//function toggles variable that controls switch position
int toggleSwitch(int t) {
  
  if(t==LOW) { return HIGH; }
  else { return LOW; }
}

Thursday, February 6, 2014

Building a Wireless Temperature Sensor Network Part 1

In this ForceTronics' inaugural project we will be building a wireless temperature sensor network that will include features such as battery powered sensors and the ability to access the temperature data over the internet via a computer or an iOS device such as an iPhone.

The two main building blocks we will be using for this project are an Arduino Board (first the Uno and then later the Yun to add internet capability) and XBee Wireless RF Modules. Now it is assumed that the reader has a basic understanding what Arduino is and some basic experience with the Arduino Uno board, if you do not have Arduino experience don't fret just go to arduino.cc to get started and come back here when you are ready. As for XBee, no experience is necessary we will go over the basics of using XBee here for this project.

Building a wireless temperature sensor network is no easy task so we will break this project up into parts and each part will be covered in a separate post. The project will be broken up into 6 parts, they are as follows:
  1. Introduction, getting started with XBee, and using XBee with Arduino (This post is part 1)
  2. Gathering data from multiple sensors
  3. Design options for powering your sensor network
  4. Design options for powering your sensor network continued 
  5. Monitoring your sensor network over the internet and logging temperature data
  6. Connecting to your sensor network with your iOS device
The focus of the rest of this post will be to learn the basics of XBee and how to use it with Arduino. In an attempt not to reinvent the wheel, we will refer to some fantastic XBee video tutorials created by "tunnelsup." The "XBee Basics" video tutorials is a series made up of 5 video, but you do not need to do all of the videos to complete this project. I am recommended that you actually get the parts and complete Lessons 1 and 4. For lessons 2 and 3 I recommend you watch them and follow along, but you do not need to actually get all the parts and do them (unless you want to). You will not need to do lesson 5 for this project, but if you want to feel free it can only help. Each video provides a parts list of what is needed to complete the tutorial, but if you want to buy in bulk to get you through part 1 and 2 of this project see the parts list at the end of this post.

XBee Basics - Lesson 1 - General Information and Initial Setup

XBee Basics - Lesson 2 - Simple Chat Program Between Two XBees



Hardware you will need for part 1 and 2 of the wireless temperature sensor network project is listed below. Two places where you can buy all this hardware is sparkfun.com and adafruit.com.
  • An Arduino Uno or similar Arduino board. When we add internet connectivity in part 4 to our sensor network we will switch to the Arduino Yun. 
  • Three XBee Modules, ZB Series 2, 2mW with Wire Antenna.
  • Either three XBee Explorers USB from SparkFun or three XBee Adapter Kits from Adafruit (used in video tutorial) for programming and connecting to our XBee modules. If you want to save some money you could just buy two XBee Explorer boards or Xvee Adapter boards, but you will need to buy parts to get your XBee board to plug into a standard breadboard.
  • At least two cables to connect your XBee Explorer boards or XBee Adapter boards to a computer. See the product details for which type cable you need with your board.
  • Three mini breadboards.
  • Three TMP36 or MCP9700 temperature sensors.
  • A power supply that is capable of outputting 3.3 V for powering two of the XBee modules. If you are using the Adafruit Xbee Adapter Kits your power supply can output anywhere between 3.3 V and 5 V since they have an onboard regulator. The third XBee module will be powered by the Arduino board. 
  • Wiring, wire strippers, and a soldering iron
That is it for part 1 of building a wireless temperature sensor network. If you have any questions on what was covered here feel free to asked them in the comments section below or email me at forcetronics@gmail.com. Stay tuned for part 2!



Wednesday, February 5, 2014

Building a Wireless Temperature Sensor Network Part 2

Welcome to part 2 of building a wireless temperature sensor network. In part 1 we learned the basics of how to use XBee and how to use it with Arduino. In this post we will start to build our sensor network and collect temperature data from multiple sensors. The parts list for this post can be found at the end of the part 1 post. Our hardware setup will be very similar to the setup in XBee Basics Lesson 4 video tutorial (found in part 1), except we will add a second XBee router with a temperature sensor and we will add a third temperature sensor to our Arduino board.

Let's get started by installing the proper firmware on our three XBee modules. We want to setup the XBee firmware the same way as in the XBee Basics Lesson 4 video except this time setup two routers instead of one. Be sure to use the same PAN ID for each. Once you have the proper firmware on the XBees let's put the hardware together as shown in the below figure.

Hardware setup for capturing temperature data from multiple XBees
As you can see in the figure, the two routers are tied to the same power supply. That is not very useful for creating a network, but this is just to get started. In part 3 we will look at options for powering each sensor so we can spread them out. You will need to solder a wire or pin to the AD3 hole on the XBee adapter board so you can connect it to the temperature sensor's output on the breadboard. Going forward I will refer to the Arduino and XBee coordinator as the "controller" since it gathers the temperature data from the router XBees, turns it into useful information, and communicates it to the user. Please note that I tied sensor 3 to one of the analog pins on the Arduino so we will use the Arduino to make the analog measurement for calculating the temperature from sensor 3. Having a temperature sensor on the controller is optional. Depending on how you choose to use your sensor network, you may or may not want to make temperature readings at the controller. Below are photos of my setup. The first photo shows the two routers on small red breadboards connected to the TMP36 or MCP9700 temperature sensors. The alligator clips you see in the picture are from the power supply, which is putting out 3.3 V since I am using the Sparkfun Xbee boards (no onboard regulator like the Adafruit versions).

The two routers each with a temperature sensor
XBee coordinator, temperature sensor, and Arduino Uno
The two main differences between this setup and the setup we saw in XBee Basics Lesson 4 is we have two routers sending the controller temperature data and we also have a temperature sensor tied to the controller. That means in the Arduino code we now need to read the address from the received frame of data from each router to determine which router sent it. We also need to read the temperature from sensor 3 and display it to the user. Below is the code for the Arduino with comments to explain what is happening in each line of code. When you go through the code you will want to have the the format or layout of an XBee API RX data frame on hand so you can understand what is happening in the code when it is handling incoming data from the routers. You can get this information from the XBee manual or from the XBee S2 Quick Reference Guide that was in lesson 3 and 4 of the XBee Basics video series. You can get the reference guide from the tunnelsup blog, if you do use it I encourage you to donate $1 to blog. Click here to go access the reference guide.

/*This program was written for the Arduino Uno. The Uno has an XBee Series 2 RF Module connected to it as a coordinator. The Uno uses the XBee coordinator to communicate with two router or end point XBees with temperature sensors. This program recieves the temperature readings from the two endpoint XBees and writes the data to the serial monitor */

/*Each Xbee has a unque 64 bit address. The first 32 bits are common to all XBee. The following four ints (each int holds an address byte) hold the unique 32 bits of the second half of the XBee address*/
 int addr1;
 int addr2;
 int addr3;
 int addr4;
 int sen3Counter = 0; //This counter variable is used print sensor 3 every 5 seconds

void setup()  { 
 Serial.begin(9600); //start the serial communication
}

void loop()  { 
  if (Serial.available() >= 21) { // Wait for coordinator to recieve full XBee frame 
    if (Serial.read() == 0x7E) { // Look for 7E because it is the start byte
      for (int i = 1; i<19; i++) { // Skip through the frame to get to the unique 32 bit address
        //get each byte of the XBee address
        if(i == 8) { addr1 = Serial.read(); }
        else if (i==9) { addr2 = Serial.read(); }
        else if (i==10) { addr3 = Serial.read(); }
        else if (i==11) { addr4 = Serial.read(); }
        else { byte discardByte = Serial.read(); } //else throwout byte we don't need it
      }
      int analogMSB = Serial.read(); // Read the first analog byte data
      int analogLSB = Serial.read(); // Read the second byte
      float volt = calculateXBeeVolt(analogMSB, analogLSB);//Convert analog values to voltage values
      Serial.println(indentifySensor(addr1,addr2,addr3,addr4)); //get identity of XBee and print it
      Serial.print("Temperature in F: ");
      Serial.println(calculateTempF(volt)); //calculate temperature value from voltage value
    }
  }
  delay(10); //delay to allow operations to complete
  //This if else statement is used to print sensor 3 value every 5 second to match the XBee routers
  //It uses the delay() function above to calculate 5 seconds
  if (sen3Counter < 500) { sen3Counter++; }
  else {
    Serial.println("Temperature from sensor 3:");//This is sensor 3
    Serial.print("Temperature in F: ");
    //the following line calculates voltage, then temperature, and then prints temp to serial monitor
    Serial.println(calculateTempF(calculateArduinoVolt(analogRead(A0))));
    sen3Counter = 0; //reset counter back to zero to start another 5 seconds
  }
}

//Function takes in the XBee address and returns the identity of the Xbee that sent the temperature data
String indentifySensor(int a1, int a2, int a3, int a4) {
  int rout1[] = {64, 176, 163, 166}; //Arrays are the 32 bit address of the two XBees routers
  int rout2[] = {64, 177, 63, 221}; 
  if(a1==rout1[0] && a2==rout1[1] && a3==rout1[2] && a4==rout1[3]) { //Check if Sensor 1
    return "Temperature from sensor 1:"; } //temp data is from XBee one
  else if(a1==rout2[0] && a2==rout2[1] && a3==rout2[2] && a4==rout2[3]) {//Check if Sensor 2
    return "Temperature from sensor 2:"; } //temp data is from XBee two
  else { return "I don't know this sensor"; }  //Data is from an unknown XBee
}

//this function calculates temp in F from temp sensor
float calculateTempF(float v1) { 
 float temp = 0;
 //calculate temp in C, .75 volts is 25 C. 10mV per degree
 if (v1 < .75) { temp = 25 - ((.75-v1)/.01); } //if below 25 C
 else if (v1 == .75) {temp = 25; }
 else { temp = 25 + ((v1 -.75)/.01); } //if above 25
 //convert to F
 temp =((temp*9)/5) + 32;
 return temp;
}

//This function takes an XBee analog pin reading and converts it to a voltage value
float calculateXBeeVolt(int analogMSB, int analogLSB) {
  int analogReading = analogLSB + (analogMSB * 256); //Turn the two bytes into an integer value
  float volt = ((float)analogReading / 1023)*1.23; //Convert the analog value to a voltage value
  return volt;
}

//This function takes an Arduino analog pin reading and converts it to a voltage value
float calculateArduinoVolt(int val) {
 float volt = (float)val * (5.0 / 1023.0); //convert ADC value to voltage
 return volt;
}

The next step is to take the code and upload it to the Arduino. Do not forget to disconnect the wire connecting the Arduino digital pin 0 to the XBee coordinator when you upload the code to the Arduino or else you will get an error. Once the code it uploaded, reconnect the wire to digital pin 0 and open the serial monitor. If you have everything setup correctly your serial monitor should look something like the one in the below figure. 

Serial monitor displaying data from each temperature sensor
It is a good idea to place all your sensors in the same area so you know that they should have a similar temperature reading. That way if one of the sensors is off you can easily spot it and investigate what the problem is. Note that these sensors have an accuracy tolerance of +/- 1 degree from the actual temperature so theoretically your sensor readings could differ by up to 2 degrees even if they are right next to each other. If your serial monitor does not look something like the figure above don't worry just go over instructions again to make sure everything is correctly setup, its easy to miss something! If you see the sensor 3 readings in your serial monitor, but you are missing sensor 1 or 2 or both that means one or both of your routers is not sending data to the coordinator. Check the power and communication lights on the XBee adapter boards to ensure they are getting power and are communicating with the coordinator. If they have power, but are not communicating check to make sure you loaded the correct configuration on them.

Well that is it for part 2. Normally I would share the list of parts you need for the next part of the project, but in part 3 and part 4 we will look at multiple options for powering the sensors in the project so read it first and then decide what works best for powering your temperature sensor network. If you have any questions on part 2 use the comment area below or feel free to email your question to me at forcetronics@gmail.com. Stay tuned for part 3!