The ForceTronics blog provides tutorials on creating fun and unique electronic projects. The goal of each project will be to create a foundation or jumping off point for amateur, hobbyist, and professional engineers to build on and innovate. Please use the comments section for questions and go to forcetronics.com for information on forcetronics consulting services.
Showing posts with label voltage regulator. Show all posts
Showing posts with label voltage regulator. Show all posts
Monday, November 16, 2015
Using the LM317 Voltage Regulator
In this video we give you an overview of the LM317 Voltage Regulator and how to configure it. The LM317 is popular voltage regulator among makers because of its low cost and its adjustable output voltage. You can purchase the LM317 at www.forcetronics.com
Monday, February 3, 2014
Building a Wireless Temperature Sensor Network Part 4
Welcome back for part 4 of building a wireless temperature sensor network. In part 3 we looked at some design options for powering our wireless sensors, DC power supply and battery technologies, as well as options for implementing a voltage conversion stage, DC to DC converter and voltage regulator. For our wireless temperature sensor network we will be powering the network controller with a low cost 9 V DC power supply. Voltage conversion from 9 V to 5 V for the controller will be handled by the Arduino Uno's built-in voltage regulator. For sensor 1 we will be using a 7.4 V Li-ion Polymer battery pack as our power source and a voltage regulator to convert the battery voltage to a 3.3 V level. For sensor 2 we will be using 4 series AA Alkaline batteries as our power source and a DC to DC converter to convert the battery voltage to a 3.3 V level.
Since batteries have a finite amount of power we need a way in our sensor 1 and 2 design to monitor the battery voltage level so we know when it is time to replace or recharge them. Besides just knowing when they need to be replaced, there is another important reason why we need to monitor the battery voltage level and that is to ensure we do not over discharge them. Over discharging batteries is a bad thing especially when it comes to Li-ion battery technologies (if you did your battery homework from part 3 you would already know this). For the Li-ion Polymer battery pack we do not want to let the voltage level to go below 6 V. One way to ensure that this does not happen is to measure the battery level periodically and notify the user when the battery level starts to approach 6 V. We could use one of the XBee's ADC pins to measure the battery level, but the ADC can only read voltage levels up to 1.2 V. So how can we measure > 6 V with an ADC that can only read up to 1.2 V? The answer is we implement a voltage divider between the battery output and ground. Our voltage divider will consist of two resistors of known values in series. The resistor closet to the battery output will be higher in value than the resistor closet to ground. We then connect the ADC pin between the two resistors. This yields a lower voltage level (a divided down voltage level) at the ADC pin, but since we know the values of the resistors we can use this lower measured voltage level to calculate the voltage level at the battery's output. Let's take a look at an updated schematic diagram of sensor 1 and 2 with our new power system added, including our voltage divider for monitoring the battery voltage level.
The voltage divider for sensor 1 and 2 is made up of the 150 kOhm and 15 kOhm resistors. For simplicity sake we will use the same divider design and low voltage level (6.0 V) for both the Li-ion Polymer battery pack and Alkaline batteries. Notice that connected in-between our voltage divider is the AD2 pin of the XBee, this is the ADC pin we will use to monitor the battery voltage level. The voltage divider resistors were chosen so that the resistor closet to the battery is 10 times larger than the resistor closet to ground (150 k / 15 k = 10). That means the voltage level at the ADC pin of the XBee will be 1/10 the value of the battery voltage. As an example, if the ADC measures 0.68 V we know the battery voltage level is 6.8 V (0.68 x 10 = 6.8). Since 6.8 V is larger than 6.0 V we know that our battery is still good.
The downside of our voltage divider circuit is that it wastes battery power since there is current flowing through it to ground. That is why we want to use high value resistors like 150 kOhm to keep the current flowing through the voltage divider to a minimum. You may be asking yourself, why not use even higher value resistors like 10 MOhm and 1 MOhm? The reason is that the XBee ADC pin has a finite resistance value. When making an ADC measurement, the ADC pin presents a 1 MOhm resistance to the circuit it is connected to (this information was obtained from the XBee manual). That means when AD2 makes a measurement it is like putting a 1 MOhm resistor in parallel with the resistor closet to ground in the voltage divider circuit. If the resistor closet to ground had a resistance value close to 1 MOhm, the ADC measurement itself would essentially change the circuit and the resulting measurement would not be accurate. So for the voltage divider we want it to be high enough in resistance so that we do not waste too much battery life, but low enough so that the ADC input resistance does not have a drastic effect on measurement accuracy. With a total series resistance of 165 kOhm in our voltage divider it only consumes ~ 42 uA of current (7 V / 165 kOhm).
With the new power design and the need to monitor the battery voltage level we have to update our XBee router firmware and the Arduino Uno code. For the XBee routers the only change we want to make to the firmware is to set pin AD2 from disabled (0) to ADC (2), everything else for the routers will stay the same. With this change in the firmware the XBee routers will now make two ADC readings every 5 seconds (one on pin AD2 and one on pin AD3). Below is the updated code for the Arduino Uno.
/*This sketch 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 XBee routers. Each XBee router has an analog pin set to measure a temperature sensor and a second analog pin set to measure the voltage level of the battery power the XBee. This program receives the temperature readings from the two router XBees and writes the data to the serial monitor. It also monitors the battery voltage level, if the battery level falls below 6.2 V it signals the user via the serial monitor that the battery is low. This sketch is part of a tutorial on building a wireless sensor network, the tutorial can be found at http://forcetronic.blogspot.com/*/
/*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 XBee address*/
int addr1;
int addr2;
int addr3;
int addr4;
int sen3Counter = 0; //This counter variable is used print sensor 3 every 5 seconds
float batDead = 6.2; //battery pack voltage level where it needs to be replaced
void setup() {
Serial.begin(9600); //start the serial communication
}
void loop() {
if (Serial.available() >= 23) { // Wait for a full XBee frame to be ready
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
Since batteries have a finite amount of power we need a way in our sensor 1 and 2 design to monitor the battery voltage level so we know when it is time to replace or recharge them. Besides just knowing when they need to be replaced, there is another important reason why we need to monitor the battery voltage level and that is to ensure we do not over discharge them. Over discharging batteries is a bad thing especially when it comes to Li-ion battery technologies (if you did your battery homework from part 3 you would already know this). For the Li-ion Polymer battery pack we do not want to let the voltage level to go below 6 V. One way to ensure that this does not happen is to measure the battery level periodically and notify the user when the battery level starts to approach 6 V. We could use one of the XBee's ADC pins to measure the battery level, but the ADC can only read voltage levels up to 1.2 V. So how can we measure > 6 V with an ADC that can only read up to 1.2 V? The answer is we implement a voltage divider between the battery output and ground. Our voltage divider will consist of two resistors of known values in series. The resistor closet to the battery output will be higher in value than the resistor closet to ground. We then connect the ADC pin between the two resistors. This yields a lower voltage level (a divided down voltage level) at the ADC pin, but since we know the values of the resistors we can use this lower measured voltage level to calculate the voltage level at the battery's output. Let's take a look at an updated schematic diagram of sensor 1 and 2 with our new power system added, including our voltage divider for monitoring the battery voltage level.
![]() |
| Sensor 1 and 2 with power design |
The downside of our voltage divider circuit is that it wastes battery power since there is current flowing through it to ground. That is why we want to use high value resistors like 150 kOhm to keep the current flowing through the voltage divider to a minimum. You may be asking yourself, why not use even higher value resistors like 10 MOhm and 1 MOhm? The reason is that the XBee ADC pin has a finite resistance value. When making an ADC measurement, the ADC pin presents a 1 MOhm resistance to the circuit it is connected to (this information was obtained from the XBee manual). That means when AD2 makes a measurement it is like putting a 1 MOhm resistor in parallel with the resistor closet to ground in the voltage divider circuit. If the resistor closet to ground had a resistance value close to 1 MOhm, the ADC measurement itself would essentially change the circuit and the resulting measurement would not be accurate. So for the voltage divider we want it to be high enough in resistance so that we do not waste too much battery life, but low enough so that the ADC input resistance does not have a drastic effect on measurement accuracy. With a total series resistance of 165 kOhm in our voltage divider it only consumes ~ 42 uA of current (7 V / 165 kOhm).
With the new power design and the need to monitor the battery voltage level we have to update our XBee router firmware and the Arduino Uno code. For the XBee routers the only change we want to make to the firmware is to set pin AD2 from disabled (0) to ADC (2), everything else for the routers will stay the same. With this change in the firmware the XBee routers will now make two ADC readings every 5 seconds (one on pin AD2 and one on pin AD3). Below is the updated code for the Arduino Uno.
/*This sketch 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 XBee routers. Each XBee router has an analog pin set to measure a temperature sensor and a second analog pin set to measure the voltage level of the battery power the XBee. This program receives the temperature readings from the two router XBees and writes the data to the serial monitor. It also monitors the battery voltage level, if the battery level falls below 6.2 V it signals the user via the serial monitor that the battery is low. This sketch is part of a tutorial on building a wireless sensor network, the tutorial can be found at http://forcetronic.blogspot.com/*/
/*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 XBee address*/
int addr1;
int addr2;
int addr3;
int addr4;
int sen3Counter = 0; //This counter variable is used print sensor 3 every 5 seconds
float batDead = 6.2; //battery pack voltage level where it needs to be replaced
void setup() {
Serial.begin(9600); //start the serial communication
}
void loop() {
if (Serial.available() >= 23) { // Wait for a full XBee frame to be ready
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(); }
//This if else statement is used to print the reading from sensor 3 once 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: ");
Serial.println(calculateTempF(calculateArduinoVolt(analogRead(A0))));
sen3Counter = 0; //reset counter back to zero
}
}
//This function takes XBee address and returns the identity of the Xbee that sent the temperature data
String indentifySensor(int a1, int a2, int a3, int a4) {
//These arrays are the unique 32 bit address of the two XBees in the network
int rout1[] = {0x40, 0xB0, 0xA3, 0xA6};
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 aMSBBat = Serial.read(); // Read the first analog byte of battery voltage level data
int aLSBBat = Serial.read(); // Read the second byte
int aMSBTemp = Serial.read(); // Read the first analog byte of temperature data
int aLSBTemp = Serial.read(); // Read the second byte
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 aMSBBat = Serial.read(); // Read the first analog byte of battery voltage level data
int aLSBBat = Serial.read(); // Read the second byte
int aMSBTemp = Serial.read(); // Read the first analog byte of temperature data
int aLSBTemp = Serial.read(); // Read the second byte
float voltTemp = calculateXBeeVolt(aMSBTemp, aLSBTemp); //Get XBee analog values and convert to voltage values
float voltBat = calculateBatVolt(aMSBBat, aLSBBat); //Get Xbee analog value and convert it to battery voltage level
float voltBat = calculateBatVolt(aMSBBat, aLSBBat); //Get Xbee analog value and convert it to battery voltage level
if(voltBat > batDead) { //This if else checks the battery voltage, if it is too low alert the user
Serial.println(indentifySensor(addr1,addr2,addr3,addr4)); //get identity of XBee and print the indentity
Serial.print("Temperature in F: ");
Serial.println(indentifySensor(addr1,addr2,addr3,addr4)); //get identity of XBee and print the indentity
Serial.print("Temperature in F: ");
Serial.println(calculateTempF(voltTemp)); //calculate temperature value from voltage value
}
else {
Serial.println(indentifySensor(addr1,addr2,addr3,addr4)); //get identity of XBee and print the indentity
Serial.print("Low battery voltage: ");
}
else {
Serial.println(indentifySensor(addr1,addr2,addr3,addr4)); //get identity of XBee and print the indentity
Serial.print("Low battery voltage: ");
Serial.println(voltBat); //print battery pack voltage level
}
}
}
delay(10); //delay to allow operations to complete
}
}
}
delay(10); //delay to allow operations to complete
//This if else statement is used to print the reading from sensor 3 once 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: ");
Serial.println(calculateTempF(calculateArduinoVolt(analogRead(A0))));
sen3Counter = 0; //reset counter back to zero
}
}
//This function takes XBee address and returns the identity of the Xbee that sent the temperature data
String indentifySensor(int a1, int a2, int a3, int a4) {
//These arrays are the unique 32 bit address of the two XBees in the network
int rout1[] = {0x40, 0xB0, 0xA3, 0xA6};
int rout2[] = {0x40, 0xB0, 0x87, 0x85};
if(a1==rout1[0] && a2==rout1[1] && a3==rout1[2] && a4==rout1[3]) {
return "Temperature from sensor 1:"; //temp data is from XBee one
}
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]) {
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
}
float calculateTempF(float v1) { //calculate temp in F from temp sensor
float temp = 0; //calculate temp in C, .75 volts is 25 C. 10mV per degree
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
}
float calculateTempF(float v1) { //calculate temp in F from temp sensor
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) {
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*(1.2/1023); //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;
}
//This function calculates the measured voltage of the battery powering the sensor
float calculateBatVolt(int aMSB, int aLSB) {
float mult = 10.0; //multiplier for calculating battery voltage
return (calculateXBeeVolt(aMSB, aLSB)*mult); //xbee volt x volt divider multiplier equals battery voltage
}
Notice from the code that the beginning of the data frame is the same the only difference is instead of just reading two bytes of analog data, we are now reading four (two readings). In the calculateBatVolt() function a multiplier of '10' is used to calculate the battery voltage, this multiplier is based on the ratio of the resisters used in the voltage divider circuit. A value of 6.2 V was used to represent a low battery level. This value was used to give a buffer zone between when you get the low battery voltage warning and when you were actually able to get to the battery and remove it from the circuit. Remember that even though you are getting a notification of a low battery, the battery is still being drained until you actually disconnect it from the circuit. As mentioned before over discharging a rechargeable battery can damage it. Below is a screen capture of a serial monitor showing our new power designs and code in action.
With sensor 1 and 2 being battery powered I spread them out over my house. Sensor 3, the controller, was placed near a drafty window hence the low reading. Notice that the sensor 2 battery voltage level drops below 6.2 V and signals the user of a low battery condition.
Well that is it for part 4, you now are equipped to add a power source, voltage conversion stage, and power source monitoring circuit to your temperature sensor designs. In part 5 we will add two new capabilities to our wireless temperature sensor network: internet monitoring and logging readings to non-volatile memory. To do this we will replace the Arduino Uno that we have been using in our controller with an Arduino Yun. 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.
float volt = (float)analogReading*(1.2/1023); //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;
}
//This function calculates the measured voltage of the battery powering the sensor
float calculateBatVolt(int aMSB, int aLSB) {
float mult = 10.0; //multiplier for calculating battery voltage
return (calculateXBeeVolt(aMSB, aLSB)*mult); //xbee volt x volt divider multiplier equals battery voltage
}
Notice from the code that the beginning of the data frame is the same the only difference is instead of just reading two bytes of analog data, we are now reading four (two readings). In the calculateBatVolt() function a multiplier of '10' is used to calculate the battery voltage, this multiplier is based on the ratio of the resisters used in the voltage divider circuit. A value of 6.2 V was used to represent a low battery level. This value was used to give a buffer zone between when you get the low battery voltage warning and when you were actually able to get to the battery and remove it from the circuit. Remember that even though you are getting a notification of a low battery, the battery is still being drained until you actually disconnect it from the circuit. As mentioned before over discharging a rechargeable battery can damage it. Below is a screen capture of a serial monitor showing our new power designs and code in action.
Well that is it for part 4, you now are equipped to add a power source, voltage conversion stage, and power source monitoring circuit to your temperature sensor designs. In part 5 we will add two new capabilities to our wireless temperature sensor network: internet monitoring and logging readings to non-volatile memory. To do this we will replace the Arduino Uno that we have been using in our controller with an Arduino Yun. 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.
Sunday, February 2, 2014
Building a Wireless Temperature Sensor Network Part 5
Welcome back for part 5 of building a wireless temperature sensor network. In part 3 and 4 we integrated a portable power source for each sensor design. In this post we will add internet monitoring and data logging capabilities to our temperature sensor network. To do this we will replace the Aruduino Uno in our sensor network controller with an Arduino Yun.
Out of all the Arduino's I have used the Arduino Yun is my favorite, because it can do so much! If you are not familiar with the Arduino Yun I encourage you to read up on it and its capabilities by clicking here. For this project we will be using the WiFi capability of the Yun to monitor our sensors over the internet and the microSD card drive to log our temperature data. If you are using a brand new Yun be sure to configure its WiFi before using it for this project, for instructions on configuring the Yun's WiFi click here. The Yun does not come with a microSD card so you will need to purchase one and plug it into the drive on the Yun for this project.
Unlike the Uno, the Yun does not have an onboard voltage regulator so we will need to add one to convert the output voltage of our DC power supply to 5 V for powering the Yun. For the voltage regulator I decided to use an LM7805C since it is low cost, outputs 5 V, and they sell it at the local Radio Shack. You could also use the LM317 regulator we used for the sensor 1 design, you would just need to tweek the output control resistors to get 5 V instead of 3.3 V at the output. Below you will find the updated schematic of the network controller with the Yun and LM7805C added in place of the Uno. Please note that you will need to use a heat sink with the voltage regulator to dissipate heat since the Yun has a microcontroller and a processor it can easily consume current levels > 300 mA.
![]() |
| Network Controller with Arduino Yun |
To add internet monitoring and data logging to our project as well as re-add serial communication to our project we will take advantage of the awesome libraries that are available for the Yun. Below is the sketch code for our project. Notice at the top of the sketch we are using five different libraries! The first is the Bridge library, this library is needed for the microcontroller to communicate with the processor on the Yun. The Bridge is needed to access a lot of the Yun's capabilities including communication over the internet. The YunServer and YunClient libraries are what we will use to communicate temperature data over the internet. These libraries use the TCP / IP protocol which uses a client / server model to communicate data. In this project the controller is the server and what ever device we use to connect and get the temperature data is the client. The SoftwareSerial library is for serial communication between the Yun and the XBee module (this capability was built-in to the Uno). The FileIO library is what we will use to log time stamped temperature data to our microSD card.
/*This sketch was written for the Arduino Yun. The Yun has an XBee Series 2 RF Module connected to it as a coordinator. The Yun uses the XBee coordinator to communicate with two XBee routers. Each XBee router has an analog pin set to measure a temperature sensor and a second analog pin set to measure the voltage level of the battery powering the XBee. This program receives the temperature readings from the two router XBees and allows the readings to be read over the internet and logs the temperature data to a microSD card. This sketch is part of a tutorial on building a wireless sensor network, the tutorial can be found at http://forcetronic.blogspot.com/*/
#include <Bridge.h> //needed for comm between microcontroller and proccesor on Yun
#include <YunServer.h> //needed for LAN or WiFi device to connect (Yun acts as server)
#include <YunClient.h> //use to manage connected clients
#include <SoftwareSerial.h> //Need to create serial comm with XBee (Yun does not have dedicated serial port like Uno)
#include <FileIO.h> //Used for logging data to a file stored on the microSD card
#define PORT 6666 //port number used to communicate temperature over the internet using TCP/IP
YunServer server(PORT); //create server object and set comm port
SoftwareSerial mySerial(10, 11); // Declare serial object and set serial comm pins (RX, TX)
int addr1; //variables to hold end point XBee address
int addr2; //Each address variable is two bytes
int addr3;//XBee address is 64 bits long but first 32 bits are common to all so just need last 32
int addr4;
String sen1Temp; //stores temperature value for XBee with sensor 1
String sen2Temp; //stores temperature value for XBee with sensor 2
String sen3Temp; //stores temperature value from Yun with sensor 3
int sen3Counter = 0; //This counter variable is used print sensor 3 every 5 seconds
float batDead = 6.2; //battery pack voltage level where it needs to be replaced
void setup() {
// put your setup code here, to run once:
mySerial.begin(9600); //Set the baud rate for serial communication
Bridge.begin(); //initiate the SPI based communication between the microcontroller and processor on the Yun
FileSystem.begin(); //Initializes the SD card and FileIO class
server.noListenOnLocalhost(); //Tells the server to begin listening for incoming connections
server.begin(); //Start the server so it is ready to take connections from clients
pinMode(13, OUTPUT); //set LED pin to output
digitalWrite(13, HIGH); //turn LED on so we know all setup is complete and is YUN is connected to WiFi
}
void loop() {
if (mySerial.available() >= 23) { // Wait for a full XBee frame to be ready
if (mySerial.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 = mySerial.read(); }
else if (i==9) { addr2 = mySerial.read(); }
else if (i==10) { addr3 = mySerial.read(); }
else if (i==11) { addr4 = mySerial.read(); }
else { byte discardByte = mySerial.read(); } //else throwout byte we don't need it
}
int aMSBBat = mySerial.read(); // Read the first analog byte of battery voltage level data
int aLSBBat = mySerial.read(); // Read the second byte
int aMSBTemp = mySerial.read(); // Read the first analog byte of temperature data
int aLSBTemp = mySerial.read(); // Read the second byte
float voltTemp = calculateXBeeVolt(aMSBTemp, aLSBTemp); //Get XBee analog values and convert to voltage values
float voltBat = calculateBatVolt(aMSBBat, aLSBBat); //Get Xbee analog value and convert it to battery voltage level
int id = indentifySensor(addr1,addr2,addr3,addr4); //save identity of sensor
if(voltBat > batDead) { //This if else statement checks the battery voltage, if it is too low alert the user
setAndLogSensorValue(id,calculateTempF(voltTemp),1); //set sensor string and log temperature only if battery is still good
}
else {
setAndLogSensorValue(id,voltBat,0); //set sensor string for low battery, temperature reading will not be logged
}
}
}
delay(10); //delay to allow operations to complete
//This if else statement is used to print the reading from sensor 3 once every ~5 second to match the XBee routers
//It uses the delay() function above to calculate 5 seconds. May need to tweek count in if statement to get 5 seconds
if (sen3Counter < 300) { sen3Counter++; }
else {
setAndLogSensorValue(3,calculateTempF(calculateArduinoVolt(analogRead(A0))),1);
sen3Counter = 0; //reset counter back to zero
}
YunClient client = server.accept(); //accept any client trying to connect
if(client.connected()){ //If we are connected to a client send identity and temperature data
if (sen1Temp.length()==0) { sen1Temp = "Empty value\n"; } //if string is empty, let client know
client.write((uint8_t*)&sen1Temp[0], sen1Temp.length()); //send sensor 1 temp or low battery warning
if (sen2Temp.length() == 0) { sen2Temp = "Empty value\n"; } //if string is empty, let client know
client.write((uint8_t*)&sen2Temp[0], sen2Temp.length()); //Send sensor 2 temp or low battery warning
if (sen3Temp.length() == 0) { sen3Temp = "Empty value\n"; } //if string is empty, let client know
client.write((uint8_t*)&sen3Temp[0], sen3Temp.length()); //Send sensor 3 temp
client.stop(); //disconnect from client
}
}
//This function takes in the XBee address and returns the identity of the Xbee that sent the temperature data
int indentifySensor(int a1, int a2, int a3, int a4) {
//These arrays are the unique 32 bit address of the two XBees in the network
int rout1[] = {0x40, 0xB0, 0xA3, 0xA6};
int rout2[] = {0x40, 0xB0, 0x87, 0x85};
if(a1==rout1[0] && a2==rout1[1] && a3==rout1[2] && a4==rout1[3]) {
return 1; //temp data is from XBee or sensor one
}
else if(a1==rout2[0] && a2==rout2[1] && a3==rout2[2] && a4==rout2[3]) {
return 2; } //temp data is from XBee or sensor two
else { return -1; } //Data is from an unknown XBee
}
float calculateTempF(float v1) { //calculate temp in F from temp sensor
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*(1.2/1023); //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;
}
//This function calculates the measured voltage of the battery powering the sensor
float calculateBatVolt(int aMSB, int aLSB) {
float mult = 10.0; //multiplier for calculating battery voltage
return (calculateXBeeVolt(aMSB, aLSB)*mult); //xbee voltage x voltage divider multiplier equals battery voltage
}
//This function builds the temperature strings that are communicated over the internet and logs time stamped temperature data to file on
//microSD card
void setAndLogSensorValue(int sen, float val, int temp) {
String dataString = getTimeStamp() + " "; //get time info and append space to the end
if (sen == 1) {
if (temp == 1) {
sen1Temp = "Sensor 1 temperature: " + String(val) + "\n";
dataString += sen1Temp;
writeToFile(dataString); //write temp value to file
}
else { sen1Temp = "Sensor 1 low bat volt: " + String(val) + "\n"; }
}
else if (sen == 2) {
if (temp == 1) {
sen2Temp = "Sensor 2 temperature: " + String(val) + "\n";
dataString += sen2Temp;
writeToFile(dataString); //write temp value to file
}
else { sen2Temp = "Sensor 2 low bat volt: " + String(val) + "\n";}
}
else {
sen3Temp = "Sensor 3 temperature: " + String(val) + "\n";
dataString += sen3Temp;
writeToFile(dataString); //write temp value to file
}
}
// This function return a string with the time stamp
String getTimeStamp() {
String result;
Process time;
// date is a command line utility to get the date and the time
// in different formats depending on the additional parameter
time.begin("date");
time.addParameter("+%D-%T"); // parameters: D for the complete date mm/dd/yy
// T for the time hh:mm:ss
time.run(); // run the command
// read the output of the command
while(time.available()>0) {
char c = time.read();
if(c != '\n')
result += c;
}
return result;
}
//This function writes data to a file called TempData on the microSD card
void writeToFile(String data) {
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
// The FileSystem card is mounted at the following "/mnt/FileSystema1"
File dataFile = FileSystem.open("/mnt/sd/TempData.txt", FILE_APPEND);
// if the file is available, write to it:
if (dataFile) {
dataFile.println(data);
dataFile.close();
}
// if the file isn't open then you could signal an error here
else { }
}
The code for the Arduino Yun sketch is well commented, but let's highlight certain areas and add some explanation:
- A server object is created in the setup function and in each iteration of the main loop it checks if there is a client device that wants to connect. If there is a client trying to connect the server accepts it, sends the latest temperature data from each sensor, and closes the connection. In the TCP / IP protocol you have to choose a port number to communicate over. We will use Port 6666 for our temperature network communication.
- Each temperature sensor reading is time stamped and stored in the TempData.txt file. If the TempData.txt file does not exist on the microSD card, it will be created. If the file does already exist it will append the new data to any existing data in the file. If sensor 1 or 2's battery gets too low that sensor's temperature reading is no longer stored in the TempData.txt file.
- Since the Yun is doing complex operations like writing to a file, reading data from the XBee, and sending readings over the internet it is not as easy as it was earlier in the project to sync sensor 3 to the same timing intervals as sensors 1 and 2. You may need to adjust the sensor 3 delay loop a bit to get the timing just right.
- When you "Verify" this sketch you will notice that the code takes up approximately 84% of program memory space in the microcontroller so be sure to carefully optimize if you need to add extra capabilities, more sensors, or more extensive error checking to this code.
The easiest way to test the internet monitoring capability of our project without having to write a program is to use a Terminal and the "netcat" command. If you have a Linux based computer or a Mac you have a Terminal. For Mac's the Terminal is located in the Mac HD --> Applications --> Utility directory. The netcat command allows you to communicate with another internet connected device, such as the Yun, using the TCP / IP protocol. To connect to the Yun using the nc command (short for netcat) we need to know the Yun's IP address and the port we want to communicate over. We know the port from our sketch (6666). If the Yun is powered on and connected to your router its IP address can be obtained from the Arduino IDE, just go to Tools --> Port. You can also obtained the IP address of the Yun by going to the Device Table of the internet router you are using (see router's instruction manual to access Device Table). To connect to the Yun with your PC Terminal use the following command sequence: nc <Yun IP Addres> 6666
Please note for this to work your computer has to be connected to the same local network or router that your Yun is connected to. For instance if you are in your home, the Yun and your computer both should be connected to your home internet router. If you are using a Windows computer and following along to this project, there is an open source netcat.exe program out there on the web. You can download it at http://nmap.org/ncat/ . I have not had the chance to try it out.
Refer to the image below of a Terminal showing the our updated project in action. You can see a connection is made, temperature data is sent to the Terminal, and then the connection is closed. From the Terminal image you can see a connection was made and temperature data was fetched three times. For this example a variable power supply was used to simulate sensor 2's battery getting low, this is detected and the user is notified through the Terminal.
Next let's look at an example data log file. This file came from the same example as the above terminal image so we would expect to see sensor 2 temperature readings to stopped being logged when its battery gets too low. From the below TempData.txt file you can see the date and time of the sensor reading is captured. If you scroll to the bottom you will notice we no longer see readings from sensor 2. This is due to its battery voltage becoming too low so the Yun no longer logs temperature data from it.
That is all for part 5 of building a wireless temperature sensor network. In part 6, the final post of this project, we will look at how to access our temperature sensor network data outside of the router or local network it is connected to. We will also look at how to access our sensor network from an iOS device, such as an iPhone or iPad. If you have any questions or comments on what was covered in this post use the Comments section below or feel free to email me at forcetronics@gmail.com.
Subscribe to:
Posts (Atom)




