Arduino Tutorial: Temperature Sensor

Tony Dahbura Tony Dahbura
Monitor the temperature around you from your iPhone!

Monitor the temperature around you from your iPhone!

If you’re like me, every morning before you leave for work you wonder whether you should wear a jacket before heading outside. You might not have time to check outside your front door, but you always have time to check your iPhone! :]

The problem is, standard weather apps tell you about the temperature in a nearby city or suburb – but not necessarily about extremely local conditions. And what about that aquarium you would like to monitor, or your mini indoor greenhouse?

This is a job for the latest open source microcontroller – the Arduino.

In this tutorial, you’ll build an Arduino project for the iPhone that lets you connect multiple temperature probes you can place in different locations and query for their readings.

Keep reading to learn more about the Arduino, find out what materials you’ll need for this tutorial, and finally – build your own networked temperature-monitoring station!

This tutorial assumes you have been through some of the earlier tutorials on the Arduino. If not, you may want to check out our beginner Electronics for iPhone Developers tutorial series before starting.

What You’ll Need

This tutorial requires some specific hardware components to interface with the temperature probes. You’ll be making use of a microcontroller (the Arduino), an Ethernet interface, and connecting status and temperature probes. If you’ve completed other Arduino tutorials on the site, you should have some of these parts – and as a budding electronics hobbyist, now is your chance to begin a collection of reusable modules!

Buildallthethings

The full parts list of required components is below, with URLs to purchase them. The total cost of all the parts is approximately $160 USD. If this seems like a lot of money, remember that you are stocking yourself with plenty of components that you can tinker with beyond this tutorial. If you’ve done previous tutorials, then you already have an Inventor’s Kit and maybe the Ethernet Shield, which drops the total price to $38.00.

For the cheaper components, it makes sense to order a few extra to have around and use for other projects you may want to do.

The Parts

  • SparkFun Inventor’s Kit for Arduino KIT-11227 at $94.95 (Quantity 1). This is the most expensive item but includes a lot of parts for later use, as well as a prototype breadboard. Remember from the Electronics for iPhone Developers tutorial series that a breadboard is just a simple way to connect wires together – the red area in the screenshot below
  • inventorskit

    If you already have various breadboards, USB cables, resistors and LEDs, then you can just buy the Arduino Uno Board DEV-11021 at $29.95 (Quantity 1). Though the inventor’s kit above is still a great buy, just to have a toolbox of spare parts!

  • Arduino Ethernet Wiz Shield DEV-09026 at $45.95 (Quantity 1).

    ethernetshield

  • Arduino Stackable Header Kit – R3 at $1.50 each. (Quantity 2, but more won’t hurt your parts bin!)

    stackableheaders

  • Temperature Sensor, also available direct from Seeed Studio, at $12.50 each. (Quantity 2, but more are nicer in case you want to expand your project later.)

    temperatureprobe

  • Grove Shield Interface Board, also available direct from Seeed Studio, at $10.00 (Quantity 1).

    groveshield

  • A port available on your network switch or router, and an Ethernet cable to connect it to. You probably have these already, but I just wanted to give you a heads up so you can be sure to locate yourself in the same room as your switch or router as you work on this tutorial – or have a very long cable! :]

Once you’ve gathered your parts together, let’s get started!

Installing the Arduino IDE

Next you need to install the Arduino Integrated Development Environment if you haven’t already. The IDE is available at http://arduino.cc/en/main/software. Download the appropriate version for your platform, Mac OS X of course!

Install the IDE by unzipping the file and placing it in your Applications folder. The IDE does require Java, so if you don’t have Java installed, open a Terminal window and type java -version to have your Mac prompt to install the language.

javainstall

A couple of seconds later, you’ll see a dialog on your screen asking if you want to download and install Java. Click the Install button.

Lastly, download the software components for this tutorial from here. The zip file includes various libraries you’ll need and a starter iPhone project for later.

While waiting for your parts to arrive, let’s discuss a bit of the theory behind what you’ll build, including the general principles behind the electronics.

A Little Electronics Theory

If you want to skip the electronics theory and get right to building, click here.

Your project will make use of Arduino shields. Shields are special boards you can purchase that support a specific function, analogous to classes when building a program.

The Ethernet Shield contains the necessary hardware to connect to an Ethernet network. It also includes all the software you need to encode and decode the electrical signals that allow devices on an Ethernet network to communicate.

The Grove Shield brings a bunch of connectors to the board, allowing you to plug in many different sensors using one common connector. Alternatively, you could wire your sensors directly to the breadboard, but these connectors ensure you don’t plug the wrong wires into the wrong pins and keep things looking neat. Seeed Studio makes many other Grove connectors that you can try out once you get the hang of working with the Arduino.

When connecting devices to a microcontroller, you need to use data pins.

The Arduino has a series of digital pins that can be set to high or low and programmed for input or output modes. Setting a pin to output mode causes a connected device to receive 5 volts when high and 0 volts when low. Setting the pin to input mode allows you to read the value as high or low, depending on whether it has 5 volts or 0 volts (otherwise called ground) set on it.

Here’s a diagram of the pins on an Arduino (don’t worry about the details for now):

Pin Layout of Processor

While this might seem like a lot of data pins, some are reserved for certain uses, while others are taken up by the shields. The Ethernet Shield, for instance, uses digital pins 10,11,12,13 (chip pin numbers 16-19). Full technical details for the Ethernet Shield are here. Various boards will commandeer various pins, so there’s only certain pins reserved for your use.

