Update note: Team member Tony Dahbura has updated this tutorial for iOS 8 and Swift! If you already built the circuit from the previous tutorial, you can skip ahead to the Swift portion of this tutorial.
If you’re like me, 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, most weather apps tell you about the temperature in a nearby city or suburb – but not necessarily about 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 a networked temperature-monitoring station with multiple temperature probes you can place in different locations. To tie it all together, you’ll also build the companion iPhone app to check the readings.
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.
Getting Started
First things first, download the starter project and components for this tutorial from here. The zip file includes various libraries you’ll need and a starter iPhone Swift project for later.
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!
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.
Parts List
- SparkFun Inventor’s Kit for Arduino KIT-12060 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.
- Arduino Ethernet Wiz Shield DEV-09026 at $45.95 (Quantity 1).
- Arduino Stackable Header Kit – R3 at $1.50 each. (Quantity 2, but more won’t hurt your parts bin!)
-
Temperature Sensor available direct from Seeed Studio, at $12.50 each. (Quantity 2)
- Grove Shield Interface Board, also available direct from Seeed Studio, at $10.00 (Quantity 1).
- A spare port on your network switch, and an Ethernet cable to connect it. Might need to work in the same room:]
Once you’ve gathered your parts together, it’s time to get started!
Installing the Arduino IDE
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!
Note:There is a version of the IDE for OS X that supports JDK 1.6 and an experimental version for Java 7+. I recommend using the experimental version for Java 7+.
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. At the prompt click the More Info… button and you will be taken to the Java download site. Download the JDK download button and select the version for Mac OS X x64. Download the latest JDK and install it.
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.
A Little Electronics Theory
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 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.
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.
Relax – there is a library to handle all the timing for you!
OK, enough theory! Let’s get back to building your temperature-monitoring station.
Installing the Libraries
While you’re waiting for all packages 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:
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/Java/libraries folder, as shown:
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.
Now it’s time to do some wiring!
Wiring It All Together
First, if you haven’t already, assemble the Arduino UNO and breadboard based on instructions that come from Sparkfun.
Next, look on the bottom of the Ethernet board for the MAC address and write it down, you’ll need to include it in the code you write to control the interface.
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.
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.
Insert the four stackable headers into the header pins on the Ethernet Shield. You need these because the Ethernet Shield’s RJ45 connector 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:
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.
After installation, the Grove Shield should look like this:
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.
Now you will wire in the LED as a status indicator. Besides, what’s an electronics project without some blinking LEDs? :]
- Connect a blue wire to one of the connectors in the blue row on the breadboard.
- Connect an orange wire to row J, column 16 on your breadboard.
- 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.
- 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:
Looking down on the Grove Shield, connect 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).
Plug the two temperature sensors into the jacks labeled D5 and D6, respectively. The connectors will only go in one way. The jacks are circled in the image below.
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.
Connect the USB cable to power and program the circuit into your computer. You may be prompted by your Mac about a new network interface, just click Cancel.
Talking to the Network
Let’s get this circuit talking to your network!
In the downloads folder are various sketch programs to assist. Find the LEDBlinkAndEthernetStarter folder and open LEDBlinkAndEthernetStarter.ino. The file will open in the Arduino IDE and you’ll see this starter code:
#include <SPI.h> #include <Ethernet.h> const int ledPin = 7; //1 byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x7D, 0x54 }; //2 EthernetServer server(80); //3 |
Here’s what’s going on in this starter sketch:
- The line that declares the pin of your status LED.
- This refers to the MAC address you recorded earlier, which is unique to your Ethernet Shield. Replace this value shown with your value.
- 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.
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 the code:
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); |
If your LED didn’t come on briefly and then turn out, there could be a few problems :
- You may have the wires in the wrong header pins. Double check the pictures 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 the TestLEDOnPin7 sketch in the downloads folder and upload it. The LED should blink as before.
If the LED keeps blinking it means you could not get an IP address correctly. Check your MAC address values and/or router settings.
Whoohoo! You have all of the pieces in place to start reading the temperatures.
Server Setup
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') { 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 sent by the client and responding with a 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 wait for more connections.
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:
And your browser should say HELLO, which is what you put in the loop
code to display!
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.
Back in loop()
, fix the code to produce JSON by adding two variables at the top of the method:
//two variables to hold your temperatures float temperatureIndoor; float temperatureOutdoor; |
Further down, where the same method outputs HTML, find the if
statement that checks for newlines. Replace it along with its entire code block with the following:
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. You should see JSON output similar to this:
{"arduino": [ {"location":"indoor","celsius":"22.77"}, {"location":"outdoor","celsius":"15.55"} ] } |
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!
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> //2 #define TEMPERATURE_INDOOR 5 #define TEMPERATURE_OUTDOOR 6 const int ledPin = 7; byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x7D, 0x54 }; //3 OneWire oneWireIndoor(TEMPERATURE_INDOOR); DallasTemperature sensorIndoor(&oneWireIndoor); //4 OneWire oneWireOutdoor(TEMPERATURE_OUTDOOR); DallasTemperature sensorOutdoor(&oneWireOutdoor); EthernetServer server(80); |
- Include the headers for the OneWire and DallasTemperature libraries.
- Declare two constants indicating to which pins the temperature sensors are wired.
- Declare an object to communicate with the indoor sensor using a OneWire object and passing it to the DallasTemperature library.
- 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 and getting the temperature 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 readTemperatureCelsius
. Scroll down in the sketch and insert that 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.
The server for this tutorial uses Celsius as the standard format (just a choice on my part). The temperature conversion to Fahrenheit is done on the iPhone if the user wants that.
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 to see the temperature fall :].
Note the slight delay on the reads, as the Arduino probes the bus and gets 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!
At this point you have a dedicated hardware-assembled temperature monitor. WOOHOO!
iOS and Swift Integration
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 ArduinoTemperatureSwift 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:
This project uses a basic Application model including a flip view for 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:
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 from your new circuit!
This is where you come in. Open ViewController.swift and locate performRestCall()
. Inside this method, place the code needed to communicate over the network with the Arduino:
var url = NSURL(string: theURL) if (url == nil || theURL == "") { showAlert("Invalid or no URL entered!") return } updateButton.enabled = false var request = NSMutableURLRequest(URL: NSURL(string: theURL)!) request.HTTPMethod = "GET" request.setValue("application/json; charset=utf=8", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Accept") request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in dispatch_async(dispatch_get_main_queue(), { if let anError = error { //error getting data self.showAlert(error.localizedDescription) } else { //process JSON self.handleResultsOfWebCall(data) } self.updateButton.enabled = true }) return }) |
This code simply takes the URL you provided to talk to the Arduino and sets up a background HTTP request using an Operation Queue. We utilize Swift closures to handle the results of the background web request.
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!
Find handleResultsOfWebCall(_:)
and add the following implementation inside the method:
var error : NSError? var jsonResults = NSJSONSerialization.JSONObjectWithData(theData, options: nil, error: &error) as! NSDictionary if let jsonError = error { println("JSON Error \(error!.localizedDescription)") showAlert("Error retrieving results-check the URL or Server!") } else { let theSensors = jsonResults["arduino"] as! NSArray for i in 0..<theSensors.count { let sensor = theSensors[i] as! NSDictionary let location = sensor["location"] as! String let temperature = sensor["celsius"] as! NSString if (location == "indoor") { indoorTemperatureCelsius = temperature.floatValue } else { outdoorTemperatureCelsius = temperature.floatValue } } lastUpdate = NSDate() saveSettings() updateDisplayWithCurrentReadings() } |
This method to see if any data was returned, you use the built-in NSJSONSerialization
handler to get the results into a NSDictionary
object. From there, you read the values returned from the sensors and save them as Celsius values.
Next the settings are saved for display later when the application starts up again. Finally, you call updateDisplayWithCurrentReadings()
to show the temperature values.
Build and run the application, and enjoy!
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?
Here’s the download for the completed Xcode project from this tutorial.
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. Feel free to change them for your climate.
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 sensors!
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!
Arduino Tutorial: Networked Temperature Sensor with Swift is a post from: Ray Wenderlich
The post Arduino Tutorial: Networked Temperature Sensor with Swift appeared first on Ray Wenderlich.