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.


Saturday, February 1, 2014

Building a Wireless Temperature Sensor Network Part 6

Welcome back for part 6, the final post in the building a wireless temperature sensor network saga. In part 5 we added internet monitoring and data logging capability to our temperature sensor network using the Arduino Yun. In this post we will discuss how we can access our wireless sensor network from behind a router firewall, giving us the capability to access it from across the street to across the globe. We will also add the ability to monitor our wireless temperature sensor network from an iOS device such as an iPhone or iPad, which gives us even further flexibility to access our temperature sensor network.

By using the ubiquitous TCP / IP protocol to communicate out the temperature data we have the ability to monitor it from almost anywhere. The roadblock we run into is the firewall in the WiFi router that the Arduino Yun is connected to restricts us to only connecting to the sensor network from within the router's local WiFi / Ethernet network. A basic home WiFi router's default configuration assumes all the devices connected to its local network are clients and will be the ones to initiate communication with a device outside the local network. Because of that any device outside of router's local network that tries to initiate communication with a device inside the router's local network must have malicious intensions. The problem is the Arduino Yun is configured as a server, but the router does not know that. To change this you would need to go into your routers settings and poke a hole in the firewall. The router's settings can typically be accessed through its web page. Refer to your router's user manual for the address to access the web page. Once you get access to your router's settings you have two options for poking a hole in the firewall to access the Yun, Port Forwarding and DMZ. Port Forwarding, which is the safer of the two but is a little more complex to setup, requires you know the TCP / IP communication port that you plan to communicate through (we used 6666 in our Arduino code) and the IP address of the Yun. DMZ just requires you to know the IP address of the Yun. With DMZ set for a specific device or IP address on its local network the router then sends any incoming communication requests from outside the firewall to the specified IP address, regardless of the port. Before moving on there are two other things you should be aware of when trying to connect to your temperature sensor network over the internet:
  1. When outside of your router's firewall you need to use the IP address of the router to connect to the Yun, not the local IP address of the Yun that the router assigns to it. To get the IP address of your router just search "My IP Address" on Google and it will show up in the results. 
  2. Your router will not assign a permanent IP address to the Yun, it will change once and awhile with no warning. To avoid this hassle go into your router settings and assign it a static IP address (an address that will not change).
I know that was a lot of information to swallow if you are not familiar with the Ethernet networks, but if you get stuck just search on the internet. There is plenty of information out there on the topics we just covered. 

To connect to and fetch temperature data from our wireless sensor network from an iOS device we will make some slight changes to our Arduino Yun code presented in the last post (part 5). Instead of posting the whole sketch again, you will just find the two sections of the code where changes were.

//The global strings for storing latest temperature reading are initialized with the string "Empty"
String sen1Temp = "Empty"; //stores temperature value for XBee with sensor 1
String sen2Temp = "Empty"; //stores temperature value for XBee with sensor 2
String sen3Temp = "Empty"; //stores temperature value from Yun with sensor 3


/*The portion of the code where we accept the client connection and send the data has been simplified since now we are only sending one string of data, instead of three separate strings. We will let the iOS code parse the string into three separate values*/
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
    String temp = sen1Temp + "," + sen2Temp + "," + sen3Temp + "\n";
    
    client.write((uint8_t*)&temp[0], temp.length()); //send sensor 1 temp or low battery warning
    client.stop(); //disconnect from client
  }

/*Lastly this function was changed to simplify the three global strings that hold the data that we will send out*/
void setAndLogSensorValue(int sen, float val, int temp) {
  String dataString = getTimeStamp() + " "; //get time info and append space to the end
  
  if (sen == 1) { //sensor 1
    if (temp == 1) {
      sen1Temp = String(val); //store latest temp value in global string
      dataString += "Sensor 1 temperature: " + sen1Temp + "\n"; //build datalog string
     writeToFile(dataString); //write temp value to file 
    }
    else { sen1Temp = "Low Battery"; }
  }
  else if (sen == 2) { //sensor 2
    if (temp == 1) {
      sen2Temp = String(val); //store latest temp value in global string
      dataString += "Sensor 2 temperature: " + sen2Temp + "\n"; //build datalog string
      writeToFile(dataString); //write temp value to file
    }
    else { sen2Temp = "Low Battery";}
  }
  else {
      sen3Temp = String(val); //store latest temp value in global string
      dataString += "Sensor 3 temperature: " + sen3Temp+ "\n"; //build datalog string
      writeToFile(dataString); //write temp value to file
  }
}