Your temperature probe uses a technology called 1-Wire bus. This technology is a bus system originally designed by Dallas Semiconductor that allows multiple devices to reside on a single data pin. The bus has a single master – the Arduino – and various workers that each sit on the bus and are identified by their own unique 64-bit serial number.

Using 1-Wire bus, each digital pin can support up to 255 devices! For more on 1-Wire bus, see this wiki page.

One Wire Bus architecture

Your temperature probe is actually a unique device on this 1-Wire bus. It is called 1-Wire because a single wire is used to talk to all the devices on the bus – hence using only one pin of your choosing, labeled DQ in the image to the right.

Because you are using only two sensors, you will connect each to a different digital pin. If you wanted to wire 10-20 sensors together then you would connect them as depicted, requiring some soldering and shielded cables. Because you are only using two of them, you will just wire them to two dedicated digital pins. The use of the Shield Interface board makes this very easy. Even though you are only using one sensor per digital pin, the sensors still communicate via the 1-Wire bus communications protocol. Relax – there is a library to handle all the timing for you!

HardwareYes

LED Detail

You will also a wire a light-emitting diode (LED) into the breadboard to act as a signal/status indicator.

LEDs are designed with positive (longer) and negative (shorter) leads. You’ll connect the negative lead to your common ground for the whole circuit (remember, ground is one of the pins on your Arduino). To power and control the LED, you’ll connect the positive lead to a digital pin. The negative lead will connect to a ground by way of a resistor (the resistor prevents too much electricity (voltage) from getting through and burning out the LED).

To turn the LED on and off, you’ll set the digital pin connected to the positive lead as an output pin. Setting this pin to high in code will turn on the LED, while setting it to low will turn it off. To blink the LED, you’ll simply turn it on and off with a delay.

The diagram below shows your wiring. The shorter ground lead of the LED is connected to the common ground, and the longer positive lead of the LED is connected to data pin 6.

ExampleCircuit_sch_ledtocpu

The value of the resistor you use depends upon how much voltage you want to pass through the LED. The smaller the value, the brighter the LED and vice-versa. The Electronics for iPhone Developers tutorial series covers how to calculate the value of this resistor.

OK, enough theory! Let’s get back to building your temperature-monitoring station.

Installing the Libraries

While you’re waiting for the last package to arrive in the mail, you can install the various libraries you’ll need to talk to the shields. The Arduino IDE already includes the libraries for communicating with the Ethernet Shield.

The temperature sensors need a library for the 1-Wire portion, and Dallas Semiconductor has some useful routines specifically for the probes. These libraries are all in the tutorial download in the folder Arduino_Libraries.

To install the libraries, first find the Arduino_Libraries folder in the resources you downloaded earlier and double click both files to unzip them into folders:

Unzipping files

Then select the icon for the SDK and Show Package Contents. The window will display a folder called Contents. Install the DallasTemperature and OneWire folders in the Contents/Resources/Java/libraries folder, as shown:

arduino_libraryinstalled

If you had the Arduino IDE open, Quit and restart to have it load the new libraries. That’s it! Check to see if they’ve loaded successfully by going to File/Examples and looking for DallasTemperature and OneWire in the menu.

arduino_menu_sdk

At this point you should have all your gear in place. Now it’s time to do some wiring!

Wiring It All Together

Before you move on to the temperature sensor, you are going to do some basic setup to get a LED blinking. If you’ve already done the Electronics for iPhone Developers tutorial series, this will be review – but don’t worry, you’ll get to the fun new stuff soon!

First, if you haven’t already, assemble the Arduino UNO and breadboard based on instructions that come with the Sparkfun Inventor’s Kit.

Next, prior to installing the Ethernet Shield, look on the bottom of the board for the MAC address and write it down, as you’ll need to include it in the code you write to control the interface.

ethernetshield_mac

Then plug the Ethernet Shield into the header pins on the ARduino board. Note that there are six pins on the back of the Ethernet Shield that also connect to the UNO board. Slide the board into place carefully and the Ethernet jack should be above the USB connector when installed.

Now you will wire in the LED as a status indicator. Besides, what’s an electronics project without some blinking LEDs?

I’ll give the instructions first, then explain the theory afterwards:

  1. Connect a blue wire to one of the connectors in the blue row on the breadboard.
  2. Connect an orange wire to row J, column 16 on your breadboard.
  3. Connect a 330-ohm resistor (orange/orange/brown) to row J, column 17. Connect the other end of the resistor in the blue row for column J.
  4. Connect the longer lead (+) of a green LED to row F, column 16 and the shorter lead (-) to row F, column 17.

Once you’ve completed the above, your breadboard should look like this:

breadboardLED

Now connect the free ends of the blue and orange wires to the Ethernet Shield:

  1. Connect the free end of the blue wire to the GND connector/header on the Ethernet Shield.
  2. Connect the free end of the orange wire to the header labeled 7 on the Ethernet Shield.

Now your Ethernet Shield should look like this:

ethernetshieldblueorange

You’ve just built a simple switch circuit, except the switch is a computer pin! By toggling the D7 data line between high and low, you will turn the LED on and off, respectively.

You’ve wired the ground lead of the LED to the ground of the whole circuit with a small resistor (330 ohms) to reduce voltage to the LED. The positive side of the LED is wired to the D7 pin of the computer chip. This pin can be controlled via software to have a high value of 5 volts or a low value of 0 volts. As you’ll see shortly, when D7 is set to high, the LED will light.

In my circuit I used a green LED that I had in my box of spare parts. The color does not matter, just choose an LED that you like the color. In the inventor’s kit from SparkFun you get red and yellow LEDs. Just choose either one!

Check your wiring to be sure all is securely fastened. Then connect one end of the USB cable into the lower connector and the other end into your Mac computer. You should see a green indicator on the shield light indicating that the system has power.

Launch the Arduino SDK by double-clicking it. You should see a blank window open for you to create a sketch, which is Arduino-speak for a program.

Wiring the LED onto the Breadboard

First things first: test your LED by making it blink.

Enter the following code into the blank editor window:

const int ledPin = 7;
 
void setup(void)
{
  pinMode(ledPin, OUTPUT);
}
 
void loop(void)
{
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
 
}

The Arduino system software is designed to run the code in setup() every time the circuit reboots – that is, every time power is removed and restored, or the reset button is pressed. At the conclusion of setup(), the chip repeatedly calls loop().

The code in setup() marks ledPin (data pin 7) as an output pin, meaning you want it to set values rather than read values. You then tell the chip to repeatedly execute the loop, which sets the pin to high (thereby giving it a value of 5 volts), delays for 1000 milliseconds (1 second), then sets the pin to low, delays and repeats.

After you’ve entered this code, select File/Upload.

If you get an error, go to Tools/Serial Port and make sure /dev/tty.usbmodemfd131 is selected and not some other /dev device. The Arduino connects as a serial device via USB. Secondly, check that your Tools/Board choice is set to Uno.

If your uploading of this test program is successful, the LED will turn on and off repeatedly once a second.

LitUpLight

Hooray! Your LED is now wired into the circuit. You will use this LED to convey status information as your temperature-monitoring system comes online.

Save this sketch as TestLEDOnPin7. You’ll use this little program to test your LED wiring, like a mini hardware unit test!

Your circuit will use more input/output pins than other tutorials you may have read in the past. Because of the large number of pins, it’s good practice to map out in advance which pins you’ll use for what. Here’s a summary:

  • Data pin 5: temperature sensor 1 (indoor)
  • Data pin 6: temperature sensor 2 (outdoor)
  • Data pin 7: status LED (currently connected)
  • Data pins 10-13: Ethernet Shield

Talking to the Network

Let’s get this circuit talking to your network!

As I mentioned in the beginning of this tutorial, you’ll need a port available on your network switch or wireless hub and to be close enough to run a cable from it to your Ethernet shield. So go ahead and run an Ethernet cable from the RJ-45 jack on the Ethernet Shield to your switch or hub.

The LED next to the Ethernet jack will glow orange if a link has been detected on your network.

Now that the physical wiring is done, you need to write some code to get the Ethernet Shield online.

Select File/New in the Arduino IDE. A new blank sketch window should appear.

Type the following code into the blank sketch window:

#include <SPI.h>
#include <Ethernet.h>
 
const int ledPin = 7;
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x7D, 0x54 };  //1
 
EthernetServer server(80);  //2

The first two lines declare some header files you need to use the Ethernet Shield. The line after that declares the pin of your status LED, just as the previous code block did. Furthermore:

  1. This refers to the MAC address you recorded earlier, which is unique to your Ethernet Shield. Replace the value shown with your value.
  2. Here you create a server object and instruct it to listen on port 80.

A server object? On a microcontroller? Yes, most libraries for the Arduino are written in C++. If you’re curious about how it works, you can peruse the source where you installed the other libraries earlier – look for the folder called Ethernet.

Note: If you would like to review the APIs for the included libraries with the Arduino IDE you can get to them here.

OK, now that you’ve defined your variables and headers, add your trusty setup code to the bottom of the file:

void setup(void) {
  pinMode(ledPin, OUTPUT);  //1
  Serial.begin(9600);
  Serial.println("LED Pin setup for output on pin ");
 
  digitalWrite(ledPin, HIGH);  //2
 
  // start the Ethernet connection and the server:
  Serial.println("Trying to get an IP address using DHCP");
 
  if (Ethernet.begin(mac) == 0) { //3
    Serial.println("Failed to configure Ethernet using DHCP");
    while (true) {
      digitalWrite(ledPin, HIGH);
      delay(200);
      digitalWrite(ledPin, LOW);
      delay(200);
    }
  }
  Serial.println();
  // start listening for clients
  Serial.print("server is at "); //4
  Serial.println(Ethernet.localIP());
 
  digitalWrite(ledPin, LOW);
 
}
 
void loop(void) {  //5
  return;
}

Here’s what’s happening step-by-step:

  1. This sets your LED control pin to output, as before. The two Serial lines turn on a handy function of the Arduino SDK: a Serial window where you can dump messages and see what’s happening.
  2. You turn on the LED to signal that the system is attempting to acquire a DHCP address, while also dumping a handy message to the console.
  3. The Ethernet.begin() instruction attempts to obtain a DHCP address. If it doesn’t get one, it will return zero. You trap this by displaying a message and sitting in a permanent loop, blinking the LED at a faster pace as a warning that something happened.
  4. If the program does obtain a DHCP address, the server will output its address to the terminal window and turn off the status LED.
  5. The loop method doesn’t do anything yet! :]

