Snippets

Grant Young ESP8266 "flat" AT commands test

Created by Grant Young
// NOTE: save this as a .ino file for use in the Arduino IDE
//
// This was a "first principles" attempt at getting AT commands working with an
// Arduino Uno + Nano, using the ESP8266 chip.
// It works, sorta.  It's very unstable, and it's very convoluted as I've not
// split things out into reasonable methods etc. (this the "flat" in the file name)
// It is not robust and has limited error handling.  It doesn't provide the ability
// to do multiple retries for failed messages etc.
// I'm indebted to the author of the WeeESP8266 library, upon which a lot of my 
// understanding is based.
//
// SEE COMMENTS IN CLASSES IMPLEMENTATION FOR A MORE DETAILED/BETTER DESCRIPTION
// OF WHAT'S GOING ON...

#include <SoftwareSerial.h>

#define API_KEY "<your-api-key>"
#define HOST_NAME "api.thingspeak.com"
#define HOST_PORT (80)
#define END_POINT "/update"

#define SSID "<your-wifi-network>"
#define PSK "<your-wifi-password>"

SoftwareSerial espSerial = SoftwareSerial(8,9);

void setup() {
  Serial.begin(9600);
  wifiInit();
  wifiRequest();
}


unsigned interval = 30000; // millis
long pMillis = millis();

void loop() {
  // put your main code here, to run repeatedly:

  if (millis() - pMillis >= interval) {
    pMillis = millis();
    wifiRequest();
  }
}



// Utility functions
void printResponse()
{
    char a;
    unsigned long start = millis();
    while (millis() - start < 5000) {
        while(espSerial.available() > 0) {
            a = espSerial.read();
            if(a == '\0') continue;
            Serial.print(a);
        }
    }
}

bool containsString(char *searchIn, char *searchFor) {
  return (strstr(searchIn, searchFor) != NULL);
}

bool containsString(String searchIn, String searchFor) {
  return (strstr(searchIn.c_str(), searchFor.c_str()) != NULL);
}

String getResponseToString(String target, uint32_t timeout = 5000)
{
    String data;
    char a;
    unsigned long start = millis();
    while (millis() - start < timeout) {
        while(espSerial.available() > 0) {
            a = espSerial.read();
            if(a == '\0') continue;
            data += a;
        }
        if (containsString(data, target)) {
            break;
        }
    }
    return data;
}

String getResponse(uint32_t timeout = 5000)
{
  String data;
    char a;
    unsigned long start = millis();
    while (millis() - start < timeout) {
        while(espSerial.available() > 0) {
            a = espSerial.read();
            if(a == '\0') continue;
            data += a;
        }
    }
    return data;
}

bool responseContainsString(String target, uint32_t timeout = 5000)
{
    String data_tmp;
    data_tmp = getResponseToString(target, timeout);
    if (containsString(data_tmp, target)) {
        return true;
    }
    return false;
}

void emptySerialBuffer() 
{
    while(espSerial.available() > 0) {
        espSerial.read();
    }
}


bool getResponseSubstring(String target, String begin, String end, String &data, uint32_t timeout = 5000)
{
    String data_tmp;
    data_tmp = getResponseToString(target, timeout);
    if (containsString(data_tmp, target)) {
        int offset = begin.length();
        int32_t index1 = data_tmp.indexOf(begin);
        int32_t index2 = data_tmp.indexOf(end, index1+offset);
        if (index1 != -1 && index2 != -1) {
            index1 += begin.length();
            data = data_tmp.substring(index1, index2);
            return true;
        }
    }
    data = "";
    return false;
}


bool wifiInit()
{
  Serial.println("wifiInit()");
  espSerial.begin(9600);
  
  emptySerialBuffer();
  espSerial.println("AT");
  if (!responseContainsString("OK")) {
    Serial.println("Error communicating with ESP826\n");
    return false;
  }
  
  emptySerialBuffer();
  espSerial.println("AT+CWMODE=1");
  if (!responseContainsString("OK")) {
    Serial.println("Error switching to station mode");
    return false;
  }

  emptySerialBuffer();
  espSerial.println("AT+CWJAP?");
  // returns:
//AT+CWJAP?
//
//+CWJAP:"<ssid>"
//
//OK

  String d;
  getResponseSubstring("OK", ":\"", "\"", d);
  if (d == SSID) {
    Serial.print("Already connected to ");
    Serial.println(SSID);
  } else {
    // AT+CWJAP="<ssid>","<pass>"
    char cmd[64];
    snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\"", SSID, PSK);
    emptySerialBuffer();
    Serial.println(cmd);
    espSerial.println(cmd);
    printResponse();
    return true;
    if (responseContainsString("OK")) {
      Serial.print("Connected to ");
      Serial.println(SSID);
    } else {
      Serial.print("Error connecting to SSID ");
      Serial.println(SSID);
      return false;
    }
  }

  emptySerialBuffer();
  espSerial.println("AT+CIPMUX=0");
  if (!responseContainsString("OK")) {
    Serial.println("Error setting MUX to single");
    return false; // as command syntax is dependent on this, need to fail
  }
  
  return true;
}


