DIY, East Bay, Featured, Life

Temperature Logger Using Particle Photon and ThingSpeak

After the success of tinkering with the Particle Photon in my Smartphone Garage Door Opener project, I knew it would be a perfect platform to power a temperature logger that updates to ThingSpeak. I wanted to build this project in order to monitor the daily temperature fluctuations in my toddler’s room as we live in an old, terribly insulated home. Without air conditioning in the summer and a lousy heater in the winter, swings can be quite wide, especially at night. In order to dial in the space heater setting this winter, I thought I’d use a Photon to take a reading every 5 minutes and plot it to a channel on ThingSpeak for later analysis. You can also do things like set triggers to tweet or message you if it hits a certain reading. Pretty cool software.

 

I should also note that I tried this project with an Arduino Pro Mini and ESP8266 wifi module first. I literally spent hours/days trying to program a sketch and configure the ESP8266 firmware via a FTDI board, but eventually gave up and turned to the Particle Photon. To be fair, there is tons of documentation around ESP8266 and all the ways to work with it, but the reality for me was that is was beyond the skills of a novice hardware tinker. I hope to be proven wrong in the future as I’d love to put my little ESP8266 to work in some other capacity.

 

Hardware

 

The hardware build for this project is a simple one. Here is what you need.
  • Particle Photon – $19 (Particle Site)
  • Mini Breadboard – $1 (Amazon)
  • One Wire Digital Temperature Sensor DS18B20 – $5.50 (Amazon)
  • 4.7k Ohm resistor – $5 for 100 (Amazon)
  • Micro usb cable – $0 (Had on hand)
  • Jumper wires (solid core wire) – $0 (Had on hand)
 Particle Photon Temp Sensor
Step 1 – Attach your Particle Core to the breadboard, leaving the micro usb plug facing away from the center of the board.

 

Step 2 – Attach the DS18B20 sensor to the breadboard (see picture below). The outer two wires on the sensor will be connected to ground. The center wire will be connected to power (with a resistor inline).

Particle Photon

Step 3 – Use a small length of wire (black) to connect the two outer wires on the sensor to the GND pin on the Particle Photon.

 

Step 4 – Use a small length of wire (red) to bring the power from the 3V3 pin on the Particle Photon to the end of your breadboard (near the sensor). Now place a 4.7K Ohm resistor in line from the red wire to the center wire on the sensor.

 

Step 5 – Now we need to connect the data line from the sensor to the Particle Photon. I used a green wire to connect the sensor’s center pin to the D2 pin on the Particle Photon. I placed the green wire after the red/resistor connection.

Particle Photon Wiring Diagram

That’s it!

 

ThingSpeak

 

ThingSpeak is an awesome platform. ThingSpeak is an open source “Internet of Things” application and API to store and retrieve data from things using HTTP over the Internet or via a Local Area Network. With ThingSpeak, you can create sensor logging applications, location tracking applications, and a social network of things with status updates.

Screen Shot 2015-11-23 at 10.11.59 PM

Head over to https://thingspeak.com/ and create a new account. Here is a tutorial on getting started – https://thingspeak.com/docs/tutorials/ . I really only filled out the following fields:

 

Name – Temperature Sensor
Description – Temperature Sensor
Field 1 – Temperature
Latitude/Longitude/Elevation – Enter the info for Oakland, CA
Make Public – Checked

 

Save and then navigate over to the API Keys tab. The Write API Key is the key we’ll need to use in our Particle Photon code in order to have the temperature logged to our ThingSpeak Channel. Copy it down.

 

Activate Particle Photon

 

I found the easiest way to activate the Particle Photon is to download the Particle App from the App Store and follow the prompts. Make sure you plug the Photon into power before performing this step.

 

Once activated through the app, head over to Particle’s Code Builder (https://build.particle.io/login) and create/login to your account. This is where we will write the code before flashing it to the Photon wirelessly.

Screen Shot 2015-11-23 at 10.15.46 PM

Writing and Flashing the Code

 

Being that I know very little about programming hardware, I searched the Particle Community forums for a base code sketch using the DS18B20 sensor and writing to ThingSpeak. Sure enough, I found one (linked here – https://community.particle.io/t/multi-ds18b20-on-thingspeak-code/14223). The sketch supports multiple sensors, but worked well enough for my project.

 

Here is my final code after making some changes to the one I found online:

 

Go to the Particle Build link and log in. You’ll need to cut and paste the below code into the window. Make sure to change the underlined code below. The first underline is for your Write API Key from ThingSpeak. The second is the frequency you’d like to read temperature at. I used 300,000ms (or every 5 minutes).

 

// This #include statement was automatically added by the Particle IDE.
#include “thingspeak/thingspeak.h”
 
// This #include statement was automatically added by the Particle IDE.
#include “OneWire/OneWire.h”
 
// This #include statement was automatically added by the Particle IDE.
#include “spark-dallas-temperature/spark-dallas-temperature.h”
 
ThingSpeakLibrary::ThingSpeak thingspeak (“YOUR WRITE API KEY HERE“);
// Data wire is plugged into port 2 on the Particle Photon
#define ONE_WIRE_BUS D2
#define TEMPERATURE_PRECISION 9
int F1temp ;
int F2temp ;
 
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
double tempF = 0.0;
double tempC = 0.0;
 
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);
 
// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;
 
// function to print a device address
void printAddress(DeviceAddress deviceAddress);
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    // zero pad the address if necessary
    if (deviceAddress[i] < 16) Serial.print(“0”);
    Serial.print(deviceAddress[i], HEX);
  }
}
 
// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress);
void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print(“Temp C: “);
  Serial.print(tempC);
  Serial.print(” Temp F: “);
  Serial.print(DallasTemperature::toFahrenheit(tempC));
}
 
// function to print a device’s resolution
void printResolution(DeviceAddress deviceAddress);
void printResolution(DeviceAddress deviceAddress)
{
  Serial.print(“Resolution: “);
  Serial.print(sensors.getResolution(deviceAddress));
  Serial.println();    
}
 
// main function to print information about a device
void printData(DeviceAddress deviceAddress);
void printData(DeviceAddress deviceAddress)
{
  Serial.print(“Device Address: “);
  printAddress(deviceAddress);
  Serial.print(” “);
  printTemperature(deviceAddress);
  Serial.println();
}
 
void setup(void)
  sensors.begin();// start serial port
  Serial.begin(9600);
  Serial.println(“Dallas Temperature IC Control Library Demo”);
  Spark.variable(“tempf”, &tempF, DOUBLE);
 
 
  // Start up the library
  sensors.begin();
 
  // locate devices on the bus
  Serial.print(“Locating devices…”);
  Serial.print(“Found “);
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(” devices.”);
 
  // report parasite power requirements
  Serial.print(“Parasite power is: “); 
  if (sensors.isParasitePowerMode()) Serial.println(“ON”);
  else Serial.println(“OFF”);
 
  // assign address manually.  the addresses below will need to be changed
  // to valid device addresses on your bus.  device address can be retrieved
  // by using either oneWire.search(deviceAddress) or individually via
  // sensors.getAddress(deviceAddress, index)
  //insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
  //outsideThermometer   = { 0x28, 0x3F, 0x1C, 0x31, 0x2, 0x0, 0x0, 0x2 };
 
  // search for devices on the bus and assign based on an index.  ideally,
  // you would do this to initially discover addresses on the bus and then 
  // use those addresses and manually assign them (see above) once you know 
  // the devices on your bus (and assuming they don’t change).
  // 
  // method 1: by index
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println(“Unable to find address for Device 0”); 
  if (!sensors.getAddress(outsideThermometer, 1)) Serial.println(“Unable to find address for Device 1”); 
 
  // method 2: search()
  // search() looks for the next device. Returns 1 if a new address has been
  // returned. A zero might mean that the bus is shorted, there are no devices, 
  // or you have already retrieved all of them.  It might be a good idea to 
  // check the CRC to make sure you didn’t get garbage.  The order is 
  // deterministic. You will always get the same devices in the same order
  //
  // Must be called before search()
  //oneWire.reset_search();
  // assigns the first address found to insideThermometer
  //if (!oneWire.search(insideThermometer)) Serial.println(“Unable to find address for insideThermometer”);
  // assigns the seconds address found to outsideThermometer
  //if (!oneWire.search(outsideThermometer)) Serial.println(“Unable to find address for outsideThermometer”);
 
  // show the addresses we found on the bus
  Serial.print(“Device 0 Address: “);
  printAddress(insideThermometer);
 
  Serial.println();
 
  Serial.print(“Device 1 Address: “);
  printAddress(outsideThermometer);
  Serial.println();
 
  // set the resolution to 9 bit
  sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION);
  sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);
 
  Serial.print(“Device 0 Resolution: “);
  //Serial.print(sensors.getResolution(insideThermometer), DEC); 
  Serial.println();
 
  Serial.print(“Device 1 Resolution: “);
  //Serial.print(sensors.getResolution(outsideThermometer), DEC); 
  Serial.println();
}
 