Select File/Upload to compile and load your code into the Arduino.

Once it’s done, select Tools/Serial Monitor and make sure the window has a baud rate of 9600. You should see some messages coming into the window as the system executes your code:

serialmonitordhcp

Note: If you do not have DHCP services on your network, you can assign an IP address to the server using the following code right after the line declaring your MAC address:

IPAddress ip(192,168,1, 121);  //pick some value on your network

Then replace Ethernet.begin(mac) with this:

Ethernet.begin(mac, ip);

Now let’s get the server to handle inbound requests! This is where loop() comes to life. Add the following after the end of setup(), replacing the current function stub:

void loop(void) {
 
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connnection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html><body>HELLO</body></html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disonnected");
    blinkLED();
  }
 
}

loop() checks if a connection has come in and displays all the connection data send by the client. This includes dumping all the HTTP headers and finally responding with a simple HELLO.

The code reads one character at a time, looking for the blank line on the request. At the conclusion of all this, you quickly blink the LED and return to listening for more connections.

Enter the following subroutine after loop() to blink the LED:

void blinkLED(void)
{
  digitalWrite(ledPin, HIGH);
  delay(500);
  digitalWrite(ledPin, LOW);
  delay(500);
 
  return;
}

Compile and upload the code by selecting File/Upload.

Let’s give the web server a spin! First reopen the Serial Monitor window by selecting Tools/Serial Monitor. Then using a browser on your Mac, open the URL: http://[the IP displayed in the Serial Monitor].

Your Serial Monitor output should show something similar to the following:

serialmonitorhtmloutput

And your browser should say HELLO, which is what you put in the loop code to display!

htmlbrowserhello

Serving Up Some JSON

The wonderful thing about the server is that you can control what it serves. And you need to serve JSON-formatted data to make reading and parsing it by external callers as easy as possible.

Note: If you are not sure what JSON is or why it’s useful, check out our Working with JSON in iOS 5 Tutorial.

Back in loop(), fix the code to produce JSON by adding two variables at the top of the method:

void loop(void) {
 
  //two variables to hold your temperatures
  float temperatureIndoor;
  float temperatureOutdoor;
 
  // listen for incoming clients
  EthernetClient client = server.available();

Further down, where the same method outputs HTML, replace all the code inside the if statement with the following changes:

if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: application/json;charset=utf-8");
          client.println("Server: Arduino");
          client.println("Connnection: close");
          client.println();
 
          temperatureIndoor = 22.77;
          temperatureOutdoor = 15.55;
 
          client.print("{\"arduino\":[{\"location\":\"indoor\",\"celsius\":\"");
          client.print(temperatureIndoor);
          client.print("\"},");
          client.print("{\"location\":\"outdoor\",\"celsius\":\"");
          client.print(temperatureOutdoor);
 
          client.print("\"}]}");
          client.println();
          break;
}

Select File/Upload to compile and upload the code again to the Arduino. Open your Serial Monitor.

Using your web browser, reload the HTML page to the Arduino. You should see JSON output similar to this:

{“arduino”:
[ {"location":"indoor","celsius":"22.77"},
{"location":"outdoor","celsius":"15.55"}
]
}

File/Save this sketch as LEDBlinkAndEthernetFakeTemperatureJSON.

Take a break and enjoy the fruits of your labor. You have an Arduino microcontroller talking JSON and serving content on your network. Try pressing the reset button on the Ethernet Shield and give the system a second – it will come right back up, ready to serve up more content!

Installing the Grove Shield

You might have initially thought about plugging the Grove Shield in first, then placing the Ethernet Shield on top of that. Unfortunately, this won’t work due to the Grove Shield not passing the 6 pins on the back through to the front.

This brings up an important point regarding Arduino shields: they are not built identically. Many times you need to come up with creative ways to mix and match different shields. The stackable headers lift the boards far enough apart to avoid shorting pins.

To install the Grove Shield, first turn off your Arduino by disconnecting it from the USB connector. Since you’ll be plugging in more components, you need to reduce the possibility of damage to the sensitive electronics. Once you’ve disconnected the board, take the two wires (blue and orange) out of the header pins.

Next, insert the four stackable headers into the header pins on the Ethernet Shield. You need to do this because the Ethernet Shield’s RJ45 connector (the place where you plug the Ethernet cable in) is large enough to short out the headers on the Grove Shield (basically, an electric charge might flow from the metal of the RJ45 connector to the Grove Shield, causing major problems!)

Each header matches the pin counts on one of the Ethernet Shield’s headers. When you’re done, your board should look like this:

stackableheadersinstalled

Carefully place the Grove Shield board in place onto the stackable headers without bending or missing any pins. The Grove Shield has only six pins going into the right and left headers, which have eight on the Ethernet Shield. Align the back pins away from the RJ45 connector and don’t worry about the two missing pins.

Upon installation, the Grove Shield should look like this:

groveshieldsideviewarrows

Note the two missing pins on the side facing you (indicated by the arrows) and on the opposite side. All other pins should be seated inside of the headers.

Reconnecting the Status LED

Looking down on the Grove Shield, reconnect your two LED wires. Plug the blue (GND) wire into the fifth pin from the front on the right side. Insert the orange wire into the ninth pin from the front on the left side (the first pin in the second connector).

LEDwiringongrovewitharrows

