Temperature Monitoring System - IOT Group 20

The temperature monitoring system uses three photons interconnected to monitor, display, and remotely log data for analysis and endless fun.

BeginnerFull instructions provided59
Temperature Monitoring System - IOT Group 20

Things used in this project

Hardware components

Temperature Sensor
Temperature Sensor
×2
Photon
Particle Photon
×3
Grove - OLED Display 1.12'' V2
Seeed Studio Grove - OLED Display 1.12'' V2
×2
5 mm LED: Yellow
5 mm LED: Yellow
×2
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×3
Resistor 4.75k ohm
Resistor 4.75k ohm
×2
Resistor 221 ohm
Resistor 221 ohm
×1
USB-A to Micro-USB Cable
USB-A to Micro-USB Cable
×3
Male/Male Jumper Wires
×21
Particle Maker Kit Sealed Temperature Sensor
Interchangeable with the smaller temp sensor that is listed. Referred to as thermistor in story section.
×2

Software apps and online services

Maker service
IFTTT Maker service
fritzing
Particle Build Web IDE
Particle Build Web IDE
Google Sheets
Google Sheets

Story

Read more

Custom parts and enclosures

Main Fritzing File

Temp Sensor 1 and 2 Fritzing File

Schematics

Main Photon Schematic

This is the schematic for the main photon unit. It includes two white LED lights, a 220 Ω resistor and a jumper wire to connect the LEDs to ground on the photon.

The two temperature sensor photons publish their data to the particle cloud. This main photon is subscribed to both photons with specific event name codes. When this main photon sees that temp sensor 1 or 2 has successfully uploaded temp data to the cloud, it blinks the corresponding LED. This indicates that both the temp sensor itself is functioning properly, and that this main photon is successfully subscribed to the individual temp sensors.

When this main photon has both of its LED indicators on at the same time, it sends a signal to the temp sensors to blink their D7 photon LEDs. This allows for someone on the temp sensor end of the system to see that the entire system is functioning properly. If the D7 on a temp sensor does not blink, then this tells the user that the system has at least one temp sensor not functioning properly. This is also shown by the main photon's white LED indicators blinking for the specific temp sensors. If one of the white LEDs on this photon is not blinking periodically, then the corresponding temp sensor is not functioning properly.

Temp Sensor 1 and 2 Schematic

Temp sensor 1 and 2 are physically exactly the same. It should be noted that the OLED screen shown in the schematic is not the OLED screen used in this project, as the Fritzing software did not have the correct OLED screen file.

To setup these photons, individually, one requires an OLED screen from the particle maker kit, a 4.7 kΩ resistor, either a small three prong temp sensor or a sealed temp sensor (both are also in the maker kit), and many jumper wires.

The differences between the temp sensors comes in the code. They publish their own distinct data ID to the particle cloud, temp1sensor123987 and temp2sensor987123 for temp sensors 1 and 2 respectively. These must be unique for each sensor because in order to subscribe to the data correctly, it helps for the data to have its own unique ID, so as to avoid any unnecessary data overlapping with other people who may be doing similar projects.

Code

Main Photon Code

C/C++
// This #include statement was automatically added by the Particle IDE.
#include <Adafruit_SSD1306.h>

int temperature;

int LEDConfirm1 = D0;
int LEDConfirm2 = D5;

bool Globalsend1, Globalsend2;

void setup() {
    pinMode(LEDConfirm1, OUTPUT);
    pinMode(LEDConfirm2, OUTPUT);
    pinMode(D7, OUTPUT);


    Particle.subscribe("temp1sensor123987", Temp1);
    
    Particle.subscribe("temp2sensor987123", Temp2);
    
}


void loop() 
{
    digitalWrite(D7, HIGH);
    delay(3000);
    digitalWrite(D7, LOW);
    delay(3000);
    if (Globalsend1 == TRUE && Globalsend2 == TRUE)
    {
        Particle.publish("MainDevice178239");
        SendData();
    }
    else
    {
        return;
    }
    return;
    return;
    return;
}

void Temp1(const char *event, const char *data)
{
    
    digitalWrite(LEDConfirm1, HIGH);
    delay(3000);
    digitalWrite(LEDConfirm1, LOW);
    delay(1000);
    Globalsend1 = TRUE;
    return;
}


