MBED_DEMOS / Mbed 2 deprecated df-2013-minihack-thermostat

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         }
00210    }
00211 }
00212 
00213 // recv and process a control message
00214 void Thermostat::processControlMessage() {
00215     
00216     if (socketio.is_connected()) {
00217         char message[SOCKETIO_MESSAGE_LENGTH];
00218         if (socketio.read(message)) {
00219             // log the message
00220             this->display("Received control message: %s",message);
00221             
00222             // process the message
00223             this->parseAndActOnControlMessage(message);
00224             
00225             // blink the RX led
00226             this->blinkTransportRxLED();
00227         }
00228         else {
00229             // no messages received - log
00230             this->display("No control message received. OK");
00231         }
00232     }
00233 }
00234 
00235 // send status (no params) to the service
00236 void Thermostat::sendStatus() {         
00237     // send the status
00238     this->sendStatus(this->getTemperature(),this->getBatteryLevel());
00239 }
00240 
00241 // send status (temp & battery) to the service
00242 void Thermostat::sendStatus(float temp, int bat) { 
00243     // incorporate location coordinates 
00244     this->sendStatus(temp,this->m_latitude,this->m_longitude,bat);
00245 }
00246 
00247 // send status (temp, lat/long, battery) to the service
00248 void Thermostat::sendStatus(float temp, float latitude, float longitude, float bat) {         
00249     // Announce 
00250     this->display("Send: status...");
00251     this->display_lcd("Sending status...");
00252                     
00253     // now send...
00254     int sent = socketio.emit(temp,latitude,longitude,bat,this->getErrorState(),this->m_status);
00255      
00256     // Log
00257     if (sent > 0) {
00258        this->display("Send success. Ready...");
00259        this->display_lcd("Send status: OK.\r\nSleeping...");
00260     }
00261     else {
00262        this->display("Send Failed: sent=%d",sent);
00263        this->display_lcd("Send status: FAILED.\r\nSleeping...");
00264     }
00265     
00266     // blink the TX led
00267     this->blinkTransportTxLED();
00268 }
00269 
00270 // connect to the heroku WebService
00271 bool Thermostat::connectWebSocketService() {    
00272     // Log
00273     this->display("Connecting to SocketIO...");
00274     this->display_lcd("SocketIO connecting...");
00275     
00276     // only connect if we are not already so
00277     if (!socketio.is_connected()) socketio.connect();
00278     
00279     // check connection status
00280     bool connected = socketio.is_connected();
00281             
00282     // log 
00283     if (connected == true) {
00284         this->display("SocketIO: Connected!");
00285         this->display_lcd("SocketIO: Connected.");
00286     }
00287     else { 
00288         this->display("SocketIO: Not connected!");
00289         this->display_lcd("SocketIO: Connect FAILED.");
00290     }
00291         
00292     // return the status
00293     return connected;
00294 }
00295 
00296 // Connect up Ethernet
00297 bool Thermostat::connectEthernet() { 
00298     bool connected = false;
00299     char *ipAddr = NULL;
00300     
00301     // Use DHCP
00302     this->display("Initializing Ethernet...");
00303     this->display_lcd("Ethernet: Initializing...");
00304     ethernet.init(); 
00305     
00306     // attempt connection
00307     this->display("Connecting Ethernet...");
00308     this->display_lcd("Ethernet: Connecting...");
00309     if (ethernet.connect() == 0) connected = true;
00310     
00311     // check connection status
00312     if (connected) {
00313         ipAddr = ethernet.getIPAddress();
00314         if (ipAddr != NULL && strlen(ipAddr) > 0)
00315             connected = true;
00316     }
00317    
00318     // log 
00319     if (connected == true) {
00320         this->display("Ethernet: Connected.\r\nIP: %s", ipAddr);
00321         this->display_lcd("Ethernet: Connected\r\nIP: %s",ipAddr);
00322     }
00323     else {
00324         this->display("Ethernet: Not connected");
00325         this->display_lcd("Ethernet: Connect FAILED.");
00326     }
00327     
00328     // return the status
00329     return connected; 
00330 }
00331 
00332 // gracefully de-register and close down the WS connection
00333 void Thermostat::gracefullyDisconnect() {    
00334     // disconnect
00335     if (socketio.is_connected()) socketio.close();
00336         
00337     // announce
00338     this->display("Disconnected.");
00339     this->display_lcd("Disconnected.");
00340     this->turnRGBLEDBlue();
00341     
00342     // reset any status
00343     this->m_status = "OK";
00344     socketio.resetMessageCounter();
00345 }
00346 
00347 // external function in main() to check for the CTRL-C key press to exit the application gracefully
00348 extern void checkForExit();
00349 
00350 // main loop
00351 void Thermostat::mainLoop() {
00352     // initialize our location
00353     this->initLocation(); 
00354     
00355     // begin the main loop
00356     while(true) {
00357         // sleep for a bit
00358         checkForExit();
00359         wait(DEFAULT_MAIN_LOOP_WAIT);
00360         
00361         // announce our position
00362         this->updateCoordinates();
00363         
00364         // if not connected... reconnect
00365         if (!socketio.is_connected()){
00366             // announce
00367             this->display("Re-connecting...");
00368             this->display_lcd("Re-connecting...");
00369             
00370             // re-connect
00371             if (this->connectWebSocketService()) {
00372                 // announce
00373                 this->display("Reconnect success");
00374                 this->display_lcd("Reconnect: SUCCESS");
00375                 this->turnRGBLEDGreen();
00376                 this->resetAllLEDs();
00377             }
00378             else {
00379                 // announce
00380                 this->display("Reconnect failure");
00381                 this->display_lcd("Reconnect: FAILED");
00382                 this->gracefullyDisconnect();
00383                 this->turnRGBLEDRed();
00384             }
00385         }
00386                 
00387         // check and react to the joystick button press
00388         if (joystick_pressed) {
00389             if (this->m_rgbLEDColor > 1) {
00390                 this->turnRGBLEDRed();
00391                 this->m_status = "FAIL";
00392             }
00393             else {
00394                 this->turnRGBLEDGreen();
00395                 this->m_status = "OK";
00396             }
00397         }
00398         else if (socketio.is_connected() && this->m_rgbLEDColor > 121) {
00399             this->turnRGBLEDGreen();
00400         }
00401         
00402         // check the status of the joystick
00403         if (joystick) {
00404             if (socketio.is_connected()) {
00405                 // announce 
00406                 this->display("Disconnecting...");
00407                 this->display_lcd("Disconnecting...");
00408                 
00409                 // disconnect
00410                 this->gracefullyDisconnect();
00411             }
00412             else if (!socketio.is_connected()){
00413                 // announce
00414                 this->display("Re-connecting...");
00415                 this->display_lcd("Re-connecting...");
00416                 
00417                 // re-connect
00418                 if (this->connectWebSocketService()) {
00419                     // announce
00420                     this->display("Reconnect success");
00421                     this->display_lcd("Reconnect: SUCCESS");
00422                     this->turnRGBLEDGreen();
00423                     this->resetAllLEDs();
00424                 }
00425                 else {
00426                     // announce
00427                     this->display("Reconnect failure");
00428                     this->display_lcd("Reconnect: FAILED");
00429                     this->gracefullyDisconnect();
00430                     this->turnRGBLEDRed();
00431                 }
00432             }
00433         }
00434         
00435         // if we are connected, send our status
00436         if (socketio.is_connected()) {
00437             // send status
00438             this->sendStatus();
00439             checkForExit();
00440         }
00441         
00442         // if we are connected, read any control we may receive
00443         if (socketio.is_connected()) {
00444             // process control messages
00445             this->processControlMessage();
00446             checkForExit();
00447         }
00448     }
00449 }
00450 
00451 // Run the Demo
00452 void Thermostat::runDemo() {
00453     // Announce
00454     this->display("Thermostat Hands-On Demo v1.0");
00455     this->display_lcd("Thermostat Hands-On\r\nDemo v1.0");
00456             
00457     // init the RGB LED
00458     this->display("Initializing LEDs...");
00459     this->turnRGBLEDBlue();
00460     
00461     // Log
00462     this->display("Connecting...");
00463     this->display_lcd("Connecting...");
00464     
00465     // connect and send the initial status
00466     if (this->connectEthernet() == true && this->connectWebSocketService() == true) {
00467         this->sendStatus();
00468         this->mainLoop();
00469     }
00470     else {
00471         this->display("Connection failure. Application exiting...");
00472         this->display_lcd("Connect: FAILURE.\r\nApplication Exiting");
00473     }
00474         
00475     // exit the application if we get here
00476     exit(1);
00477 }