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
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 }
Generated on Wed Jul 13 2022 05:16:04 by 1.7.2