Dreamforce 2013 MiniHack Thermostat Challenge - completed code

Dependencies:   C12832_lcd EthernetInterface-ansond-patched HTTPClient-thermostat-remotes LM75B MMA7660 SocketIO WebSocketClient-ThermostatDemo mbed-rtos mbed picojson

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Thermostat.cpp Source File

Thermostat.cpp

00001 /* Thermostat.cpp */
00002 /* Copyright (C) 2013 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019  
00020 //
00021 // 2013 DreamForce MiniHack - 
00022 // Look for the method: Thermostat::parseAndActOnControlMessage(char *json)
00023 // From there, add code to look for a "text" message - send its contents to the LCD panel
00024 // using the this->display(char *) method. You can use the other messages as a reference.
00025 // 
00026 
00027  /*
00028   * Operation: with the MBED appboard - the following is available:
00029   *     1. connect/disconnect: push and hold the joystick forward (towards potentiometer) until a connect/disconnect message is seen
00030   *     2. control messages  
00031   *         "led1" --> "on" or "off" (led1 is also used as the TX status...)
00032   *         "led2" --> "on" or "off" (led2 is also used as the RX status...)
00033   *         "led3" --> "on" or "off"
00034   *         "led4" --> "on" or "off"
00035   *         "blink" --> <dont care> should blink all 4 LEDs a few times
00036   *         "reset" --> <dont care> will reset the simulated error state for the device
00037   *     3. simulated error state: depress and hold the joystick until the RGB LED turns red
00038   *     4. rotate the potentiometer closest to the LCD panel to manipulate the battery level (0 - 100%)
00039   *     5. rotate the potentiometer nearest the RGB LED to add/subtract 10 degrees from the current ambient temperature
00040   */
00041 
00042 // Primary include
00043 #include "Thermostat.h"
00044 
00045 // DreamForce 2013 Tunables START
00046 
00047 //
00048 // Our default Latitude and Longitude
00049 //
00050 #define DEFAULT_LATITUDE                     37.7842
00051 #define DEFAULT_LONGITUDE                   -122.4016  
00052 
00053 //
00054 // Name our endpoint something unique
00055 //
00056 #define DEFAULT_ENDPOINT_NAME               "DreamForce Thermostat"
00057 
00058 // DreamForce 2013 Tunables END
00059 
00060 // Wait Loop default sleep per iteration
00061 #define DEFAULT_MAIN_LOOP_WAIT              2.0     // 2 seconds
00062 
00063 // JSON parsing support
00064 #include "picojson.h"
00065 
00066 //
00067 // Accelerometer Support
00068 //
00069 #include "MMA7660.h"
00070 MMA7660 acc(p28, p27);
00071 
00072 //
00073 // Temperature Sensor Support
00074 //
00075 #include "LM75B.h"
00076 LM75B temp_sensor(p28,p27);
00077 
00078 //
00079 // Ethernet support
00080 //
00081 #include "EthernetInterface.h"
00082 EthernetInterface ethernet;
00083 
00084 //
00085 // Thermostat SocketIO Support
00086 //
00087 #include "ThermostatSocketIO.h"
00088 ThermostatSocketIO socketio(DEFAULT_ENDPOINT_NAME);
00089 
00090 // Include LED Utils
00091 #include "Thermostat-LEDUtils.h"
00092 
00093 // Include Base Utils
00094 #include "Thermostat-BaseUtils.h"
00095 
00096 // Include Location Stubs
00097 #include "Thermostat-Location.h"
00098 
00099 // Default constructor
00100 Thermostat::Thermostat() {
00101     this->m_temperature = 0.0;
00102     this->m_battery = 50.0;
00103     this->m_status = "OK";
00104 }
00105 
00106 // Destructor
00107 Thermostat::~Thermostat() {
00108     // close down connections
00109     socketio.close();
00110     ethernet.disconnect();
00111     this->turnRGBLEDBlue();
00112 }
00113 
00114 // get the temp
00115 float Thermostat::getTemperature() {
00116     // get Pot 2 an additive/subtractive value to the ambient temp
00117     float scale = Pot2.read();
00118     scale = scale * 20.0;    // scale to 0 - 20
00119     scale = scale - 10.0;    // scale -10 to +10
00120         
00121     // Get the Temperature (in C)
00122     float c = temp_sensor.read();
00123     float f = ((9/5) * c ) + 32;
00124     
00125     // now get the ambient temp and scale it
00126     this->m_temperature =  c + scale;    
00127     this->display("Temp %.1f C (%.1f F)",this->m_temperature,f);
00128     this->display_lcd("Temp: %.1f C (%.1f F)",this->m_temperature,f);
00129      
00130     // return the temperature    
00131     return this->m_temperature;
00132 }
00133 
00134 // get the current battery level
00135 float Thermostat::getBatteryLevel() {  
00136     // get Pot 1 an additive/subtractive value to simulate battery level
00137     this->m_battery = Pot1.read();
00138     this->m_battery = this->m_battery * 100.0;  // scale to 0 - 100;
00139         
00140     // return the battery level
00141     return this->m_battery;
00142 }
00143 
00144 // receive from the heroku SocketIO web service
00145 char *Thermostat::receiveFromWSService(char *buffer) {
00146     bool success = socketio.read(buffer);
00147     if (success == true)
00148         this->display("SocketIO: Read success");
00149     else
00150         this->display("SocketIO: Read failure");
00151     
00152     return buffer;
00153 }    
00154 
00155 // translate the LED status 
00156 int Thermostat::translateLEDStatus(const char *status, int current) {
00157    int i_status = current; 
00158      
00159    if (status != NULL && strlen(status) > 0) {
00160         if (strcmp(status,"on") == 0 || strcmp(status,"ON") == 0 || strcmp(status,"On") == 0 || strcmp(status,"1") == 0 || strcmp(status,"oN") == 0)
00161             i_status = 1;
00162         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)
00163             i_status = 0;
00164    }
00165    
00166    // return the status
00167    return i_status; 
00168 }
00169 
00170 // reset the device status to OK
00171 void Thermostat::resetDeviceStatus() {
00172     this->turnRGBLEDGreen();
00173     this->m_status = "OK";
00174     socketio.resetMessageCounter();
00175     this->resetAllLEDs();
00176 }
00177 
00178 // basic parsing and processing of the control message
00179 //
00180 // Control Message Format: 5:::{"name":"control-device","args":[{"hello":"world"}]}
00181 //
00182 void Thermostat::parseAndActOnControlMessage(char *json) {
00183    picojson::value v;
00184       
00185    if (json != NULL && strlen(json) > 0 && strstr(json,"{") != NULL) {
00186        // move past the socket.io header
00187        char *json_proper = strstr(json,"{");
00188        
00189        // parse the packet
00190        string err = picojson::parse(v, json_proper, json_proper + strlen(json_proper));
00191        
00192        // the args value is an array of json values
00193        picojson::array list = v.get("args").get<picojson::array>();
00194        
00195        // loop through the array and parse/process each element
00196        for (picojson::array::iterator iter = list.begin(); iter != list.end(); ++iter) {
00197             // Toggle LEDs
00198             if ((*iter).get("led1") != NULL) led1.write(this->translateLEDStatus((*iter).get("led1").get<string>().c_str(),(int)led1));
00199             if ((*iter).get("led2") != NULL) led2.write(this->translateLEDStatus((*iter).get("led2").get<string>().c_str(),(int)led2));
00200             if ((*iter).get("led3") != NULL) led3.write(this->translateLEDStatus((*iter).get("led3").get<string>().c_str(),(int)led3));
00201             if ((*iter).get("led4") != NULL) led4.write(this->translateLEDStatus((*iter).get("led4").get<string>().c_str(),(int)led4));
00202             if ((*iter).get("reset") != NULL) this->resetDeviceStatus();
00203             if ((*iter).get("blink") != NULL) this->blinkAllLEDs();
00204             
00205             //
00206             // 2013 DreamForce MiniHack - add code to look for a "text" message - send its contents to the LCD panel
00207             // using the this->displayTextMessage(char *) method
00208             //
00209             // Answer:
00210             //
00211             if ((*iter).get("text") != NULL) this->displayTextMessage((*iter).get("text").get<string>().c_str()); 
00212         }
00213    }
00214 }
00215 
00216 // recv and process a control message
00217 void Thermostat::processControlMessage() {
00218     
00219     if (socketio.is_connected()) {
00220         char message[SOCKETIO_MESSAGE_LENGTH];
00221         if (socketio.read(message)) {
00222             // log the message
00223             this->display("Received control message: %s",message);
00224             
00225             // process the message
00226             this->parseAndActOnControlMessage(message);
00227             
00228             // blink the RX led
00229             this->blinkTransportRxLED();
00230         }
00231         else {
00232             // no messages received - log
00233             this->display("No control message received. OK");
00234         }
00235     }
00236 }
00237 
00238 // send status (no params) to the service
00239 void Thermostat::sendStatus() {         
00240     // send the status
00241     this->sendStatus(this->getTemperature(),this->getBatteryLevel());
00242 }
00243 
00244 // send status (temp & battery) to the service
00245 void Thermostat::sendStatus(float temp, int bat) { 
00246     // incorporate location coordinates 
00247     this->sendStatus(temp,this->m_latitude,this->m_longitude,bat);
00248 }
00249 
00250 // send status (temp, lat/long, battery) to the service
00251 void Thermostat::sendStatus(float temp, float latitude, float longitude, float bat) {         
00252     // Announce 
00253     this->display("Send: status...");
00254     this->display_lcd("Sending status...");
00255                     
00256     // now send...
00257     int sent = socketio.emit(temp,latitude,longitude,bat,this->getErrorState(),this->m_status);
00258      
00259     // Log
00260     if (sent > 0) {
00261        this->display("Send success. Ready...");
00262        this->display_lcd("Send status: OK.\r\nSleeping...");
00263     }
00264     else {
00265        this->display("Send Failed: sent=%d",sent);
00266        this->display_lcd("Send status: FAILED.\r\nSleeping...");
00267     }
00268     
00269     // blink the TX led
00270     this->blinkTransportTxLED();
00271 }
00272 
00273 // connect to the heroku WebService
00274 bool Thermostat::connectWebSocketService() {    
00275     // Log
00276     this->display("Connecting to SocketIO...");
00277     this->display_lcd("SocketIO connecting...");
00278     
00279     // only connect if we are not already so
00280     if (!socketio.is_connected()) socketio.connect();
00281     
00282     // check connection status
00283     bool connected = socketio.is_connected();
00284             
00285     // log 
00286     if (connected == true) {
00287         this->display("SocketIO: Connected!");
00288         this->display_lcd("SocketIO: Connected.");
00289     }
00290     else { 
00291         this->display("SocketIO: Not connected!");
00292         this->display_lcd("SocketIO: Connect FAILED.");
00293     }
00294         
00295     // return the status
00296     return connected;
00297 }
00298 
00299 // Connect up Ethernet
00300 bool Thermostat::connectEthernet() { 
00301     bool connected = false;
00302     char *ipAddr = NULL;
00303     
00304     // Use DHCP
00305     this->display("Initializing Ethernet...");
00306     this->display_lcd("Ethernet: Initializing...");
00307     ethernet.init(); 
00308     
00309     // attempt connection
00310     this->display("Connecting Ethernet...");
00311     this->display_lcd("Ethernet: Connecting...");
00312     if (ethernet.connect() == 0) connected = true;
00313     
00314     // check connection status
00315     if (connected) {
00316         ipAddr = ethernet.getIPAddress();
00317         if (ipAddr != NULL && strlen(ipAddr) > 0)
00318             connected = true;
00319     }
00320    
00321     // log 
00322     if (connected == true) {
00323         this->display("Ethernet: Connected.\r\nIP: %s", ipAddr);
00324         this->display_lcd("Ethernet: Connected\r\nIP: %s",ipAddr);
00325     }
00326     else {
00327         this->display("Ethernet: Not connected");
00328         this->display_lcd("Ethernet: Connect FAILED.");
00329     }
00330     
00331     // return the status
00332     return connected; 
00333 }
00334 
00335 // gracefully de-register and close down the WS connection
00336 void Thermostat::gracefullyDisconnect() {    
00337     // disconnect
00338     if (socketio.is_connected()) socketio.close();
00339         
00340     // announce
00341     this->display("Disconnected.");
00342     this->display_lcd("Disconnected.");
00343     this->turnRGBLEDBlue();
00344     
00345     // reset any status
00346     this->m_status = "OK";
00347     socketio.resetMessageCounter();
00348 }
00349 
00350 // external function in main() to check for the CTRL-C key press to exit the application gracefully
00351 extern void checkForExit();
00352 
00353 // main loop
00354 void Thermostat::mainLoop() {
00355     // initialize our location
00356     this->initLocation(); 
00357     
00358     // begin the main loop
00359     while(true) {
00360         // sleep for a bit
00361         checkForExit();
00362         wait(DEFAULT_MAIN_LOOP_WAIT);
00363         
00364         // announce our position
00365         this->updateCoordinates();
00366         
00367         // if not connected... reconnect
00368         if (!socketio.is_connected()){
00369             // announce
00370             this->display("Re-connecting...");
00371             this->display_lcd("Re-connecting...");
00372             
00373             // re-connect
00374             if (this->connectWebSocketService()) {
00375                 // announce
00376                 this->display("Reconnect success");
00377                 this->display_lcd("Reconnect: SUCCESS");
00378                 this->turnRGBLEDGreen();
00379                 this->resetAllLEDs();
00380             }
00381             else {
00382                 // announce
00383                 this->display("Reconnect failure");
00384                 this->display_lcd("Reconnect: FAILED");
00385                 this->gracefullyDisconnect();
00386                 this->turnRGBLEDRed();
00387             }
00388         }
00389                 
00390         // check and react to the joystick button press
00391         if (joystick_pressed) {
00392             if (this->m_rgbLEDColor > 1) {
00393                 this->turnRGBLEDRed();
00394                 this->m_status = "FAIL";
00395             }
00396             else {
00397                 this->turnRGBLEDGreen();
00398                 this->m_status = "OK";
00399             }
00400         }
00401         else if (socketio.is_connected() && this->m_rgbLEDColor > 121) {
00402             this->turnRGBLEDGreen();
00403         }
00404         
00405         // check the status of the joystick
00406         if (joystick) {
00407             if (socketio.is_connected()) {
00408                 // announce 
00409                 this->display("Disconnecting...");
00410                 this->display_lcd("Disconnecting...");
00411                 
00412                 // disconnect
00413                 this->gracefullyDisconnect();
00414             }
00415             else if (!socketio.is_connected()){
00416                 // announce
00417                 this->display("Re-connecting...");
00418                 this->display_lcd("Re-connecting...");
00419                 
00420                 // re-connect
00421                 if (this->connectWebSocketService()) {
00422                     // announce
00423                     this->display("Reconnect success");
00424                     this->display_lcd("Reconnect: SUCCESS");
00425                     this->turnRGBLEDGreen();
00426                     this->resetAllLEDs();
00427                 }
00428                 else {
00429                     // announce
00430                     this->display("Reconnect failure");
00431                     this->display_lcd("Reconnect: FAILED");
00432                     this->gracefullyDisconnect();
00433                     this->turnRGBLEDRed();
00434                 }
00435             }
00436         }
00437         
00438         // if we are connected, send our status
00439         if (socketio.is_connected()) {
00440             // send status
00441             this->sendStatus();
00442             checkForExit();
00443         }
00444         
00445         // if we are connected, read any control we may receive
00446         if (socketio.is_connected()) {
00447             // process control messages
00448             this->processControlMessage();
00449             checkForExit();
00450         }
00451     }
00452 }
00453 
00454 // Run the Demo
00455 void Thermostat::runDemo() {
00456     // Announce
00457     this->display("Thermostat Hands-On Demo v1.0");
00458     this->display_lcd("Thermostat Hands-On\r\nDemo v1.0");
00459             
00460     // init the RGB LED
00461     this->display("Initializing LEDs...");
00462     this->turnRGBLEDBlue();
00463     
00464     // Log
00465     this->display("Connecting...");
00466     this->display_lcd("Connecting...");
00467     
00468     // connect and send the initial status
00469     if (this->connectEthernet() == true && this->connectWebSocketService() == true) {
00470         this->sendStatus();
00471         this->mainLoop();
00472     }
00473     else {
00474         this->display("Connection failure. Application exiting...");
00475         this->display_lcd("Connect: FAILURE.\r\nApplication Exiting");
00476     }
00477         
00478     // exit the application if we get here
00479     exit(1);
00480 }