Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 } 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 }
Generated on Tue Jul 12 2022 20:39:47 by