void Temp2(const char *event, const char *data)
{
    
    digitalWrite(LEDConfirm2, HIGH);
    delay(3000);
    digitalWrite(LEDConfirm2, LOW);
    delay(1000);
    Globalsend2 = TRUE;
    return;
}


void SendData()
{
    String MainConfirmation = "hello";
    Particle.publish("MainDevice178239", MainConfirmation);
    delay(10000);
    return;
}

Temp Sensor 1 Code

C/C++
// This #include statement was automatically added by the Particle IDE.
#include <Adafruit_SSD1306.h>

// This #include statement was automatically added by the Particle IDE.
#include <OneWire.h>


/*
This sketch is used to read the temperature from
the water resistant temperature sensor obtained
from the particle maker kit.  It will read the data
from the sensor then publish it to the particle cloud
from which IFTTT can be used to log the date, time
and temp to a google spreadsheet.

This information was obtained from a tutorial on particle.io
which can be found at:
https://docs.particle.io/tutorials/topics/maker-kit


*/

/************************************************************************
This sketch reads the temperature from a 1-Wire device and then publishes
to the Particle cloud. From there, IFTTT can be used to log the date,
time, and temperature to a Google Spreadsheet. Read more in our tutorial
here: https://docs.particle.io/tutorials/topics/maker-kit

This sketch is the same as the example from the OneWire library, but
with the addition of three lines at the end to publish the data to the
cloud.

Use this sketch to read the temperature from 1-Wire devices
you have attached to your Particle device (core, p0, p1, photon, electron)

Temperature is read from: DS18S20, DS18B20, DS1822, DS2438

Expanding on the enumeration process in the address scanner, this example
reads the temperature and outputs it from known device types as it scans.

I/O setup:
These made it easy to just 'plug in' my 18B20 (note that a bare TO-92
sensor may read higher than it should if it's right next to the Photon)

D3 - 1-wire ground, or just use regular pin and comment out below.
D4 - 1-wire signal, 2K-10K resistor to D5 (3v3)
D5 - 1-wire power, ditto ground comment.

A pull-up resistor is required on the signal line. The spec calls for a 4.7K.
I have used 1K-10K depending on the bus configuration and what I had out on the
bench. If you are powering the device, they all work. If you are using parisidic
power it gets more picky about the value.
************************************************************************/

#define OLED_DC     D3
#define OLED_CS     D2
#define OLED_RESET  D5
Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
int sensorPin = D4;
int x, minX;                        //variable for scrolling code

OneWire ds = OneWire(D4);  // 1-wire signal on pin D4

unsigned long lastUpdate = 0;

float lastTemp;

void myHandler (const char *event, const char *data)
{
    digitalWrite(D7, HIGH);
    delay(5000);
    digitalWrite(D7, LOW);
    delay(1000);
}

void setup() {
    
    Serial.begin(9600);
    // Set up 'power' pins, comment out if not used!
    pinMode(D3, OUTPUT);
    pinMode(D5, OUTPUT);
    digitalWrite(D3, LOW);
    digitalWrite(D5, HIGH);
  
    display.begin(SSD1306_SWITCHCAPVCC);
    
    //displayed text information
    display.setTextSize(3);         //text size
    display.setTextColor(WHITE);    //text color
    
    pinMode(D7, OUTPUT);
    Particle.subscribe("MainDevice178239", myHandler);

}

// up to here, it is the same as the address acanner
// we need a few more variables for this example

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  // The order is changed a bit in this example
  // first the returned address is printed

  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  // second the CRC is checked, on fail,
  // print error and just return to try again

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();

  // we have a good address at this point
  // what kind of chip do we have?
  // we will set a type_s value for known types or just return

  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS1820/DS18S20");
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    case 0x26:
      Serial.println("  Chip = DS2438");
      type_s = 2;
      break;
    default:
      Serial.println("Unknown device type.");
      return;
  }

  // this device has temp so let's read it

  ds.reset();               // first clear the 1-wire bus
  ds.select(addr);          // now select the device we just found
  // ds.write(0x44, 1);     // tell it to start a conversion, with parasite power on at the end
  ds.write(0x44, 0);        // or start conversion in powered mode (bus finishes low)

  // just wait a second while the conversion takes place
  // different chips have different conversion times, check the specs, 1 sec is worse case + 250ms
  // you could also communicate with other devices if you like but you would need
  // to already know their address to select them.

  delay(1000);     // maybe 750ms is enough, maybe not, wait 1 sec for conversion

  // we might do a ds.depower() (parasite) here, but the reset will take care of it.

  // first make sure current values are in the scratch pad

  present = ds.reset();
  ds.select(addr);
  ds.write(0xB8,0);         // Recall Memory 0
  ds.write(0x00,0);         // Recall Memory 0

  // now read the scratch pad

  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE,0);         // Read Scratchpad
  if (type_s == 2) {
    ds.write(0x00,0);       // The DS2438 needs a page# to read
  }

  // transfer and print the values

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s == 2) raw = (data[2] << 8) | data[1];
  byte cfg = (data[4] & 0x60);

  switch (type_s) {
    case 1:
      raw = raw << 3; // 9 bit resolution default
      if (data[7] == 0x10) {
        // "count remain" gives full 12 bit resolution
        raw = (raw & 0xFFF0) + 12 - data[6];
      }
      celsius = (float)raw * 0.0625;
      break;
    case 0:
      // at lower res, the low bits are undefined, so let's zero them
      if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
      if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
      if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
      // default is 12 bit resolution, 750 ms conversion time
      celsius = (float)raw * 0.0625;
      break;

    case 2:
      data[1] = (data[1] >> 3) & 0x1f;
      if (data[2] > 127) {
        celsius = (float)data[2] - ((float)data[1] * .03125);
      }else{
        celsius = (float)data[2] + ((float)data[1] * .03125);
      }
  }

  // remove random errors
  if((((celsius <= 0 && celsius > -1) && lastTemp > 5)) || celsius > 125) {
      celsius = lastTemp;
  }

  fahrenheit = celsius * 1.8 + 32.0;
  lastTemp = celsius;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");

  // now that we have the readings, we can publish them to the cloud
  String temperature = String(fahrenheit); // store temp in "temperature" string
  Particle.publish("temp1sensor123987", temperature);
  delay(25000); // 20 second delay
  
  //Code to display the temperature data on the OLED screen
  display.clearDisplay();
  display.setCursor(x/2, 7);
  display.print(fahrenheit);
  display.print("  *F");
  display.display();
}

Temp Sensor 2 Code

C/C++
// This #include statement was automatically added by the Particle IDE.
#include <OneWire.h>

// This #include statement was automatically added by the Particle IDE.
#include <Adafruit_SSD1306.h>


/*
This sketch is used to read the temperature from
the water resistant temperature sensor obtained
from the particle maker kit.  It will read the data
from the sensor then publish it to the particle cloud
from which IFTTT can be used to log the date, time
and temp to a google spreadsheet.

This information was obtained from a tutorial on particle.io
which can be found at:
https://docs.particle.io/tutorials/topics/maker-kit


*/

/************************************************************************
This sketch reads the temperature from a 1-Wire device and then publishes
to the Particle cloud. From there, IFTTT can be used to log the date,
time, and temperature to a Google Spreadsheet. Read more in our tutorial
here: https://docs.particle.io/tutorials/topics/maker-kit

This sketch is the same as the example from the OneWire library, but
with the addition of three lines at the end to publish the data to the
cloud.

Use this sketch to read the temperature from 1-Wire devices
you have attached to your Particle device (core, p0, p1, photon, electron)

Temperature is read from: DS18S20, DS18B20, DS1822, DS2438

Expanding on the enumeration process in the address scanner, this example
reads the temperature and outputs it from known device types as it scans.

I/O setup:
These made it easy to just 'plug in' my 18B20 (note that a bare TO-92
sensor may read higher than it should if it's right next to the Photon)

D3 - 1-wire ground, or just use regular pin and comment out below.
D4 - 1-wire signal, 2K-10K resistor to D5 (3v3)
D5 - 1-wire power, ditto ground comment.

A pull-up resistor is required on the signal line. The spec calls for a 4.7K.
I have used 1K-10K depending on the bus configuration and what I had out on the
bench. If you are powering the device, they all work. If you are using parisidic
power it gets more picky about the value.
************************************************************************/

#define OLED_DC     D3
#define OLED_CS     D2
#define OLED_RESET  D5
Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
int sensorPin = D4;
int x, minX;                        //variable for scrolling code

