Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
Stephanie VicarteIrak MayerElizabeth Vicarte
Published © GPL3+

Heatstroke Prevention for Babies and Pets

A safety device that monitors and notifies parents of temperatures inside cars to eliminate the risk of infants or pets overheating inside.

IntermediateFull instructions providedOver 1 day1,038
Heatstroke Prevention for Babies and Pets

Things used in this project

Hardware components

Boron
Particle Boron
×1
Adafruit TFT FeatherWing - 2.4" 320x240 Touchscreen For All Feathers
×1
Adafruit Ultimate GPS FeatherWing
×1
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1
Grove - Round Force Sensor (FSR402)
Seeed Studio Grove - Round Force Sensor (FSR402)
×1

Software apps and online services

Visual Studio Code Extension for Arduino
Microsoft Visual Studio Code Extension for Arduino

Story

Read more

Schematics

Event program flow chart

Is the Boron program flow chart for the temperature

Heat Stroke Prevention System

Overall flow for the Heat Stroke Prevention System

Code

TempDHT.ino

Arduino
This program uses the Particle Boron LTE board and the DHT022 temperature sensor to create a warning heat detector to prevent heat stroke on babies left unattended on closed cars during hot days. The system uses webhooks to call Twilio and send SMS messages to upto 2 contact emergency numbers.
/*

Program: TempDHT.ino
Description: This program uses the Particle Boron LTE board and the DHT022 temperature sensor to create a warning heat detector to prevent heat stroke on babies left
unattended on closed cars during hot days. The system uses webhooks to call Twilio and send SMS messages to upto 2 contact emergency numbers.

    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.


*/

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

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

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

//The following are credits of sample program I use as starters 
/***************************************************
  This is our GFX example for the Adafruit ILI9341 Breakout and Shield
  ----> http://www.adafruit.com/products/1651

  Check out the links above for our tutorials and wiring diagrams
  These displays use SPI to communicate, 4 or 5 pins are required to
  interface (RST is optional)
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/
// Example testing sketch for various DHT humidity/temperature sensors
// Written by ladyada, public domain


#if defined(PARTICLE)
 #include <Adafruit_mfGFX.h>
 #include "Adafruit_ILI9341.h"
 
 // For the Adafruit shield, these are the default.
 #define TFT_DC D5
 #define TFT_CS D4
 
#else
 #include "SPI.h"
 #include <Adafruit_mfGFX.h>
 #include "Adafruit_ILI9341.h"
 
 // For the Adafruit shield, these are the default.
 #define TFT_DC 9
 #define TFT_CS 10
#endif

#include <Adafruit_GPS.h>

// what's the name of the hardware serial port?
#define GPSSerial Serial1

// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);


#define DHTPIN 2     // what pin we're connected to

#define DHTTYPE DHT22		// DHT 22 (AM2302)

String body = "Current Temperature ";
String body_warning = "Temperature WARNING ";
String footer_warning = ". Time elapsed in seconds ";
String body_danger = "DANGER, temperature reach ";
String footer_danger = ". Second contact notified.";
String footer_location = ". Location : ";
String body_panic = "PANIC, open windows contact 911. Temperature ";
String footer_panic = ". Third contact notified";

// Connect pin 1 (on the left) of the sensor to +5V
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

DHT dht(DHTPIN, DHTTYPE);

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, 0);

//Define constants for device status
#define STATUS_UNKNOWN  0
#define STATUS_OK       1
#define STATUS_WARNING  2
#define STATUS_DANGER   3
#define STATUS_PANIC    4
//Define constants for temperature strenght
#define RANGE_OK        81
#define RANGE_WARNING   89
#define RANGE_DANGER    99

#define WARNING_TIME 5 //mins
#define MSG_WARNING_MAX 4 //every 5 mins
#define WARNING_MAX_TIME 15
#define DANGER_TIME 3 //every 3 mins.
#define DANGER_MAX_TIME 15 //after that it becomes panic. 
#define MSG_DANGER_MAX 5
#define PANIC_TIME 1  //every minute

unsigned long thresholdTime=5000;
unsigned long warningThreshold = 1000*60*WARNING_MAX_TIME;
unsigned long dangerThreshold = 1000*60*DANGER_MAX_TIME;
unsigned long warningTriggerThrs = 1000*60*WARNING_TIME;
unsigned long dangerTriggerThrs = 1000*60*DANGER_TIME;  
unsigned long panicTriggerThrs = 1000*60*PANIC_TIME;

const int FSR_PIN = A0;
const int FSR_PIN2 = A1;
unsigned long startTime=0;
unsigned long currentTime=0;
unsigned long eventStartTimer =0; 
unsigned long eventTimer=0;
unsigned long eventLastTimer=0;
unsigned int eventStatus=STATUS_OK;


unsigned int warningTemp = RANGE_OK; //80 degrees Farenhait
unsigned int DangerTemp = RANGE_WARNING;  //95 degrees Farenhait

unsigned int msgCounterWarning = 0;
unsigned int msgCounterDanger = 0;

//Variables that defines the display dimensions.
int rectHeight;
int rectWidth;
int radius;

//More display constants to grid the screen
#define DISCON_MAX  5
#define GRID_MAX_X    1
#define GRID_MAX_Y    1
#define USE_SCREEN_Y    1 
#define USE_SCREEN_DIV  2 
//Amount of time to rescan the network
#define REFRESH_THRESHOLD 3000000
//More display variables
int OffsetX=0;
int OffsetY=0;
int radiusThickness=10;
//Debug control flags. Did not work that well.
#define DEBUG_ESPNOW  1
//#define DEBUG_TS      1
//Device selected flag.
int FlgSelect = 0;

void ClearAreaScreen(int posX,int posY, int cWidth,int cHeight)
{
  tft.fillRect(posX,posY,cWidth,cHeight,ILI9341_BLACK);
}

//This function clear the screen and sets the default values for text.
void ClearScreen()
{
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 2);
  tft.setTextColor(ILI9341_WHITE,ILI9341_BLACK);  
  tft.setTextSize(2);  
}

//This function updates the status of the display interface and logs the changes per device.
void UpdateStatus(int Pos,int numTotX,int Status,int offsetPos)
{
  int PosX = Pos%numTotX;
  int PosY = Pos/numTotX;
  int startY = rectHeight * offsetPos;
  String tmpStr;
  
  switch (Status)
  {
    case STATUS_UNKNOWN:
        tft.fillCircle((PosX*rectWidth)+rectWidth/2,startY + (PosY*rectHeight)+rectHeight/2,radius,ILI9341_WHITE);
      break;
    case STATUS_OK:
        tft.fillCircle((PosX*rectWidth)+rectWidth/2,startY + (PosY*rectHeight)+rectHeight/2,radius,ILI9341_GREEN);
      break;
    case STATUS_WARNING:
        tft.fillCircle((PosX*rectWidth)+rectWidth/2,startY + (PosY*rectHeight)+rectHeight/2,radius,ILI9341_YELLOW);
      break;
    case STATUS_DANGER:
        tft.fillCircle((PosX*rectWidth)+rectWidth/2,startY + (PosY*rectHeight)+rectHeight/2,radius,ILI9341_RED);
      break;
  }
}

//Refresh the status of the cube, depending on the RSSI value passed.
void RefreshCube(int Pos,int Value,int offsetPos)
{
   if (Value <= RANGE_OK)
   {
      radiusThickness=55;
      //eventStatus = STATUS_OK;
      UpdateStatus(Pos,GRID_MAX_X,STATUS_OK,offsetPos);
   }
   else if (Value <= RANGE_WARNING)
   {
      //eventStatus = STATUS_WARNING;
      radiusThickness=55;
      UpdateStatus(Pos,GRID_MAX_X,STATUS_WARNING, offsetPos);
   }
   else if (Value <= RANGE_DANGER)
   {
      radiusThickness=55;
      //eventStatus = STATUS_DANGER;
      UpdateStatus(Pos,GRID_MAX_X,STATUS_DANGER,offsetPos);
   }
   else if (Value > RANGE_DANGER)
   {
      radiusThickness=55;
      //eventStatus = STATUS_PANIC;
      UpdateStatus(Pos,GRID_MAX_X,STATUS_PANIC,offsetPos);
   }
   else
   {
      radiusThickness=55;
      UpdateStatus(Pos,GRID_MAX_X,STATUS_UNKNOWN,offsetPos);
   }
}


//Creates the grid on the display according to the number of bins to supper per row and column
void CreateGrid(int numNodesHorz, int numNodesVert,int offsetPos)
{
  int i,j;
  int cx = USE_SCREEN_Y*(tft.height()/USE_SCREEN_DIV)-1;
  int startY=0;

  rectHeight = cx / numNodesVert;
  rectWidth = tft.width() / numNodesHorz;
  radius = (((rectHeight < rectWidth)? rectHeight: rectWidth)/2)-2;
  startY = rectHeight * offsetPos;

  for(i=0;i<numNodesVert;i++)
  {
    for (j=0;j<numNodesHorz;j++)
    {
      tft.drawRect(j*rectWidth,startY+(i*rectHeight),rectWidth,rectHeight,ILI9341_WHITE);
      tft.fillCircle((j*rectWidth)+rectWidth/2,startY+((i*rectHeight)+rectHeight/2),radius+4,ILI9341_WHITE);
      tft.fillCircle((j*rectWidth)+rectWidth/2,startY+((i*rectHeight)+rectHeight/2),radius-radiusThickness,ILI9341_GREEN);
    }
  }

  tft.drawRect(tft.width()-rectWidth,tft.height()-rectHeight,rectWidth,rectHeight,ILI9341_CYAN);
  
}

void initGPS()
{
  // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
  GPS.begin(9600);
  // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  // Set the update rate
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
  // For the parsing code to work nicely and have time to sort thru the data, and
  // print it out we don't suggest using anything higher than 1 Hz
     
  // Request updates on antenna status, comment out to keep quiet
  GPS.sendCommand(PGCMD_ANTENNA);

  delay(1000);
  
  // Ask for firmware version
  GPSSerial.println(PMTK_Q_RELEASE);
}

void setup() {

  pinMode(FSR_PIN, INPUT);
  pinMode(FSR_PIN2, INPUT);

  Serial.begin(9600);
  Serial.println("ILI9341 Test!"); 
  
  dht.begin();
  tft.begin();

  // read diagnostics (optional but can help debug problems)
  uint8_t x = tft.readcommand8(ILI9341_RDMODE);
  Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDMADCTL);
  Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDPIXFMT);
  Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDIMGFMT);
  Serial.print("Image Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDSELFDIAG);
  Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); 
  

  Serial.println(F("Done!"));
    tft.fillScreen(ILI9341_BLACK);
    CreateGrid(GRID_MAX_X,GRID_MAX_Y,0);
  startTime = millis();

  initGPS();
}

bool doneFlg=false;
#define DATASIZE 48
uint8_t pressData[DATASIZE];
const float VCC = 4.98; // Measured voltage of Ardunio 5V line
const float R_DIV = 3230.0; // Measured resistance of 3.3k resistor

float calcPressure(float fsrADC)
{
  float fsrV;
  float fsrR;
  float fsrG;
  float force = 0.0;

  if (fsrADC != 0) // If the analog reading is non-zero
  {
    // Use ADC reading to calculate voltage:
    fsrV = fsrADC * VCC / 1023.0;
    // Use voltage and static resistor value to 
    // calculate FSR resistance:
    fsrR = R_DIV * (VCC / fsrV - 1.0);
    //Serial.println("Resistance: " + String(fsrR) + " ohms");
    // Guesstimate force based on slopes in figure 3 of
    // FSR datasheet:
    
    fsrG = 1.0 / fsrR; // Calculate conductance
    // Break parabolic curve down into two linear slopes:
    if (fsrR <= 600) 
      force = (fsrG - 0.00075) / 0.00000032639;
    else
      force =  fsrG / 0.000000642857;
  }

  return force;
}

void printTempData()
{
    float hi = dht.getHeatIndex();
    float dp = dht.getDewPoint();
    float k = dht.getTempKelvin();
    float h = dht.getHumidity();
    float t = dht.getTempCelcius();
    float f = dht.getTempFarenheit();

     if (isnan(h) || isnan(t) || isnan(f)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }
 
    tft.fillScreen(ILI9341_BLACK);
    tft.setCursor(0, 0);
    tft.setTextColor(ILI9341_BLUE);
    tft.setTextSize(2);

    tft.print("Humid: "); 
    tft.println(h);
    tft.print("% - ");
    tft.print("Temp: "); 
    tft.println(t);
    tft.print("*C ");
    tft.println(f);
    tft.print("*F ");
    tft.println(k);
    tft.print("*K - ");
    tft.print("DewP: ");
    tft.println(dp);
    tft.print("*C - ");
    tft.print("HeatI: ");
    tft.println(hi);
    tft.println("*C");
    tft.println(Time.timeStr());

}

void printGPSData()
{
    ClearAreaScreen(tft.width()-rectWidth,tft.height()-rectHeight,rectWidth,rectHeight);
    tft.setCursor(12+(tft.width()-rectWidth),(tft.height()-rectHeight)+20); 

    tft.println("\nTime: ");
    tft.print(GPS.hour, DEC); tft.print(':');
    tft.print(GPS.minute, DEC); tft.print(':');
    tft.print(GPS.seconds, DEC); tft.print('.');
    tft.println(GPS.milliseconds);
    tft.print("Date: ");
    tft.print(GPS.day, DEC); tft.print('/');
    tft.print(GPS.month, DEC); tft.print("/20");
    tft.println(GPS.year, DEC);
    tft.print("Fix: "); tft.print((int)GPS.fix);
    tft.println(" quality: "); tft.print((int)GPS.fixquality);
    if (GPS.fix) {
      ClearScreen();
      tft.print("Location: ");
      tft.print(GPS.latitude, 4); Serial.print(GPS.lat);
      tft.print(", ");
      tft.print(GPS.longitude, 4); Serial.println(GPS.lon);
      tft.print("Speed (knots): "); Serial.println(GPS.speed);
      tft.print("Angle: "); Serial.println(GPS.angle);
      tft.print("Altitude: "); Serial.println(GPS.altitude);
      tft.print("Satellites: "); Serial.println((int)GPS.satellites);
    }
  
}

String formatGPS4SMS()
{
  String retval = ""; //"https://www.google.com/maps/?q=1234.56,7891.23";

    if (GPS.fix) 
    {
      String tmpCoord = String(GPS.latitude);
      String tmpCoord2 = tmpCoord.substring(0,2)+"%20"+tmpCoord.substring(2);
      tmpCoord = String(GPS.longitude);
      String tmpCoord3 = tmpCoord.substring(0,2)+"%20"+tmpCoord.substring(2);
      retval = "https://www.google.com/maps/?q="+tmpCoord2+String(GPS.lat)+","+tmpCoord3+String(GPS.lon);

    }

  return retval;
}

void GetGPS()
{
  GPS.read();
  if (GPS.newNMEAreceived()) {
    if (!GPS.parse(GPS.lastNMEA())) 
      return; 
  }
 
}

void sendWarning(float f)
{
  String msg2Send = body_warning+String(f)+footer_warning+String((millis()-eventStartTimer)/1000);
  Particle.publish("twilio_sms", msg2Send, PRIVATE);
}

void sendDanger(float f)
{
  String msg2Send = body_danger+String(f)+footer_warning+String((millis()-eventStartTimer)/1000)+footer_danger;
  Particle.publish("twilio_sms", msg2Send, PRIVATE);
  msg2Send = msg2Send + footer_location + formatGPS4SMS();
  Particle.publish("twilio_warning_sms", msg2Send, PRIVATE);
}

void sendPanic(float f)
{
  String msg2Send = body_panic+String(f)+footer_warning+String((millis()-eventStartTimer)/1000)+footer_panic;
  Particle.publish("twilio_sms", msg2Send, PRIVATE);
  msg2Send = msg2Send + footer_location + formatGPS4SMS();
  Particle.publish("twilio_warning_sms", msg2Send, PRIVATE);
  Particle.publish("twilio_danger_sms", msg2Send, PRIVATE);
}

void loop(void) {

  int pfsrADC = analogRead(FSR_PIN);
  int pfsrADC2 = analogRead(FSR_PIN2);
  float force=0.0;
  float force2=0.0;
 
 //Comment this if you want to overpass the weight sensors.
 if (pfsrADC < 100 && pfsrADC2 < 100)
 {
   delay(10000);
   return;
 }
 
   currentTime = millis();
  GetGPS();

  if (currentTime-startTime > thresholdTime)
  {
    startTime = millis();
    float f = dht.getTempFarenheit();
    
  // Check if any reads failed and exit early (to try again).
    if ( isnan(f)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }

    RefreshCube(0,f,0);
 
    ClearAreaScreen(tft.width()-rectWidth,tft.height()-rectHeight,rectWidth,rectHeight);
    tft.setCursor(36+(tft.width()-rectWidth),(tft.height()-rectHeight)+50); 
    if (eventStatus == STATUS_OK)
      tft.setTextColor(ILI9341_GREEN);
    else if (eventStatus == STATUS_WARNING)
      tft.setTextColor(ILI9341_YELLOW);
    else if (eventStatus == STATUS_DANGER)
       tft.setTextColor(ILI9341_RED);
    else if (eventStatus == STATUS_PANIC)
       tft.setTextColor(ILI9341_PURPLE);
    
    tft.setTextSize(5);
    tft.printf("%3.1f F", f);

    if (f < RANGE_OK)
    {
      if (eventStatus != STATUS_OK)
      {
        eventStatus = STATUS_OK;
        msgCounterWarning = 0;
        msgCounterDanger = 0;
        eventStartTimer = 0;
        eventTimer = 0;
      }
    }
    else if (f > RANGE_OK && f <= RANGE_WARNING)
    {
        if (eventStatus != STATUS_WARNING)
        {
          eventStatus = STATUS_WARNING;
          msgCounterWarning = 0;
          msgCounterDanger = 0;
          eventStartTimer = millis();
          eventTimer = 0;
          sendWarning(f);
        }
        else
        {
          eventTimer = millis();
          if ((eventTimer - eventStartTimer) < warningThreshold)
          {
            if ((eventTimer - eventLastTimer) > warningTriggerThrs)
            {
              sendWarning(f);
              msgCounterWarning++;
              eventLastTimer = eventTimer;
            }
          }
        }
    }
    else if (f > RANGE_WARNING && f < RANGE_DANGER)
    {
        if (eventStatus == STATUS_PANIC)
        {
            eventTimer = millis();
            if ((eventTimer - eventLastTimer) > panicTriggerThrs)
            {
              sendPanic(f);
              msgCounterDanger++;
              eventLastTimer = eventTimer;
            }
        }
        else
        {
          if (eventStatus != STATUS_DANGER)
          {
            eventStatus = STATUS_DANGER;
            msgCounterWarning = 0;
            msgCounterDanger = 0;
            eventStartTimer = millis();
            eventTimer = 0;
            sendDanger(f);
          }
          else
          {
            eventTimer = millis();
            if ((eventTimer - eventStartTimer) < dangerThreshold)
            {
              if ((eventTimer - eventLastTimer) > dangerTriggerThrs)
              {
                sendDanger(f);
                msgCounterDanger++;
                eventLastTimer = eventTimer;
              }
            }
            else
            {
              eventStatus = STATUS_PANIC;
              sendPanic(f);
            }
            if (msgCounterDanger > MSG_DANGER_MAX)
            {
              eventStatus = STATUS_PANIC;
              sendPanic(f);
            }
          }
        }

    }
    else if (f > RANGE_DANGER)
    {
        if (eventStatus != STATUS_PANIC)
        {
          eventStatus = STATUS_PANIC;
          msgCounterWarning = 0;
          msgCounterDanger = 0;
          eventStartTimer = millis();
          eventTimer = 0;
        }
        else
        {
            eventTimer = millis();
            if ((eventTimer - eventLastTimer) > panicTriggerThrs)
            {
              sendPanic(f);
              msgCounterDanger++;
              eventLastTimer = eventTimer;
            }
        }
    }
  }
}

Credits

Stephanie Vicarte
14 projects • 12 followers
Irak Mayer
18 projects • 10 followers
Elizabeth Vicarte
13 projects • 7 followers

Comments