Dreamforce 2013 MiniHack Thermostat Challenge - remotes
Dependencies: C12832_lcd EthernetInterface-ansond-patched HTTPClient-thermostat-remotes LM75B MMA7660 SocketIO WebSocketClient-ThermostatDemo mbed-rtos mbed picojson
Fork of df-2013-minihack-thermostat-complete by
Diff: Thermostat.cpp
- Revision:
- 0:26c48388f725
- Child:
- 1:3faa003ad6e6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Thermostat.cpp Tue Nov 05 21:31:01 2013 +0000 @@ -0,0 +1,458 @@ +/* Thermostat.cpp */ +/* Copyright (C) 2013 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +// +// 2013 DreamForce MiniHack - +// Look for the method: Thermostat::parseAndActOnControlMessage(char *json) +// From there, add code to look for a "text" message - send its contents to the LCD panel +// using the this->display(char *) method. You can use the other messages as a reference. +// + + /* + * Operation: with the MBED appboard - the following is available: + * 1. connect/disconnect: push and hold the joystick forward (towards potentiometer) until a connect/disconnect message is seen + * 2. control messages + * "led1" --> "on" or "off" (led1 is also used as the TX status...) + * "led2" --> "on" or "off" (led2 is also used as the RX status...) + * "led3" --> "on" or "off" + * "led4" --> "on" or "off" + * "blink" --> <dont care> should blink all 4 LEDs a few times + * "reset" --> <dont care> will reset the simulated error state for the device + * 3. simulated error state: depress and hold the joystick until the RGB LED turns red + * 4. rotate the potentiometer closest to the LCD panel to manipulate the battery level (0 - 100%) + * 5. rotate the potentiometer nearest the RGB LED to add/subtract 10 degrees from the current ambient temperature + */ + +// Primary include +#include "Thermostat.h" + +// DreamForce 2013 Tunables START + +// +// Our default Latitude and Longitude +// +#define DEFAULT_LATITUDE 37.7842 +#define DEFAULT_LONGITUDE -122.4016 + +// +// Name our endpoint something unique +// +#define DEFAULT_ENDPOINT_NAME "DreamForce Thermostat" + +// DreamForce 2013 Tunables END + +// Wait Loop default sleep per iteration +#define DEFAULT_MAIN_LOOP_WAIT 2.0 // 2 seconds + +// JSON parsing support +#include "picojson.h" + +// +// Accelerometer Support +// +#include "MMA7660.h" +MMA7660 acc(p28, p27); + +// +// Temperature Sensor Support +// +#include "LM75B.h" +LM75B temp_sensor(p28,p27); + +// +// Ethernet support +// +#include "EthernetInterface.h" +EthernetInterface ethernet; + +// +// Thermostat SocketIO Support +// +#include "ThermostatSocketIO.h" +ThermostatSocketIO socketio(DEFAULT_ENDPOINT_NAME); + +// Include LED Utils +#include "Thermostat-LEDUtils.h" + +// Include Base Utils +#include "Thermostat-BaseUtils.h" + +// Include Location Stubs +#include "Thermostat-Location.h" + +// Default constructor +Thermostat::Thermostat() { + this->m_temperature = 0.0; + this->m_battery = 50.0; + this->m_status = "OK"; +} + +// Destructor +Thermostat::~Thermostat() { + // close down connections + socketio.close(); + ethernet.disconnect(); + this->turnRGBLEDBlue(); +} + +// get the temp +float Thermostat::getTemperature() { + // get Pot 2 an additive/subtractive value to the ambient temp + float scale = Pot2.read(); + scale = scale * 20.0; // scale to 0 - 20 + scale = scale - 10.0; // scale -10 to +10 + + // Get the Temperature (in C) + float c = temp_sensor.read(); + float f = ((9/5) * c ) + 32; + + // now get the ambient temp and scale it + this->m_temperature = c + scale; + this->display("Temp %.1f C (%.1f F)",this->m_temperature,f); + this->display_lcd("Temp: %.1f C (%.1f F)",this->m_temperature,f); + + // return the temperature + return this->m_temperature; +} + +// get the current battery level +float Thermostat::getBatteryLevel() { + // get Pot 1 an additive/subtractive value to simulate battery level + this->m_battery = Pot1.read(); + this->m_battery = this->m_battery * 100.0; // scale to 0 - 100; + + // return the battery level + return this->m_battery; +} + +// receive from the heroku SocketIO web service +char *Thermostat::receiveFromWSService(char *buffer) { + bool success = socketio.read(buffer); + if (success == true) + this->display("SocketIO: Read success"); + else + this->display("SocketIO: Read failure"); + + return buffer; +} + +// translate the LED status +int Thermostat::translateLEDStatus(const char *status, int current) { + int i_status = current; + + if (status != NULL && strlen(status) > 0) { + if (strcmp(status,"on") == 0 || strcmp(status,"ON") == 0 || strcmp(status,"On") == 0 || strcmp(status,"1") == 0 || strcmp(status,"oN") == 0) + i_status = 1; + if (strcmp(status,"off") == 0 || strcmp(status,"OFF") == 0 || strcmp(status,"Off") == 0 || strcmp(status,"0") == 0 || strcmp(status,"oFF") == 0 || strcmp(status,"oF") == 0 || strcmp(status,"ofF") == 0) + i_status = 0; + } + + // return the status + return i_status; +} + +// reset the device status to OK +void Thermostat::resetDeviceStatus() { + this->turnRGBLEDGreen(); + this->m_status = "OK"; + socketio.resetMessageCounter(); + this->resetAllLEDs(); +} + +// basic parsing and processing of the control message +// +// Control Message Format: 5:::{"name":"control-device","args":[{"hello":"world"}]} +// +void Thermostat::parseAndActOnControlMessage(char *json) { + picojson::value v; + + if (json != NULL && strlen(json) > 0 && strstr(json,"{") != NULL) { + // move past the socket.io header + char *json_proper = strstr(json,"{"); + + // parse the packet + string err = picojson::parse(v, json_proper, json_proper + strlen(json_proper)); + + // the args value is an array of json values + picojson::array list = v.get("args").get<picojson::array>(); + + // loop through the array and parse/process each element + for (picojson::array::iterator iter = list.begin(); iter != list.end(); ++iter) { + // Toggle LEDs + if ((*iter).get("led1") != NULL) led1.write(this->translateLEDStatus((*iter).get("led1").get<string>().c_str(),(int)led1)); + if ((*iter).get("led2") != NULL) led2.write(this->translateLEDStatus((*iter).get("led2").get<string>().c_str(),(int)led2)); + if ((*iter).get("led3") != NULL) led3.write(this->translateLEDStatus((*iter).get("led3").get<string>().c_str(),(int)led3)); + if ((*iter).get("led4") != NULL) led4.write(this->translateLEDStatus((*iter).get("led4").get<string>().c_str(),(int)led4)); + if ((*iter).get("reset") != NULL) this->resetDeviceStatus(); + if ((*iter).get("text") != NULL) this->display((*iter).get("text").get<string>().c_str()); + if ((*iter).get("blink") != NULL) this->blinkAllLEDs(); + + // + // 2013 DreamForce MiniHack - add code to look for a "text" message - send its contents to the LCD panel + // using the this->display(char *) method + // + // Answer: + // + if ((*iter).get("text") != NULL) this->display((*iter).get("text").get<string>().c_str()); + } + } +} + +// recv and process a control message +void Thermostat::processControlMessage() { + + if (socketio.is_connected()) { + char message[SOCKETIO_MESSAGE_LENGTH]; + if (socketio.read(message)) { + // log the message + this->display("Received control message: %s",message); + + // process the message + this->parseAndActOnControlMessage(message); + + // blink the RX led + this->blinkTransportRxLED(); + } + else { + // no messages received - log + this->display("No control message received. OK"); + } + } +} + +// send status (no params) to the service +void Thermostat::sendStatus() { + // send the status + this->sendStatus(this->getTemperature(),this->getBatteryLevel()); +} + +// send status (temp & battery) to the service +void Thermostat::sendStatus(float temp, int bat) { + // incorporate location coordinates + this->sendStatus(temp,this->m_latitude,this->m_longitude,bat); +} + +// send status (temp, lat/long, battery) to the service +void Thermostat::sendStatus(float temp, float latitude, float longitude, float bat) { + // Announce + this->display("Send: status..."); + this->display_lcd("Sending status..."); + + // now send... + int sent = socketio.emit(temp,latitude,longitude,bat,this->getErrorState(),this->m_status); + + // Log + if (sent > 0) { + this->display("Send success. Ready..."); + this->display_lcd("Send status: OK.\r\nSleeping..."); + } + else { + this->display("Send Failed: sent=%d",sent); + this->display_lcd("Send status: FAILED.\r\nSleeping..."); + } + + // blink the TX led + this->blinkTransportTxLED(); +} + +// connect to the heroku WebService +bool Thermostat::connectWebSocketService() { + // Log + this->display("Connecting to SocketIO..."); + this->display_lcd("SocketIO connecting..."); + + // only connect if we are not already so + if (!socketio.is_connected()) socketio.connect(); + + // check connection status + bool connected = socketio.is_connected(); + + // log + if (connected == true) { + this->display("SocketIO: Connected!"); + this->display_lcd("SocketIO: Connected."); + } + else { + this->display("SocketIO: Not connected!"); + this->display_lcd("SocketIO: Connect FAILED."); + } + + // return the status + return connected; +} + +// Connect up Ethernet +bool Thermostat::connectEthernet() { + bool connected = false; + char *ipAddr = NULL; + + // Use DHCP + this->display("Initializing Ethernet..."); + this->display_lcd("Ethernet: Initializing..."); + ethernet.init(); + + // attempt connection + this->display("Connecting Ethernet..."); + this->display_lcd("Ethernet: Connecting..."); + if (ethernet.connect() == 0) connected = true; + + // check connection status + if (connected) { + ipAddr = ethernet.getIPAddress(); + if (ipAddr != NULL && strlen(ipAddr) > 0) + connected = true; + } + + // log + if (connected == true) { + this->display("Ethernet: Connected.\r\nIP: %s", ipAddr); + this->display_lcd("Ethernet: Connected\r\nIP: %s",ipAddr); + } + else { + this->display("Ethernet: Not connected"); + this->display_lcd("Ethernet: Connect FAILED."); + } + + // return the status + return connected; +} + +// gracefully de-register and close down the WS connection +void Thermostat::gracefullyDisconnect() { + // disconnect + if (socketio.is_connected()) socketio.close(); + + // announce + this->display("Disconnected."); + this->display_lcd("Disconnected."); + this->turnRGBLEDBlue(); + + // reset any status + this->m_status = "OK"; + socketio.resetMessageCounter(); +} + +// external function in main() to check for the CTRL-C key press to exit the application gracefully +extern void checkForExit(); + +// main loop +void Thermostat::mainLoop() { + // initialize our location + this->initLocation(); + + // begin the main loop + while(true) { + // sleep for a bit + checkForExit(); + wait(DEFAULT_MAIN_LOOP_WAIT); + + // announce our position + this->updateCoordinates(); + + // check and react to the joystick button press + if (joystick_pressed) { + if (this->m_rgbLEDColor > 1) { + this->turnRGBLEDRed(); + this->m_status = "FAIL"; + } + else { + this->turnRGBLEDGreen(); + this->m_status = "OK"; + } + } + else if (socketio.is_connected() && this->m_rgbLEDColor > 121) { + this->turnRGBLEDGreen(); + } + + // check the status of the joystick + if (joystick) { + if (socketio.is_connected()) { + // announce + this->display("Disconnecting..."); + this->display_lcd("Disconnecting..."); + + // disconnect + this->gracefullyDisconnect(); + } + else if (!socketio.is_connected()){ + // announce + this->display("Re-connecting..."); + this->display_lcd("Re-connecting..."); + + // re-connect + if (this->connectWebSocketService()) { + // announce + this->display("Reconnect success"); + this->display_lcd("Reconnect: SUCCESS"); + this->turnRGBLEDGreen(); + this->resetAllLEDs(); + } + else { + // announce + this->display("Reconnect failure"); + this->display_lcd("Reconnect: FAILED"); + this->gracefullyDisconnect(); + this->turnRGBLEDRed(); + } + } + } + + // if we are connected, send our status + if (socketio.is_connected()) { + // send status + this->sendStatus(); + checkForExit(); + } + + // if we are connected, read any control we may receive + if (socketio.is_connected()) { + // process control messages + this->processControlMessage(); + checkForExit(); + } + } +} + +// Run the Demo +void Thermostat::runDemo() { + // Announce + this->display("Thermostat Hands-On Demo v1.0"); + this->display_lcd("Thermostat Hands-On\r\nDemo v1.0"); + + // init the RGB LED + this->display("Initializing LEDs..."); + this->turnRGBLEDBlue(); + + // Log + this->display("Connecting..."); + this->display_lcd("Connecting..."); + + // connect and send the initial status + if (this->connectEthernet() == true && this->connectWebSocketService() == true) { + this->sendStatus(); + this->mainLoop(); + } + else { + this->display("Connection failure. Application exiting..."); + this->display_lcd("Connect: FAILURE.\r\nApplication Exiting"); + } + + // exit the application if we get here + exit(1); +} \ No newline at end of file