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

Dependencies:   MaximInterface mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ESP8266.cpp Source File

ESP8266.cpp

00001 /*******************************************************************************
00002 * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
00003 *
00004 * Permission is hereby granted, free of charge, to any person obtaining a
00005 * copy of this software and associated documentation files (the "Software"),
00006 * to deal in the Software without restriction, including without limitation
00007 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008 * and/or sell copies of the Software, and to permit persons to whom the
00009 * Software is furnished to do so, subject to the following conditions:
00010 *
00011 * The above copyright notice and this permission notice shall be included
00012 * in all copies or substantial portions of the Software.
00013 *
00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00017 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
00018 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020 * OTHER DEALINGS IN THE SOFTWARE.
00021 *
00022 * Except as contained in this notice, the name of Maxim Integrated
00023 * Products, Inc. shall not be used except as stated in the Maxim Integrated
00024 * Products, Inc. Branding Policy.
00025 *
00026 * The mere transfer of this software does not imply any licenses
00027 * of trade secrets, proprietary technology, copyrights, patents,
00028 * trademarks, maskwork rights, or any other form of intellectual
00029 * property whatsoever. Maxim Integrated Products, Inc. retains all
00030 * ownership rights.
00031 *******************************************************************************/
00032 
00033 #include <cstdlib>
00034 #include <Timer.h>
00035 #include <wait_api.h>
00036 #include "ESP8266.hpp"
00037 
00038 static inline void disableRecvData() { __disable_irq(); }
00039 
00040 static inline void enableRecvData() { __enable_irq(); }
00041 
00042 ESP8266::ESP8266 (const PinName tx, const PinName rx, const PinName rst,
00043                  const PinName CH_PD, const int baud,
00044                  mbed::Serial * debugMsgIntf)
00045     : AT_intf(tx, rx), resetPin(rst), powerDownPin(CH_PD),
00046       debugMsg(debugMsgIntf), parseRecvReset(false) {
00047   AT_intf.baud(baud);
00048   AT_intf.attach(this, &ESP8266::recv_AT_data_cb);
00049 
00050   // Ensure that device is not held in reset
00051   if (resetPin.is_connected())
00052     resetPin = 1;
00053   // Power down device at startup due to high current demand
00054   setPowered(false);
00055 }
00056 
00057 void ESP8266::reset() {
00058   if (resetPin.is_connected()) {
00059     resetPin = 0;
00060     wait_ms(100);
00061     resetPin = 1;
00062     wait_ms(1000);
00063   }
00064 }
00065 
00066 bool ESP8266::powered () const {
00067   bool isPowered;
00068   if (powerDownPin.is_connected()) {
00069     isPowered = powerDownPin.read();
00070   } else {
00071     isPowered = false;
00072   }
00073   return isPowered;
00074 }
00075 
00076 void ESP8266::setPowered(bool powered) {
00077   if (powerDownPin.is_connected())
00078     powerDownPin = powered;
00079 }
00080 
00081 ESP8266::CmdResult ESP8266::performSelfTest() {
00082   return sendCommand(CmdBuilder(""));
00083 }
00084 
00085 ESP8266::CmdResult ESP8266::setCurrentWifiMode(const ESP8266::WifiMode mode) {
00086   CmdBuilder builder("CWMODE_CUR");
00087   builder.addRawArgument(mode);
00088   return sendCommand(builder);
00089 }
00090 
00091 ESP8266::CmdResult ESP8266::joinCurrentAccessPoint(const std::string & ssid,
00092                                                    const std::string & pwd,
00093                                                    const std::string & bssid) {
00094   CmdBuilder builder("CWJAP_CUR");
00095   builder.addStringArgument(ssid);
00096   builder.addStringArgument(pwd);
00097   if (bssid != "")
00098     builder.addStringArgument(bssid);
00099   return sendCommand(builder);
00100 }
00101 
00102 ESP8266::CmdResult ESP8266::quitAccessPoint() {
00103   return sendCommand(CmdBuilder("CWQAP"));
00104 }
00105 
00106 ESP8266::CmdResult ESP8266::setMaxRFTXPower(const float power_dBm) {
00107   int power_arg = (int)(power_dBm * 4);
00108   if (power_arg > 82)
00109     power_arg = 82;
00110   else if (power_arg < 0)
00111     power_arg = 0;
00112 
00113   CmdBuilder builder("RFPOWER");
00114   builder.addRawArgument(power_arg);
00115   return sendCommand(builder);
00116 }
00117 
00118 ESP8266::CmdResult ESP8266::ping(const std::string & IP) {
00119   CmdBuilder builder("PING");
00120   builder.addStringArgument(IP);
00121   return sendCommand(builder);
00122 }
00123 
00124 ESP8266::CmdResult ESP8266::openConnection(const ESP8266::ConnType type,
00125                                            const std::string & remoteIP,
00126                                            const unsigned int remotePort) {
00127   CmdBuilder builder("CIPSTART");
00128   builder.addStringArgument((type == TCP) ? "TCP" : "UDP");
00129   builder.addStringArgument(remoteIP);
00130   builder.addRawArgument(remotePort);
00131   return sendCommand(builder);
00132 }
00133 
00134 ESP8266::CmdResult ESP8266::closeConnection() {
00135   return sendCommand(CmdBuilder("CIPCLOSE"));
00136 }
00137 
00138 ESP8266::CmdResult ESP8266::sendData(const std::string & data) {
00139   CmdBuilder builder("CIPSEND");
00140   builder.addRawArgument(data.length());
00141   ESP8266::CmdResult result = sendCommand(builder);
00142   if (result == ESP8266::AT_OK)
00143     result = send_AT_data(data, false);
00144   return result;
00145 }
00146 
00147 ESP8266::CmdResult ESP8266::sendCommand(const CmdBuilder & cmd) {
00148   return send_AT_data(cmd.str(), true);
00149 }
00150 
00151 bool ESP8266::recvIpDataReadable() {
00152   bool result;
00153 
00154   disableRecvData(); // Lock queue access
00155 
00156   result = !recvIpDataBuffer.empty();
00157 
00158   enableRecvData(); // Unlock queue access
00159 
00160   return result;
00161 }
00162 
00163 char ESP8266::getcRecvIpData() {
00164   char received;
00165 
00166   disableRecvData(); // Lock queue access
00167 
00168   // Pop next char or set to NTC if not data in buffer
00169   if (!recvIpDataBuffer.pop(received))
00170     received = '\0';
00171 
00172   enableRecvData(); // Unlock queue access
00173 
00174   return received;
00175 }
00176 
00177 void ESP8266::clearRecvData() {
00178   disableRecvData(); // Lock queue access
00179 
00180   recvIpDataBuffer.reset();
00181   parseRecvReset = true;
00182 
00183   enableRecvData(); // Unlock queue access
00184 }
00185 
00186 ESP8266::CmdResult ESP8266::send_AT_data(const std::string & cmdString,
00187                                          const bool expectEcho) {
00188   const int timeout_ms = 10000;
00189 
00190   mbed::Timer timer;
00191   ESP8266::CmdResult result = ESP8266::HardwareError;
00192   std::string response;
00193 
00194   disableRecvData(); // Lock for manual data handling in this procedure
00195 
00196   // Flush receive buffer
00197   while (AT_intf.readable())
00198     AT_intf.getc();
00199 
00200   // Begin counting for timeout
00201   timer.start();
00202 
00203   for (size_t i = 0; i < cmdString.length(); i++) {
00204     // Write next character
00205     while (!AT_intf.writeable()) {
00206       if (timer.read_ms() > timeout_ms) {
00207         result = TimeoutError;
00208         goto exit;
00209       }
00210     }
00211     AT_intf.putc(cmdString[i]);
00212     // Wait for echo
00213     if (expectEcho && (cmdString[i] != '\r') && (cmdString[i] != '\n')) {
00214       while (!AT_intf.readable()) {
00215         if (timer.read_ms() > timeout_ms) {
00216           result = TimeoutError;
00217           goto exit;
00218         }
00219       }
00220       // Compare to written character
00221       if (AT_intf.getc() != cmdString[i]) {
00222         // Handle error
00223         result = ESP8266::HardwareError;
00224         goto exit;
00225       }
00226     }
00227   }
00228 
00229   while (result == ESP8266::HardwareError) {
00230     // Wait to receive something
00231     response.clear();
00232     while (!read_line(response))
00233       ;
00234 
00235     // Check if valid response
00236     if (response == "OK")
00237       result = ESP8266::AT_OK;
00238     else if (response == "FAIL")
00239       result = ESP8266::AT_FAIL;
00240     else if (response == "ERROR")
00241       result = ESP8266::AT_ERROR;
00242     else if (response == "SEND OK") // Used by AT+CIPSEND
00243       result = ESP8266::AT_OK;
00244     else if (response == "ALREADY CONNECT") // Used by AT+CIPSTART
00245       result = ESP8266::AT_OK;
00246 
00247     if (timer.read_ms() > timeout_ms) {
00248       result = TimeoutError;
00249       break;
00250     }
00251   }
00252 
00253 exit:
00254   enableRecvData(); // Enable interrupt processing
00255   return result;
00256 }
00257 
00258 bool ESP8266::read_line(std::string & line) {
00259   char received;
00260 
00261   while (AT_intf.readable()) {
00262     received = AT_intf.getc();
00263     if (received == '\n') {
00264       return true;
00265     } else if (received != '\r') {
00266       line.push_back(received);
00267     }
00268   }
00269   return false;
00270 }
00271 
00272 void ESP8266::recv_AT_data_cb() {
00273   while (AT_intf.readable()) {
00274     char received = AT_intf.getc();
00275     parseRecvIpData(received);
00276     parseRecvConnClosedMsg(received);
00277     parseRecvReset = false;
00278   }
00279 }
00280 
00281 void ESP8266::parseRecvIpData(const char received) {
00282   enum DataRecvState { Header, Length, Data, Reset };
00283 
00284   static const char findChars[] = "+IPD,";
00285   static const size_t maxSizeDigits = 4;
00286 
00287   static size_t dataFinishedSize = 0;
00288   static size_t index = 0;
00289   static char sizeDigits[] = {'\0', '\0', '\0', '\0', '\0'};
00290   static DataRecvState state = Header;
00291 
00292   if (parseRecvReset)
00293     state = Reset;
00294 
00295   switch (state) {
00296   case Reset:
00297   default:
00298     index = 0;
00299     dataFinishedSize = 0;
00300     state = Header;
00301     // Continue processing switch
00302 
00303   case Header:
00304     if (received == findChars[index]) {
00305       if (findChars[++index] == '\0') {
00306         index = 0;
00307         state = Length;
00308       }
00309     } else {
00310       state = Reset;
00311     }
00312     break;
00313 
00314   case Length:
00315     if ((received <= '9') && (received >= '0')) {
00316       if (index < maxSizeDigits) {
00317         sizeDigits[index++] = received;
00318       } else {
00319         state = Reset;
00320       }
00321     } else if (received == ':') {
00322       dataFinishedSize = std::atoi(sizeDigits);
00323       if (dataFinishedSize == 0) {
00324         state = Reset;
00325       } else {
00326         index = 0;
00327         state = Data;
00328       }
00329     } else {
00330       state = Reset;
00331     }
00332     break;
00333 
00334   case Data:
00335     if (index < dataFinishedSize) {
00336       recvIpDataBuffer.push(received);
00337       index++;
00338     } else {
00339       state = Reset;
00340     }
00341     break;
00342   };
00343 }
00344 
00345 void ESP8266::parseRecvConnClosedMsg(const char received) {
00346   static const char findChars[] = "CLOSED";
00347 
00348   static int index = 0;
00349 
00350   bool shouldReset = parseRecvReset;
00351 
00352   if (received == findChars[index]) {
00353     if (findChars[++index] == '\0') {
00354       printDbgMsg(findChars);
00355       shouldReset = true;
00356     }
00357   } else {
00358     shouldReset = true;
00359   }
00360 
00361   if (shouldReset) {
00362     index = 0;
00363   }
00364 }
00365 
00366 void ESP8266::printDbgMsg(const char * message) {
00367   if (debugMsg != NULL)
00368     debugMsg->printf("%s", message);
00369 }
00370 
00371 ESP8266::CmdBuilder::CmdBuilder (const std::string & cmd) { clear(cmd); }
00372 
00373 void ESP8266::CmdBuilder::clear(const std::string & cmd) {
00374   numArgs = 0;
00375   cmdStream.str("");
00376 
00377   cmdStream << "AT";
00378   if (cmd != "")
00379     cmdStream << "+" << cmd;
00380 }
00381 
00382 void ESP8266::CmdBuilder::addStringArgument(const std::string & arg) {
00383   std::ostringstream argStream;
00384   argStream << "\"" << arg << "\"";
00385   addRawArgument<std::string>(argStream.str());
00386 }
00387 
00388 std::string ESP8266::CmdBuilder::str() const {
00389   std::string cmdString = cmdStream.str();
00390   cmdString.append("\r\n");
00391   return cmdString;
00392 }