OneWire ds = OneWire(D4);  // 1-wire signal on pin D4

unsigned long lastUpdate = 0;

float lastTemp;

void myHandler (const char *event, const char *data)
{
    digitalWrite(D7, HIGH);
    delay(5000);
    digitalWrite(D7, LOW);
    delay(1000);
}

void setup() {
    
    Serial.begin(9600);
    // Set up 'power' pins, comment out if not used!
    pinMode(D3, OUTPUT);
    pinMode(D5, OUTPUT);
    digitalWrite(D3, LOW);
    digitalWrite(D5, HIGH);
  
    display.begin(SSD1306_SWITCHCAPVCC);
    
    //displayed text information
    display.setTextSize(3);         //text size
    display.setTextColor(WHITE);    //text color
    
    pinMode(D7, OUTPUT);
    Particle.subscribe("MainDevice178239", myHandler);

}

// up to here, it is the same as the address acanner
// we need a few more variables for this example

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  // The order is changed a bit in this example
  // first the returned address is printed

  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  // second the CRC is checked, on fail,
  // print error and just return to try again

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();

  // we have a good address at this point
  // what kind of chip do we have?
  // we will set a type_s value for known types or just return

  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS1820/DS18S20");
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    case 0x26:
      Serial.println("  Chip = DS2438");
      type_s = 2;
      break;
    default:
      Serial.println("Unknown device type.");
      return;
  }

  // this device has temp so let's read it

  ds.reset();               // first clear the 1-wire bus
  ds.select(addr);          // now select the device we just found
  // ds.write(0x44, 1);     // tell it to start a conversion, with parasite power on at the end
  ds.write(0x44, 0);        // or start conversion in powered mode (bus finishes low)

  // just wait a second while the conversion takes place
  // different chips have different conversion times, check the specs, 1 sec is worse case + 250ms
  // you could also communicate with other devices if you like but you would need
  // to already know their address to select them.

  delay(1000);     // maybe 750ms is enough, maybe not, wait 1 sec for conversion

  // we might do a ds.depower() (parasite) here, but the reset will take care of it.

  // first make sure current values are in the scratch pad

  present = ds.reset();
  ds.select(addr);
  ds.write(0xB8,0);         // Recall Memory 0
  ds.write(0x00,0);         // Recall Memory 0

  // now read the scratch pad

  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE,0);         // Read Scratchpad
  if (type_s == 2) {
    ds.write(0x00,0);       // The DS2438 needs a page# to read
  }

  // transfer and print the values

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s == 2) raw = (data[2] << 8) | data[1];
  byte cfg = (data[4] & 0x60);

  switch (type_s) {
    case 1:
      raw = raw << 3; // 9 bit resolution default
      if (data[7] == 0x10) {
        // "count remain" gives full 12 bit resolution
        raw = (raw & 0xFFF0) + 12 - data[6];
      }
      celsius = (float)raw * 0.0625;
      break;
    case 0:
      // at lower res, the low bits are undefined, so let's zero them
      if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
      if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
      if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
      // default is 12 bit resolution, 750 ms conversion time
      celsius = (float)raw * 0.0625;
      break;

    case 2:
      data[1] = (data[1] >> 3) & 0x1f;
      if (data[2] > 127) {
        celsius = (float)data[2] - ((float)data[1] * .03125);
      }else{
        celsius = (float)data[2] + ((float)data[1] * .03125);
      }
  }

  // remove random errors
  if((((celsius <= 0 && celsius > -1) && lastTemp > 5)) || celsius > 125) {
      celsius = lastTemp;
  }

  fahrenheit = celsius * 1.8 + 32.0;
  lastTemp = celsius;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");

  // now that we have the readings, we can publish them to the cloud
  String temperature = String(fahrenheit); // store temp in "temperature" string
  Particle.publish("temp2sensor987123", temperature);
  delay(25000); // 2 second delay
  
  //Code to display the temperature data on the OLED screen
  display.clearDisplay();
  display.setCursor(x/2, 7);
  display.print(fahrenheit);
  display.print("  *F");
  display.display();
}

Credits

John Fridy

John Fridy

1 project • 3 followers
Jonathan Borkowski

Jonathan Borkowski

1 project • 2 followers
Nicholas Kakavitsas

Nicholas Kakavitsas

1 project • 3 followers

Comments