Skip to content

Instantly share code, notes, and snippets.

@MZachmann
Created December 1, 2016 16:09
Show Gist options
  • Save MZachmann/be7f140b5d40683e8a588246355eebbe to your computer and use it in GitHub Desktop.
Save MZachmann/be7f140b5d40683e8a588246355eebbe to your computer and use it in GitHub Desktop.
Photon Source for REST Controller
// include the HttpClient code so we can send REST commands
#include "HttpClient.h"
// -----------------------------------------------
// Defines
// -----------------------------------------------
// turn this on to usb test
#define USE_DUMMY 0
// for now hardcode IR and temperature sensors to a 4-count
#define num_sense 4
// seconds between sending of new data
#define POLL_TIME 20
// amount of temperature change to force sending
#define TEMPERATURE_MAX 100
// some way to test a USB-connected with the latest software
#if USE_DUMMY
#define ROOM_NAME "DummyTest"
#define LOGGING 1
#else
#define ROOM_NAME "Office"
#define LOGGING 0
#endif
// -----------------------------------------------
// Declaring the global variables.
// -----------------------------------------------
int allowGet = 1; // for debugging, this lets us hit a key to start stuff up
// this is the value used to determine if we're a V1 board
int testTempRead = 1; // make this global so we can print it later in the loop
// Headers currently need to be set at init, useful for API keys etc.
http_header_t httpHeaders[] = {
// { "Content-Type", "application/json" },
// { "Accept" , "application/json" },
{ "Accept" , "*/*"},
{ NULL, NULL } // NOTE: Always terminate headers will NULL
};
http_request_t httpRequest;
http_response_t httpResponse;
// Define the pins we're going to call pinMode on
int led2 = D7; // This one is the built-in tiny one to the right of the USB jack
//V1 definitions
int temps[num_sense] = { A2, A3, A4, A5 };
int irs[num_sense] = { D1, D2, D3, D4 };
// autosense what we have available
bool hasInitialed = false;
bool tempAvails[num_sense] = { false,false,false,false};
bool irAvails[num_sense] = {false,false,false,false};
// readings
int readTemps[num_sense] = { 0,0,0,0};
bool readIrs[num_sense] = {false,false,false,false};
int lastTimeRead = Time.now();
bool isV1 = false; // is this a version 1 board (no A0 input and D1,D2,D3,D4 digitals)
// -----------------------------------------------
// Setup code
// This routine runs only once upon reset
// -----------------------------------------------
void setup()
{
#if LOGGING
Serial.begin(19200); // Open serial over USB.
#endif
// Figure out of this is a V1 board
// If it's a V2 board then A0 will be populated with a thermistor
pinMode( A0, INPUT_PULLDOWN); // per the doc this shouldn't be necessary since analogRead sets it to AN_INPUT
testTempRead = analogRead(A0); // per my test this will read < 1000 the first time due to the pulldown request (?)
// after we do the pulldown request. it will then end up around 1100 per my testing
isV1 = (testTempRead < 1400); // a V1 board will not have a thermistor at A0 and so it will read <1000 usually
if(isV1) // !!! Warning this test is Photon dependent
{
pinMode( A0, INPUT_PULLDOWN); // per the doc this shouldn't be necessary since analogRead sets it to AN_INPUT
}
// this gives better accuracy
setADCSampleTime(ADC_SampleTime_84Cycles);
// set the arrays based on V1 or V2
if(!isV1)
{
int newTemps[num_sense] = { A0, A3, A4, A5 };
int newIrs[num_sense] = { D0, D1, D2, D3 };
for(int i=0; i<num_sense; i++)
{
temps[i] = newTemps[i];
irs[i] = newIrs[i];
}
}
// Initialize the IR input pins
// It's important you do this here, inside the setup() function rather than outside it or in the loop function.
for(int i=0; i<num_sense; i++)
{
pinMode( irs[i], INPUT_PULLDOWN);
}
pinMode(led2, OUTPUT); // Turn on the D7 led as output to show IR status
// determine which temperature sensors we have. Any sensor will read less than 3800 for sure
for(int i=0; i<num_sense; i++)
{
readTemps[i] = analogRead(temps[i]);
tempAvails[i] = (readTemps[i] < 3800);
}
// determine which IR sensors are connected, it's not clear how to do this since it seems to be open collector
// so the following code just doesn't work
// for(int i=0; i<num_sense; i++)
// {
// pinMode( irs[i], INPUT_PULLUP);
// bool isIr = (digitalRead(irs[i]) == HIGH);
// pinMode( irs[i], INPUT_PULLDOWN);
// irAvails[i] = (digitalRead(irs[i]) == HIGH) == isIr; // has it flipped with different pullup/down?
// }
}
// -----------------------------------------------
// Is the network ready?
// -----------------------------------------------
bool isNetworkReady()
{
bool isValidNet = true;
IPAddress gate = WiFi.gatewayIP();
if( ! WiFi.ready())
{
isValidNet = false;
#if LOGGING
Serial.print("Not wifi ready.");
#endif
}
else if(gate[0] != 192 || gate[1] != 168 || gate[2] != 0)
{
// the gateway is always in our local 192.168.0.xxx network
isValidNet = false;
#if LOGGING
Serial.print("Invalid gateway: ");
Serial.println(gate);
#endif
}
#if LOGGING
if(isValidNet)
{
Serial.print("Wifi address: ");
Serial.println(WiFi.localIP());
Serial.print("Subnet mask: ");
Serial.println(WiFi.subnetMask());
// Prints out the gateway IP over Serial.
Serial.print("Gateway IP: ");
Serial.println(WiFi.gatewayIP());
}
#endif
return isValidNet;
}
// -----------------------------------------------
// convert a bool array to 1,0 strings
// -----------------------------------------------
String joinBools(bool theArray[])
{
String allBool = "";
for(int i=0; i<num_sense; i++)
{
if(i)
{
allBool = allBool + ",";
}
allBool = allBool + (theArray[i] ? "1" : "0");
}
return allBool;
}
// -----------------------------------------------
// convert an int array to ,d strings
// -----------------------------------------------
String joinInts(int theArray[])
{
String allInts = "";
for(int i=0; i<num_sense; i++)
{
if(i)
{
allInts = allInts + ",";
}
allInts = allInts + String(theArray[i]);
}
return allInts;
}
// -----------------------------------------------
// figure out what sensors we have and send that information to the server
// -----------------------------------------------
String getDefinition()
{
String room = ROOM_NAME;
String setDef = "/sensor/rest/SetDef?Name="+room;
setDef = setDef + "&Temps=";
setDef = setDef + joinBools(tempAvails);
setDef = setDef + "&IRs=";
setDef = setDef + joinBools(irAvails);
setDef = setDef + "&Ver=";
setDef = setDef + (isV1 ? "1" : "2");
#if LOGGING
Serial.println("set definition== "+setDef);
#endif
return setDef;
}
// send and http request
void SendHttpPath(String path)
{
// Request path and body can be set at runtime or at setup.
httpRequest.ip = IPAddress(192,168,0,92);
httpRequest.port = 80; // http default port using nginx forwarding
httpRequest.path = path;
httpRequest.hostname = NULL;
#if LOGGING
Serial.print("Path: ");
Serial.print("192.168.0.92");
Serial.println(path);
#endif
#if !USE_DUMMY
HttpClient httpClient;
httpClient.get(httpRequest, httpResponse, httpHeaders);
#endif
}
// This routine gets called repeatedly, like once every 5-15 milliseconds.
// Spark firmware interleaves background CPU activity associated with WiFi + Cloud activity with your code.
// Make sure none of your code delays or blocks for too long (like more than 5 seconds), or weird things can happen.
void loop()
{
bool sendIrs = false; // turn on the IR led?
bool forceSend = false; // if we have a change in status, push it immediately
int newTemps[num_sense];
// read the sensors
for(int i=0; i<num_sense; i++)
{
// we can't save the new temperatures because it may 'slowly' increase
// don't bother with sensors not hooked up, this may increase analog accuracy also
if(tempAvails[i])
newTemps[i] = analogRead(temps[i]);
else
newTemps[i] = readTemps[i];
int diff = abs(newTemps[i] - readTemps[i]);
if(diff > TEMPERATURE_MAX)
{
forceSend = true;
}
bool newIR = (digitalRead(irs[i]) == HIGH);
if(newIR != readIrs[i])
{
forceSend = true;
}
readIrs[i] = newIR;
// if it has gone high, say so to LED
if(readIrs[i])
sendIrs = true;
delayMicroseconds(100); // for the a/d i think
}
// only broadcast every few seconds
int newTime = Time.now();
if((!forceSend) && ((newTime - lastTimeRead) < POLL_TIME) )
{
return;
}
lastTimeRead = newTime;
// flip the LED on if we have an IR high
digitalWrite(led2, sendIrs ? HIGH : LOW);
// now update the read temperatures
for(int i=0; i<num_sense; i++)
{
readTemps[i] = newTemps[i];
}
#if LOGGING
if(isV1)
{
Serial.print("This is a V1 board reading: ");
}
else
{
Serial.print("This is a V2 board reading: ");
}
Serial.println(testTempRead);
Serial.print("forceSend is ");
Serial.println(forceSend ? "on" : "off");
#endif
bool isValidNet = isNetworkReady();
// Send the current status REST command
if(isValidNet && (allowGet > 0))
{
// send the definition to the server once
if(! hasInitialed)
{
hasInitialed = true;
// room definition
String setDef = getDefinition();
SendHttpPath(setDef);
}
String tempStr = "Temps=";
tempStr += joinInts(readTemps);
String irStr = "IR=";
irStr += joinBools(readIrs);
// send current status
String room = ROOM_NAME;
String path = "/sensor/rest/SetRoom?Name=" + room + "&" + tempStr + "&" + irStr;
SendHttpPath(path);
}
#if LOGGING
if(Serial.available())
{
int rd = Serial.read();
switch(rd)
{
case 'n' :
allowGet = 0;
break;
case 'y' :
allowGet = 5;
break;
default :
break;
}
}
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment