// Tea Alarm - t.trevethan 2016
// Arduino Uno or Pro Mini
//
// MLX9015 IR thermometor connected to pins 2 (SCL) and 3 (SDA)
// ESP8266-01 connected to pins 10 (RX) and 11 (TX)
// Red LED connected to pin 5
// Blue LED connected to pin 6
// Piezo buzzer connected to pin 8
// Status LED connected to pin 9
// Battery+ connected to A4 through 19.4kOhm/4.7kOhm voltage divider
//define the pins for software I2C
#define SDA_PORT PORTD
#define SDA_PIN 3 //define the SDA pin
#define SCL_PORT PORTD
#define SCL_PIN 2 //define the SCL pin
#define INPUT_SIZE 80 // size of string array for parsing
//include libraries for IR thermometer, buzzer tones, EEPROM access, software serial and strings
#include "MLX90615.h"
#include "pitches.h"
#include
#include //Software Serial to interface with ESP8266
#include
#include // progmem library
SoftwareSerial ESP8266(10,11); //RX,TX
MLX90615 mlx90615;
#define IP "184.106.153.149" // thingspeak.com IP address
String GET1 = "GET /apps/thinghttp/send_request?api_key=XXXXXXXXXXXXXXXXXX"; // GET request to activate thingHTTP app
String GET2 = "GET /update?api_key=XXXXXXXXXXXXXX&field"; // GET request to send temperature data to thingspeak
String field1="1="; // object temperature
String field2="&field2="; // ambient temperature
String field3="&field3="; // mode
String field4="&field4="; // battery voltage
String field5="&field5="; // threshold temperature
String TWT = "GET /apps/thingtweet/1/statuses/update?key=XXXXXXXXXXXXXXXX&status=Oh%20no!%20I%20am%20now%20";
String twtf1 = "%20degrees%20after%20being%20left%20for%20";
String twtf2 = "%20minutes%20%23coldtea%0A";
String cmd;
// melodies for acknowledgements:
int melody1[] = {
NOTE_A6, NOTE_E6};
int melody2[] = {
NOTE_D7,NOTE_D7,NOTE_D7};
int melody3[] = {
NOTE_E6, NOTE_A6};
//pins
int ledb = 6; // blue led
int ledr = 5; // red led
int buzz = 8; // buzzer
int statled = 9; // status
float otemp = 0.0; // object temperature
float atemp = 0.0; // ambient temperature
float oldtemp = 0.0; // previous object temperatures
float oldtemp2 = 0.0;
float bb = 0.0; // blue temperature scale variable
float br = 0.0; // red temperature scale variable
int brightnessb = 0; // blue LED PWM intensity for analogueWrite()
int brightnessr = 0; // red LED PWM intensity for analogueWrite()
unsigned long tcool = 0; // time elapsed since tea placed on device (ms)
unsigned long tpost = 0; // time elapsed since last thingspeak post
int ipost = 0;
int incon = 0; // active WiFi connection to local AP
float Aref = 1.08598; // the internal reference voltage for battery measurement
float vbat; // battery voltage
// the threshold temperature
// retrieve from EEPROM address 1 and convert to float
int address = 1;
byte value;
float ttemp;
float tcold = 35.0; // the 'tea gone cold' threshold
// the device mode:
// 0: initialise - no cup
// 1: hot cup present
// 2: hot cup cooling
// 3: hot cup cooled past threshold - alarm
// 4: tea gone cold - send tweet
// 5: temperature setting mode
int ttmode = 0;
// function to blink the status LED
void stat_blink(int dur,int del,int rpt, int pin)
{
for(int i = 0; i < rpt; i++)
{
digitalWrite(pin,HIGH);
delay(dur);
digitalWrite(pin,LOW);
delay(del);
}
}
void setup()
{
analogReference(INTERNAL); // use the internal ~1.1volt reference for battery indication
value = EEPROM.read(address); // retrieve from EEPROM address 1
ttemp = value; // convert to float
Serial.begin(9600); // UART serial for debugging
Serial.print("ttemp: ");
Serial.println(ttemp);
mlx90615.init(); // initialize soft i2c wires
ESP8266.begin(9600); // start communication with the ESP8266 via soft serial
pinMode(ledb, OUTPUT);
pinMode(ledr, OUTPUT);
pinMode(statled, OUTPUT);
// if hot cup detected on switch on then bypass the WiFi initialisation
delay(1000);
Serial.print("init t: ");
otemp = mlx90615.printTemperature(MLX90615_OBJECT_TEMPERATURE); // sample cup temp
Serial.println(otemp);
if(otemp < 40.0) { // do WiFi initialisation process: GET http://168.192.4.1/~ssid~password~
delay(4000);
while(ESP8266.available()) { // clearing the softserial buffer
Serial.write(ESP8266.read());
}
ESP8266.println("AT+CWJAP?");
Serial.println("Check AP");
delay(2000);
if(ESP8266.find("No AP")) // if no AP connected, set up server to recieve new credentials
{
Serial.println("No AP");
ESP8266.println("AT+CWMODE=2"); // set AP mode
stat_blink(200,200,4,statled);
delay(500);
ESP8266.println("AT+CIPMUX=1"); // enable multiple TCP connections
stat_blink(400,400,3,statled);
ESP8266.println("AT+CIPSERVER=1,80");
// Serial.println("Setting up local server");
delay(1000);
while(ESP8266.available()) { // clearing the softserial buffer
Serial.write(ESP8266.read());
}
stat_blink(100,100,100,statled); // flash statled rapidly for 20 seconds while recieving formatted HTTP request
char input[INPUT_SIZE + 1];
byte size = ESP8266.readBytes(input, INPUT_SIZE); // read serial stream into string
Serial.print("size: ");
Serial.println(size);
Serial.println(input);
input[size] = 0;
char* dum = strtok(input, "~"); // using the strtok function to parse the serial string
char* ssid = strtok(NULL, "~");
char* psswrd = strtok(NULL, "~");
Serial.print("SSID: ");
Serial.println(ssid);
Serial.print("password: ");
Serial.println(psswrd);
ESP8266.println("AT+RST"); // this resets the ESP8266.
Serial.println("AT+RST");
delay(5500);
stat_blink(100,100,4,statled);
ESP8266.println("AT+CWMODE=1"); // set the ESP to station mode
delay(2000);
stat_blink(100,100,3,statled);
while(ESP8266.available()){ // clear serial buffer
Serial.write(ESP8266.read());
}
cmd = "AT+CWJAP=\"";
cmd += ssid;
cmd += "\",\"";
cmd += psswrd;
cmd += "\"";
Serial.println(cmd); // join the specified AP
ESP8266.println(cmd); // connect to given WiFi SSID
delay(5000);
if(ESP8266.find("CONNECTED")){
// Serial.println("CONNECTED");
incon = 1;
digitalWrite(statled,HIGH);
}
else{
// Serial.println("Not connected");
incon = 0;
digitalWrite(statled,LOW);
}
}
else { // AP already set up: good to go and switch on status LED
// Serial.println("CONNECTED");
incon = 1;
digitalWrite(statled,HIGH);
}
}
else { // if hot cup on turn on, switch off updates and turn off status LED
// Serial.println("WIFI NOT CONNECTED");
incon = 0;
digitalWrite(statled,LOW);
}
}
/////////main loop
void loop()
{
unsigned long currentMillis = millis(); // get the current time (in ms)
oldtemp2 = oldtemp; // save previous values of object temperature
oldtemp = otemp;
Serial.print("o: ");
otemp = mlx90615.printTemperature(MLX90615_OBJECT_TEMPERATURE); // get the object temperature
Serial.println(otemp);
Serial.print("a: ");
atemp = mlx90615.printTemperature(MLX90615_AMBIENT_TEMPERATURE); // get the ambient temperature
Serial.println(atemp);
int avalue = analogRead(A4); //read divided voltage from A1
vbat = avalue*Aref*5.255319/1024; //battery voltage
// detect hot cup (either at startup or after being placed on detector) - this is done by detecting an increase in object temperature above the threshold
// and place into mode 1
if(otemp > ttemp && ttmode == 0) {
ttmode = 1;
Serial.println("mode 1");
//play acnowledgement
for (int thisNote = 0; thisNote < 2; thisNote++) {
tone(buzz, melody3[thisNote], 200);
delay(200);
noTone(buzz);
}
}
// detect steady temperature from hot cup - temperature remaining above threshold for three cycles
// and place into mode 2
if(otemp > ttemp && oldtemp > ttemp && oldtemp2 > ttemp && ttmode == 1) {
ttmode = 2;
Serial.println("mode 2");
//play acnowledgement
for (int thisNote = 0; thisNote < 2; thisNote++) {
tone(buzz, melody1[thisNote], 200);
delay(200);
noTone(buzz);
}
tcool = currentMillis; // start the timer
if(incon == 1) {
tpost = currentMillis - 10000; // start posting in 15 seconds
ESP8266.println("AT+RST"); // reset the ESP8266.
Serial.println("RST");
}
}
// detect cup removed from detector: sharp drop in temperature (> 10 degrees) over 1 cycle.
// and place into mode 0
if(otemp < (ttemp - 2.0) && oldtemp > (ttemp + 2.0) && ttmode == 2) {
ttmode = 0;
Serial.println("Cup removed");
tcool = 0; // reset the timer
}
// temperature dropped below threshold
// and place into mode 3 and sound alarm
if(otemp < ttemp && otemp > (ttemp - 2.0) && oldtemp < (ttemp + 2.0) && ttmode == 2) {
ttmode = 3;
Serial.println("alarm: mode 3");
int timepassed = (currentMillis - tcool)/1000; // time since mode 2 entered (s)
int mins = timepassed/60; // convert to minutes
int secs = timepassed%60; // and seconds
Serial.print("cooling: ");
Serial.print(mins);
Serial.print(" mins ");
Serial.print(secs);
Serial.println(" secs");
if(incon == 1) {
// send SMS message via ThingHTTP
ESP8266.println("AT+RST"); // this resets the ESP8266.
Serial.println("RST");
delay(7000);
// ESP8266.println("AT"); // check ESP8266 is OK
// delay(1500)
// if(ESP8266.find("OK")){
// Serial.println("OK");
// Serial.println("Connected");
// }
cmd = "AT+CIPSTART=\"TCP\",\""; // connect with thingspeak server
cmd += IP; // concatenating the cmd string with IP
cmd += "\",80"; // port 80
ESP8266.println(cmd); // pass command to ESP8266
Serial.println(cmd);
delay(5000);
if(ESP8266.find("Error")){
Serial.println("CIPSTART Error");
delay(5000);
ESP8266.println(cmd);
Serial.println(cmd);
delay(5000);
}
cmd = GET1; // sending HTTP get to ThingHTTP
cmd += "\r\n\r\n";
ESP8266.print("AT+CIPSEND=");
ESP8266.println(cmd.length());
Serial.print("AT+CIPSEND=");
Serial.println(cmd.length());
delay(8000);
if(ESP8266.find(">")){ // check that the prompt is recieved
ESP8266.print(cmd); // pass GET command to ESP8266
Serial.print(">");
Serial.println(cmd);
}
else
{
Serial.println("CIPSEND error");
ESP8266.println("AT+RST"); // try again
delay(7000);
ESP8266.println(cmd); // pass command to ESP8266
Serial.println(cmd);
delay(5000);
if(ESP8266.find("Error")){
delay(5000);
ESP8266.println(cmd);
Serial.println(cmd);
delay(5000);
}
cmd = GET1; // sending HTTP get to ThingHTTP
cmd += "\r\n\r\n";
ESP8266.print("AT+CIPSEND=");
ESP8266.println(cmd.length());
Serial.print("AT+CIPSEND=");
Serial.println(cmd.length());
delay(8000);
if(ESP8266.find(">")){ // check that the prompt is recieved
ESP8266.print(cmd); // pass GET command to ESP8266
Serial.print(">");
Serial.println(cmd);
}
}
ipost = 0;
tpost = currentMillis - 10000; // start posting in 10 seconds
}
//sound alarm (40 cycles)
for (int thisNote = 0; thisNote < 30; thisNote++) {
tone(buzz, melody2[0], 200);
delay(200);
noTone(buzz);
delay(300);
Serial.println("ALARM!");
//if the cup is picked up, stop the alarm and enter mode 0
// get the object temperature
otemp = mlx90615.printTemperature(MLX90615_OBJECT_TEMPERATURE);
if(otemp < tcold) {
thisNote = 40;
ttmode = 0;
Serial.println("alarm cancelled");
tcool = 0;
}
}
}
// temperature dropped below cold threshold
// and place into mode 0 and send tweet
if(otemp < tcold && otemp > tcold - 2.0 && ttmode == 3) {
ttmode = 0;
Serial.println("Tea cold: mode 0");
int timepassed = (currentMillis - tcool)/1000; // time since mode 2 entered (s)
int mins = timepassed/60; // convert to minutes
int secs = timepassed%60; // and seconds
// send tweet
ESP8266.println("AT+RST"); // this resets the ESP8266.
Serial.println("AT+RST");
delay(5000);
cmd = "AT+CIPSTART=\"TCP\",\""; // connect with thingspeak server
cmd += IP; // concatenating the cmd string with IP
cmd += "\",80"; // port 80
ESP8266.println(cmd); // pass command to ESP8266
Serial.println(cmd);
delay(4000);
if(ESP8266.find("Error")){
Serial.println("AT+CIPSTART Error");
delay(5000);
ESP8266.println(cmd);
Serial.println(cmd);
delay(3000);
}
cmd = TWT; // command to ThingTweet
cmd += otemp;
cmd += twtf1;
cmd += mins;
cmd += twtf2;
cmd += "\r\n\r\n";
ESP8266.print("AT+CIPSEND=");
ESP8266.println(cmd.length());
Serial.print("AT+CIPSEND=");
Serial.println(cmd.length());
delay(7000);
if(ESP8266.find(">")){ // check that the prompt is recieved
ESP8266.print(cmd); // pass GET command to ESP8266
Serial.print(">");
Serial.println(cmd);
}
else
{
Serial.println("CIPSEND error");
}
tcool = 0; //reset timer
}
// cup removed when in alarm mode
if(otemp < 30.0 && ttmode == 3) {
ttmode = 0;
Serial.println("Cup removed");
tcool = 0; // reset timer
}
// light the RGB led according the object temperature in mode 1,2,3 - red for hot and blue for cold
if(ttmode == 1 || ttmode == 2 || ttmode == 3) {
br = constrain((otemp - 30.0)*8.5,0,255); // constrain the temperature range 30C - 60C to 0 - 255
brightnessr = (int)br; // convert to integers
brightnessb = 255-br;
}
else {
brightnessr = 0; // or turn off in any other mode
brightnessb = 0;
}
analogWrite(ledr, brightnessr);
analogWrite(ledb, brightnessb);
// enter threshold temperature programming mode: sub-zero (frozen object temperature for 3 cycles)
// mode 5
if(otemp < 0.0 && oldtemp < 0.0 && oldtemp2 < 0.0 && ttmode == 0) {
ttmode = 5;
Serial.println("mode 5");
//play acnowledgement amd blink blue led three times
for (int thisNote = 0; thisNote < 3; thisNote++) {
tone(buzz, melody2[thisNote], 200);
brightnessb = 255;
analogWrite(ledb, brightnessb);
delay(200);
brightnessb = 0;
analogWrite(ledb, brightnessb);
noTone(buzz);
delay(400);
}
delay(2000);
// loop 24 times to record program:
// object temperature is sampled every 2 seconds. If it is below zero, ttemp is reduced by 1. If it is above 40C, ttemp is increased by 1.
for(int pcount = 0; pcount < 24; pcount++) {
otemp = mlx90615.printTemperature(MLX90615_OBJECT_TEMPERATURE);
Serial.print("O temp: ");
Serial.println(otemp);
if(otemp < 0.0) {
ttemp = ttemp - 1; // reduce the threshold temperature by 1 degree
brightnessb = 255;
analogWrite(ledb, brightnessb); // flash cold (blue) acknowedgement
tone(buzz, NOTE_C7, 200); // beep once
delay(200);
brightnessb = 0;
analogWrite(ledb, brightnessb);
noTone(buzz);
Serial.print("- ttemp: ");
Serial.println(ttemp);
}
else if(otemp > 40.0) {
ttemp = ttemp + 1; // increase the threshold temperature by 1 degree
brightnessr = 255;
analogWrite(ledr, brightnessr); // flash red (hot) acknowedgement
tone(buzz, NOTE_C7, 200); // beep once
delay(200);
brightnessr = 0;
analogWrite(ledr, brightnessr);
noTone(buzz);
Serial.print("+ ttemp: ");
Serial.println(ttemp);
}
// if sampled object temperature between 0 and 40C, exit.
else {
pcount = 24; // exit program mode
}
delay(2000);
}
Serial.print("Exiting");
//play acnowledgement and flash blue LED
for (int thisNote = 0; thisNote < 3; thisNote++) {
tone(buzz, melody2[thisNote], 200);
brightnessb = 255;
analogWrite(ledb, brightnessb);
delay(100);
brightnessb = 0;
analogWrite(ledb, brightnessb);
noTone(buzz);
delay(100);
}
ttmode = 0;
value = ttemp;
EEPROM.write(address, value); // write the new ttemp value to the EEPROM
}
Serial.print("Mode: ");
Serial.println(ttmode);
// post to data thingspeak every 60 seconds if in mode 1,2 or 3
if(incon == 1) {
if(ttmode == 2 || ttmode == 3)
{
if(currentMillis - tpost >= 20000 && ipost == 0)
{
tpost = currentMillis;
ipost = 1;
cmd = "AT+CIPSTART=\"TCP\",\""; // connect with thingspeak server
cmd += IP; // concatenating the cmd string with IP
cmd += "\",80"; // port 80
ESP8266.println(cmd); // pass command to ESP8266
Serial.println(cmd);
}
if(currentMillis - tpost >= 8000 && ipost == 1)
{
if(ESP8266.find("Error")){
Serial.println("CIPSTART Error");
}
tpost = currentMillis;
ipost = 2;
cmd = GET2;
cmd += field1;
cmd += otemp;
cmd += field2;
cmd += atemp;
cmd += field3;
cmd += ttmode;
cmd += field4;
cmd += vbat;
cmd += field5;
cmd += ttemp;
cmd += "\r\n\r\n";
ESP8266.print("AT+CIPSEND=");
ESP8266.println(cmd.length());
Serial.print("AT+CIPSEND=");
Serial.println(cmd.length());
}
if(currentMillis - tpost >= 8000 && ipost == 2)
{
tpost = currentMillis;
ipost = 0;
if(ESP8266.find(">")){
ESP8266.print(cmd); // GET command string to ESP8266
Serial.print(">");
Serial.println(cmd);
}
else
{
Serial.println("CIPSEND error2");
}
}
}
}
delay(1000);
}