bool wifiRequest()
{
  Serial.print("\n\nwifiRequest()--");
  Serial.print((millis()/1000));
  Serial.println("s");

  // make sure we haven't lost comms
  emptySerialBuffer();
  espSerial.println("AT");
  if (!responseContainsString("OK")) {
    Serial.println("Error communicating with ESP826");
    espSerial.println("AT+RST"); // attempt reset
    delay(5000); // TODO: be more graceful
    return false;
  }

  
  char cmd[64]; // this accommodates the requirements of this demo
  snprintf(cmd, sizeof(cmd), "AT+CIPSTART=\"TCP\",\"%s\",%i", HOST_NAME, HOST_PORT);
  // AT+CIPSTART="TCP","api.thingspeak.com",80
  //Serial.println(cmd);
  emptySerialBuffer();
  espSerial.println(cmd);
  String r = getResponse();
  
//  Serial.print("Response:\n");
//  Serial.print(r);
  
  if (containsString(r, "OK") || containsString(r, "ALREAY CONNECT")) {
    // Yes... the current version of the firmware responds with this typo?!
    // But, if we get this response, we're ok to continue
    // do nothing
  } else if (containsString(r, "ERROR")) {
    Serial.print("Error connecting to ");
    Serial.print(HOST_NAME);
    Serial.print(":");
    Serial.print(HOST_PORT);
    Serial.print("Response:\n");
    Serial.print(r);
    return false;
  }

  // either OK or ALREADY CONNECTED, so good to continue

  // create the request string
  // setup some dummy data (to come from sensors/EEPROM etc.)
  char siteID[] = "001";
  char monitorID[] = "001";
  char sensorType[] = "elec";
  float f_curr = 0.23f;
  float f_volt = 240.0f;

  char c_curr[6];
  dtostrf(f_curr, 4, 2, c_curr);
  char c_volt[7];
  dtostrf(f_volt, 4, 2, c_volt);
  
  char payload[64]; // 60 is expected max
  snprintf(payload, sizeof(payload), "field1=%s&field2=%s&field3=%s&field4=%s&field5=%s", siteID, monitorID, sensorType, c_curr, c_volt);
  //Serial.println(payload);
  int payloadLength = static_cast<int>(strlen(payload)); // strlen returns size_t

  // could also build using strcat
  char requestTemplate[152] = "POST %s HTTP/1.1\n"
    "Host: %s\n"
    "Connection: keep-alive\n" // had issues with this being set to "close"
    "X-THINGSPEAKAPIKEY: %s\n"
    "Content-Type: application/x-www-form-urlencoded\n"
    "Content-Length: %i"
    "\n\n%s";

  char request[256];
  snprintf(request, sizeof(request), requestTemplate, END_POINT, HOST_NAME, API_KEY, payloadLength, payload);

  // send the request
  // the CIPSEND command occurs in two parts
  // the first sends the length
  // a prompt is returned
  // then the HTTP request is sent
  int requestLength = static_cast<int>(strlen(request));
  //AT+CIPSEND=<length>
  snprintf(cmd, sizeof(cmd), "AT+CIPSEND=%i", requestLength);
  //Serial.println(cmd);
  emptySerialBuffer();
  espSerial.println(cmd);
  if (responseContainsString(">")) {
    //Serial.print(request);
    // send the payload
    emptySerialBuffer(); // just in case there's any remaining cruft
    espSerial.print(request);
  } else {
    Serial.println("Error sending request.  Response:");
    Serial.print(r);
    Serial.print("\n");
  }
  
  //char response[256]{0};
  Serial.println("Waiting for response... ");
  
  // get the response
  // this is running in a stable fashion presently
  // but doesn't allow for testing status/response
  // it also, by necessity, requires 5+ secs to execute
  // you would need to check the response to see if the +IPD: value and then read 
  // to number of bytes specified to not wait the full 10 seconds
  unsigned long start = millis();
//  String resp = "";
//  char statusResp[128]{0};
//  int c = 0;
//  int idx = 0;
//  int maxIdx = 128;
  char a;
  while (millis() - start < 5000) {
   if(espSerial.available()) {
//      c = espSerial.read();
//      a = (char) c;
//      if (idx < maxIdx) {
//        statusResp[idx] = c;
//        idx++;
//      }
      //resp += a; // building a string fails—gets truncated
      a = (char) espSerial.read();
      Serial.print(a);
    }
  }

//  if (strstr(statusResp, "200 OK") != NULL) {
//    Serial.println("RESPONSE: OK");
//  } else {
//    Serial.println("RESPONSE: ERROR");
//  }

  
  // close the connection
  emptySerialBuffer();
  //Serial.println("AT+CIPCLOSE");
  //espSerial.println("AT+CIPCLOSE");
  // the above was causing errors
  // following this thread https://github.com/adafruit/Adafruit_ESP8266/issues/2
  // suggests following two techniques, the second of which appears to be working
  //espSerial.println("AT+CIPSERVER=0");
  espSerial.println("AT+CIPSTATUS");
  espSerial.println("AT+CIPSTATUS");
  espSerial.println("AT+CIPCLOSE"); // closes the TCP connection
  
  return true;
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.