MAXREFDES143#: DeepCover Embedded Security in IoT Authenticated Sensing & Notification

Dependencies:   MaximInterface mbed

The MAXREFDES143# is an Internet of Things (IoT) embedded security reference design, built to protect an industrial sensing node by means of authentication and notification to a web server. The hardware includes a peripheral module representing a protected sensor node monitoring operating temperature and remaining life of a filter (simulated through ambient light sensing) and an mbed shield representing a controller node responsible for monitoring one or more sensor nodes. The design is hierarchical with each controller node communicating data from connected sensor nodes to a web server that maintains a centralized log and dispatches notifications as necessary. The mbed shield contains a Wi-Fi module, a DS2465 coprocessor with 1-Wire® master function, an LCD, LEDs, and pushbuttons. The protected sensor node contains a DS28E15 authenticator, a DS7505 temperature sensor, and a MAX44009 light sensor. The mbed shield communicates to a web server by the onboard Wi-Fi module and to the protected sensor node with I2C and 1-Wire. The MAXREFDES143# is equipped with a standard shield connector for immediate testing using an mbed board such as the MAX32600MBED#. The simplicity of this design enables rapid integration into any star-topology IoT network requiring the heightened security with low overhead provided by the SHA-256 symmetric-key algorithm.

More information about the MAXREFDES143# is available on the Maxim Integrated website.

Files at this revision

API Documentation at this revision

Comitter:
IanBenzMaxim
Date:
Thu Apr 14 19:48:01 2016 +0000
Parent:
0:19b8608fc4ee
Child:
2:e67d29a371db
Commit message:
Add initial source files.

Changed in this revision

