DreamForce 2013 Hands-On Demo
Dependencies: C12832_lcd EthernetInterface-ansond-patched HTTPClient-thermostat-remotes LM75B MMA7660 SocketIO WebSocketClient-ThermostatDemo mbed-rtos mbed picojson
Fork of ThermostatHandsOn by
Thermostat.cpp
- Committer:
- ansond
- Date:
- 2013-11-01
- Revision:
- 3:6afb87443041
- Parent:
- 2:be42baea4c81
- Child:
- 4:048550d178c5
File content as of revision 3:6afb87443041:
/* 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. */ /* * 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 * "text" --> <string> should be displayed to the LCD screen as well as the serial console * "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" // // 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" // 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(); } } } // 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); }