Showing posts with label wireless sensor network. Show all posts
Showing posts with label wireless sensor network. Show all posts

Wednesday, September 26, 2018

Easy Way to Create a Wireless Sensor Network

In this video we look at an easy way with not very much code to setup a wireless network using the nRF24L01 Transceiver and Arduino.



//***************************Master or Receiver code*****************
/*This code was used for a video tutorial on the ForceTronics YouTube Channel
 * This code is free and open for anybody to use and modify at your own risk
*/

#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/

const uint8_t pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const uint8_t pinCSN = 10; //This pin is used for SPI comm chip select
RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) 
const uint64_t rAddress = 0xB00B1E50C3LL;  //Create pipe address for the network and notice I spelled boobies because I am mature, the "LL" is for LongLong type
const uint8_t rFChan = 89; //Set channel frequency default (chan 84 is 2.484GHz to 2.489GHz)

//Create a structure to hold fake sensor data and channel data
struct PayLoad {
  uint8_t chan;
  uint8_t sensor;
};

PayLoad payload; //create struct object

void setup() {
  wirelessSPI.begin();  //Start the nRF24 module
  wirelessSPI.setChannel(rFChan); //set communication frequency channel
  wirelessSPI.openReadingPipe(1,rAddress);  //This is receiver or master so we need to be ready to read data from transmitters
  wirelessSPI.startListening();    // Start listening for messages
  Serial.begin(115200);  //serial port to display received data
  Serial.println("Network master is online...");
}

void loop() {
  if(wirelessSPI.available()){ //Check if recieved data
     wirelessSPI.read(&payload, sizeof(payload)); //read packet of data and store it in struct object
     Serial.print("Received data packet from node: ");
     Serial.println(payload.chan); //print node number or channel
     Serial.print("Node sensor value is: ");
     Serial.println(payload.sensor); //print node's sensor value
     Serial.println(); 
  }
}

//***************************Node or Transmitter code*****************
/*This code was used for a video tutorial on the ForceTronics YouTube Channel
 * This code is free and open for anybody to use and modify at your own risk
*/

#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/

const uint8_t pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const uint8_t pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command
RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) 
const uint64_t wAddress = 0xB00B1E50C3LL;  //Create pipe address to send data, the "LL" is for LongLong type
const uint8_t rFChan = 89; //Set channel default (chan 84 is 2.484GHz to 2.489GHz)
const uint8_t rDelay = 7; //this is based on 250us increments, 0 is 250us so 7 is 2 ms
const uint8_t rNum = 5; //number of retries that will be attempted 
const uint8_t chan1 = 2; //D2 pin for node channel check
const uint8_t chan2 = 3; //D3 pin for node channel check
const uint8_t chan3 = 4; //D4 pin for node channel check

//stuct of payload to send fake sensor data and node channel
struct PayLoad {
  uint8_t chan;
  uint8_t sensor;
};

PayLoad payload; //create struct object

void setup() {
  pinMode(chan1,INPUT_PULLUP); //set channel select digital pins to input pullup
  pinMode(chan2,INPUT_PULLUP);
  pinMode(chan3,INPUT_PULLUP);
  wirelessSPI.begin();  //Start the nRF24 module
  wirelessSPI.setChannel(rFChan); 
  wirelessSPI.setRetries(rDelay,rNum); //if a transmit fails to reach receiver (no ack packet) then this sets retry attempts and delay between retries   
  wirelessSPI.openWritingPipe(wAddress); //open writing or transmit pipe
  wirelessSPI.stopListening(); //go into transmit mode
  randomSeed(analogRead(0)); //set random seed for fake sensor data
  setChannel();  //checks current channel setting for transceiver
}

void loop() {
  delay(3000); //send data every 3 seconds
  payload.sensor = random(0,255); //get made up sensor value
  if (!wirelessSPI.write(&payload, sizeof(payload))){  //send data and remember it will retry if it fails
    delay(random(5,20)); //as another back up, delay for a random amount of time and try again
    if (!wirelessSPI.write(&payload, sizeof(payload))){
      //set error flag if it fails again
    }
  }

}

//check for low digital pin to set node address
void setChannel() {
  if(!digitalRead(chan1)) payload.chan = 1;
  else if(!digitalRead(chan2)) payload.chan = 2;
  else if(!digitalRead(chan3)) payload.chan = 3;
  else payload.chan = 0;
}

Thursday, August 10, 2017

Getting Started with Arduino and the ThingSpeak Cloud

In this video we look at how to use Arduino with the ThingSpeak IoT, cloud, and analytics platform. In the video we look at two use cases of ThingSpeak. For the first use case we log wireless sensor data to the cloud and perform post processing on the data using MATLAB. In the second use case we monitor a room with a wireless motion detector setup and have ThingSpeak send out a Tweet if any movement is detected in the room.



//*****************Arduino code from video**********************************
/*
This sketch was created for a video tutorial on the ForceTronics YouTube that shows how to use Arduino with the ThingSpeak cloud
This code is public domain and free for anybody to use or modify at their own risk

Note this code was leveraged from:
 Arduino --> ThingSpeak Channel via MKR1000 Wi-Fi
 Created: May 7, 2016 by Hans Scharler (http://www.nothans.com)
*/
   
#include <SPI.h>
#include <WiFi101.h> //This sketch should work with any Arduino or shield that can use the WiFi101 library

char ssid[] = "YourNetwork"; //  your network SSID (name)
char pass[] = "YourPassword"; // your network password

int status = WL_IDLE_STATUS;

// Initialize the Wifi clients
WiFiClient tClient; //this one is used to post temperature data
WiFiClient mClient; //this one is used to post motion detection data

// ThingSpeak Settings
char server[] = "api.thingspeak.com";
String writeAPIKey = "YourWriteKey";

//Timing variables for tracking temperature posting
unsigned long tempLastConnectionTime = 0; // track the last connection time
const unsigned long tempPostingInterval = 295000L; // post temp ADC value just under every 5 min

//Timing and logic variables for tracking temperature posting
unsigned long mLastConnectionTime = 0; // track the last connection time
const unsigned long mPostingInterval = 30000L; //checks to see if motion was detected every 30 sec
bool mReset = true; //tracks if motion detection has been reset

void setup() {
  pinMode(2,INPUT); //digital pin used to read output of motion detector
  pinMode(6,OUTPUT); //LED pin used to know when data is sent to cloud
  
  // attempt to connect to Wifi network
  while ( status != WL_CONNECTED) {
    // Connect to WPA/WPA2 Wi-Fi network
    status = WiFi.begin(ssid, pass);
    // wait 10 seconds for connection
    delay(10000);
  }
}

void loop() {
  //timer to know when it is time to post temp data to cloud
  if (millis() - tempLastConnectionTime > tempPostingInterval) {
    digitalWrite(6,HIGH); //Use the LED to see if Arduino gets heldup posting data to the cloud
    tempHttpRequest(); //function that formats data strings and posts data to ThingSpeak cloud
    digitalWrite(6,LOW);
  }

  if (millis() - mLastConnectionTime > mPostingInterval) {
    digitalWrite(6,HIGH); //Use the LED to see if Arduino gets heldup posting data to the cloud
    if(digitalRead(2) && mReset) { //if motion was detected post it
      mReset = false; //motion detected so reset variable
      mHttpRequest(); //if true motion was detected so post to cloud
    }
    else mReset = true; //reset logic tracker
    
    digitalWrite(6,LOW);
  }

}

//Posts temp data to thingspeak cloud
void tempHttpRequest() {
  // read analog pin 0 with temp sensor connected
  int sensorValue = analogRead(0);

  // create data string to send to ThingSpeak
 String data = String("field1=" + String(sensorValue, DEC)); 

  // POST data to ThingSpeak
  if (tClient.connect(server, 80)) {
    tClient.println("POST /update HTTP/1.1");
    tClient.println("Host: api.thingspeak.com");
    tClient.println("Connection: close");
    tClient.println("User-Agent: ArduinoWiFi/1.1");
    tClient.println("X-THINGSPEAKAPIKEY: "+writeAPIKey);
    tClient.println("Content-Type: application/x-www-form-urlencoded");
    tClient.print("Content-Length: ");
    tClient.print(data.length());
    tClient.print("\n\n");
    tClient.print(data);
     // close any connection before sending a new request
    tClient.stop();
    // note the last connection time
    tempLastConnectionTime = millis();
  }
}

//posts motion detection data to thingspeak cloud
void mHttpRequest() {

  // create data string to send to ThingSpeak, we always send a one here to indicate motion detected
 String data = String("field2=" + String(1,DEC)); 

  // POST data to ThingSpeak
  if (mClient.connect(server, 80)) {
    mClient.println("POST /update HTTP/1.1");
    mClient.println("Host: api.thingspeak.com");
    mClient.println("Connection: close");
    mClient.println("User-Agent: ArduinoWiFi/1.1");
    mClient.println("X-THINGSPEAKAPIKEY: "+writeAPIKey);
    mClient.println("Content-Type: application/x-www-form-urlencoded");
    mClient.print("Content-Length: ");
    mClient.print(data.length());
    mClient.print("\n\n");
    mClient.print(data);
     // close any connection before sending a new request
    mClient.stop();
    // note the last connection time
    mLastConnectionTime = millis();
  }
}

%*************************MATLAB code from video***************************
% Channel ID to read raw ADC temp data from
readChannelID = chanID;
% Temperature Field ID
TemperatureFieldID = 1;

% Channel Read API Key 
readAPIKey = 'YourReadKey';

% Channel ID to write temp data to:
writeChannelID = [chanID];
% API key for write channel:
writeAPIKey = 'YourWriteKey';

%Read raw ADC temp data
aDC = thingSpeakRead(readChannelID, 'Fields', TemperatureFieldID, 'ReadKey', readAPIKey);

%Convert raw 10 bit ADC data to voltage
volt = aDC*(3.3/1023);

%Using analog temp sensor TMP36
%calculate temp in C, .75 volts is 25 C. 10mV per degree
 if volt < .75 
     temp = 25 - ((.75-volt)/.01); %if below 25 C
 elseif volt == .75 
         temp = 25;
 else 
     temp = 25 + ((volt -.75)/.01);  %if above 25
 end

% Convert to Fahrenheit
tempF = (9/5*temp) + 32;

%This writes temp value to below console for debugging
display(tempF);

%write temp F value to channel
thingSpeakWrite(writeChannelID, 'Fields',1,'Values',tempF, 'Writekey', writeAPIKey);

Tuesday, March 15, 2016

Building a Wireless Sensor Network with the nRF24L01 Part 5

In Part 5 of building a wireless sensor network with Arduino and the nRF24L01+ transceiver we take a look at our brand new PCB boards and look at the code for adding the DS18S20 and the STTS751 temperature sensors to the design. You can access the PCB Eagle files and the Arduino code from GitHub: https://github.com/ForceTronics/nRF24L01_Wireless_Sensor_Dev_Board






Wednesday, January 20, 2016

Building a Wireless Sensor Network with the nRF24L01 Part 4

In part 4 of Building a Wireless Sensor Network with the nRF24L01 we take a look at the design's PCB layout in Eagle software as well as cover some software and hardware updates to the design.


You can access the updated code and PCB files from GitHub: https://github.com/ForceTronics/nRF24L01_Wireless_Sensor_Dev_Board


Wednesday, January 6, 2016

Building a Wireless Sensor Network with the nRF24L01 Part 3

In part three we take a look at the updated hardware schematic of the router / end device design, how the router / end device settings work, and we go over the initial software of the router / end device. You can find the code from this video in GitHub at https://github.com/ForceTronics/nRF24L01_Wireless_Sensor_Dev_Board


Tuesday, December 22, 2015

Building a Wireless Sensor Network with the nRF24L01 Part 2

In part 2 we focus on powering our wireless sensor node. We talk about batteries, battery sizing, estimating battery life, and battery monitoring. If you have any feedback or questions use the comments section below.


Updated Schematic for Part 2


Monday, December 7, 2015

Building a Wireless Sensor Network with the nRF24L01 Part 1

This is part 1 in a series where we look at how to build a large wireless network using Arduino and the nRF24L01+ Transceiver Modules. At the end of this series you will have a reference design for a wireless sensor development board and the code needed to turn the wireless sensor developments boards into a network. You will be able purchase all the hardware for this project at my site: www.forcetronics.com


Initial Hardware Design

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!


Tuesday, February 4, 2014

Building a Wireless Temperature Sensor Network Part 3

Welcome back for part 3 of building a wireless temperature sensor network. In part 2 we started to build our sensor network and collect temperature data from multiple sensors. In this post (part 3) and continuing into the next post (part 4) we will look at some design options for powering our wireless sensors so we can spread them out to anywhere we need to monitor the temperature.

The two power source options we will consider for powering our wireless sensors are battery power and an AC line powered DC power supply. What I mean by “AC line powered DC power supply” is the low cost constant voltage power supplies that often come with consumer electronics or that can be bought standalone from places like Radio Shack. In the below picture is an example of low cost DC power supply that puts out a constant 12 VDC and up to 200 mA.


Before deciding on a power source, lets first cover the need for a voltage conversion stage in our sensor designs. Whether we use a power supply or a battery pack they probably will not output the exact voltage we need for the sensors, which is 5 V for the controller and approximately 3.3 V for the routers hence the need for a voltage conversion stage. For doing voltage conversion there are two main options available: voltage regulators and DC to DC converters. Each has their advantages and disadvantages. Voltage regulators are low cost and less complex, but are less efficient (more power loss in the conversion process). DC to DC converters are more efficient, but are more complex to implement and can be more expensive. Let me add explanation around the complexity of DC to DC converters. When you implement a DC to DC converter you need to add a lot of “supporting components” (resistors, capacitors, inductors) at the input and output of the DC to DC converter to have it operate correctly, which is beyond the scope of this post. But there is a way around it, some companies sell DC to DC converters with the “supporting components” already built-in. These DC to DC converters are easy to implement like voltage regulators, but still deliver great efficiency. Their downside is cost, they will easily be 5 or more times the price of a voltage regulator.

Which voltage conversion method you use in your design depends on factors such as power efficiency needs, budget, project time table. For instance, is power efficiency critical to your design? If your sensor needs to be battery powered and it is going to be located in a place where it can not be easily accessed than you probably want to go with a DC to DC converter. If your sensor will be near a power outlet or if changing the battery and recharging it is easy than the voltage regulator is a good choice. In this tutorial we will us both conversion methods for example purposes.

If you want to power your sensors using battery power the most common battery technologies for this task include alkaline, nickel metal hydride (NiMH), lithium ion (Li-ion), and lithium ion polymer (Li-ion polymer). Alkaline batteries are the ones you buy at the store for your TV remote or flashlight, they are typically not rechargeable. The other battery technologies listed above are rechargeable and have their advantages and disadvantages. Covering the details of each battery technology and their safety precautions is beyond the scope of this tutorial. Luckily there is plenty of information on the internet on these battery technologies and how to use them safely so if you plan to use batteries to power your sensor do your homework first. Especially if you want to use the Li-ion or Li-ion polymer technologies because they are the most dangerous. For this tutorial we will use two different battery technologies for example purposes, alkaline and Li-ion polymer.

Here is a breakdown of the power source and voltage conversion technique that will be used for each part of the wireless sensor network:
  • For the controller the Arduino Uno has a built-in voltage regulator at the power input connector that takes 7 to 12 VDC power source and converts it to 5 V to power the Uno. A 9 V DC power supply will be used to power the controller. The power supply was obtained from an old cordless phone.
  • For sensor 1 (XBee router with temperature sensor) we will use the LM317 voltage regulator for the conversion stage. This is a common low cost regulator that has an adjustable output voltage. You can find it at RadioShack. The datasheet, which you can easily find online, explains how to set the output voltage using basic components. For instructions on how I set the output voltage to 3.3 V for this project see the last section of this post. The power source we will use is a 7.4 volt Li-ion polymer rechargeable battery pack. 
  • For sensor 2 we will use the TSR12433 DC to DC converter that requires no external components, just plug in and go. This was $15 and it was purchased from Adafruit. The power source we will use is four series Alkaline batteries. You could easily get away with three series Alkaline batteries, four was used because I had a holder for four batteries.
What is nice about the XBee modules is they use very low power in general so with the power setups we are using we are going to get good battery life. Let's do some quick calculations to look at what kind of battery life we can expect with these two battery powered setups. To do this let's first look at the amp hour (abbreviated 'Ah') ratings of our two battery based power sources:
  1. The 7.4 V Li-ion Polymer is rated for 2200 mAh or 2.2 Ah.
  2. The Ah ratings for Alkaline batteries varies widely depending on the brand, a good rule of thumb is the higher the cost the more Ah you are getting. We will use the conservative estimate of 1500 mAh or 1.5 Ah for each battery so a total of 6000 mAh or 6 Ah
Next for our battery life calculation we need to know the average current consumption of sensor 1 and sensor 2. We need to know on average how much current does the XBee router, temperature sensor, and power conversion stage consume. To do this I measured the current consumption of each sensor design using an Agilent N6705B DC power analyzer. I am fortunate enough to have access to this device at work since it is out of the price range of most hobbyists. For sensor 1 the average current consumption was 15.59 mA, for simplicity we will round up to 16 mA. For sensor 2 the average current consumption was 6.58 mA, for simplicity we will round up to 7 mA. Please note that using the regulator more than doubles the current consumption of our sensor. This results in the following battery life calculations:
  1. Sensor 1 --> 2200 mAh / 16 mA = 137.5 hours of battery life or ~ 6 days
  2. Sensor 2 --> 6000 mAh / 7 mA = 857.1 hours of battery life or ~ 36 days
The lower current consumption of the DC to DC converter combined with the higher Ah rating of the Alkaline batteries results in ~ 6 times longer battery life for sensor 1 compared to sensor 2. This is the power design direction you would want to go with if your sensor is going to be used in an area that is not easy to access. The sensor 2's power design is a lower cost example since the regulator is cheap and you do not have to keep buying new batteries since the Li-ion Polymer is rechargeable. 

As a comparison of the sensor 1 and 2's current consumption compared to the controller's (sensor 3), the controller's average current consumption is 102.6 mA. As you can see the Arduino consumes quite a bit more power compared to the XBee. You can further increase the battery life of your sensors by using the "sleep" capabilities of the XBee modules. We will not be getting into the details of the sleep capabilities of the XBee modules in this project, but refer to the XBee manual for details on using the sleep capabilities. 

That is it for part 3 of building a wireless sensor network. We will see you back soon for part 4 where we will look at a schematic diagram of our sensors with the new power sections added, we will implement a way to know when our batteries need to be replaced, and make the needed changes to our Arduino code to accommodate these changes. 

Setting the output of the LM317 voltage regulator
The LM317 is a widely used low cost regulator. It is great part to keep in stock around you electronics lab bench since its output voltage level is adjustable so you can fit it into any project you are working on. It is made by a couple different manufacturers including Fairchild, you can find its data sheet by following the link: http://www.fairchildsemi.com/ds/LM/LM317.pdf. The figure below is from page 5 of the datasheet and it shows the circuit setup and equation to set the output voltage level to the value you desire.


From the above equation we need to solve for R2 because that is the unknown. Also since IADJ is so small we can just take the last term out of the equation. Solving for R2 and dropping out the last term we get:
R2 = (Vo*R1)/1.25 - R1. Now we just need to plug in our known values which are 3.3 V for Vo and for R1 I used a 301 Ohm resistor that was laying around. plugging in our values to the equation we get 494 Ohms for R2. To implement R2 a 1 kOhm adjustable resistor was used. The resistor was adjusted to about ~500 Ohms. I then connected its input to the battery and its output to the sensor. I used a DMM to measure the resulting output voltage of the regulator and fine tuned the resistor value to get exactly 3.3 VDC, the desired output voltage.

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.

Sensor 1 and 2 with power design 
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
       

       //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 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
     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(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: ");
        Serial.println(voltBat); //print battery pack voltage level
     }
   }
}

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
   }
   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
    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 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.

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
The Yun does not have dedicated serial communication capabilities like the Uno so we will have to add serial communication capability in our sketch code to be able to communicate with the XBee module. The benefit of not having dedicated serial communication pins is we can now use any digital pins on the Arduino as our communication pins, instead of always having to use D0 and D1. Notice from the figure that we are using D10 as the receive pin and D11 as the transmit pin.

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:

  1. 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.
  2. 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. 
  3. 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. 
  4. 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.