Plug the two temperature sensors into the white jacks labeled D5 and D6, respectively. The connectors will only go in one way. The D5 and D6 jacks are circled in the image below.

LEDwiringongroved5d6marked

Reconnect the Ethernet RJ45 cable and finally, connect the USB cable to power the circuit. Your LED should come on solid during address acquisition and then turn off.

Open the Serial Monitor to watch the messages. Using your browser, connect to the server and make sure it is still serving the JSON data correctly.

If your LED didn’t come on, there could be a few problems:

  • You may have the wires in the wrong header pins. Double check the picture above.
  • Make sure that all of the pins for the Grove Sheild and stackable headers are in their slots, and that none are bent (missing their slots).
  • You might not have pushed the Grove Shield or Stackable Headers down firmly enough.

If you aren’t sure if they are connected correctly, open your TestLEDOnPin7 sketch and upload it. The LED should blink as before.

Once you’ve got it working, whoohoo! You finally have all of the pieces in place to start reading the temperatures.

Reading Temperatures

Open the LEDBlinkAndEthernetFakeTemperatureJSON sketch and File/Save As… as LEDBlinkAndEthernetTemperatureJSON. This file is where you’re going to interface with the temperature sensors.

At the top of the sketch file, add the following code (replacing the current lines up until setup):

#include <SPI.h>
#include <Ethernet.h>
#include <OneWire.h>   //1
#include <DallasTemperature.h>
 
//The data wire for the indoor sensor is plugged into pin d5 on the Arduino
// on pin 5 & 6 (a 4.7K resistor is necessary) - the temperature probe has one built in.
//2
#define TEMPERATURE_INDOOR 5
#define TEMPERATURE_OUTDOOR 6
 
const int ledPin = 7;
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x7D, 0x54 };
 
//3
//setup an instance to communicate with your sensor
OneWire oneWireIndoor(TEMPERATURE_INDOOR);
DallasTemperature sensorIndoor(&oneWireIndoor);
 
//4
OneWire oneWireOutdoor(TEMPERATURE_OUTDOOR);
DallasTemperature sensorOutdoor(&oneWireOutdoor);
 
EthernetServer server(80);
  1. Include the headers for the OneWire and DallasTemperature libraries.
  2. Declare two constants indicating to which pins the temperature sensors are wired.
  3. Declare an object to communicate with the indoor sensor using a OneWire object and passing it to the DallasTemperature library.
  4. Repeat the code to create an object for the outdoor sensor.

The sensor on pin 5 will be your indoor sensor, while the sensor on pin 6 will be placed outdoors.

The DallasTemperature object uses pieces of the Arduino OneWire library to support communications on the bus. Hence, you need to define both objects.

Inside setup(), add calls to sensorIndoor.begin() and sensorOutdoor.begin():

void setup(void) {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("LED Pin setup for output on pin ");
 
  digitalWrite(ledPin, HIGH);
 
  sensorIndoor.begin();
  sensorOutdoor.begin();
 
  // start the Ethernet connection and the server:
  Serial.println("Trying to get an IP address using DHCP");

Now on to the loop() code, which does the heavy lifting of responding to user requests from the network and producing the temperature as part of the results.

Earlier in the JSON section, you had two variables, temperatureIndoor and temperatureOutdoor, that for testing purposes had hard-coded temperature values. Now comes the magic of the sensors: you’ll use these same variables, but store real temperatures in them.

Change the two lines that set values for temperatureIndoor and temperatureOutdoor to instead call the readTemperatureCelsius() subroutine:

   client.println();
 
   temperatureIndoor = readTemperatureCelsius(sensorIndoor);
   temperatureOutdoor = readTemperatureCelsius(sensorOutdoor);
 
   client.print("{\"arduino\":[{\"location\":\"indoor\",\"celsius\":\"");

You may be wondering about the source of this magical subroutine called readTemperatureCelsius. Let’s cover that next!

Scroll down in the sketch and insert the following subroutine above the blinkLED subroutine:

float readTemperatureCelsius(DallasTemperature sensor) {
  sensor.requestTemperatures();
  float temperature = sensor.getTempCByIndex(0); 
  Serial.print("Celsius Temperature for device is: ");
  Serial.println(temperature);  //zero is first sensor if we had multiple on bus
  return temperature;
}

This subroutine takes a DallasTemperature sensor object and executes a requestTemperature command on the proper bus. It returns the temperature as a floating point value using sensor.GetTempCByIndex(). This method requests the Arduino to signal the bus and communicate with the first probe present (position 0) on the bus.

Since you only have one probe on the bus, it will return the temperature of that probe in Celsius. If you wanted the temperature in Fahrenheit, you would make the call to GetTempFByIndex().

Note: The github source to the Dallas Temperature library is here. There is no API reference per se, but if you peruse the DallasTemperature.h file you will see some great comments on the different method calls available.

The OneWire library and documentation is available here. This web page includes basic usage instructions plus more than enough information on OneWire bus and protocols.

The server for this tutorial uses Celsius as the standard format (just a choice on my part), and you will convert it to Fahrenheit on the iPhone if the user desires that. The temperature reads take a fair amount of time to complete, so it is more efficient for your microcontroller to read the temperature once versus twice for each probe and do the conversion on the iPhone.

Save the sketch and upload to your Arduino by clicking File/Upload. Hit the server with a browser. Open the Serial Monitor to watch the output. You should see the temperature of the room in which you’re working!

Try holding your hand around one of the sensors and reading the temperature a couple of times to watch it rise. Let go of the sensor and access it again to see the temperature fall. Resist the temptation to stick the probe in a bodily orifice of an unwilling creature. :]