DS7505.cpp Show annotated file Show diff for this revision Revisions of this file
DS7505.hpp Show annotated file Show diff for this revision Revisions of this file
Display.cpp Show annotated file Show diff for this revision Revisions of this file
Display.hpp Show annotated file Show diff for this revision Revisions of this file
ESP8266.cpp Show annotated file Show diff for this revision Revisions of this file
ESP8266.hpp Show annotated file Show diff for this revision Revisions of this file
Factory.cpp Show annotated file Show diff for this revision Revisions of this file
Factory.hpp Show annotated file Show diff for this revision Revisions of this file
MAX44009.cpp Show annotated file Show diff for this revision Revisions of this file
MAX44009.hpp Show annotated file Show diff for this revision Revisions of this file
OneWire.lib Show annotated file Show diff for this revision Revisions of this file
SensorNode.cpp Show annotated file Show diff for this revision Revisions of this file
SensorNode.hpp Show annotated file Show diff for this revision Revisions of this file
WebServerInterface.cpp Show annotated file Show diff for this revision Revisions of this file
WebServerInterface.hpp Show annotated file Show diff for this revision Revisions of this file
common.cpp Show annotated file Show diff for this revision Revisions of this file
common.hpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DS7505.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,277 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include "DS7505.hpp"
+#include "mbed.h"
+
+#define I2C_WRITE 0
+#define I2C_READ 1
+
+static const int I2C_WRITE_OK = 0;
+static const uint8_t DS7505_Config_SD_Bit = 0x01; // Enable shutdown mode
+
+DS7505::DS7505(I2C & I2C_interface, uint8_t I2C_address)
+  : m_current_config(Config_9b_Res, true), m_I2C_interface(I2C_interface), m_I2C_address(I2C_address)
+{
+  
+}
+
+uint8_t DS7505::get_measure_delay_ms(Config_Resolution resolution)
+{
+  uint8_t measure_delay_ms;
+  
+  switch (resolution)
+  {
+    case Config_9b_Res:
+      measure_delay_ms = 25;
+      break;
+    case Config_10b_Res:
+      measure_delay_ms = 50;
+      break;
+    case Config_11b_Res:
+      measure_delay_ms = 100;
+      break;
+    case Config_12b_Res:
+      measure_delay_ms = 200;
+      break;
+    default:
+      measure_delay_ms = 0;
+      break;
+  }
+  
+  return measure_delay_ms;
+}
+
+bool DS7505::read_temp_sensor_data(uint16_t & sensor_data) const
+{
+  bool result;
+  uint8_t upperByte, lowerByte;
+  int sub_res;
+  
+  sensor_data = 0;
+  m_I2C_interface.start();
+  sub_res = m_I2C_interface.write(m_I2C_address | I2C_READ);
+  if (sub_res == I2C_WRITE_OK)
+  {
+    upperByte = m_I2C_interface.read(I2C::ACK);
+    lowerByte = m_I2C_interface.read(I2C::NoACK);
+  }
+  m_I2C_interface.stop();
+  if (sub_res == I2C_WRITE_OK)
+  {
+    sensor_data = ((((uint16_t)upperByte) << 8) | lowerByte);
+    result = true;
+  }
+  else
+  {
+    // Handle hardware malfunction
+    result = false;
+  }
+  
+  return result;
+}
+
+bool DS7505::set_register_pointer(Register pointer_reg) const
+{
+  int res;
+  
+  m_I2C_interface.start();
+  res = m_I2C_interface.write(m_I2C_address | I2C_WRITE);
+  if (res == I2C_WRITE_OK)
+  {
+    res = m_I2C_interface.write(pointer_reg);
+  }
+  m_I2C_interface.stop();
+  
+  return (res == I2C_WRITE_OK);
+}
+
+bool DS7505::write_register(Register write_reg, uint8_t write_val) const
+{
+  bool res;
+  
+  m_I2C_interface.start();
+  res = m_I2C_interface.write(m_I2C_address | I2C_WRITE);
+  if (res == I2C_WRITE_OK)
+  {
+    res = m_I2C_interface.write(write_reg);
+    if (res == I2C_WRITE_OK)
+      res = m_I2C_interface.write(write_val);
+  }
+  m_I2C_interface.stop();
+  
+  return (res == I2C_WRITE_OK);
+}
+
+bool DS7505::write_current_config() const
+{
+  uint8_t DS7505_Config_Val = m_current_config.resolution;
+  if (m_current_config.enable_shutdown_mode)
+    DS7505_Config_Val |= DS7505_Config_SD_Bit;
+  return write_register(Configuration_Reg, DS7505_Config_Val);
+}
+
+DS7505::Result DS7505::set_resolution(uint8_t resolution)
+{   
+  switch (resolution)
+  {
+    case 1:
+      m_current_config.resolution = Config_9b_Res;
+      break;
+    case 2:
+      m_current_config.resolution = Config_10b_Res;
+      break;
+    case 3:
+      m_current_config.resolution = Config_11b_Res;
+      break;
+    case 4:
+      m_current_config.resolution = Config_12b_Res;
+      break;
+    default:
+      return Out_of_Range;
+  }
+  
+  // Write DS7505 configuration
+  if (!write_current_config())
+  {
+    // Handle hardware malfunction
+    return Hardware_Failure;
+  }
+  
+  // Set pointer to temperature register
+  if (!set_register_pointer(Temperature_Reg))
+  {
+    // Handle hardware malfunction
+    return Hardware_Failure;
+  }
+  
+  return Success;
+}
+
+DS7505::Result DS7505::read_temp_sensor(uint16_t & sensor_data) const
+{
+  bool res;
+  
+  if (m_current_config.enable_shutdown_mode)
+  {
+    // Disable shutdown mode
+    m_current_config.enable_shutdown_mode = false;
+    res = write_current_config();
+    if (!res)
+      return Hardware_Failure;
+  
+    // DS7505 measures temperature
+  
+    // Enable shutdown mode
+    m_current_config.enable_shutdown_mode = true;
+    res = write_current_config();
+    if (!res)
+      return Hardware_Failure;
+    
+    // Set pointer to temperature register
+    res = set_register_pointer(Temperature_Reg);
+    if (!res)
+      return Hardware_Failure;
+    
+    // Sleep for maximum time needed for sample
+    wait_ms(get_measure_delay_ms(m_current_config.resolution));
+  }
+  // else: shutdown mode disabled
+  //    DS7505 is constantly measuring temperature
+  
+  // Read temperature from sensor
+  if (!read_temp_sensor_data(sensor_data))
+  {
+    return Hardware_Failure;
+  }
+  
+  return Success;
+}
+
+DS7505::Result DS7505::read_current_temp(int16_t & temperature) const
+{
+  uint16_t sensor_data;
+  Result result;
+  
+  result = read_temp_sensor(sensor_data);
+  if (result == Success)
+  {
+    // Convert temperature to have an exponent of 10^-2
+    temperature = ((int8_t)(sensor_data >> 8)) * 100;
+    if (sensor_data & 0x0080)
+      temperature += 50; // 0.5
+    if (sensor_data & 0x0040)
+      temperature += 25; // 0.25
+    if (sensor_data & 0x0020)
+      temperature += 13; // 0.125
+    if (sensor_data & 0x0010)
+      temperature += 6; // 0.0625
+  }
+  return result;
+}
+
+DS7505::Result DS7505::read_current_temp(double & temperature) const
+{
+  uint16_t sensor_data;
+  Result result;
+  
+  result = read_temp_sensor(sensor_data);
+  if (result == Success)
+  {
+    // Convert sensor data to floating-point temperature
+    temperature = ((int8_t)(sensor_data >> 8));
+    if (sensor_data & 0x0080)
+      temperature += 0.5;
+    if (sensor_data & 0x0040)
+      temperature += 0.25;
+    if (sensor_data & 0x0020)
+      temperature += 0.125;
+    if (sensor_data & 0x0010)
+      temperature += 0.0625;
+  }
+  return result;
+}
+
+DS7505::Result DS7505::read_current_temp(int8_t & temperature) const
+{
+  uint16_t sensor_data;
+  Result result;
+  
+  result = read_temp_sensor(sensor_data);
+  if (result == Success)
+  {
+    // Convert sensor data to integer temperature
+    temperature = ((int8_t)(sensor_data >> 8));
+  }
+  return result;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DS7505.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,141 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _DS7505_HPP
+#define _DS7505_HPP
+
+#include <cstdint>
+
+namespace mbed
+{
+  class I2C;
+}
+
+/// Interface to the DS7505 temperature sensor.
+class DS7505
+{
+public:
+  enum Result
+  {
+    Success,
+    Hardware_Failure,
+    Out_of_Range
+  };
+  
+  /// @param I2C_interface A configured I2C interface to use for communication.
+  /// @param I2C_address Device bus address in mbed format.
+  DS7505(mbed::I2C & I2C_interface, std::uint8_t I2C_address);
+  
+  /// Set the resolution for temperature conversions.
+  /// @param resolution Number of decimal bit from 1 to 4.
+  Result set_resolution(std::uint8_t resolution);
+  
+  /// Reads the current temperature with an exponent of 10^-2.
+  /// @note Compatible with Bluetooth characteristic: org.bluetooth.characteristic.temperature.
+  Result read_current_temp(std::int16_t & temperature) const;
+  
+  /// Reads the current temperature as a floating point value.
+  Result read_current_temp(double & temperature) const;
+  
+  /// Reads the current temperature as an integer value.
+  Result read_current_temp(std::int8_t & temperature) const;
+  
+private:
+  /// Bit resolution of temperature conversions.
+  enum Config_Resolution
+  {
+    Config_9b_Res = 0x00,
+    Config_10b_Res = 0x20,
+    Config_11b_Res = 0x40,
+    Config_12b_Res = 0x60
+  };
+  
+  /// DS7505 Register addresses.
+  enum Register
+  {
+    Temperature_Reg = 0x00,
+    Configuration_Reg = 0x01,
+    Thyst_Reg = 0x02,
+    Tos_Reg = 0x03
+  };
+  
+  /// Represents a DS7505 configuration.
+  struct Config
+  {
+    Config_Resolution resolution;
+    bool enable_shutdown_mode;
+    
+    Config(Config_Resolution resolution, bool enable_shutdown_mode)
+      : resolution(resolution), enable_shutdown_mode(enable_shutdown_mode) { }
+  };
+  
+  /// @note Mark as mutable to allow manipulation by read_temp_sensor().
+  mutable Config m_current_config;
+  
+  mbed::I2C & m_I2C_interface;
+  std::uint8_t m_I2C_address;
+  
+  /// Returns the maximum time needed in ms for a sample at the specified resolution.
+  static std::uint8_t get_measure_delay_ms(Config_Resolution resolution);
+  
+  /// Reads the current temperature via I2C.
+  /// Assumes that the I2C register pointer is already set to the temperature register.
+  /// @param sensor_data Output for raw data from DS7505 with upper and lower bytes combined.
+  /// @returns True on success.
+  bool read_temp_sensor_data(std::uint16_t & sensor_data) const;
+  
+  /// Reads the current temperature with support for shutdown mode.
+  /// @param sensor_data Output for raw data from DS7505 with upper and lower bytes combined.
+  /// @returns Success or Hardware_Failure.
+  Result read_temp_sensor(std::uint16_t & sensor_data) const;
+  
+  /// Sets the I2C register pointer for the next operation.
+  /// @param pointer_reg Desired register to set.
+  /// @returns True on success.
+  /// @note Allow marking const since not public.
+  bool set_register_pointer(Register pointer_reg) const;
+  
+  /// Writes to a device register via I2C.
+  /// @param write_reg Register to write to.
+  /// @param write_val Value to write to the register.
+  /// @returns True on success.
+  /// @note Allow marking const since not public
+  bool write_register(Register write_reg, std::uint8_t write_val) const;
+  
+  /// Writes the current configuration via I2C.
+  /// @returns True on success.
+  /// @note Allow marking const since not public
+  bool write_current_config() const;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Display.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,283 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include <sstream>
+#include "Display.hpp"
+#include "mbed.h"
+
+//LCD Commands
+//If the RS bit is set to logic 1, these display bytes are stored in the display RAM at the address specified by the data pointer. The data pointer is
+//automatically updated and the data is directed to the intended ST7036i device. If the RS bit of the last control byte is set to
+//logic 0, these command bytes will be decoded and the setting of the device will be changed according to the received commands.
+enum LCD_Commands
+{
+  ControlByte = 0x00,	        //Only one control byte will be sent. Only a stream of data bytes is allowed to follow.
+  ControlByte_RS_Set = 0x40,  	//Only one control byte will be sent with the RS bit set. Only a stream of data bytes is allowed to follow.
+  ControlBytes = 0x80,          //Another control byte will follow, unless an I2C Stop condition is received.
+  ControlBytes_RS_Set = 0xC0, 	//RS Set and another control byte will follow, unless an I2C Stop condition is received.
+};
+
+//LCD Instructions
+enum LCD_Instructions
+{
+  ClearDisplay = 0x01,
+  Display_OFF = 0x08,     	//Display off
+  Display_ON = 0x0C,     	        //Display on, cursor off, cursor position off
+  ReturnHome = 0x02,
+  SetDdramAddress = 0x80
+};
+
+// LED Driver Port Registers
+// Initial port state 0x80
+enum LED_Driver_Ports
+{
+  P1 = 0x01,
+  P2 = 0x02,  // Blue LED
+  P3 = 0x03,  // Green LED
+  P4 = 0x04   // Red LED
+};
+
+// Convert a byte color value into the representation used by the MAX7306 PWM registers
+static std::uint8_t convertColorToPwmRegVal(std::uint8_t color)
+{
+  const std::uint8_t staticOffRegVal = 0x80; // LED is static off by setting to input
+  const std::uint8_t staticOnRegVal = 0x00; // LED is static on
+  const std::uint8_t minOnRegVal = 0x01; // LED on for minimum duty cycle
+  
+  std::uint8_t regVal;
+  if (color == 0x00) // Use static off for no color
+  {
+    regVal = staticOffRegVal;
+  }
+  else if (color == 0xFF) // Use static on for full color
+  {
+    regVal = staticOnRegVal;
+  }
+  else // Use standard PWN for all other values
+  {
+    // The 3 least significant bits cannot be rendered with the MAX7306
+    regVal = color >> 3;
+    if (regVal == staticOnRegVal)
+      regVal = minOnRegVal;
+  }
+  return regVal;
+}
+
+Display::Display(I2C & I2C_intf, uint8_t LCD_I2C_addr, uint8_t LED_driver_I2C_addr)
+  : m_I2C_intf(I2C_intf), m_LCD_I2C_addr(LCD_I2C_addr), m_LED_driver_I2C_addr(LED_driver_I2C_addr)
+{
+  
+}
+
+void Display::initialize(void)
+{
+  initializeLCD();
+  initializeLED_Driver();
+}
+
+void Display::initializeLED_Driver(void)
+{
+  const std::uint8_t Configuration26 = 0x26;  //intial port state 0xEC
+  const std::uint8_t Configuration27 = 0x27;  //intial port state 0x8F
+  
+  //Intial mode
+  //write to Configuration Register 0x26
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LED_driver_I2C_addr);
+  m_I2C_intf.write(Configuration26);
+  //RST does  reset PWM/blink counters, RST resets registers to power-on-reset state
+  m_I2C_intf.write(0x1F); 
+  m_I2C_intf.stop();
+  
+  //Write to Configuration Register 0x27
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LED_driver_I2C_addr);
+  m_I2C_intf.write(Configuration27);
+  //Enable bus time out, set P1,P2,P3 to be controlled by their registers (0x01,0x02,0x03)
+  m_I2C_intf.write(0x0E); 
+  m_I2C_intf.stop();
+}
+
+void Display::setBackLightColor(const Color & color)
+{  
+  // Red
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LED_driver_I2C_addr);
+  m_I2C_intf.write(P4);
+  m_I2C_intf.write(convertColorToPwmRegVal(color.R)); 
+  m_I2C_intf.stop();
+   
+  // Green
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LED_driver_I2C_addr);
+  m_I2C_intf.write(P3);
+  m_I2C_intf.write(convertColorToPwmRegVal(color.G)); 
+  m_I2C_intf.stop();
+  
+  // Blue
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LED_driver_I2C_addr);
+  m_I2C_intf.write(P2);
+  m_I2C_intf.write(convertColorToPwmRegVal(color.B)); 
+  m_I2C_intf.stop();
+}
+
+void Display::clearLine(Line line)
+{
+  writeCompleteLine("", line);
+  setCursorPosition(line);
+}
+
+void Display::clearDisplay(void)
+{
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LCD_I2C_addr);
+  m_I2C_intf.write(ControlByte);     //No more control bytes will be sent
+  m_I2C_intf.write(ClearDisplay);
+  m_I2C_intf.stop();
+}
+
+void Display::initializeLCD(void)
+{
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LCD_I2C_addr);
+  m_I2C_intf.write(ControlByte);    //No more control bytes will be sent
+  //************************************************************************************
+  m_I2C_intf.write(0x38);               //Function Set IS[2:1] = 0,0 (&h38 = Single height font, 0x3C = double height font)
+  m_I2C_intf.write(0x39);               //Function Set IS[2:1] = (0,1)
+  //When IS[2:1]=(0,0): normal instruction be selected(refer instruction table 0)
+  //When IS[2:1]=(0,1): extension instruction be selected(refer instruction table 1 )
+  //When IS[2:1]=(1,0): extension instruction be selected(refer instruction table 2 )
+  m_I2C_intf.write(0x14);               //BIAS SET
+  m_I2C_intf.write(0x70);               //CONTRAST (was 0x78)
+  m_I2C_intf.write(0x5E);               //POWER/ICON CONTROL/CONTRAST (upper two bits)
+  m_I2C_intf.write(0x6D);               //FOLLOWER CONTROL
+  m_I2C_intf.write(Display_ON);      //Display on, cursor on, cursor position on
+  m_I2C_intf.write(ClearDisplay);    //Clear Display
+  m_I2C_intf.write(0x06);            //ENTRY MODE
+  //************************************************************************************
+  m_I2C_intf.stop();
+}
+
+void Display::writeCharacter(std::uint8_t character)
+{
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LCD_I2C_addr);
+  m_I2C_intf.write(ControlByte_RS_Set);	//No more control bytes will be sent
+  m_I2C_intf.write(character);	        //Display on, cursor on, cursor position on
+  m_I2C_intf.stop();	
+}
+
+void Display::writeText(const std::string & text)
+{
+  const char RETURN_CHAR = 0x16;
+  
+  std::size_t length = text.length();
+  if (length > lineLength)
+    length = lineLength;
+  
+  //Write to LCD
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LCD_I2C_addr);
+  m_I2C_intf.write(ControlByte_RS_Set);
+  
+  for(std::size_t i = 0; i < length; i++)
+  {
+    if(text[i] != RETURN_CHAR)
+      m_I2C_intf.write(text[i]);
+  }
+  
+  m_I2C_intf.stop();
+}
+
+void Display::setCursorPosition(Line line, std::size_t position)
+{  
+  if (position > (lineLength - 1)) // Set to last line character for values outside the upper bound
+    position = (lineLength - 1);
+
+  m_I2C_intf.start();
+  m_I2C_intf.write(m_LCD_I2C_addr);
+  m_I2C_intf.write(ControlByte); // No more control bytes will be sent
+  if(line == SecondLine) // Offset for second line
+    position += 0x40;
+  m_I2C_intf.write(SetDdramAddress | position);
+  m_I2C_intf.stop();
+}
+
+void Display::writeLine(const std::string & text, Line line)
+{  
+  setCursorPosition(line);
+  writeText(text);
+}
+
+void Display::writeCompleteLine(const std::string & text, Line line)
+{
+  // Add padding to user's string
+  std::string writeText(text);
+  if (writeText.length() < lineLength)
+    writeText.append(lineLength - writeText.length(), ' ');
+  
+  writeLine(writeText, line);
+}
+
+void Display::writeMessage(const std::string & message)
+{
+  if (message.length() > lineLength)
+  {
+    // Find split point
+    std::istringstream messageStream(message);
+    std::string word;
+    std::size_t splitIndex = 0;
+    do
+    {
+      if (word.length() > 0)
+        splitIndex += (word.length() + 1);
+      std::getline(messageStream, word, ' ');
+    } while ((splitIndex + word.length()) <= lineLength);
+    if (splitIndex == 0) // First word is too long
+    {
+      writeCompleteLine(message.substr(0, lineLength), FirstLine);
+      writeCompleteLine(message.substr(lineLength), SecondLine);
+    }
+    else
+    {
+      writeCompleteLine(message.substr(0, splitIndex - 1), FirstLine);
+      writeCompleteLine(message.substr(splitIndex), SecondLine);
+    }
+  }
+  else
+  {
+    writeCompleteLine(message, FirstLine);
+    writeCompleteLine("", SecondLine);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Display.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,108 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _DISPLAY_HPP
+#define _DISPLAY_HPP
+
+#include <cstdint>
+#include <string>
+
+namespace mbed { class I2C; }
+
+/// Interface to the Newhaven Display NHD-C0220BiZ-FS(RGB)-FBW-3VM LCD module
+/// and MAX7306 PWM LED driver for backlight color selection.
+class Display
+{
+public:
+  /// Display line referenced from top
+  enum Line
+  {
+    FirstLine = 0,
+    SecondLine
+  };
+  
+  /// 24-bit RGB color for the backlight.
+  struct Color
+  {
+    std::uint8_t R, G, B;
+    Color(std::uint8_t R, std::uint8_t G, std::uint8_t B)
+      : R(R), G(G), B(B) { }
+  };
+  
+  /// Length in character os a display line.
+  static const std::size_t lineLength = 20;
+  
+  /// @param I2C_interface A configured I2C interface to use for communication.
+  /// @param LCD_I2C_address LCD module bus address in mbed format.
+  /// @param LED_driver_I2C_addr PWM LED driver (MAX7306) bus address in mbed format.
+  Display(mbed::I2C & I2C_intf, std::uint8_t LCD_I2C_addr, std::uint8_t LED_driver_I2C_addr);
+  
+  /// Initialize display components.
+  void initialize(void);
+  
+  /// Clear all display lines.
+  void clearDisplay(void);
+  
+  /// Clear a specific display line.
+  void clearLine(Line line);
+  
+  /// Write a single character to the display at the current cursor position.
+  void writeCharacter(std::uint8_t character);
+  
+  /// Write text to the display at the current cursor position.
+  void writeText(const std::string & text);
+  
+  /// Set cursor to a certain line and zero-index position within the line.
+  void setCursorPosition(Line line, std::size_t position = 0);
+  
+  /// Writes text to the display starting at the beginning of the line.
+  void writeLine(const std::string & text, Line line);
+  
+  /// Writes text to the display starting at the beginning of the line and clears the remainder of the line.
+  void writeCompleteLine(const std::string & text, Line line);
+  
+  // Writes a message to the display with text wrapping allowed at spaces.
+  void writeMessage(const std::string & text);
+
+  // Set the display backlight to a certain color.
+  void setBackLightColor(const Color & color);
+  
+private:  
+  mbed::I2C & m_I2C_intf;
+  std::uint8_t m_LCD_I2C_addr, m_LED_driver_I2C_addr;
+  
+  void initializeLCD(void);
+  void initializeLED_Driver(void);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ESP8266.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,486 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include "ESP8266.hpp"
+
+ESP8266* ESP8266::defaultInstance = NULL;
+
+static inline void disableRecvData()
+{
+  __disable_irq();
+}
+
+static inline void enableRecvData()
+{
+  __enable_irq();
+}
+
+void ESP8266::setDefaultInstance(ESP8266 * const instance)
+{
+  if (instance != NULL)
+    defaultInstance = instance;
+}
+
+ESP8266** ESP8266::getDefaultInstance()
+{
+  return &defaultInstance;
+}
+
+ESP8266::ESP8266(const PinName tx, const PinName rx, const PinName rst, const PinName CH_PD, const int baud, Serial * debugMsgIntf)
+  : AT_intf(tx, rx), resetPin(rst), powerDownPin(CH_PD), debugMsg(debugMsgIntf), parseRecvReset(false)
+{
+  AT_intf.baud(baud);
+  AT_intf.attach(this, &ESP8266::recv_AT_data_cb);
+  
+  // Ensure that device is not held in reset
+  if (resetPin.is_connected())
+    resetPin = 1;
+  // Power down device at startup due to high current demand
+  setPowered(false);
+  
+  if (defaultInstance == NULL)
+    defaultInstance = this;
+}
+
+ESP8266::~ESP8266()
+{
+  if (defaultInstance == this)
+    defaultInstance = NULL;
+}
+
+void ESP8266::reset()
+{
+  if (resetPin.is_connected())
+  {
+    resetPin = 0;
+    wait_ms(10);
+    resetPin = 1;
+    wait_ms(1000);
+  }
+}
+
+bool ESP8266::powered() const
+{
+  bool isPowered;
+  if (powerDownPin.is_connected())
+  {
+    isPowered = powerDownPin.read();
+  }
+  else
+  {
+    isPowered = false;
+  }
+  return isPowered;
+}
+
+void ESP8266::setPowered(bool powered)
+{
+  if (powerDownPin.is_connected())
+    powerDownPin = powered;
+}
+
+ESP8266::CmdResult ESP8266::performSelfTest()
+{
+  return sendCommand(CmdBuilder(""));
+}
+
+ESP8266::CmdResult ESP8266::setCurrentWifiMode(const ESP8266::WifiMode mode)
+{
+  CmdBuilder builder("CWMODE_CUR");
+  builder.addRawArgument(mode);
+  return sendCommand(builder);
+}
+
+ESP8266::CmdResult ESP8266::joinCurrentAccessPoint(const std::string & ssid, const std::string & pwd, const std::string & bssid)
+{
+  CmdBuilder builder("CWJAP_CUR");
+  builder.addStringArgument(ssid);
+  builder.addStringArgument(pwd);
+  if (bssid != "")
+    builder.addStringArgument(bssid);
+  return sendCommand(builder);
+}
+
+ESP8266::CmdResult ESP8266::quitAccessPoint()
+{
+  return sendCommand(CmdBuilder("CWQAP"));
+}
+
+ESP8266::CmdResult ESP8266::setMaxRFTXPower(const float power_dBm)
+{
+  int power_arg = (int)(power_dBm * 4);
+  if (power_arg > 82)
+    power_arg = 82;
+  else if (power_arg < 0)
+    power_arg = 0;
+  
+  CmdBuilder builder("RFPOWER");
+  builder.addRawArgument(power_arg);
+  return sendCommand(builder);
+}
+
+ESP8266::CmdResult ESP8266::ping(const std::string & IP)
+{
+  CmdBuilder builder("PING");
+  builder.addStringArgument(IP);
+  return sendCommand(builder);
+}
+
+ESP8266::CmdResult ESP8266::openConnection(const ESP8266::ConnType type, const std::string & remoteIP, const unsigned int remotePort)
+{
+  CmdBuilder builder("CIPSTART");
+  builder.addStringArgument((type == TCP) ? "TCP" : "UDP");
+  builder.addStringArgument(remoteIP);
+  builder.addRawArgument(remotePort);
+  return sendCommand(builder);
+}
+
+ESP8266::CmdResult ESP8266::closeConnection()
+{
+  return sendCommand(CmdBuilder("CIPCLOSE"));
+}
+
+ESP8266::CmdResult ESP8266::sendData(const std::string & data)
+{
+  CmdBuilder builder("CIPSEND");
+  builder.addRawArgument(data.length());
+  ESP8266::CmdResult result = sendCommand(builder);
+  if (result == ESP8266::AT_OK)
+    result = send_AT_data(data, false);
+  return result;
+}
+
+ESP8266::CmdResult ESP8266::sendCommand(const CmdBuilder & cmd)
+{
+  return send_AT_data(cmd.str(), true);
+}
+
+bool ESP8266::recvIpDataReadable()
+{
+  bool result;
+  
+  disableRecvData(); // Lock queue access
+  
+  result = !recvIpDataBuffer.empty();
+  
+  enableRecvData(); // Unlock queue access
+  
+  return result;
+}
+
+char ESP8266::getcRecvIpData()
+{
+  char received;
+  
+  disableRecvData(); // Lock queue access
+  
+  // Pop next char or set to NTC if not data in buffer
+  if (!recvIpDataBuffer.pop(received))
+    received = '\0';
+  
+  enableRecvData(); // Unlock queue access
+  
+  return received;
+}
+
+void ESP8266::clearRecvData()
+{
+    disableRecvData(); // Lock queue access
+    
+    recvIpDataBuffer.reset();
+    parseRecvReset = true;
+    
+    enableRecvData(); // Unlock queue access
+}
+
+ESP8266::CmdResult ESP8266::send_AT_data(const std::string & cmdString, const bool expectEcho)
+{
+  const int timeout_ms = 10000;
+  
+  Timer timer;
+  ESP8266::CmdResult result = ESP8266::HardwareError;
+  std::string response;
+  
+  disableRecvData(); // Lock for manual data handling in this procedure
+  
+  // Flush receive buffer
+  while (AT_intf.readable())
+    AT_intf.getc();
+  
+  // Begin counting for timeout
+  timer.start();
+  
+  for (size_t i = 0; i < cmdString.length(); i++)
+  {
+    // Write next character
+    while (!AT_intf.writeable())
+    {
+      if (timer.read_ms() > timeout_ms)
+      {
+        result = TimeoutError;
+        goto exit;
+      }
+    }
+    AT_intf.putc(cmdString[i]);
+    // Wait for echo
+    if (expectEcho && (cmdString[i] != '\r') && (cmdString[i] != '\n'))
+    {
+      while (!AT_intf.readable())
+      {
+        if (timer.read_ms() > timeout_ms)
+        {
+          result = TimeoutError;
+          goto exit;
+        }
+      }
+      // Compare to written character
+      if (AT_intf.getc() != cmdString[i])
+      {
+        // Handle error
+        result = ESP8266::HardwareError;
+        goto exit;
+      }
+    }
+  }
+  
+  while (result == ESP8266::HardwareError)
+  {
+    // Wait to receive something
+    response.clear();
+    while (!read_line(response)) ;
+    
+    // Check if valid response
+    if (response == "OK")
+      result = ESP8266::AT_OK;
+    else if (response == "FAIL")
+      result = ESP8266::AT_FAIL;
+    else if (response == "ERROR")
+      result = ESP8266::AT_ERROR;
+    else if (response == "SEND OK") // Used by AT+CIPSEND
+      result = ESP8266::AT_OK;
+    else if (response == "ALREADY CONNECT") // Used by AT+CIPSTART
+      result = ESP8266::AT_OK;
+    
+    if (timer.read_ms() > timeout_ms)
+    {
+      result = TimeoutError;
+      break;
+    }
+  }
+  
+exit:
+  enableRecvData(); // Enable interrupt processing
+  return result;
+}
+
+bool ESP8266::read_line(std::string & line)
+{
+  char received;
+  
+  while (AT_intf.readable())
+  {
+    received = AT_intf.getc();
+    if (received == '\n')
+    {
+      return true;
+    }
+    else if (received != '\r')
+    {
+      line.push_back(received);
+    }
+  }
+  return false;
+}
+
+void ESP8266::recv_AT_data_cb()
+{
+  while (AT_intf.readable())
+  {
+    char received = AT_intf.getc();
+    parseRecvIpData(received);
+    parseRecvConnClosedMsg(received);
+    parseRecvReset = false;
+  }
+}
+
+void ESP8266::parseRecvIpData(const char received)
+{
+  enum DataRecvState
+  {
+    Header,
+    Length,
+    Data,
+    Reset
+  };
+  
+  static const char findChars[] = "+IPD,";
+  static const size_t maxSizeDigits = 4;
+  
+  static size_t dataFinishedSize = 0;
+  static int index = 0;
+  static char sizeDigits[] = { '\0', '\0', '\0', '\0', '\0' };
+  static DataRecvState state = Header;
+  
+  if (parseRecvReset)
+    state = Reset;
+    
+  switch (state)
+  {
+  case Reset:
+  default:
+    index = 0;
+    dataFinishedSize = 0;
+    state = Header;
+    // Continue processing switch
+    
+  case Header:
+    if (received == findChars[index])
+    {
+      if (findChars[++index] == '\0')
+      {
+        index = 0;
+        state = Length;
+      }
+    }
+    else
+    {
+      state = Reset;
+    }
+    break;
+    
+  case Length:    
+    if ((received <= '9') && (received >= '0'))
+    {
+      if (index < maxSizeDigits)
+      {
+        sizeDigits[index++] = received;
+      }
+      else
+      {
+        state = Reset;
+      }
+    }
+    else if (received == ':')
+    {
+      dataFinishedSize = atoi(sizeDigits);
+      if (dataFinishedSize == 0)
+      {
+        state = Reset;
+      }
+      else
+      {
+        index = 0;
+        state = Data;
+      }
+    }
+    else
+    {
+      state = Reset;
+    }
+    break;
+  
+  case Data:
+    if (index < dataFinishedSize)
+    {
+      recvIpDataBuffer.push(received);
+      index++;
+    }
+    else
+    {
+      state = Reset;
+    }
+    break;
+  };
+}
+
+void ESP8266::parseRecvConnClosedMsg(const char received)
+{
+  static const char findChars[] = "CLOSED";
+  
+  static int index = 0;
+  
+  bool shouldReset = parseRecvReset;
+  
+  if (received == findChars[index])
+  {
+    if (findChars[++index] == '\0')
+    {
+      printDbgMsg(findChars);
+      shouldReset = true;
+    }
+  }
+  else
+  {
+    shouldReset = true;
+  }
+  
+  if (shouldReset)
+  {
+    index = 0;
+  }
+}
+
+void ESP8266::printDbgMsg(const char * message)
+{
+  if (debugMsg != NULL)
+    debugMsg->printf("%s", message);
+}
+
+ESP8266::CmdBuilder::CmdBuilder(const std::string & cmd)
+{
+  clear(cmd);
+}
+
+void ESP8266::CmdBuilder::clear(const std::string & cmd)
+{
+  numArgs = 0;
+  cmdStream.str("");
+  
+  cmdStream << "AT";
+  if (cmd != "")
+    cmdStream << "+" << cmd;
+}
+
+void ESP8266::CmdBuilder::addStringArgument(const std::string & arg)
+{
+  std::ostringstream argStream;
+  argStream << "\"" << arg << "\"";
+  addRawArgument<std::string>(argStream.str());
+}
+
+std::string ESP8266::CmdBuilder::str() const
+{
+  std::string cmdString = cmdStream.str();
+  cmdString.append("\r\n");
+  return cmdString;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ESP8266.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,216 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _ESP8266_HPP
+#define _ESP8266_HPP
+
+#include <string>
+#include <sstream>
+
+#include "mbed.h"
+#include "CircularBuffer.h"
+
+/// Interface to the ESP8266 Wi-Fi module.
+class ESP8266
+{
+public:
+  /// Result of sending an AT command.
+  enum CmdResult
+  {
+    AT_OK = 1,
+    AT_FAIL = 0,
+    AT_ERROR = -1,
+    HardwareError = -2,
+    TimeoutError = -3
+  };
+  
+  /// ESP8266 Wi-Fi mode.
+  enum WifiMode
+  {
+    station_mode = 1,
+    softAP_mode = 2,
+    softAP_station_mode = 3
+  };
+  
+  /// Connection type.
+  enum ConnType
+  {
+    TCP,
+    UDP
+  };
+  
+  /// Recovery time between Send Data operation as specified by datasheet.
+  static const unsigned int sendDataRecoveryTimeMs = 1000;
+  
+  /// Builds command strings for the ESP8266 with proper formatting.
+  class CmdBuilder
+  {
+  public:
+    /// @param cmd Command of the format "AT+[cmd]".
+    CmdBuilder(const std::string & cmd = "");
+    
+    /// Clear all arguments.
+    /// @param cmd Command of the format "AT+[cmd]".
+    void clear(const std::string & cmd);
+    
+    /// Append an argument using the default string conversion for that type.
+    /// @param arg Argument to append to the command.
+    template <typename T> void addRawArgument(const T & arg)
+    {
+      cmdStream << ((numArgs == 0) ? "=" : ",") << arg;
+      numArgs++;
+    }
+    
+    /// Append a string argument with proper quoting.
+    /// @param arg Argument to append to the command.
+    void addStringArgument(const std::string & arg);
+    
+    /// Create a string suitable for use with sendCommand().
+    /// @returns The formatted command string.
+    std::string str() const;
+    
+  private:
+    int numArgs;
+    std::ostringstream cmdStream;
+  };
+  
+  /// @{
+  /// Default instance support for use with mbed Sockets.
+  static void setDefaultInstance(ESP8266 * const instance);
+  static ESP8266** getDefaultInstance();
+  /// @}
+  
+  /// @param tx Transmit pin from mbed to ESP8266.
+  /// @param rx Receive pin from ESP8266 to mbed.
+  /// @param rst Reset pin on ESP8266.
+  /// @param CH_PD Power-down pin on ESP8266.
+  /// @param baud Baud rate that the ESP8266 is using.
+  /// @param debugMsgIntf Optional serial interface for debugging messages.
+  ESP8266(const PinName tx, const PinName rx, const PinName rst, const PinName CH_PD, const int baud, Serial * debugMsgIntf = NULL);
+  ~ESP8266();
+  
+  /// Reset the ESP8266 via the hardware reset pin.
+  void reset();
+  
+  // Update the baud rate for the ESP8266.
+  void setBaud(int baud) { AT_intf.baud(baud); }
+  
+  /// @{
+  /// Control if the ESP8266 is powered via the hardware power-down pin.
+  bool powered() const;
+  void setPowered(bool powered);
+  /// @}
+  
+  /// Perform a self-test on the ESP8266.
+  CmdResult performSelfTest();
+  
+  /// Set the current Wi-Fi mode.
+  CmdResult setCurrentWifiMode(const WifiMode mode);
+  
+  /// Join a Wi-Fi access point.
+  /// @param ssid Network SSID to connect to.
+  /// @param pwd Network password.
+  /// @param bssid Optional network BSSID.
+  CmdResult joinCurrentAccessPoint(const std::string & ssid, const std::string & pwd, const std::string & bssid = "");
+  
+  /// Quit the current access point.
+  CmdResult quitAccessPoint();
+  
+  /// Set the maximum WiFi tranmission power.
+  /// @param power_dBm Power in dBm valid from 0 to 20.5 in 0.25 dBm increments.
+  CmdResult setMaxRFTXPower(const float power_dBm);
+  
+  /// Ping a host via the current access point.
+  /// @param IP IP address or resolvable hostname.
+  CmdResult ping(const std::string & IP);
+  
+  /// Open a connection to a host via the current access point.
+  /// @param type TCP or UPD connection.
+  /// @param remoteIP IP address or resolvable hostname to connect to.
+  /// @param remotePort Port on the host to connect to.
+  CmdResult openConnection(const ConnType type, const std::string & remoteIP, const unsigned int remotePort);
+  
+  /// Close the connection to the current host.
+  CmdResult closeConnection();
+  
+  /// Send data to the currently connected host.
+  /// @param data May be in text or binary form.
+  CmdResult sendData(const std::string & data);
+  
+  /// Send an AT command to the ESP8266.
+  /// @param cmd Formatted command to send.
+  CmdResult sendCommand(const CmdBuilder & cmd);
+  
+  /// Check if received IP data is available in the buffer.
+  /// @note Allow some processing delay to happen between calls to this function.
+  /// @returns True if data is available.
+  bool recvIpDataReadable();
+  /// Get the next character of received IP data from the buffer.
+  char getcRecvIpData();
+  /// Clear all received data from the buffer.
+  void clearRecvData();
+  
+private:
+  static ESP8266 * defaultInstance; ///< Default instance support for use with mbed Sockets.
+  
+  Serial AT_intf;
+  DigitalOut resetPin;
+  mutable DigitalOut powerDownPin; ///< @note Mark as mutable for use in powered().
+  CircularBuffer<char, 1024> recvIpDataBuffer; ///< Received IP data buffer.
+  Serial * debugMsg;
+  volatile bool parseRecvReset; ///< Indicates when AT interface received data parsers should be reset.
+  
+  /// Send raw AT data to the ESP8266.
+  /// @param cmdString Data to send.
+  /// @param expectEcho True if the ESP8266 will echo sent data back.
+  CmdResult send_AT_data(const std::string & cmdString, const bool expectEcho);
+  
+  /// Attempts to read an entire line terminated with \r\n from the AT interface.
+  /// \r will be preserved in the final string and \n will be stripped.
+  /// @param line Buffer to store received characters in.
+  /// @returns True if an entire line was read.
+  bool read_line(std::string & line);
+  
+  /// Callback for when data is received on the AT interface.
+  void recv_AT_data_cb();
+  /// Parse the next character received on the AT interface checking for valid IP data.
+  void parseRecvIpData(const char received);
+  /// Parse the next character receive on the AT interface for the connection closed message.
+  void parseRecvConnClosedMsg(const char received);
+  
+  /// Print a message on the debugging interface if setup.
+  /// @param message Null terminated string.
+  void printDbgMsg(const char * message);
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Factory.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,191 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include "Factory.hpp"
+#include "SensorNode.hpp"
+#include "common.hpp"
+#include "OneWire_Masters/DS2465/DS2465.hpp"
+#include "OneWire_Memory/Authenticators/DS28E15_22_25/DS28E15_22_25.hpp"
+#include "mbed.h"
+
+const std::uint8_t Factory::masterSecret[] = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x21,
+                                              0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x31, 0x32 };
+const std::uint8_t Factory::invalidMasterSecret[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+bool Factory::provision(DS2465 & ds2465)
+{
+  bool result = (ds2465.setMasterSecret(masterSecret) == ISha256MacCoprocessor::Success);
+  if (result)
+  {
+    SensorNode::AuthData authData;
+    DS28E15_22_25::Page pageData;
+    std::memset(pageData, SensorNode::defaultPaddingByte, pageData.length);
+    std::memcpy(pageData, authData.segment, authData.segment.length);
+    result = (ds2465.writeScratchpad(pageData, pageData.length) == OneWireMaster::Success);
+  }
+  if (result)
+  {
+    result = (ds2465.copyScratchpadToPage(0) == OneWireMaster::Success);
+    if (result)
+      wait_ms(DS2465::eepromPageWriteDelayMs);
+  }
+  
+  return result;
+}
+
+bool Factory::provision(SensorNode & sensorNode, bool validSecret)
+{
+  const int blockNum = sensorNode.authData.pageNum / 2;
+  const DS28E15_22_25::BlockProtection desiredProtection(false, false, false, true, blockNum); // Authentication Protection only
+  
+  // Reset to starting defaults
+  sensorNode.authData.reset();
+  
+  // Read current protection status
+  DS28E15_22_25::BlockProtection protectionStatus;
+  bool result;
+  // Select device through Skip ROM
+  result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  if (result)
+    result = (sensorNode.ds28e15_22_25.readBlockProtection(blockNum, protectionStatus) == OneWireSlave::Success);
+  // Check if invalid protections are set
+  if (result)
+    result = ((protectionStatus.statusByte() & ~(desiredProtection.statusByte())) == 0x00);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Load secret into scratchpad
+  if (result)
+    result = (sensorNode.ds28e15_22_25.writeScratchpad(validSecret ? masterSecret : invalidMasterSecret) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Load master secret from scratchpad without locking
+  if (result)
+    result = (sensorNode.ds28e15_22_25.loadSecret(false) == OneWireSlave::Success);
+  
+  // Setup is complete if not using a valid secret
+  if (!validSecret)
+    return result;
+  
+  // Create constant partial secret
+  DS28E15_22_25::Scratchpad partialSecret;
+  DS28E15_22_25::Page pageData;
+  std::memset(partialSecret, SensorNode::defaultPaddingByte, partialSecret.length);
+  
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Read page data
+  if (result)
+    result = (sensorNode.ds28e15_22_25.readPage(sensorNode.authData.pageNum, pageData, false) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Load partial secret into scratchpad
+  if (result)
+    result = (sensorNode.ds28e15_22_25.writeScratchpad(partialSecret) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Compute secret
+  if (result)
+    result = (sensorNode.ds28e15_22_25.computeSecret(sensorNode.authData.pageNum, false) == OneWireSlave::Success);
+  // Configure slave secret on DS2465
+  if (result)
+    result = (DS28E15_22_25::computeNextSecret(sensorNode.ds2465, pageData, sensorNode.authData.pageNum, partialSecret, sensorNode.ds28e15_22_25.romId, sensorNode.ds28e15_22_25.manId) == ISha256MacCoprocessor::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Enable authentication protection if not set
+  if (result && (protectionStatus != desiredProtection))
+    result = (sensorNode.ds28e15_22_25.writeAuthBlockProtection(sensorNode.ds2465, desiredProtection, protectionStatus) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Write initial filter life and set all other segments to default value
+  if (result)
+  {
+    DS28E15_22_25::Segment blankSegment;
+    std::memset(blankSegment, SensorNode::defaultPaddingByte, blankSegment.length);
+    for (std::size_t i = 0; i < (DS28E15_22_25::Page::length / DS28E15_22_25::Segment::length); i++)
+    {
+      result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+      if (result)
+        result = (sensorNode.ds28e15_22_25.writeAuthSegment(sensorNode.ds2465, sensorNode.authData.pageNum, i,
+                                                            ((i == sensorNode.authData.segmentNum) ? sensorNode.authData.segment : blankSegment),
+                                                            reinterpret_cast<const DS28E15_22_25::Segment::Buffer &>(static_cast<const DS28E15_22_25::Page::Buffer &>(pageData)[i * sizeof(DS28E15_22_25::Segment::Buffer)]), false) == OneWireSlave::Success);
+      
+      if (!result)
+        break;
+    }
+  }
+  
+  // Reload secret with known page values
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Load master secret into scratchpad
+  if (result)
+    result = (sensorNode.ds28e15_22_25.writeScratchpad(masterSecret) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Load master secret
+  if (result)
+    result = (sensorNode.ds28e15_22_25.loadSecret(false) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Read page data
+  if (result)
+    result = (sensorNode.ds28e15_22_25.readPage(sensorNode.authData.pageNum, pageData, false) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Write partial secret to scratchpad
+  if (result)
+    result = (sensorNode.ds28e15_22_25.writeScratchpad(partialSecret) == OneWireSlave::Success);
+  // Select device through Skip ROM
+  if (result)
+    result = (sensorNode.ds2465.OWSkipROM() == OneWireMaster::Success);
+  // Compute secret
+  if (result)
+    result = (sensorNode.ds28e15_22_25.computeSecret(sensorNode.authData.pageNum, false) == OneWireSlave::Success);
+  // Configure slave secret on DS2465
+  if (result)
+    result = (DS28E15_22_25::computeNextSecret(sensorNode.ds2465, pageData, sensorNode.authData.pageNum, partialSecret, sensorNode.ds28e15_22_25.romId, sensorNode.ds28e15_22_25.manId) == ISha256MacCoprocessor::Success);
+
+  return result;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Factory.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,61 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _FACTORY_HPP
+#define _FACTORY_HPP
+
+#include <cstdint>
+
+class DS2465;
+class SensorNode;
+
+/// Represents the secure factory that will perform the initial provisioning of
+/// Controllers (DS2465) and Sensor Nodes (DS28E15) for later authentication.
+class Factory
+{
+public:
+  /// Provision the DS2465 on a Controller.
+  /// @returns True on success.
+  bool provision(DS2465 & ds2465);
+  
+  /// Provision the DS28E15 on a Sensor Node.
+  /// @param validSecret True to provision using the valid system secret or false to imitate an invalid Controller.
+  /// @returns True on success.
+  bool provision(SensorNode & sensorNode, bool validSecret);
+  
+private:
+  static const std::uint8_t masterSecret[]; ///< The valid master secret for the system.
+  static const std::uint8_t invalidMasterSecret[]; ///< An invalid master secret for example purposes.
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX44009.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,86 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include "MAX44009.hpp"
+#include "mbed.h"
+
+static const int I2C_OK = 0;
+
+MAX44009::MAX44009(I2C & I2C_interface, uint8_t I2C_address)
+  : m_I2C_interface(I2C_interface), m_I2C_address(I2C_address)
+{
+  
+}
+
+MAX44009::Result MAX44009::read_current_lux(double & lux) const
+{
+  char I2C_data;
+  uint8_t mantissa, exponent;
+  
+  I2C_data = Lux_High_Byte_Reg;
+  if (m_I2C_interface.write(m_I2C_address, &I2C_data, 1, true) != I2C_OK)
+  {
+    m_I2C_interface.stop();
+    return Hardware_Failure;
+  }
+  if (m_I2C_interface.read(m_I2C_address, &I2C_data, 1, true) != I2C_OK)
+  {
+    m_I2C_interface.stop();
+    return Hardware_Failure;
+  }
+  mantissa = (I2C_data << 4);
+  exponent = (I2C_data >> 4);
+  
+  I2C_data = Lux_Low_Byte_Reg;
+  if (m_I2C_interface.write(m_I2C_address, &I2C_data, 1, true) != I2C_OK)
+  {
+    m_I2C_interface.stop();
+    return Hardware_Failure;
+  }
+  if (m_I2C_interface.read(m_I2C_address, &I2C_data, 1, false) != I2C_OK)
+  {
+    m_I2C_interface.stop();
+    return Hardware_Failure;
+  }
+  mantissa |= (I2C_data & 0x0F);
+  
+  unsigned long calc_result = 1;
+  if (exponent > 0)
+  {
+    calc_result <<= exponent;
+  }
+  calc_result *= mantissa;
+  lux = calc_result * 0.045;
+  
+  return Success;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX44009.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,71 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _MAX44009_HPP
+#define _MAX44009_HPP
+
+#include <cstdint>
+
+namespace mbed { class I2C; }
+
+/// Interface to the MAX44009 optical light sensor.
+class MAX44009
+{
+public:
+  enum Result
+  {
+    Success,
+    Hardware_Failure
+  };
+  
+  /// @param I2C_interface A configured I2C interface to use for communication.
+  /// @param I2C_address Device bus address in mbed format.
+  MAX44009(mbed::I2C & I2C_interface, std::uint8_t I2C_address);
+  
+  /// Read the current lux from the MAX44009.
+  /// @returns Success or Hardware_Failure.
+  Result read_current_lux(double & lux) const;
+  
+private:
+  /// MAX44009 Register addresses.
+  enum Register
+  {
+    Lux_High_Byte_Reg = 0x03,
+    Lux_Low_Byte_Reg = 0x04
+  };
+  
+  mbed::I2C & m_I2C_interface;
+  std::uint8_t m_I2C_address;
+};
+
+#endif
--- a/OneWire.lib	Wed Apr 13 18:54:30 2016 +0000
+++ b/OneWire.lib	Thu Apr 14 19:48:01 2016 +0000
@@ -1,1 +1,1 @@
-https://developer.mbed.org/teams/Maxim-Integrated/code/OneWire/#071ae5d090d1
+https://developer.mbed.org/teams/Maxim-Integrated/code/OneWire/#268612a10614
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SensorNode.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,260 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include "SensorNode.hpp"
+#include "common.hpp"
+#include "OneWire_Masters/DS2465/DS2465.hpp"
+#include "mbed.h"
+
+#ifdef TARGET_MAX32600
+#include "max32600.h"
+#include "clkman_regs.h"
+#include "tpu_regs.h"
+#else
+#include <cstdlib>
+#endif
+
+bool SensorNode::rngInitialized = false;
+
+void SensorNode::initializeRng()
+{
+#ifdef TARGET_MAX32600
+  MXC_CLKMAN->clk_config |= (MXC_F_CLKMAN_CLK_CONFIG_CRYPTO_ENABLE | MXC_F_CLKMAN_CLK_CONFIG_CRYPTO_RESET_N); // Enable crypto oscillator
+  while ((MXC_CLKMAN->intfl & MXC_F_CLKMAN_INTFL_CRYPTO_STABLE) != MXC_F_CLKMAN_INTFL_CRYPTO_STABLE) ; // Wait for crypto oscillator stability
+  MXC_CLKMAN->clk_ctrl |= MXC_F_CLKMAN_CLK_CTRL_CRYPTO_GATE_N; // Disable crypto clock gating
+  MXC_CLKMAN->crypt_clk_ctrl_2_prng = MXC_CLKMAN->clk_ctrl_10_prng = 1; // Set PRNG clock to crypto clock
+  MXC_CLKMAN->clk_gate_ctrl2 |=  (1 << MXC_F_CLKMAN_CLK_GATE_CTRL2_TPU_CLK_GATER_POS); // Use dynamic clock gating
+#endif
+}
+
+SensorNode::SensorNode(I2C & i2c, std::uint8_t ds7505_i2c_addr, std::uint8_t max44009_i2c_addr, DS2465 & ds2465)
+  : m_initialLux(1), ds28e15_22_25(ds2465), ds7505(i2c, ds7505_i2c_addr), max44009(i2c, max44009_i2c_addr), ds2465(ds2465)
+{
+  if (!rngInitialized)
+  {
+    initializeRng();
+    rngInitialized = true;
+  }
+}
+
+bool SensorNode::initializeSensors()
+{
+  return (max44009.read_current_lux(m_initialLux) == MAX44009::Success);
+}
+
+bool SensorNode::setSecret()
+{
+  DS28E15_22_25::Scratchpad scratchpad;
+  DS28E15_22_25::Page pageData;
+  
+  // Create constant partial secret
+  std::memset(scratchpad, defaultPaddingByte, scratchpad.length);
+  // Read page data
+  bool result = (ds2465.readMemory(DS2465::ADDR_USER_MEM_PAGE_0, pageData, pageData.length, false) == OneWireMaster::Success);
+  // Calculate secret
+  if (result)
+  {
+    result = (DS28E15_22_25::computeNextSecret(ds2465, pageData, authData.pageNum, scratchpad, ds28e15_22_25.romId, ds28e15_22_25.manId) == ISha256MacCoprocessor::Success);
+  }
+  return result;
+}
+
+bool SensorNode::checkProvisioned(bool & provisioned)
+{
+  DS28E15_22_25::BlockProtection protectionStatus;
+  bool result;
+  
+  // Select device through Skip ROM
+  result = (ds2465.OWSkipROM() == OneWireMaster::Success);
+  if (result)
+    result = (ds28e15_22_25.readBlockProtection(0, protectionStatus) == OneWireSlave::Success);
+  if (result)
+  {
+    if (!protectionStatus.noProtection())
+    {
+      // Select device through Skip ROM
+      result = (ds2465.OWSkipROM() == OneWireMaster::Success);
+      if (result)
+        result = (ds28e15_22_25.readSegment(authData.pageNum, authData.segmentNum, authData.segment) == OneWireSlave::Success);
+      if (result)
+        provisioned = true;
+    }
+    else
+    {
+      provisioned = false;
+    }
+  }
+  return result;
+}
+
+bool SensorNode::checkAuthentic(unsigned int userEntropy)
+{
+  DS28E15_22_25::Scratchpad challenge;
+  DS28E15_22_25::Page pageData;
+  
+  // Select device through Skip ROM
+  if (ds2465.OWSkipROM() != OneWireMaster::Success)
+    return false;
+  // Read page data
+  if (ds28e15_22_25.readPage(authData.pageNum, pageData, false) != OneWireSlave::Success)
+    return false;
+  
+  // Create random challenge
+  // Use hardare RNG on MAX32600
+#ifdef TARGET_MAX32600 
+  MXC_TPU->prng_user_entropy = userEntropy;
+#else
+  std::srand(userEntropy);
+#endif
+  for (std::size_t i = 0; i < challenge.length; i++)
+  {
+#ifdef TARGET_MAX32600
+    challenge[i] = MXC_TPU->prng_rnd_num;
+#else
+    challenge[i] = std::rand(); 
+#endif
+  }
+
+  // Select device through Skip ROM
+  if (ds2465.OWSkipROM() != OneWireMaster::Success)
+    return false;
+  // Write challenge to scratchpad
+  if (ds28e15_22_25.writeScratchpad(challenge) != OneWireSlave::Success)
+    return false;
+  // Select device through Skip ROM
+  if (ds2465.OWSkipROM() != OneWireMaster::Success)
+    return false;
+  // Have device compute MAC
+  DS28E15_22_25::Mac nodeMac;
+  if (ds28e15_22_25.computeReadPageMac(0, false, nodeMac) != OneWireSlave::Success)
+    return false;
+  // Compute expected MAC
+  DS28E15_22_25::Mac controllerMac;
+  if (DS28E15_22_25::computeAuthMac(ds2465, pageData, authData.pageNum, challenge, ds28e15_22_25.romId, ds28e15_22_25.manId, controllerMac) != ISha256MacCoprocessor::Success)
+    return false;
+  // Check if authentic
+  return (nodeMac == controllerMac);
+}
+
+bool SensorNode::readSensorData(SensorData & sensorData)
+{
+  bool result;
+  std::int8_t temp;
+  
+  // Read temperature sensor
+  result = (ds7505.read_current_temp(temp) == DS7505::Success);
+  
+  if (result)
+  {
+    sensorData.temp = temp;
+    
+    // Read light sensor
+    double currentLux;
+    result = (max44009.read_current_lux(currentLux) == MAX44009::Success);
+    if (result)
+    {
+      // Convert lux to remaining filter life
+      sensorData.filterLife = (unsigned int)((currentLux / m_initialLux) * 100);
+    }
+  }
+  
+  return result;
+}
+
+bool SensorNode::checkAndWriteAuthData(SensorData & sensorData)
+{
+  bool result = true;
+  
+  if (sensorData.filterLife > authData.filterLife)
+  {
+    sensorData.filterLife = authData.filterLife;
+  }
+  else if (sensorData.filterLife < authData.filterLife)
+  {
+    AuthData oldAuthData(authData);
+    authData.filterLife = sensorData.filterLife;
+    // Select device through Skip ROM
+    result = (ds2465.OWSkipROM() == OneWireMaster::Success);
+    // Write new filter life to DS28E15
+    if (result)
+      result = (ds28e15_22_25.writeAuthSegment(ds2465, authData.pageNum, authData.segmentNum, authData.segment, oldAuthData.segment, false) == OneWireSlave::Success);
+  }
+  
+  return result;
+}
+
+SensorNode::State SensorNode::detect(unsigned int userEntropy)
+{
+  bool provisioned;
+  
+  ds2465.OWSetSpeed(DS2465::SPEED_OVERDRIVE);
+  
+  if (ds2465.OWReadROM(ds28e15_22_25.romId) != OneWireMaster::Success)
+    return UnableToCommunicate;
+  
+  if (!checkProvisioned(provisioned))
+    return UnableToCommunicate;
+  
+  if (!provisioned)
+      return NotProvisioned;
+  
+  if (!setSecret())
+    return UnableToCommunicate;
+    
+  if (!checkAuthentic(userEntropy))
+    return NotAuthentic;
+  
+  if (!initializeSensors())
+    return UnableToCommunicate;
+  
+  return Authentic;
+}
+
+SensorNode::State SensorNode::authenticatedReadSensorData(unsigned int userEntropy, SensorData & sensorData)
+{
+  ds2465.OWSetSpeed(DS2465::SPEED_OVERDRIVE);
+  
+  if (!setSecret())
+    return UnableToCommunicate;
+  
+  if (!checkAuthentic(userEntropy))
+    return NotAuthentic;
+  
+  if (!readSensorData(sensorData))
+    return UnableToCommunicate;
+  
+  if (!checkAndWriteAuthData(sensorData))
+    return NotAuthentic;
+  
+  return Authentic;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SensorNode.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,156 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _SENSORNODE_HPP
+#define _SENSORNODE_HPP
+
+#include <cstdint>
+#include <cstring>
+
+#include "DS7505.hpp"
+#include "MAX44009.hpp"
+#include "OneWire_Memory/Authenticators/DS28E15_22_25/DS28E15_22_25.hpp"
+
+class DS2465;
+class RomId;
+class SensorData;
+namespace mbed { class I2C; }
+
+/// Interface to the Authenticated Sensor Node peripheral board.
+class SensorNode
+{
+public:
+  enum State
+  {
+    UnableToCommunicate, ///< I2C or 1-Wire communication failure.
+    NotProvisioned, ///< DS28E15 has not been provisioned.
+    NotAuthentic, ///< DS28E15 is not authentic.
+    Authentic ///< DS218E15 is authentic.
+  };
+  
+  /// @param i2c Configured I2C communication interface.
+  /// @param ds7505_i2c_addr I2C bus address of the DS7505 in mbed format.
+  /// @param max44009_i2c_addr I2C bus address of the MAX44009 in mbed format.
+  /// @param ds2465 Interface to DS2465 on the Controller.
+  SensorNode(mbed::I2C & i2c, std::uint8_t ds7505_i2c_addr, std::uint8_t max44009_i2c_addr, DS2465 & ds2465);
+  
+  /// Detect if an authentic Sensor Node is connected.
+  /// @param userEntropy Additional entropy to supply to the RNG.
+  /// @returns Authentic on success.
+  State detect(unsigned int userEntropy);
+  
+  /// Read the current temperature and filter life measurements with authentication.
+  /// @param userEntropy Additional entropy to supply to the RNG.
+  /// @param sensorData Measurements output location.
+  /// @returns Authentic on success.
+  State authenticatedReadSensorData(unsigned int userEntropy, SensorData & sensorData);
+  
+  /// Reads the current temperature and filter life measurements.
+  /// @param sensorData Measurements output location.
+  /// @returns True on success or false if unable to communicate with a sensor.
+  bool readSensorData(SensorData & sensorData);
+  
+  /// Get the ROM ID for this sensor node.
+  const RomId & romId() const { return ds28e15_22_25.romId; }
+  
+  /// Get the initial lux measurement for this sensor node.
+  double initialLux() const { return m_initialLux; }
+  
+  // Grant access to hardware interfaces for provisioning.
+  friend class Factory;
+  
+private:
+  /// Authenticated data stored in DS28E15.
+  struct AuthData
+  {
+    static const std::uint8_t initialFilterLife = 100;
+    DS28E15_22_25::Segment segment;
+    std::uint8_t & filterLife;
+    unsigned int pageNum, segmentNum;
+    
+    AuthData() : filterLife(segment[0]), pageNum(0), segmentNum(0) { reset(); }
+    
+    void reset()
+    {
+      std::memset(segment, 0, segment.length);
+      filterLife = initialFilterLife;
+    }
+  };
+  
+  /// Padding value used in creation of the Slave Secret.
+  static const std::uint8_t defaultPaddingByte = 0x00;
+  
+  /// Indicates hardware RNG is initialized.
+  static bool rngInitialized;
+  // Initialize the hardware RNG.
+  static void initializeRng();
+  
+  /// Initial lux measurement taken on initialization.
+  /// Assumed to be the maximum intensity that will be observed.
+  double m_initialLux;
+  
+  /// Authenticated data stored on the DS28E15.
+  AuthData authData;
+  
+  // Hardware interfaces
+  DS28E15_22_25 ds28e15_22_25; ///< DS28E15 for authentication.
+  DS7505 ds7505; ///< DS7505 temperature sensor.
+  MAX44009 max44009; ///< MAX44009 optical light sensor.
+  DS2465 & ds2465; ///< Interface to DS2465 on Controller.
+  
+  /// Initialize sensors for measurement.
+  /// @returns True on success.
+  bool initializeSensors();
+  
+  /// Select the Slave Secret for this Sensor Node in the Controller.
+  /// @returns True on success.
+  bool setSecret();
+  
+  /// Check if the Sensor Board is provisioned.
+  /// @param provisioned True if the sensor board is provisioned.
+  /// @returns True if provisioning check was successful.
+  bool checkProvisioned(bool & provisioned);
+  
+  /// Check if the Sensor Board is authentic.
+  /// @param userEntropy Additional entropy to supply to the RNG.
+  /// @returns True if the Sensor Board passed the authentication check.
+  bool checkAuthentic(unsigned int userEntropy);
+  
+  /// Checks if the authenticated data stored in the DS28E15 needs to be updated.
+  /// Updates the authenticated data if necessary.
+  /// @param sensorData Current sensor data to check.
+  /// @returns True on success.
+  bool checkAndWriteAuthData(SensorData & sensorData);
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebServerInterface.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,331 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include <vector>
+
+#include "WebServerInterface.hpp"
+#include "ESP8266.hpp"
+#include "OneWire_Masters/ISha256MacCoprocessor.hpp"
+#include "common.hpp"
+#include "mbed.h"
+
+const char WebServerInterface::wifiSsid[] = "WifiSsid";
+const char WebServerInterface::wifiPassword[] = "WifiPassword";
+const char WebServerInterface::serverAddress[] = "website.com";
+const char WebServerInterface::serverPostPath[] = "/post.php";
+const char WebServerInterface::serverChallengePath[] = "/challenge.php";
+
+// HTTP formatting constants
+static const char keyValSeparator = '=';
+static const char fieldSeparator = '&';
+static const std::string newline = "\r\n";
+static const char sessionIdKey[] = "SessionId";
+
+// Authentication MAC constants
+static const std::size_t challengeLen = 32;
+static const std::uint8_t defaultPaddingByte = 0x00;
+
+/// Select the Transport Secret for the web server in the Controller.
+/// @returns True on success.
+static bool setHttpPostSecret(ISha256MacCoprocessor & MacCoproc)
+{
+  ISha256MacCoprocessor::DevicePage fillData;
+  std::memset(fillData, defaultPaddingByte, fillData.length);
+  return (MacCoproc.computeSlaveSecret(fillData, fillData, reinterpret_cast<ISha256MacCoprocessor::SlaveSecretData &>(fillData)) == ISha256MacCoprocessor::Success);
+}
+
+WebServerInterface::WebServerInterface(ESP8266 & esp8266, mbed::Serial * pc)
+  : esp8266(esp8266), pc(pc)
+{
+  
+}
+
+bool WebServerInterface::initialize()
+{
+  esp8266.setPowered(true);
+  esp8266.reset();
+  bool result = (esp8266.performSelfTest() == ESP8266::AT_OK);
+  if (!result)
+  {
+    return false;
+  }
+  result = (esp8266.setCurrentWifiMode(ESP8266::softAP_station_mode) == ESP8266::AT_OK);
+  if (!result)
+  {
+    return false;
+  }
+  result = (esp8266.setMaxRFTXPower(10) == ESP8266::AT_OK);
+  if (!result)
+  {
+    return false;
+  }
+  result = (esp8266.joinCurrentAccessPoint(wifiSsid, wifiPassword) == ESP8266::AT_OK);
+  if (!result)
+  {
+    return false;
+  }
+  return true;
+}
+
+/// Format an HTTP GET request as a string for transmission.
+/// @param host Web server address.
+/// @param path Web server location to retrieve.
+/// @param sessionId Session ID used to identify this controller.
+/// @returns GET request string.
+static std::string formatHttpGet(const std::string & host, const std::string & path, const std::string & sessionId)
+{
+  std::ostringstream httpGetStream;
+  httpGetStream << "GET " << path;
+  if (sessionId.length() > 0)
+    httpGetStream << '?' << sessionIdKey << keyValSeparator << sessionId;
+  httpGetStream << " HTTP/1.1" << newline;
+  httpGetStream << "Host: " << host << newline;
+  httpGetStream << newline;
+  return httpGetStream.str();
+}
+
+/// Computes a MAC using the Transport Secret to sign HTTP POST requests.
+/// @param macCoproc Coprocessor such as the DS2465 used to calculate the authentication MAC.
+/// @param input Message array used for MAC calculation.
+/// @param ilen Length of array input.
+/// @param output Calculated MAC output.
+static void calculateHttpPostMac(const ISha256MacCoprocessor & macCoproc, const std::uint8_t * input, std::size_t ilen, ISha256MacCoprocessor::Mac & output)
+{
+  ISha256MacCoprocessor::DeviceScratchpad block;
+  std::size_t index = 0;
+  ISha256MacCoprocessor::AuthMacData padding;
+  std::memset(padding, defaultPaddingByte, padding.length);
+  std::memset(output, defaultPaddingByte, output.length); // Set initial hash value
+  while (index < ilen)
+  {
+    if ((index + block.length) <= ilen) // Full block
+    {
+      std::memcpy(block, &input[index], block.length);
+      index += block.length;
+    }
+    else // Partial block with padding
+    {
+      std::memcpy(block, &input[index], ilen - index);
+      std::memset(&block[ilen - index], defaultPaddingByte, block.length - (ilen - index));
+      index = ilen;
+    }
+    // Write data to coprocessor and hash block
+    macCoproc.computeAuthMac(output, block, padding, output);
+  }
+}
+
+/// Format an HTTP POST request as a string for transmission.
+/// @param host Web server address.
+/// @param path Web server location to receive POST.
+/// @param sessionId Session ID used to identify this Controller.
+/// @param macCoproc Coprocessor such as the DS2465 used to calculate the authentication MAC.
+/// @param event Event message type.
+/// @param initialPostBody Message body as determined by the event message type.
+/// @param challenge Challenge previously received from web server for use in authentication MAC.
+/// @returns POST request string.
+static std::string formatHttpPost(const std::string & host, const std::string & path, const std::string & sessionId,
+                                  const ISha256MacCoprocessor & macCoproc, PostEvent event, const std::string & initialPostBody,
+                                  const std::uint8_t (&challenge)[challengeLen])
+{
+  const std::size_t headerReserve = 115, bodyReserve = 200;
+  
+  std::string httpPost;
+  httpPost.reserve(initialPostBody.length() + headerReserve + bodyReserve);
+  
+  // Add session ID to post body
+  if (sessionId.length() > 0)
+  {
+    httpPost += sessionIdKey;
+    httpPost += keyValSeparator;
+    httpPost += sessionId;
+  }
+  
+  // Add event to post body
+  std::string eventString;
+  switch (event)
+  {
+  case SensorDataEvent:
+    eventString = "SensorData";
+    break;
+    
+  case InvalidSensorEvent:
+    eventString = "InvalidSensor";
+    break;
+  }
+  if (eventString.length() > 0)
+  {
+    if (httpPost.length() > 0)
+      httpPost += fieldSeparator;
+    httpPost += "Event";
+    httpPost += keyValSeparator;
+    httpPost += eventString;
+  }
+  
+  // Add initial post body
+  if (initialPostBody.length() > 0)
+  {
+    if (httpPost.length() > 0)
+      httpPost += fieldSeparator;
+    httpPost += initialPostBody;
+  }
+  
+  // Combine initial post body with initial secret and hash
+  std::vector<std::uint8_t> hashInput;
+  hashInput.reserve(challengeLen + httpPost.length());
+  hashInput.insert(hashInput.end(), challenge, challenge + challengeLen);
+  hashInput.insert(hashInput.end(), httpPost.begin(), httpPost.end());
+  ISha256MacCoprocessor::Mac mac;
+  calculateHttpPostMac(macCoproc, &hashInput[0], hashInput.size(), mac);
+  
+  char contentLen[5];
+  std::snprintf(contentLen, sizeof(contentLen) / sizeof(char), "%u", (hashInput.size() - challengeLen) + (mac.length * charsPerByte) + 5 /* &MAC= */);
+  
+  // Construct full post request
+  httpPost = "";
+  httpPost += "POST ";
+  httpPost += path;
+  httpPost += " HTTP/1.1";
+  httpPost += newline;
+  httpPost += "Host: ";
+  httpPost += host;
+  httpPost += newline;
+  httpPost += "Accept: */*";
+  httpPost += newline;
+  httpPost += "Content-Length: ";
+  httpPost += contentLen;
+  httpPost += newline;
+  httpPost += "Content-Type: application/x-www-form-urlencoded";
+  httpPost += newline;
+  httpPost += newline;
+  // Add post body
+  httpPost.append(reinterpret_cast<char *>(&hashInput[challengeLen]), hashInput.size() - challengeLen);
+  // Convert hash to hex string and add to post body
+  httpPost += fieldSeparator;
+  httpPost += "MAC";
+  httpPost += keyValSeparator;
+  byteArrayToHexString(mac, mac.length, httpPost);
+  httpPost += newline;
+  
+  return httpPost;
+}
+
+bool WebServerInterface::authPostHttpEvent(ISha256MacCoprocessor & macCoproc, PostEvent event, const std::string & postData, bool setSecret)
+{
+  const std::string challengeSearch(newline + newline);
+  bool result;
+  std::uint8_t challenge[challengeLen];
+  std::string response;
+  
+  std::memset(challenge, defaultPaddingByte, challengeLen);
+  response.reserve(300);
+  
+  if (setSecret)
+  {
+    result = setHttpPostSecret(macCoproc);
+    if (!result)
+      return result;
+  }
+  
+  // Open connection
+  esp8266.clearRecvData(); // Clear received data buffer
+  result = (esp8266.openConnection(ESP8266::TCP, serverAddress, 80) == ESP8266::AT_OK);
+  if (result)
+  {
+    // Request challenge
+    result = (esp8266.sendData(formatHttpGet(serverAddress, serverChallengePath, sessionId)) == ESP8266::AT_OK);
+    if (result)
+    {
+      // Receive server response
+      for (int i = 0; i < 10; i++)
+      {
+        while (esp8266.recvIpDataReadable())
+        {
+          char read = esp8266.getcRecvIpData();
+          if (pc != NULL)
+            pc->putc(read);
+          if (response.length() < response.capacity())
+          {
+            response += read;
+          }
+          else
+          {
+            wait_ms(ESP8266::sendDataRecoveryTimeMs); // Wait for ESP8266 specified recovery time
+            goto close_get_connection;
+          }
+        }
+        wait_ms(100);
+      }
+      // Close connection
+    close_get_connection:
+      esp8266.closeConnection();
+      
+      // Parse challenge from response
+      std::size_t challengePos = response.find(challengeSearch);
+      if ((challengePos != std::string::npos) && ((challengePos + challengeSearch.length() + (challengeLen * charsPerByte)) <= response.length()))
+      {
+        challengePos += challengeSearch.length();
+        for (std::size_t i = 0; i < challengeLen; i++)
+        {
+          std::sscanf(response.substr(challengePos + (i * charsPerByte), charsPerByte).c_str(), "%2hhx", &challenge[i]);
+        }
+      }
+      
+      // Post sensor data
+      result = (esp8266.openConnection(ESP8266::TCP, serverAddress, 80) == ESP8266::AT_OK);
+      if (result)
+      {
+        result = (esp8266.sendData(formatHttpPost(serverAddress, serverPostPath, sessionId, macCoproc, event, postData, challenge)) == ESP8266::AT_OK);
+        wait_ms(ESP8266::sendDataRecoveryTimeMs); // Wait for ESP8266 specified recovery time
+      }
+    }
+    
+    // Close connection
+    esp8266.closeConnection();
+  }
+  
+  return result;
+}
+
+std::string WebServerInterface::formatSensorDataPostBody(const SensorData & sensorData)
+{
+  // Create initial post body string from input data
+  std::ostringstream postBodyStream;
+  postBodyStream << "Temp" << keyValSeparator << static_cast<int>(sensorData.temp);
+  postBodyStream << fieldSeparator;
+  postBodyStream << "FilterLife" << keyValSeparator << static_cast<unsigned>(sensorData.filterLife);
+  postBodyStream << fieldSeparator;
+  postBodyStream << "TempAlarm" << keyValSeparator << (sensorData.tempAlarm() ? "true" : "false");
+  postBodyStream << fieldSeparator;
+  postBodyStream << "FilterLifeAlarm" << keyValSeparator << (sensorData.filterLifeAlarm() ? "true" : "false");
+  return postBodyStream.str();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebServerInterface.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,94 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _WEBSERVERINTERFACE_HPP
+#define _WEBSERVERINTERFACE_HPP
+
+#include <string>
+
+/// The message type descibed by the POST.
+enum PostEvent
+{
+  SensorDataEvent, ///< Adding sensor data to the log.
+  InvalidSensorEvent ///< Reporting an invalid sensor node.
+};
+
+namespace mbed { class Serial; }
+class ESP8266;
+class ISha256MacCoprocessor;
+struct SensorData;
+
+/// Network interface to the web server supporting authenticated posting of event
+/// through an HTTP challenge-respones scheme with SHA-256 data signing.
+class WebServerInterface
+{
+public:
+   /// Session ID used by the web server to distinguish between multiple Controllers.
+  std::string sessionId;
+  
+  /// @param esp8266 Interface to ESP8266 for Wi-Fi access.
+  /// @param pc Optional serial interface for received web traffic.
+  WebServerInterface(ESP8266 & esp8266, mbed::Serial * pc = NULL);
+  
+  /// Initialize network interface and connect to access point.
+  /// @returns True on success.
+  bool initialize();
+  
+  /// Send an authenticated event message to the web server.
+  /// @param macCoProc Coprocessor such as the DS2465 used to calculate the authentication MAC.
+  /// @param event Event message type.
+  /// @postData Message body as determined by the event message type.
+  /// @setSecret True if the Transport Secret needs to be selected in the coprocessor.
+  /// @returns True on success.
+  bool authPostHttpEvent(ISha256MacCoprocessor & macCoproc, PostEvent event, const std::string & postData, bool setSecret);
+  
+  /// Format sensor data as text suitable for use in a POST body.
+  /// @param sensorData Sensor data to format.
+  /// @returns Data formatted for web server.
+  static std::string formatSensorDataPostBody(const SensorData & sensorData); 
+ 
+private:
+  /// @{
+  /// Configuration strings.
+  static const char wifiSsid[];
+  static const char wifiPassword[];
+  static const char serverAddress[];
+  static const char serverPostPath[];
+  static const char serverChallengePath[];
+  /// @}
+  
+  ESP8266 & esp8266;
+  mbed::Serial * pc;
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,54 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include "common.hpp"
+
+const std::size_t charsPerByte = 2;
+
+std::string byteArrayToHexString(const std::uint8_t * byteArray, std::size_t byteArraySize)
+{
+  std::string hexString;
+  hexString.reserve(byteArraySize * charsPerByte);
+  byteArrayToHexString(byteArray, byteArraySize, hexString);
+  return hexString;
+}
+
+void byteArrayToHexString(const std::uint8_t * byteArray, std::size_t byteArraySize, std::string & hexString)
+{
+  char hexBuf[charsPerByte + 1];
+  for (std::size_t i = 0; i < byteArraySize; i++)
+  {
+    std::snprintf(hexBuf, (sizeof(hexBuf) / sizeof(hexBuf[0])), "%2.2X", byteArray[i]);
+    hexString.append(hexBuf, charsPerByte);
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common.hpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,62 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#ifndef _COMMON_HPP
+#define _COMMON_HPP
+
+#include <string>
+#include <cstdint>
+
+/// Contains a set of sensor measurements.
+struct SensorData
+{
+  static const std::int8_t tempAlarmLevel = 26;
+  static const std::uint8_t filterLifeAlarmLevel = 20;
+  
+  std::int8_t temp;
+  std::uint8_t filterLife;
+  
+  bool tempAlarm() const { return (temp >= tempAlarmLevel); }
+  bool filterLifeAlarm() const { return (filterLife <= filterLifeAlarmLevel); }
+};
+
+/// Characters per byte for hex data.
+extern const std::size_t charsPerByte;
+
+/// Creates a new hex string from a byte array.
+std::string byteArrayToHexString(const std::uint8_t * byteArray, std::size_t byteArraySize);
+
+/// Appends a hex string created from a byte array to an existing string.
+void byteArrayToHexString(const std::uint8_t * byteArray, std::size_t byteArraySize, std::string & hexString);
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,475 @@
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+
+#include <sstream>
+
+#include "common.hpp"
+#include "WebServerInterface.hpp"
+#include "Factory.hpp"
+#include "SensorNode.hpp"
+#include "OneWire_Masters/DS2465/DS2465.hpp"
+#include "Display.hpp"
+#include "RomId.hpp"
+#include "ESP8266.hpp"
+#include "mbed.h"
+
+/// Main status for the program.
+enum Status
+{
+  InitializingController, ///< Configure DS2465 and connect to network.
+  DisplaySessionId, ///< Display ID for use with website.
+  SensorNodeNeedsDetection, ///< Prompt user to insert Sensor Node.
+  DetectingSensorNode, ///< Check if Sensor Node present.
+  SensorNodeNeedsProvision, ///< Sensor Node needs to be provisioned.
+  ProvisioningSensorNode, ///< Provisioning Sensor Node to factory defaults.
+  NormalOperation, ///< The normal demo operation state.
+  SensorNodeNotAuthentic, ///< Sensor Node failed authentication check.
+  ControllerInitializationError, ///< Failed to initialize Controller.
+  ControllerHardwareError, ///< Controller hardware failed unexpectedly.
+  SensorNodeHardwareError ///< Sensor Node hardware failed unexpectedly.
+};
+
+/// @{
+/// Configuration options.
+static const unsigned int webPostIntervalMs = 10000;
+static const unsigned int webPostRetryIntervalMs = 1000;
+static const std::uint8_t maxConsecutiveWebPostErrors = 3;
+/// @}
+
+/// @{
+/// LCD display colors.
+static const Display::Color Teal(0x00, 0xB2, 0xA9);
+static const Display::Color Red(0xFF, 0x00, 0x00);
+static const Display::Color Green(0x00, 0xFF, 0x00);
+/// @}
+
+/// @{
+/// Peripheral and pin definitions
+static Serial pc(USBTX, USBRX);
+static DigitalIn provisionButton(P2_0);
+static DigitalIn invalidateButton(P1_5);
+static DigitalOut tempAlarmLed(P2_1, 1);
+static DigitalOut filterLifeAlarmLed(P2_3, 1);
+static I2C i2c(P2_6, P2_7);
+static Display lcd(i2c, 0x78, 0x98);
+static DS2465 ds2465(i2c, 0x30);
+static SensorNode sensorNode(i2c, 0x90, 0x94, ds2465);
+static Factory factory;
+static ESP8266 esp8266(P1_1, P1_0, P1_2, P1_3, 38400);
+static WebServerInterface webIntf(esp8266, &pc);
+/// @}
+
+static bool useInvalidSecret = false; ///< Imitate an invalid controller when posting to web server.
+static unsigned int randomSeed = 0; ///< Create extra entropy for challenge.
+static Status currentStatus = InitializingController;
+static bool result = false;
+static std::uint8_t consecutiveWebPostErrors = 0; ///< Account for a few network errors in case of flaky connection.
+static Timer webPostTimer; ///< Software timer to track web posting interval.
+
+static void blinkLeds(unsigned int time_ms); ///< Invert LEDs for a given amount of time.
+static bool buttonPressed(DigitalIn & button); ///< Checks if button is pressed (returns true) and waits for release.
+static void displayStatus(Status status); ///< Display status message on LCD.
+static void displaySensorData(const SensorData & sensorData); ///< Display sensor data on the LCD.
+static bool readWebSessionId(std::string & sessionId); ///< Read device's web session ID from it's nonvolatile storage.
+
+#ifdef ASSEMBLY_TEST
+#include "AssemblyTest.cpp"
+#endif
+
+int main()
+{
+  blinkLeds(500);
+  
+#ifdef ASSEMBLY_TEST
+  assemblyTest();
+#endif
+  
+  while (true)
+  {
+    Status nextStatus = currentStatus;
+    switch (currentStatus)
+    {
+    case InitializingController:
+      pc.baud(115200);
+      i2c.frequency(100000);
+      webPostTimer.start();
+      
+      // Set initial LCD state
+      lcd.initialize();
+      displayStatus(currentStatus);
+      
+      // Connect to Wifi network
+      result = webIntf.initialize();
+      
+      // Read session ID
+      if (result)
+      {
+        result = readWebSessionId(webIntf.sessionId);
+      }
+      
+      // Provision DS2465 with master secret and page data
+      if (result)
+      {
+        result = factory.provision(ds2465);
+      }
+      
+      if (result)
+      {
+        nextStatus = DisplaySessionId;
+      }
+      else
+      {
+        nextStatus = ControllerInitializationError;
+      }
+      break;
+      
+    case DisplaySessionId:
+      // Wait for user to press Provision button
+      if (buttonPressed(provisionButton))
+      {
+        nextStatus = SensorNodeNeedsDetection;
+      }
+      break;
+      
+    case SensorNodeNeedsDetection:
+      // Wait for user to press Provision button
+      if (buttonPressed(provisionButton))
+      {
+        nextStatus = DetectingSensorNode;
+      }
+      break;
+      
+    case DetectingSensorNode:
+      // Perform Sensor Node detection sequence
+      switch (sensorNode.detect(randomSeed))
+      {
+      case SensorNode::UnableToCommunicate:
+      default:
+        nextStatus = SensorNodeHardwareError;
+        break;
+        
+      case SensorNode::NotProvisioned:
+        nextStatus = SensorNodeNeedsProvision;
+        break;
+        
+      case SensorNode::NotAuthentic:
+        nextStatus = SensorNodeNotAuthentic;
+        break;
+        
+      case SensorNode::Authentic:
+        nextStatus = NormalOperation;
+        break;
+      }
+      break;
+      
+    case SensorNodeNeedsProvision:
+      // Wait for user to press Provision button
+      if (buttonPressed(provisionButton))
+      {
+        nextStatus = ProvisioningSensorNode;
+      }
+      break;
+      
+    case ProvisioningSensorNode:      
+      if (!buttonPressed(invalidateButton)) // Provision normally
+      {
+        if (factory.provision(sensorNode, true))
+        {
+          nextStatus = NormalOperation;
+        }
+        else
+        {
+          nextStatus = SensorNodeNotAuthentic;
+        }
+      }
+      else // Invalidate button also pressed; Load invalid secret
+      {        
+        // Provision with invalid secret
+        if (factory.provision(sensorNode, false))
+        {
+          nextStatus = NormalOperation;
+        }
+        else
+        {
+          nextStatus = SensorNodeHardwareError;
+        }
+      }
+      break;
+      
+    case NormalOperation:      
+      // Check if user pressed Provision button
+      if (buttonPressed(provisionButton))
+      {
+        // Re-provision Sensor Node
+        nextStatus = ProvisioningSensorNode;
+      }
+      // Check if user pressed Invalidate button
+      else if (buttonPressed(invalidateButton))
+      {
+        // Toggle between using valid and invalid secret
+        // 1 blink = invalid; 2 blinks = valid
+        useInvalidSecret = !useInvalidSecret;
+        blinkLeds(100);
+        if (!useInvalidSecret)
+        {
+          wait_ms(100);
+          blinkLeds(100);
+        }
+      }
+      // Check node and display measurements
+      else
+      {
+        SensorData sensorData;
+        // Read sensor data with authentication
+        switch (sensorNode.authenticatedReadSensorData(randomSeed, sensorData))
+        {
+        case SensorNode::Authentic:
+          // Update measurements on LCD
+          displaySensorData(sensorData);
+          
+          // Update alarm LEDs
+          tempAlarmLed = !sensorData.tempAlarm(); // Active Low
+          filterLifeAlarmLed = !sensorData.filterLifeAlarm(); // Active Low
+          
+          // Send measurements to web if time interval reached
+          if (webPostTimer.read_ms() >= webPostIntervalMs)
+          {
+            // Format, sign, and transmit data to web server
+            result = webIntf.authPostHttpEvent(ds2465, SensorDataEvent, WebServerInterface::formatSensorDataPostBody(sensorData), !useInvalidSecret);
+            if (result)
+            {
+              // Reset timer count after logging sample complete
+              webPostTimer.reset();
+              consecutiveWebPostErrors = 0;
+            }
+            // There was likely an error establishing a web connection
+            else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors)
+            {
+              // Wait and try again
+              wait_ms(webPostRetryIntervalMs);
+            }
+            // Too many retry attempts
+            else
+            {
+              // Assume we have lost network connection
+              nextStatus = ControllerHardwareError;
+            }
+          }
+          break;
+          
+        case SensorNode::NotAuthentic:
+          nextStatus = SensorNodeNotAuthentic;
+          break;
+          
+        case SensorNode::UnableToCommunicate:
+        default:
+          nextStatus = SensorNodeHardwareError;
+          break;
+        }
+      }
+      break;
+      
+    case SensorNodeNotAuthentic:
+      // Wait for user to press Provision button
+      if (buttonPressed(provisionButton))
+      {
+        nextStatus = ProvisioningSensorNode;
+      }
+      // Try to authenticate and return to normal operation
+      else if (webPostTimer.read_ms() >= webPostIntervalMs)
+      {
+        // Send event message to server
+        result = webIntf.authPostHttpEvent(ds2465, InvalidSensorEvent, "", !useInvalidSecret);
+        if (result)
+        {
+          // Reset timer count after logging complete
+          webPostTimer.reset();
+          consecutiveWebPostErrors = 0;
+        }
+        else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors)
+        {
+          // There was likely an error establishing a web connection
+          // Wait and try again
+          wait_ms(webPostRetryIntervalMs);
+        }
+        else
+        {
+          nextStatus = ControllerHardwareError;
+        }
+        
+        // Try to authenticate again
+        nextStatus = SensorNodeNeedsDetection;
+      }
+      break;
+      
+    case ControllerInitializationError:
+    case ControllerHardwareError:
+    case SensorNodeHardwareError:
+    default:
+      // Do nothing until user resets
+      break;
+    }
+    // Check if status changed
+    if (currentStatus != nextStatus)
+    {
+      currentStatus = nextStatus;
+      displayStatus(currentStatus); // Display status message on LCD
+    }
+    
+    // Change seed value on every loop pass
+    randomSeed++;
+  }
+}
+
+/// Blink all LEDs for a certain amount of time.
+/// @param time_ms Time in ms to blink for.
+static void blinkLeds(unsigned int time_ms)
+{
+  tempAlarmLed = !tempAlarmLed;
+  filterLifeAlarmLed = !filterLifeAlarmLed;
+  wait_ms(time_ms);
+  tempAlarmLed = !tempAlarmLed;
+  filterLifeAlarmLed = !filterLifeAlarmLed;
+}
+
+/// Check if a button is pressed and wait for it to be release.
+/// @param button Active low button to check.
+/// @returns True if pressed.
+static bool buttonPressed(DigitalIn & button)
+{
+  const int buttonPressed = 0; // Active low
+  if (button == buttonPressed)
+  {
+    while (button == buttonPressed) ;
+    return true;
+  }
+  // else
+  return false;
+}
+
+/// Display the current status of the Controller on the LCD display.
+static void displayStatus(Status status)
+{
+  switch (status)
+  {
+  case InitializingController:
+    lcd.writeMessage("Initializing Controller...");
+    lcd.setBackLightColor(Teal);
+    break;
+    
+  case DisplaySessionId:
+    lcd.writeLine("ID: " + webIntf.sessionId, Display::FirstLine);
+    lcd.writeLine("Provision to begin", Display::SecondLine);
+    lcd.setBackLightColor(Teal);
+    break;
+    
+  case SensorNodeNeedsDetection:
+    lcd.writeMessage("Insert Sensor Node and press Provision");
+    lcd.setBackLightColor(Teal);
+    break;
+    
+  case DetectingSensorNode:
+    lcd.writeMessage("Detecting Sensor Node...");
+    lcd.setBackLightColor(Teal);
+    break;
+    
+  case SensorNodeNeedsProvision:
+    lcd.writeMessage("Sensor Node Needs Provision");
+    lcd.setBackLightColor(Teal);
+    break;
+    
+  case ProvisioningSensorNode:
+    lcd.writeMessage("Provisioning Sensor Node");
+    lcd.setBackLightColor(Teal);
+    break;
+    
+  case NormalOperation:
+    // Everything handled in displaySensorData()
+    break;
+    
+  case SensorNodeNotAuthentic:
+    lcd.writeMessage("Sensor Node Not Authentic");
+    lcd.setBackLightColor(Red);
+    break;
+    
+  case ControllerInitializationError:
+    lcd.writeMessage("Initialization Error Check Wi-Fi");
+    lcd.setBackLightColor(Red);
+    break;
+    
+  case ControllerHardwareError:
+    lcd.writeMessage("Controller Hardware Error");
+    lcd.setBackLightColor(Red);
+    break;
+    
+  case SensorNodeHardwareError:
+    lcd.writeMessage("Sensor Node Hardware Error");
+    lcd.setBackLightColor(Red);
+    break;
+  }
+}
+
+/// Display sensor data on the LCD display during normal operation.
+static void displaySensorData(const SensorData & sensorData)
+{ 
+  std::ostringstream stream;
+  stream << "Chiller Temp: " << (int)sensorData.temp << "C";
+  lcd.writeCompleteLine(stream.str(), Display::FirstLine);
+  stream.str(""); // Clear stream
+  stream << "Filter Life: " << (unsigned int)sensorData.filterLife << "%";
+  lcd.writeCompleteLine(stream.str(), Display::SecondLine);
+  lcd.setBackLightColor((sensorData.tempAlarm() || sensorData.filterLifeAlarm()) ? Red : Green);
+}
+
+/// Read the Session ID to use with the web server from ROM.
+/// @note Session ID is taken from the ROM ID of the MAX66242.
+/// @param[out] Session ID string.
+/// @returns True on success.
+static bool readWebSessionId(std::string & sessionId)
+{  
+  const uint8_t I2C_address = 0x32;
+  const uint8_t ROM_address = 0x68;
+  RomId romId;
+  
+  // Set register pointer
+  if (i2c.write(I2C_address, (const char *)&ROM_address, 1) != 0)
+    return false;
+  // Read ROM ID
+  if (i2c.read(I2C_address, (char *)(&(static_cast<RomId::ByteBuffer &>(romId))), RomId::byteLen) != 0)
+    return false;
+  // Check if CRC valid
+  if (!romId.crc8Valid())
+    return false;
+  sessionId = byteArrayToHexString(romId, RomId::byteLen);
+  return true;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Thu Apr 14 19:48:01 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/082adc85693f
\ No newline at end of file