MAXREFDES143#: DeepCover Embedded Security in IoT Authenticated Sensing & Notification
Dependencies: MaximInterface mbed
main.cpp
00001 /******************************************************************************* 00002 * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a 00005 * copy of this software and associated documentation files (the "Software"), 00006 * to deal in the Software without restriction, including without limitation 00007 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00008 * and/or sell copies of the Software, and to permit persons to whom the 00009 * Software is furnished to do so, subject to the following conditions: 00010 * 00011 * The above copyright notice and this permission notice shall be included 00012 * in all copies or substantial portions of the Software. 00013 * 00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00015 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00017 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES 00018 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 00019 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 00020 * OTHER DEALINGS IN THE SOFTWARE. 00021 * 00022 * Except as contained in this notice, the name of Maxim Integrated 00023 * Products, Inc. shall not be used except as stated in the Maxim Integrated 00024 * Products, Inc. Branding Policy. 00025 * 00026 * The mere transfer of this software does not imply any licenses 00027 * of trade secrets, proprietary technology, copyrights, patents, 00028 * trademarks, maskwork rights, or any other form of intellectual 00029 * property whatsoever. Maxim Integrated Products, Inc. retains all 00030 * ownership rights. 00031 *******************************************************************************/ 00032 00033 #include <sstream> 00034 #include <mbed.h> 00035 #include <MaximInterface/Devices/DS2465.hpp> 00036 #include <MaximInterface/Platforms/mbed/I2CMaster.hpp> 00037 #include <MaximInterface/Platforms/mbed/Sleep.hpp> 00038 #include <MaximInterface/Utilities/RomId.hpp> 00039 #include "SensorData.hpp" 00040 #include "WebServerInterface.hpp" 00041 #include "Factory.hpp" 00042 #include "SensorNode.hpp" 00043 #include "Display.hpp" 00044 #include "ESP8266.hpp" 00045 00046 using namespace MaximInterface; 00047 00048 /// Main status for the program. 00049 enum Status { 00050 InitializingController, ///< Configure DS2465 and connect to network. 00051 DisplaySessionId, ///< Display ID for use with website. 00052 SensorNodeNeedsDetection, ///< Prompt user to insert Sensor Node. 00053 DetectingSensorNode, ///< Check if Sensor Node present. 00054 SensorNodeNeedsProvision, ///< Sensor Node needs to be provisioned. 00055 ProvisioningSensorNode, ///< Provisioning Sensor Node to factory defaults. 00056 NormalOperation, ///< The normal demo operation state. 00057 SensorNodeNotAuthentic, ///< Sensor Node failed authentication check. 00058 ControllerInitializationError, ///< Failed to initialize Controller. 00059 ControllerHardwareError, ///< Controller hardware failed unexpectedly. 00060 SensorNodeHardwareError ///< Sensor Node hardware failed unexpectedly. 00061 }; 00062 00063 /// @{ 00064 /// Configuration options. 00065 static const int webPostIntervalMs = 10000; 00066 static const int webPostRetryIntervalMs = 1000; 00067 static const int maxConsecutiveWebPostErrors = 3; 00068 /// @} 00069 00070 /// @{ 00071 /// LCD display colors. 00072 static const Display::Color Teal = {0x00, 0xB2, 0xA9}; 00073 static const Display::Color Red = {0xFF, 0x00, 0x00}; 00074 static const Display::Color Green = {0x00, 0xFF, 0x00}; 00075 static const Display::Color Purple = {0x6E, 0x25, 0x85}; 00076 /// @} 00077 00078 /// @{ 00079 /// Peripheral and pin definitions 00080 static DigitalIn provisionButton(D13); 00081 static DigitalIn invalidateButton(D5); 00082 static DigitalOut tempAlarmLed(D11, 1); 00083 static DigitalOut filterLifeAlarmLed(D10, 1); 00084 static I2C i2c(D14, D15); 00085 static Display lcd(i2c, 0x78, 0x98); 00086 static MaximInterface::mbed::I2CMaster i2cWrapper(i2c); 00087 static DS2465 ds2465(MaximInterface::mbed::Sleep::instance(), i2cWrapper, 0x30); 00088 static SensorNode sensorNode(i2c, 0x90, 0x94, ds2465); 00089 static ESP8266 esp8266(D1, D0, D2, D3, 38400); 00090 static WebServerInterface webIntf(esp8266); 00091 /// @} 00092 00093 /// Imitate an invalid controller when posting to web server. 00094 static bool useInvalidSecret = false; 00095 static unsigned int randomSeed = 0; ///< Create extra entropy for challenge. 00096 static Status currentStatus = InitializingController; 00097 static bool result = false; 00098 /// Account for a few network errors in case of flaky connection. 00099 static uint8_t consecutiveWebPostErrors = 0; 00100 static Timer webPostTimer; ///< Software timer to track web posting interval. 00101 static Timer retryTimer; ///< Software timer to track authentication retries. 00102 00103 /// Invert LEDs for a given amount of time. 00104 static void blinkLeds(int time_ms); 00105 /// Checks if button is pressed (returns true) and waits for release. 00106 static bool buttonPressed(DigitalIn & button); 00107 /// Display status message on LCD. 00108 static void displayStatus(Status status); 00109 /// Display sensor data on the LCD. 00110 static void displaySensorData(const SensorData & sensorData); 00111 /// Read device's web session ID from it's nonvolatile storage. 00112 static bool readWebSessionId(RomId & sessionId); 00113 00114 #ifdef ASSEMBLY_TEST 00115 #include "AssemblyTest.hpp" 00116 #endif 00117 00118 int main() { 00119 blinkLeds(500); 00120 00121 #ifdef ASSEMBLY_TEST 00122 assemblyTest(); 00123 #endif 00124 00125 while (true) { 00126 Status nextStatus = currentStatus; 00127 switch (currentStatus) { 00128 case InitializingController: 00129 i2c.frequency(100000); 00130 webPostTimer.start(); 00131 00132 // Set initial LCD state 00133 lcd.initialize(); 00134 displayStatus(currentStatus); 00135 00136 // Connect to Wifi network 00137 result = webIntf.initialize(); 00138 00139 // Read session ID 00140 if (result) { 00141 RomId sessionId; 00142 result = readWebSessionId(sessionId); 00143 if (result) { 00144 webIntf.setSessionId(sessionId); 00145 } 00146 } 00147 00148 // Provision DS2465 with master secret and page data 00149 if (result) { 00150 result = provisionCoprocessor(ds2465); 00151 } 00152 00153 if (result) { 00154 nextStatus = DisplaySessionId; 00155 } else { 00156 nextStatus = ControllerInitializationError; 00157 } 00158 break; 00159 00160 case DisplaySessionId: 00161 // Wait for user to press Provision button 00162 if (buttonPressed(provisionButton)) { 00163 nextStatus = SensorNodeNeedsDetection; 00164 } 00165 break; 00166 00167 case SensorNodeNeedsDetection: 00168 // Wait for user to press Provision button 00169 if (buttonPressed(provisionButton)) { 00170 nextStatus = DetectingSensorNode; 00171 } 00172 break; 00173 00174 case DetectingSensorNode: 00175 // Perform Sensor Node detection sequence 00176 switch (sensorNode.detect(randomSeed)) { 00177 case SensorNode::UnableToCommunicate: 00178 default: 00179 nextStatus = SensorNodeHardwareError; 00180 break; 00181 00182 case SensorNode::NotProvisioned: 00183 nextStatus = SensorNodeNeedsProvision; 00184 break; 00185 00186 case SensorNode::NotAuthentic: 00187 nextStatus = SensorNodeNotAuthentic; 00188 break; 00189 00190 case SensorNode::Authentic: 00191 nextStatus = NormalOperation; 00192 break; 00193 } 00194 break; 00195 00196 case SensorNodeNeedsProvision: 00197 // Wait for user to press Provision button 00198 if (buttonPressed(provisionButton)) { 00199 nextStatus = ProvisioningSensorNode; 00200 } 00201 break; 00202 00203 case ProvisioningSensorNode: 00204 if (!buttonPressed(invalidateButton)) { // Provision normally 00205 if (provisionSensorNode(sensorNode, true)) { 00206 nextStatus = NormalOperation; 00207 } else { 00208 nextStatus = SensorNodeNotAuthentic; 00209 } 00210 } else { // Invalidate button also pressed; Load invalid secret 00211 // Provision with invalid secret 00212 if (provisionSensorNode(sensorNode, false)) { 00213 nextStatus = NormalOperation; 00214 } else { 00215 nextStatus = SensorNodeHardwareError; 00216 } 00217 } 00218 break; 00219 00220 case NormalOperation: 00221 // Check if user pressed Provision button 00222 if (buttonPressed(provisionButton)) { 00223 // Re-provision Sensor Node 00224 nextStatus = ProvisioningSensorNode; 00225 } 00226 // Check if user pressed Invalidate button 00227 else if (buttonPressed(invalidateButton)) { 00228 // Toggle between using valid and invalid secret 00229 // 1 blink = invalid; 2 blinks = valid 00230 useInvalidSecret = !useInvalidSecret; 00231 blinkLeds(100); 00232 if (!useInvalidSecret) { 00233 wait_ms(100); 00234 blinkLeds(100); 00235 } 00236 } 00237 // Check node and display measurements 00238 else { 00239 SensorData sensorData; 00240 // Read sensor data with authentication 00241 switch ( 00242 sensorNode.authenticatedReadSensorData(randomSeed, sensorData)) { 00243 case SensorNode::Authentic: 00244 // Update measurements on LCD 00245 displaySensorData(sensorData); 00246 00247 // Update alarm LEDs 00248 tempAlarmLed = !sensorData.tempAlarm(); // Active Low 00249 filterLifeAlarmLed = !sensorData.filterLifeAlarm(); // Active Low 00250 00251 // Send measurements to web if time interval reached 00252 if (webPostTimer.read_ms() >= webPostIntervalMs) { 00253 // Format, sign, and transmit data to web server 00254 result = webIntf.authPostHttpEvent( 00255 ds2465, SensorDataEvent, 00256 WebServerInterface::formatSensorDataPostBody(sensorData), 00257 !useInvalidSecret); 00258 if (result) { 00259 // Reset timer count after logging sample complete 00260 webPostTimer.reset(); 00261 consecutiveWebPostErrors = 0; 00262 } 00263 // There was likely an error establishing a web connection 00264 else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) { 00265 // Wait and try again 00266 wait_ms(webPostRetryIntervalMs); 00267 } 00268 // Too many retry attempts 00269 else { 00270 // Assume we have lost network connection 00271 nextStatus = ControllerHardwareError; 00272 } 00273 } 00274 break; 00275 00276 case SensorNode::NotAuthentic: 00277 nextStatus = SensorNodeNotAuthentic; 00278 break; 00279 00280 case SensorNode::UnableToCommunicate: 00281 default: 00282 nextStatus = SensorNodeHardwareError; 00283 break; 00284 } 00285 } 00286 break; 00287 00288 case SensorNodeNotAuthentic: 00289 // Wait for some time before retrying authentication 00290 retryTimer.reset(); 00291 retryTimer.start(); 00292 do { 00293 // Wait for user to press Provision button 00294 if (buttonPressed(provisionButton)) { 00295 nextStatus = ProvisioningSensorNode; 00296 break; 00297 } 00298 // Try to authenticate and return to normal operation 00299 else if (webPostTimer.read_ms() >= webPostIntervalMs) { 00300 // Send event message to server 00301 result = webIntf.authPostHttpEvent(ds2465, InvalidSensorEvent, "", 00302 !useInvalidSecret); 00303 if (result) { 00304 // Reset timer count after logging complete 00305 webPostTimer.reset(); 00306 consecutiveWebPostErrors = 0; 00307 00308 // Try to authenticate again 00309 nextStatus = SensorNodeNeedsDetection; 00310 } else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) { 00311 // There was likely an error establishing a web connection 00312 // Wait and try again 00313 wait_ms(webPostRetryIntervalMs); 00314 } 00315 // Too many retry attempts 00316 else { 00317 // Assume we have lost network connection 00318 nextStatus = ControllerHardwareError; 00319 break; 00320 } 00321 } 00322 } while (retryTimer.read_ms() < webPostIntervalMs); 00323 retryTimer.stop(); 00324 break; 00325 00326 case ControllerInitializationError: 00327 case ControllerHardwareError: 00328 case SensorNodeHardwareError: 00329 default: 00330 // Do nothing until user resets 00331 break; 00332 } 00333 // Check if status changed 00334 if (currentStatus != nextStatus) { 00335 currentStatus = nextStatus; 00336 displayStatus(currentStatus); // Display status message on LCD 00337 } 00338 00339 // Change seed value on every loop pass 00340 randomSeed++; 00341 } 00342 } 00343 00344 /// Blink all LEDs for a certain amount of time. 00345 /// @param time_ms Time in ms to blink for. 00346 static void blinkLeds(int time_ms) { 00347 tempAlarmLed = !tempAlarmLed; 00348 filterLifeAlarmLed = !filterLifeAlarmLed; 00349 wait_ms(time_ms); 00350 tempAlarmLed = !tempAlarmLed; 00351 filterLifeAlarmLed = !filterLifeAlarmLed; 00352 } 00353 00354 /// Check if a button is pressed and wait for it to be release. 00355 /// @param button Active low button to check. 00356 /// @returns True if pressed. 00357 static bool buttonPressed(DigitalIn & button) { 00358 const int buttonPressed = 0; // Active low 00359 if (button == buttonPressed) { 00360 while (button == buttonPressed) 00361 ; 00362 return true; 00363 } 00364 // else 00365 return false; 00366 } 00367 00368 /// Display the current status of the Controller on the LCD display. 00369 static void displayStatus(Status status) { 00370 switch (status) { 00371 case InitializingController: 00372 lcd.writeMessage("Initializing Controller..."); 00373 lcd.setBackLightColor(Teal); 00374 break; 00375 00376 case DisplaySessionId: 00377 lcd.writeLine("ID: " + webIntf.sessionIdString(), Display::FirstLine); 00378 lcd.writeLine("Provision to begin", Display::SecondLine); 00379 lcd.setBackLightColor(Teal); 00380 break; 00381 00382 case SensorNodeNeedsDetection: 00383 lcd.writeMessage("Insert Sensor Node and press Provision"); 00384 lcd.setBackLightColor(Teal); 00385 break; 00386 00387 case DetectingSensorNode: 00388 lcd.writeMessage("Detecting Sensor Node..."); 00389 lcd.setBackLightColor(Teal); 00390 break; 00391 00392 case SensorNodeNeedsProvision: 00393 lcd.writeMessage("Sensor Node Needs Provision"); 00394 lcd.setBackLightColor(Teal); 00395 break; 00396 00397 case ProvisioningSensorNode: 00398 lcd.writeMessage("Provisioning Sensor Node"); 00399 lcd.setBackLightColor(Teal); 00400 break; 00401 00402 case NormalOperation: 00403 // Everything handled in displaySensorData() 00404 break; 00405 00406 case SensorNodeNotAuthentic: 00407 lcd.writeMessage("Sensor Node Not Authentic"); 00408 lcd.setBackLightColor(Purple); 00409 break; 00410 00411 case ControllerInitializationError: 00412 lcd.writeMessage("Initialization Error Check Wi-Fi"); 00413 lcd.setBackLightColor(Red); 00414 break; 00415 00416 case ControllerHardwareError: 00417 lcd.writeMessage("Controller Hardware Error: Check Wi-Fi"); 00418 lcd.setBackLightColor(Red); 00419 break; 00420 00421 case SensorNodeHardwareError: 00422 lcd.writeMessage("Sensor Node Hardware Error"); 00423 lcd.setBackLightColor(Red); 00424 break; 00425 } 00426 } 00427 00428 /// Display sensor data on the LCD display during normal operation. 00429 static void displaySensorData(const SensorData & sensorData) { 00430 std::ostringstream stream; 00431 stream << "Chiller Temp: " << static_cast<int>(sensorData.temp) << "C"; 00432 lcd.writeCompleteLine(stream.str(), Display::FirstLine); 00433 stream.str(""); // Clear stream 00434 stream << "Filter Life: " << static_cast<unsigned int>(sensorData.filterLife) 00435 << "%"; 00436 lcd.writeCompleteLine(stream.str(), Display::SecondLine); 00437 lcd.setBackLightColor(Green); 00438 } 00439 00440 /// Read the Session ID to use with the web server from ROM. 00441 /// @note Session ID is taken from the ROM ID of the MAX66242. 00442 /// @param[out] Session ID string. 00443 /// @returns True on success. 00444 static bool readWebSessionId(RomId & sessionId) { 00445 const uint8_t I2C_address = 0x32; 00446 const uint8_t ROM_address = 0x68; 00447 RomId romId; 00448 00449 // Set register pointer 00450 if (i2c.write(I2C_address, reinterpret_cast<const char *>(&ROM_address), 1) != 0) 00451 return false; 00452 // Read ROM ID 00453 if (i2c.read(I2C_address, reinterpret_cast<char *>(romId.data()), 00454 romId.size()) != 0) 00455 return false; 00456 // Check if CRC valid 00457 if (!valid(romId)) 00458 return false; 00459 sessionId = romId; 00460 return true; 00461 }
Generated on Wed Jul 13 2022 21:31:03 by 1.7.2