Note that there’s a slight delay on the reads, as the Arduino must probe the bus and get the results back before it can send them out. Also note that your browser sometimes sends multiple GET requests each time you browse to a URL (behind the scenes, it is trying to find a icon for the web page, which of course doesn’t exist in your case). Your iOS application will be more efficient than this!

OK! At this point you have a dedicated hardware-assembled temperature monitor. WOOHOO!

Icanprobe

At Long Last, the Starter Project

Now comes the iOS! You will build an iPhone app to get your temperatures. To start things off, open the included Xcode project from the tutorial download. You can find it in the folder Arduino Temperature Starter. The starter project has the following functionality:

  • It provides a simple user interface for the application.
  • The supported interface orientation is set to portrait.
  • It includes a custom open-source digital-looking font to show temperatures.
  • It contains a configuration/settings screen.
  • It provides methods to save and load your settings, including the URL and the temperature display format.

Open the project in Xcode and build and run. You should see the following:

initial_run_nosettings

This project uses the Utility Application template, which includes a flip view for entering your settings. You will handle the communications and parse the results.

Tap the info button at the bottom of the screen and input the URL for your Arduino server:

configscreenios

Click the Done button and on the main screen, tap Update. You should see a message to the debug window telling you that your settings have been saved, but wait… there are no temperatures!

This is where you come in. Open MainViewController.m and locate performRestCall. Inside this method, place the code needed to communicate over the network with the Arduino:

- (void)performRestCall
{
    //communicate in the background to the web service using Grand Central Dispatch
    NSURL *url = [NSURL URLWithString:self.theURL];
    if (!url) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
                                                        message:@"Invalid URL entered!"
                                                       delegate:self cancelButtonTitle:nil
                                              otherButtonTitles:@"OK", nil ];
        [alert show];
        return;
    }
 
    self.updateButton.enabled = NO;  //disable update button until we complete
    dispatch_async(kBgQueue, ^{
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        [request setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData];
        [request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
        NSURLResponse *response;
        NSError *error;
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
        if (!data) {
            NSLog(@"%@",[error localizedDescription]);
        }
        [self performSelectorOnMainThread : @selector(handleResultsOfWebCall: ) withObject:data waitUntilDone:YES];
    });
 
}

This code block looks a bit large, but it simply takes the URL you provided to talk to the Arduino and sets up a background HTTP request using GCD. It also disables the Update button until the results have come back.

This method plays nice by specifying the format for the returned content as application/json. Although the Arduino always returns JSON, it is a good and courteous networking practice to specify this in the headers!

The code receives the results and calls handleResultsOfWebCall on the main thread. This method parses and displays the results.

Let’s get to that now!

Locate handleResultsOfWebCall and set it up like this:

- (void)handleResultsOfWebCall:(NSData *)theResults
{
    if (theResults) {
        NSError *error;
        //NSLog(@"results=%@",[NSString stringWithUTF8String:[theResults bytes]] );
        NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData:theResults options:kNilOptions error:&error];
        NSArray *theSensors = jsonResults[@"arduino"];
 
        for (int i=0;i<[theSensors count];i++) {
            NSString *location = [theSensors[i] valueForKey:@"location"];
            NSString *theTemperature;
            theTemperature = [theSensors[i] valueForKey:@"celsius"];
            if ([location compare:@"indoor"]==NSOrderedSame) {
                indoorTemperatureCelsius = [theTemperature floatValue];
            } else {
                outdoorTemperatureCelsius = [theTemperature floatValue];
            }
        }
        lastUpdate = [NSDate date]; //set to current date and time
        [self updateDisplayWithCurrentReadings];
    } else {
        NSLog(@"Error retrieving results-check the URL!");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
                                                        message:@"Error retrieving results-check the URL or Server!"
                                                       delegate:self cancelButtonTitle:nil
                                              otherButtonTitles:@"OK", nil ];
        [alert show];
    }
 
    self.updateButton.enabled = YES;
}

This method first checks to see if any results came back. If not, it displays an error message. If data was returned, you use the built-in NSJSONSerialization handler to get the results into an NSDictionary object. From there, you read the values returned from the sensors and save them as Celsius values.

You set the lastUpdate variable to the current date and time and then call updateDisplayWithCurrentReadings to show the temperature values.

Finally, no matter the outcome, you re-enable the Update button, which you’d turned off during the asynchronous call above.

Compile and run the application, and enjoy your temperature readings!

samplescreenshotwithtemps

Congratulations! You have a mobile application and temperature server that lets you read temperatures from sensors you can place anywhere. My setup has one sensor outside of a window and one inside. It’s really nice to be able to check the temperature quickly in the morning before heading out the door.

Where to Go from Here?

Powering your Arduino project is easy. Grab an iPad adapter and plug the Arduino cable into it instead of your Mac!

You will notice the display shows the temperatures in different colors, based on the range of the value. This color handling is in the setFieldColor method. If you are in a particular climate where you don’t agree with the color ranges, feel free to change them.

You also may not like the sensor changing the IP address if it reboots, based on your DHCP settings. If that’s the case, modify the Arduino code to include a predefined IP address. This was described earlier in the tutorial, but for reference, you need to add a line near the top of the LEDBlinkAndEthernetTemperatureJSON file, like this:

byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x7D, 0x54 };  
IPAddress ip(192,168,1, 121);  //or some value you want

Then replace the if (Ethernet.begin(mac) == 0) line with:

Ethernet.begin(mac, ip);  //in place of Ethernet.begin(mac).

What you’ve done in this tutorial is easily adapted to many other uses! Here are a few ideas:

  • What if you wanted your app to record the temperatures from the probes over a given time period and chart the information using graphs?
  • According to Seeed Studio, you can use the temperature sensors to check human body temperature, like a medical thermometer. Why not write an app for that?
  • There are many different sensors available from Grove that will plug into the connectors on the board. Why not get something to measure soil moisture and monitor your favorite potted plant?
  • The Arduino web site give lots of details on the programming language as well as references to all of the built-in libraries. It is available at http://arduino.cc/.
  • For some interesting online tutorials check out https://learn.sparkfun.com/tutorials.
  • There are a ton of prebuilt shields that can read RFID cards, control a robot, link relays or tie into any type of system you desire. Check out http://www.seeedstudio.com/depot/shield-t-2.html?ref=top. Or check out https://www.sparkfun.com/categories/240 for some other interesting shields.

I hope you enjoyed this tutorial and that it has inspired you to continue with your Arduino investigations. In the meantime, if you have any questions or comments, please join the forum discussion below!

Tony Dahbura
Tony Dahbura

