I should really dump this in a Github repo.
Arduino code:
//*****************************************************************************
/// @file
/// @brief
/// Arduino SmartThings Shield Kitchen temp probes
/// @note
/// ______________
/// | |
/// | SW[] |
/// |[]RST |
/// | AREF |--
/// | GND |--
/// | 13 |--X Othercrisp DHT
/// | 12 |--X Moistcrisp DHT
/// | 11 |--X Fridge DHT
/// --| 3.3V 10 |--X Freezer DHT
/// --| 5V 9 |--
/// --| GND 8 |--
/// --| GND |
/// --| Vin 7 |--X CLK
/// | 6 |--X CS
/// --| A0 5 |--X DO for Broiler
/// --| A1 ( ) 4 |--X DO for Oven
/// --| A2 3 |--X THING_RX
/// --| A3 ____ 2 |--X THING_TX
/// --| A4 | | 1 |--
/// --| A5 | | 0 |--
/// |____| |____|
/// |____|
///
//*****************************************************************************
#include //TODO need to set due to some weird wire language linker, should we absorb this whole library into smartthings
#include
#include
//thermocouple additions
#include "SPI.h"
#include "Adafruit_MAX31855.h"
//*****************************************************************************
// Pin Definitions | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
//For the Yun, have to reassign RX pin
#define PIN_THING_RX 8
#define PIN_THING_TX 2
#define DHT_FREEZER_PIN 10
#define DHT_FRIDGE_PIN 11
#define DHT_MOISTCRISP_PIN 12
#define DHT_OTHERCRISP_PIN 13
#define DHTTYPE DHT22
//tcouple additions
#define DO_oven 4
#define DO_broiler 5
#define CS 9
#define CLK 7
DHT dhtFreezer(DHT_FREEZER_PIN, DHTTYPE);
DHT dhtFridge(DHT_FRIDGE_PIN, DHTTYPE);
DHT dhtMoistcrisp(DHT_MOISTCRISP_PIN, DHTTYPE);
DHT dhtOthercrisp(DHT_OTHERCRISP_PIN, DHTTYPE);
//tcouple additions
Adafruit_MAX31855 thermocouple_oven(CLK, CS, DO_oven);
Adafruit_MAX31855 thermocouple_broiler(CLK, CS, DO_broiler);
//*****************************************************************************
// Global Variables | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
SmartThingsCallout_t messageCallout; // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout); // constructor
bool isDebugEnabled; // enable or disable debug in this example
int stateLED; // state to track last set value of LED
int stateNetwork; // state of the network
//*****************************************************************************
// Local Functions | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void on()
{
stateLED = 1; // save state as 1 (on)
smartthing.shieldSetLED(0, 0, 2);
smartthing.send("on"); // send message to cloud
}
//*****************************************************************************
void off()
{
stateLED = 0; // set state to 0 (off)
smartthing.shieldSetLED(0, 0, 0);
smartthing.send("off"); // send message to cloud
}
//*****************************************************************************
void setNetworkStateLED()
{
SmartThingsNetworkState_t tempState = smartthing.shieldGetLastNetworkState();
if (tempState != stateNetwork)
{
switch (tempState)
{
case STATE_NO_NETWORK:
if (isDebugEnabled) Serial.println(F("NO_NETWORK"));
smartthing.shieldSetLED(2, 0, 0); // red
break;
case STATE_JOINING:
if (isDebugEnabled) Serial.println(F("JOINING"));
smartthing.shieldSetLED(2, 0, 0); // red
break;
case STATE_JOINED:
if (isDebugEnabled) Serial.println(F("JOINED"));
smartthing.shieldSetLED(0, 0, 0); // off
break;
case STATE_JOINED_NOPARENT:
if (isDebugEnabled) Serial.println(F("JOINED_NOPARENT"));
smartthing.shieldSetLED(2, 0, 2); // purple
break;
case STATE_LEAVING:
if (isDebugEnabled) Serial.println(F("LEAVING"));
smartthing.shieldSetLED(2, 0, 0); // red
break;
default:
case STATE_UNKNOWN:
if (isDebugEnabled) Serial.println(F("UNKNOWN"));
smartthing.shieldSetLED(0, 2, 0); // green
break;
}
stateNetwork = tempState;
}
}
//*****************************************************************************
// API Functions | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void setup()
{
// setup default state of global variables
isDebugEnabled = true;
stateLED = 0; // matches state of hardware pin set below
stateNetwork = STATE_JOINED; // set to joined to keep state off if off
//DHT 11
// setup hardware pins
dhtFreezer.begin();
dhtFridge.begin();
dhtMoistcrisp.begin();
dhtOthercrisp.begin();
if (isDebugEnabled)
{ // setup debug serial port
Serial.begin(9600); // setup serial with a baud rate of 9600
Serial.println(F("setup..")); // print out 'setup..' on start
////give the sensor some time to calibrate
//Serial.print("calibrating sensor ");
//for (int i = 0; i < calibrationTime; i++){
// Serial.print(".");
// delay(1000);
//}
//Serial.println(" done");
//Serial.println("SENSOR ACTIVE");
delay(50);
}
}
//*****************************************************************************
// Keep Track of Sensor Readings
//int currentLight;
int currentFridgeHumidity;
int currentFreezerHumidity;
int currentMoistcrispHumidity;
int currentOthercrispHumidity;
int currentOvenTemperature;
int currentBroilerTemperature;
int currentFridgeTemperature;
int currentFreezerTemperature;
int currentMoistcrispTemperature;
int currentOthercrispTemperature;
// Intervals
unsigned long humidityInterval = (2 * 5 * 1000);
unsigned long temperatureInterval = (2 * 5 * 1000);
unsigned long reportInterval = (2 * 5 * 1000);
unsigned long lastHumidityCheckAt = 0;
unsigned long lastTempCheckAt = 0;
unsigned long lastReportAt = 0;
void loop()
{
// run smartthing logic
smartthing.run();
// Timing code
unsigned long currentMillis = millis();
// First and at checkInterval
if (currentMillis - lastHumidityCheckAt > humidityInterval || lastHumidityCheckAt == 0)
{
lastHumidityCheckAt = currentMillis;
// Do a check
checkHumidity();
}
if (currentMillis - lastTempCheckAt > temperatureInterval || lastTempCheckAt == 0)
{
lastTempCheckAt = currentMillis;
// Do a check
checkTemperature();
}
// First and at reportInterval
if (currentMillis - lastReportAt > reportInterval || lastReportAt == 0)
{
lastReportAt = currentMillis;
// Do a report
reportData();
}
// setNetworkStateLED();
// checkHumidity();
// checkTemperature();
// delay(5000);
}
void checkData()
{
checkHumidity();
checkTemperature();
}
void checkHumidity()
{
Serial.println("Checking humidity...");
// Read humidity
float hFreezer = dhtFreezer.readHumidity();
float hFridge = dhtFridge.readHumidity();
float hMoistcrisp = dhtMoistcrisp.readHumidity();
float hOthercrisp = dhtOthercrisp.readHumidity();
// Discard any data that is NaN
if (isnan(hFreezer) || isnan(hFridge) || isnan(hMoistcrisp) || isnan(hOthercrisp))
{
Serial.println("Failed to read from one or more of the DHTs");
}
else {
Serial.print("Fridge Humidity:");
Serial.println(hFridge);
currentFridgeHumidity = hFridge;
Serial.print("Freezer Humidity:");
Serial.println(hFreezer);
currentFreezerHumidity = hFreezer;
Serial.print("Moist Crisper Humidity:");
Serial.println(hMoistcrisp);
currentMoistcrispHumidity = hMoistcrisp;
Serial.print("Other Crisper Humidity:");
Serial.println(hOthercrisp);
currentOthercrispHumidity = hOthercrisp;
}
}
void checkTemperature()
{
Serial.println("Checking temperature...");
// Read temperature
//Tcouples first
double cOven = thermocouple_oven.readFarenheit();
double cBroiler = thermocouple_broiler.readFarenheit();
if (isnan(cOven) || isnan(cBroiler)){
Serial.println("Error with Tcouple(s)");
}
else {
Serial.print("Tcouple Oven Temp:");
Serial.println(cOven);
currentOvenTemperature = cOven;
Serial.print("Tcouple Broiler Temp:");
Serial.println(cBroiler);
currentBroilerTemperature = cBroiler;
}
//DHTs next
float tFreezer = dhtFreezer.readTemperature(true);
float tFridge = dhtFridge.readTemperature(true);
float tMoistcrisp = dhtMoistcrisp.readTemperature(true);
float tOthercrisp = dhtOthercrisp.readTemperature(true);
// Discard any data that is NaN
if (isnan(tFreezer) || isnan(tFridge) || isnan(tMoistcrisp) || isnan(tOthercrisp))
{
Serial.println(F("Failed to read from one or more of the DHTs"));
}
else {
Serial.print(F("Fridge Temp:"));
Serial.println(tFridge);
currentFridgeTemperature = tFridge;
Serial.print(F("Freezer Temp:"));
Serial.println(tFreezer);
currentFreezerTemperature = tFreezer;
Serial.print(F("Moist Crisper Temp:"));
Serial.println(tMoistcrisp);
currentMoistcrispTemperature = tMoistcrisp;
Serial.print(F("Other Crisper Temp:"));
Serial.println(tOthercrisp);
currentOthercrispTemperature = tOthercrisp;
}
}
void reportData()
{
Serial.println(F("Reporting data..."));
// We must insist on actually having some data to send
if (isnan(currentFridgeHumidity) || isnan(currentFridgeTemperature) || isnan(currentFreezerHumidity) || isnan(currentOthercrispHumidity) || isnan(currentMoistcrispHumidity) || isnan(currentOvenTemperature) || isnan(currentBroilerTemperature) || isnan(currentFreezerTemperature) || isnan(currentOthercrispTemperature) || isnan(currentMoistcrispTemperature)) {
Serial.println(F("We're not in a loop are we?"));
checkData();
reportData();
return;
}
Serial.println("Messages to be delivered to ST");
// Report humidity
String humidityFridgeMessage = "A";
humidityFridgeMessage.concat(currentFridgeHumidity);
smartthing.send(humidityFridgeMessage);
Serial.println(humidityFridgeMessage);
String humidityFreezerMessage = "B";
humidityFreezerMessage.concat(currentFreezerHumidity);
smartthing.send(humidityFreezerMessage);
Serial.println(humidityFreezerMessage);
String humidityOthercrispMessage = "C";
humidityOthercrispMessage.concat(currentOthercrispHumidity);
smartthing.send(humidityOthercrispMessage);
Serial.println(humidityOthercrispMessage);
String humidityMoistcrispMessage = "D";
humidityMoistcrispMessage.concat(currentMoistcrispHumidity);
smartthing.send(humidityMoistcrispMessage);
Serial.println(humidityMoistcrispMessage);
// Report Temps
String tempOvenMessage = "E";
tempOvenMessage.concat(currentOvenTemperature);
smartthing.send(tempOvenMessage);
Serial.println(tempOvenMessage);
String tempBroilerMessage = "F";
tempBroilerMessage.concat(currentBroilerTemperature);
smartthing.send(tempBroilerMessage);
Serial.println(tempBroilerMessage);
String tempFridgeMessage = "G";
tempFridgeMessage.concat(currentFridgeTemperature);
smartthing.send(tempFridgeMessage);
Serial.println(tempFridgeMessage);
String tempFreezerMessage = "H";
tempFreezerMessage.concat(currentFreezerTemperature);
smartthing.send(tempFreezerMessage);
Serial.println(tempFreezerMessage);
String tempOthercrispMessage = "I";
tempOthercrispMessage.concat(currentOthercrispTemperature);
smartthing.send(tempOthercrispMessage);
Serial.println(tempOthercrispMessage);
String tempMoistcrispMessage = "J";
tempMoistcrispMessage.concat(currentMoistcrispTemperature);
smartthing.send(tempMoistcrispMessage);
Serial.println(tempMoistcrispMessage);
}
//*****************************************************************************
void messageCallout(String message)
{
// if debug is enabled print out the received message
if (isDebugEnabled)
{
Serial.print(F("Received message: '"));
Serial.print(message);
Serial.println("' ");
}
// if message contents equals to 'on' then call on() function
// else if message contents equals to 'off' then call off() function
if (message.equals("on"))
{
on();
}
else if (message.equals("off"))
{
off();
}
else if (message.equals("poll"))
{
reportData();
}
}
And for the Groovy:
metadata { // Automatically generated. Make future change here. definition (name: "Christoph Multi Thermometers", namespace: "scordinskyc", author: "Christopher Scordinsky") { capability "Refresh" capability "Polling" capability "Temperature Measurement" capability "Relative Humidity Measurement" capability "Sensor" fingerprint profileId: "0104", deviceId: "0138", inClusters: "0000" } // Simulator metadata simulator { // status messages // status "ping": "catchall: 0104 0000 01 01 0040 00 6A67 00 00 0000 0A 00 0A70696E67" //status "hello": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A48656c6c6f20576f726c6421" } // UI tile definitions tiles { valueTile("tempOven", "device.tempOven", width: 1, height: 1, inactiveLabel: false) { state("temperature", label: 'Oven ${currentValue}°F', unit:"F", backgroundColors: [ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], [value: 59, color: "#90d2a7"], [value: 74, color: "#44b621"], [value: 84, color: "#f1d801"], [value: 95, color: "#d04e00"], [value: 96, color: "#bc2323"] ] ) } valueTile("tempBroiler", "device.tempBroiler", width: 1, height: 1, inactiveLabel: false) { state("temperature", label: 'Broiler ${currentValue}°F', unit:"F", backgroundColors: [ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], [value: 59, color: "#90d2a7"], [value: 74, color: "#44b621"], [value: 84, color: "#f1d801"], [value: 95, color: "#d04e00"], [value: 96, color: "#bc2323"] ] ) } valueTile("tempFridge", "device.tempFridge", width: 1, height: 1, inactiveLabel: false) { state("temperature", label: 'Fridge\n${currentValue}°F', unit:"F", backgroundColors: [ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], [value: 59, color: "#90d2a7"], [value: 74, color: "#44b621"], [value: 84, color: "#f1d801"], [value: 95, color: "#d04e00"], [value: 96, color: "#bc2323"] ] ) } valueTile("tempFreezer", "device.tempFreezer", width: 1, height: 1, inactiveLabel: false) { state("temperature", label: 'Freezer\n${currentValue}°F', unit:"F", backgroundColors: [ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], [value: 59, color: "#90d2a7"], [value: 74, color: "#44b621"], [value: 84, color: "#f1d801"], [value: 95, color: "#d04e00"], [value: 96, color: "#bc2323"] ] ) } valueTile("tempOthercrisp", "device.tempOthercrisp", width: 1, height: 1, inactiveLabel: false) { state("temperature", label: 'Other Crisper\n${currentValue}°F', unit:"F", backgroundColors: [ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], [value: 59, color: "#90d2a7"], [value: 74, color: "#44b621"], [value: 84, color: "#f1d801"], [value: 95, color: "#d04e00"], [value: 96, color: "#bc2323"] ] ) } valueTile("tempMoistcrisp", "device.tempMoistcrisp", width: 1, height: 1, inactiveLabel: false) { state("temperature", label: 'Moist Crisper\n${currentValue}°F', unit:"F", backgroundColors: [ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], [value: 59, color: "#90d2a7"], [value: 74, color: "#44b621"], [value: 84, color: "#f1d801"], [value: 95, color: "#d04e00"], [value: 96, color: "#bc2323"] ] ) } valueTile("humidityFridge", "device.humidityFridge", width: 1, height: 1, inactiveLabel: false) { state "humidity", label:'Fridge\n${currentValue}% humidity', unit:"" } valueTile("humidityFreezer", "device.humidityFreezer", width: 1, height: 1, inactiveLabel: false) { state "humidity", label:'Freezer\n${currentValue}% humidity', unit:"" } valueTile("humidityMoistcrisp", "device.humidityMoistcrisp", width: 1, height: 1, inactiveLabel: false) { state "humidity", label:'Moist\nCrisper ${currentValue}% humidity', unit:"" } valueTile("humidityOthercrisp", "device.humidityOthercrisp", width: 1, height: 1, inactiveLabel: false) { state "humidity", label:'Other\nCrisper ${currentValue}% humidity', unit:"" } standardTile("refresh", "device.poll") { state "default", label:'Refresh', action:"device.poll()", icon:"st.secondary.refresh" } main (["tempOven", "tempBroiler", "tempFridge", "tempFreezer","tempMoistcrisp", "tempOthercrisp","humidityFridge", "humidityFreezer", "humidityMoistcrisp", "humidityOthercrisp"]) details(["tempOven", "tempBroiler", "tempFridge", "tempFreezer","tempMoistcrisp", "tempOthercrisp","humidityFridge", "humidityFreezer", "humidityMoistcrisp", "humidityOthercrisp"]) } } Map parse(String description) { def value = zigbee.parse(description)?.text log.debug "Parsing '${description}'" // Not super interested in ping, can we just move on? if (value == "ping" || value == " ") { return } def linkText = getLinkText(device) def descriptionText = getDescriptionText(description, linkText, value) def handlerName = value def isStateChange = value != "ping" def displayed = value && isStateChange def result = [ value: value, handlerName: handlerName, linkText: linkText, descriptionText: descriptionText, isStateChange: isStateChange, displayed: displayed ] log.debug result.value if (value in ["!on","!off"]) { result.name = "switch" result.value = value[1..-1] // log.debug result.value // } else if (value && value[0] == "%") { // result.name = "level" // result.value = value[1..-1] } else if (value && value[0] == "A") { result.name = "humidityFridge"; result.value = value[1..-1]; result.unit = "%" // log.debug 'HFridge' // log.debug result.value } else if (value && value[0] == "B") { result.name = "humidityFreezer"; result.value = value[1..-1]; result.unit = "%" // log.debug 'HFreez' // log.debug result.value } else if (value && value[0] == "C") { result.name = "humidityOthercrisp"; result.value = value[1..-1]; result.unit = "%" // log.debug 'HOthCr' // log.debug result.value } else if (value && value[0] == "D") { result.name = "humidityMoistcrisp"; result.value = value[1..-1]; result.unit = "%" // log.debug 'HMoisCr' // log.debug result.value } else if (value && value[0] == "E") { result.name = "tempOven"; result.value = value[1..-1]; result.unit = "F" // log.debug 'TOv' // log.debug result.value } else if (value && value[0] == "F") { result.name = "tempBroiler"; result.value = value[1..-1]; result.unit = "F" // log.debug 'TBroiler' // log.debug result.value } else if (value && value[0] == "G") { result.name = "tempFridge"; result.value = value[1..-1]; result.unit = "F" // log.debug 'TFridge' // log.debug result.value } else if (value && value[0] == "H") { result.name = "tempFreezer"; result.value = value[1..-1]; result.unit = "F" // log.debug 'TFreezer' // log.debug result.value } else if (value && value[0] == "I") { result.name = "tempOthercrisp"; result.value = value[1..-1]; result.unit = "F" // log.debug 'TOtherCrisp' // log.debug result.value } else if (value && value[0] == "J") { result.name = "tempMoistcrisp"; result.value = value[1..-1]; result.unit = "F" // log.debug 'TMoistCrisp' // log.debug result.value } else { result.name = null; log.debug result.value } // if ( (value && value[0] == "%") ) // { // result.unit = "%" // } log.debug result.descriptionText createEvent(name: result.name, value: result.value) } def poll() { zigbee.smartShield(text: "poll").format() }