To create apps for the iOS you need XCode, which is Apple's software development environment and it only works on Macs. XCode uses the Objective C programming language which is based off of C but its syntax is vastly different (in my opinion). XCode is free to download and you can test your iOS code on an iPhone or iPad software simulator that runs on your Mac. To actually put your code on a physical iPhone or iPad or to post an app on the App Store you need to pay for a $99 per year subscription. For this project a simple iOS app called "SensorMonitor" was put together with a basic user interface for example purposes. Before we discuss the code, below is a screen grab of the SensorMonitor iOS app that connects to the wireless sensor network and fetches the temperature data. The IP address of the wireless network is typed into the text box at the top of the app. When the "Get Temperature Data" button is pushed the app connects to the sensor network, receives the temperature data, and prints the temperature next to the corresponding sensor label. Below on the left is a screen shot of the SensorMonitor app before the button was pushed to retrieve the temperature data. On the right is a screen shot of the app after the button was pushed and the temperature data is displayed next to the corresponding sensor label.  


Even for a simple iOS app like SensorMonitor there is too much code and different files to display and cover in this post. What is shown below is the "IBAction" function from the app's code. The IBAction function is called when the "Get Temperature Data" button is pressed, the function name is "goConnect." The goConnect function is commented to explain what is happening at each line. 

//This function is called when the "Get Temperature Data" button on the user interface is pressed
-(IBAction)goConnect { //connect button action method
    NSError *hError; //declare variable to store error info
    NSString *tempVal; //string to hold temperature data from Yun
    
    if (![io openWithAddress:self.ipTextField.text port:6666 error:&hError]) { //connect to Arduino Yun on port 6666
        //Connection failed print the error to the message label
        self.message.text = [[NSString alloc] initWithString:[hError localizedDescription]]; //display error if connection failed
        [io closeWithError:&hError]; //if already connected and button was pressed, disconnect from Yun
    }
    else { //the connection was a success
        //get sensor 1 temperature data
        if(![io scan:&tempVal trimNewLine:YES error:&hError]) { //read the first sensor value
            self.message.text = [[NSString alloc] initWithString:[hError localizedDescription]]; //if error occurred display error details
            self.sen1.text = @"Read Failed"; //temp data read failed so tell user
        }
        else {
            self.sen1.text = tempVal;
            NSArray *tempData = [tempVal componentsSeparatedByString:@","];
            //self.sen2.text = [tempData objectAtIndex:1];
            self.sen1.text = [@"Sensor 1: " stringByAppendingString:[tempData objectAtIndex:0]]; //Add the sensor 1 temperature data to the Sensor 1 label
            self.sen2.text = [@"Sensor 2: " stringByAppendingString:[tempData objectAtIndex:1]]; //set sensor 2 label to the temperature value just read
            self.sen3.text = [@"Sensor 3: " stringByAppendingString:[tempData objectAtIndex:2]]; //set sensor 3 label to the temperature value just read
    
            self.message.text = @" "; //There were no errors so leave message blank
        }
        [io closeWithError:&hError]; //if already connected and button was pressed, disconnect from Yun
    }
}

If you are interested in accessing the SensorConnect app's code in full just email me at forcetronics@gmail.com and I will send it to you. The value of the SensorConnect app to someone who wants to monitor a sensor network with an iOS device is the code that does the communication with the Yun using TCP / IP protocol. Myself and a colleague of mine created an Objective C API made up of three classes to communicate over Ethernet. In the above function, the "io" object is a handle to the API for connection and communication with the Arduino Yun.

In this 6 part project we went over how to build your own wireless temperature sensor network that included features such as:
  • Temperature data communicated using Zigbee protocol
  • Battery powered sensors
  • Time stamped temperature data logging
  • Monitoring over the internet using an iOS device
This project was meant to serve as a foundation or starting point for you to build your own custom temperature sensor or any type of sensor network. If you found any of the information in this project useful please click on one of the ads in this blog to help fund future projects. If you have any comments or questions related to this post or this project use the Comment section below or send me an email at forcetronics@gmail.com. See you back soon for the next project!