Tony is a hardware/software engineer from Virginia with more than 30 years software development experience. His first “real” computer was an Apple ][ and he has loved Apple Systems ever since! Tony has been working with iOS for about 3 years. He has worked on server systems, Java development, and various flavors of Unix.

In his spare time, after attending his kids sporting events, he does some independent software development work under FullMoon Manor LLC, helping folks realize the joy of custom apps to meet their needs. You can follow him on Twitter or connect on LinkedIn.

User Comments

9 Comments

  • Tony,
    Thanks so much! Perhaps you or someone more knowledgable than me in basic CS can advise me or point me in the right direction...

    The sensors I am working with return a float and a uint16_t. If I want this converted as float on the iOS side, how would I best do this given the following:

    I am using BTLE (instead of Ethernet) via the RedParkLab BLE Shield. I have a temperature sensor that returns float and a lux sensor that return uint_16. The BLE Shield's arduino library has a call to ble_write(unsigned char *); So I receive a pointer to a bunch-oh-bytes in my iOS app. I AM CHALLENGED ON CONVERTING FROM BUNCH-Oh-BYTES to float or uint16_t.

    E.g. of uint16_t, in the Arduino sketch i send something similar to following over BTLE to my iOS app.
    uint16_t data = 0x34EF;
    ble_write(data >> 8);
    ble_write(data);

    So I get these bytes, and I try something like:
    uint16_t sensorValue = CFSwapInt16LittleToHost(*(uint16_t*)dataPointer);

    Where dataPointer comes in as (*(uint16_t *)dataPointer) (i.e.: cast of ble_write bytes received, first data >> 8 then data...

    But I am clueless because I don't get the right answer..

    Harder still is to wrap my head around sending a bunch-oh-bytes and converting to float. I HAVE done the following:
    in Arduino sketch:

    float t = dht.readTemperature();
    control = 0x02;
    dtostrf(t, 4, 2, buf);
    // Serial.print("Temperature: ");Serial.println(t);
    ble_write(0x02); // 0x02 is the first byte of data returned, telling the app that the following bytes are temperature data
    for (int i = 0; i < 5; i++)
    ble_write(buf[i]);

    Then in my iOS app (where data starts at data[1] = i.e.: after control byte):
    NSString *input = [[NSString alloc]initWithBytes:data length:4 encoding:NSUTF8StringEncoding];
    return [input floatValue];
    HOWEVER, this seems weird to convert to string in Arduino sketch, then convert to NSString so that I can use floatValue? I assume there is some "spiffy better way" that is over my head given my current knowledge.

    SO, any data conversion advice between what is received via Arduino+sensors+network ->iOS app in which sensor data typically comes in as float or uint16_t?

    THANK YOU and KIND REGARDS.
    sketchy
  • please upload source code
    krudos
  • I feel so poor after looking at the first Arduino tutorial... me and my poor teenage self. :(
    DKL
  • Sketchy:
    Typically in communications stream I like to send the data as ASCII. Not familiar with the RedParkLab shield, but somewhere in their library there is probably a routine to take value as a float and output it as an ascii string. Otherwise you need to figure out which floating point format the system is using (e.g. IEEE or some derivative).

    My suggestion is to use the Serial log window and work on a to get the numbers to output there and then send the bytes as the actual ASCII characters of each digit. When they come in on the iOS side generate a string and use the built in libraries. Otherwise you are going to need to write a lot of bit shifting and checks for positive versus negative formats which can get ugly and error prone.

    Check out the following web sites for some information if you decide to write your own converters for the binary streams (on the iOS side), remembering you can use all the operators of C in your Objective C class where you get the data.

    http://www.geeksforgeeks.org/binary-rep ... en-number/

    http://en.wikipedia.org/wiki/Single_pre ... int_format

    Your integer values are actually easier to handle because the bytes represent the number positional format. The math is is simple for hex depending on the data format you have the values in. If they are something you can read the byte values then add the lower half 8 bits (1 byte) to 256 * the upper byte.

    If the iOS app has each byte in a string then you will need to get that from a hex string to an int value. For example the $34EF value would become 256*52+ 239 ($EF).

    A trick to do this quick (remember that A=10, B=11...F=15) is to take a two character hex number e.g. 1A and take the right side digit A in this case which is 10 and add that to the left digit value * 16. So $1A = 10 + (1 * 16) = 26 decimal.

    Depending on how your values come into the routine in iOS you may need to do this math in code, my recommendation is write this as method and call it with a range of values to test you are doing the conversions correctly-set up a loop and start at the lowest value and go through them one at time an make sure your calculations are producing the correct results for all the inputs (e.g. the values are 1 greater each time for the output).

    It sounds like you are close but just need to get the number conversions working correctly. This web site also has some detailed explanations on number formats and such:

    http://www.swarthmore.edu/NatSci/echeev ... umSys.html

    /tony



    sketchy wrote:Tony,
    Thanks so much! Perhaps you or someone more knowledgable than me in basic CS can advise me or point me in the right direction...

    The sensors I am working with return a float and a uint16_t. If I want this converted as float on the iOS side, how would I best do this given the following:

    I am using BTLE (instead of Ethernet) via the RedParkLab BLE Shield. I have a temperature sensor that returns float and a lux sensor that return uint_16. The BLE Shield's arduino library has a call to ble_write(unsigned char *); So I receive a pointer to a bunch-oh-bytes in my iOS app. I AM CHALLENGED ON CONVERTING FROM BUNCH-Oh-BYTES to float or uint16_t.

    E.g. of uint16_t, in the Arduino sketch i send something similar to following over BTLE to my iOS app.
    uint16_t data = 0x34EF;
    ble_write(data >> 8);
    ble_write(data);

    So I get these bytes, and I try something like:
    uint16_t sensorValue = CFSwapInt16LittleToHost(*(uint16_t*)dataPointer);

    Where dataPointer comes in as (*(uint16_t *)dataPointer) (i.e.: cast of ble_write bytes received, first data >> 8 then data...

    But I am clueless because I don't get the right answer..

    Harder still is to wrap my head around sending a bunch-oh-bytes and converting to float. I HAVE done the following:
    in Arduino sketch:

    float t = dht.readTemperature();
    control = 0x02;
    dtostrf(t, 4, 2, buf);
    // Serial.print("Temperature: ");Serial.println(t);
    ble_write(0x02); // 0x02 is the first byte of data returned, telling the app that the following bytes are temperature data
    for (int i = 0; i iOS app in which sensor data typically comes in as float or uint16_t?

    THANK YOU and KIND REGARDS.
    Tony Dahburatdahbura
  • Sketchy:
    As a follow on I had a minute and checked into the RedBearLab site. Their shield has available a sample iOS app with source code and they show conversion of unsigned int to values. Their code looks like this:

    else if (data[i] == 0x0B)
    {
    UInt16 Value;

    Value = data[i+2] | data[i+1] << 8;
    lblAnalogIn.text = [NSString stringWithFormat:@"%d", Value];
    }

    which says if the first value is OB (which must be a marker for them to prefix this data type) then take the last byte of the two byte pair and OR it with the leftmost byte after shifting it to the left 8 which is like a multiply of 256 as I described below. So a byte stream of 0B34EF would result in Value=13551.
    Tony Dahburatdahbura
  • @krudos: You can find the source code in the resources for this tutorial.
    rwenderlich
  • Thanks All! @tdahbura - yah. Thanks so much. DUH on my part. Here's my code. Works great (less filling! Now, onto CS 101 - bytes to floats!! :-) ....
    //I'm interested in light characteristics...such a sunny thing. Well at least in the summer.
    - (void)didReceiveColorTemperature:(unsigned char *)data {
    uint16_t colorTemperature = data[0] << 8 | data[1];
    self.colorTempLabel.text = [NSString stringWithFormat:@"%d",colorTemperature];
    }
    Another thing I'm noticing - I like to use the serial port to debug on the Arduino side. Some of the sensors I have (like this one) take awhile to get a result...thus the amount of delay() in the Sketch becomes important. Just thought I'd mention it because it keeps sneaking up on me. i.e.: my iOS app sends "get me that guy's sensor data" too fast/too many times and then the communications gets screwed up. i.e.: the iOS app gets disconnected and will not connect until I unplug (power down) my Arduino then plug it back in (to a USB). In case this happens to you. OR in case here is another place where advice is warranted.

    Again - thank you for the comments (weird = I didn't get my customary notification...check my spam i guess).

    Thanks for the links to the web sites. "Google is my friend" is so much nicer than what used to be said: "RTFM" :-)
    sketchy
  • Awesome tutorial! Thanks guys.

    Do you have any experience sending this data to a server rather than locally? I'm wondering how you would go about sending the data from the arduino to a server like your personal website so that you could access the data from wherever rather than having to be on the same network?
    ecerney
  • Can you make something similar but connecting Arduino with Mac by USB?
    AndrewK

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Our Books

Our Team

Tutorial Team

  • Kirill Muzykov

... 50 total!

Update Team

  • Ray Fix

... 15 total!

Editorial Team

  • Matt Galloway
  • Ryan Nystrom

... 23 total!

Code Team

  • Orta Therox

... 3 total!

Translation Team

  • Jose De La Roca
  • Team Tyran

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!