void loop(void)
  // call sensors.requestTemperatures() to issue a global temperature 
  // request to all devices on the bus
  Serial.print(“Requesting temperatures…”);
  sensors.requestTemperatures();
  Serial.println(“DONE”);
  delay(300000);
 
 
  // print the device information
  printData(insideThermometer);
  printData(outsideThermometer);sensors.requestTemperatures();
  tempC = sensors.getTempCByIndex(0);
  tempF = tempC * 9.0 / 5.0 + 32.0;
  int WT = sensors.getTempC(insideThermometer);
 F1temp = (WT * 9.0)/ 5.0 + 32.0; 
 int OT = sensors.getTempC(outsideThermometer);
 F2temp = (OT * 9.0)/ 5.0 + 32.0; 
  bool valSet = thingspeak.recordValue(1, String(F1temp, DEC));
  bool valSet1 = thingspeak.recordValue(8, String(F2temp, DEC));
  bool valsSent= thingspeak.sendValues();
  bool valsSent1= thingspeak.sendValues();

 

Known issues: I have been running this code for a few weeks and the only bug I’ve found is an occasional reading of -196 degrees Fahrenheit. I think it happens when the Photon loses wifi connection and reconnects. I don’t know how to fix it. I found ways to exclude this data through the ThingSpeak site that seems to work fine.

 

Now hit the Save icon (little folder on left side. After it has saved, hit the Verify button. This runs a check against your code. If everything checks out, hit the Flash button (little lightning bolt). It is usually pretty easy. It will cycle through a couple colors on the Photon LED to let you know what is happening and then will be done when it is breathing Cyan.

 

Here is a link to the flashing process and LED colors – https://docs.particle.io/guide/getting-started/modes/photon/

 

Viewing and Customizing Your Data on ThingSpeak

 

Now that your sensor is working and reporting data to your ThingSpeak Channel, you can customize the visualization to make it more meaningful. Here are a few changes I made.

Screen Shot 2015-11-23 at 10.22.26 PM

Screen Shot 2015-11-23 at 10.23.33 PM

– Edit my line chart to deal with the -196 degree reading (Data Min, Data Max), show trailing 24 hour readings (Results).
– Add Google Gauge Plugin (you can use the base code by choosing (Apps – Google Gauge). I changed the color ranges in my gauge to the below:

 

HTML
<html>
  <head>
  <title>Google Gauge – ThingSpeak</title>
  %%PLUGIN_CSS%%
  %%PLUGIN_JAVASCRIPT%%
  </head>
  <body>
    <div id=”container”>
      <div id=”inner”>
        <div id=”gauge_div”></div>
      </div>
    </div>
  </body>
</html>

 

CSS
<style type=”text/css”>
  body { background-color: #ddd; }
  #container { height: 100%; width: 100%; display: table; }
  #inner { vertical-align: middle; display: table-cell; }
  #gauge_div { width: 120px; margin: 0 auto; }
</style>

 

Javascript
<script type=’text/javascript’ src=’https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js’></script>
<script type=’text/javascript’ src=’https://www.google.com/jsapi’></script>
<script type=’text/javascript’>
  // set your channel id here
  var channel_id = 56934;
  // set your channel’s read api key here if necessary
  var api_key = ‘BADGNKS6V7L6DCKE’;
  // maximum value for the gauge
  var max_gauge_value = 100;
  // name of the gauge
  var gauge_name = ‘Temp’;
  // global variables
  var chart, charts, data;
  // load the google gauge visualization
  google.load(‘visualization’, ‘1’, {packages:[‘gauge’]});
  google.setOnLoadCallback(initChart);
  // display the data
  function displayData(point) {
    data.setValue(0, 0, gauge_name);
    data.setValue(0, 1, point);
    chart.draw(data, options);
  }
  // load the data
  function loadData() {
    // variable for the data point
    var p;
    // get the data from thingspeak
    $.getJSON(‘https://api.thingspeak.com/channels/’ + channel_id + ‘/feed/last.json?api_key=’ + api_key, function(data) {
      // get the data point
      p = data.field1;
      // if there is a data point display it
      if (p) {
        p = Math.round((p / max_gauge_value) * 100);
        displayData(p);
      }
    });
  }
  // initialize the chart
  function initChart() {
    data = new google.visualization.DataTable();
    data.addColumn(‘string’, ‘Label’);
    data.addColumn(‘number’, ‘Value’);
    data.addRows(1);
    chart = new google.visualization.Gauge(document.getElementById(‘gauge_div’));
    options = {width: 200, height: 200, redFrom: 90, redTo: 100, yellowFrom:77, yellowTo: 90, greenFrom: 65, greenTo: 77, minorTicks: 5};
    loadData();
    // load new data every 15 seconds
    setInterval(‘loadData()’, 15000);
  }
</script>

 

Other Considerations

 

Wired or Battery – I started off by plugging the unit directly into the wall and letting it run, which was fine. Then I started thinking about running it off of a battery pack. I currently have it running off of a brick battery pack from Anker to see how long it will last. It makes it a bit easier to keep out of the way of a curious toddler than wants to eat every cord he gets his hands on.
IMG_8193 copy
Thanks for checking out my project. Please let my know what you think in the comments below.