Skip to content

Instantly share code, notes, and snippets.

@xythobuz
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xythobuz/6894fe581980b7caa258 to your computer and use it in GitHub Desktop.
Save xythobuz/6894fe581980b7caa258 to your computer and use it in GitHub Desktop.
ESP8266 mbed - Getting Started

ESP8266 & mbed - Getting Started

I've recently got an ESP8266 ESP-01 module. These little things are very cheap WiFi Chipsets with an integrated TCP/IP Stack. The onboard MCU can be programmed directly, but this is focusing on using the UART AT-command firmware for the module.

New Firmware

When this chip was released, the corresponding SDK was open-sourced. This led to many different Firmwares that are available on the net, all with different features and bugs. The most important step is flashing the newest AT Firwmare. If you don't do this, you will have problems with random reboots or unusable AT commands.

Unfortunately, the official SDK is no longer open-source. Take a look at this Google Drive folder to find some available firmware files. I have made the most progress with the newest one, 0.952 support SmartLink, which was the only one that allowed me to properly send HTTP using the AT+CIPSEND command.

To flash the new firmware, use themadinventor's esptool. To enable the firmware flashing mode, connect the ESP as usual, except for GPIO0, which has to be pulled low. (RX and TX to your USB adapter, GND to GND and GPIO0, VCC everywhere else). If esptool does not want to connect on the first try, reset the ESP a few times and try different timings between reset and start of the flashing process.

Afterwards, connect GPIO0 to GND and check if you can talk to your module again. Note that you may need to use another Baudrate, mine switched from 9600 to 115200 with the newest firmware.

mbed Serial

It is important to have reliable UART communication to the ESP module. When using mbed normal Serial class, the buffer is much too small, and data is lost on fast large transmissions. The code in this gist is using the MODSERIAL mbed library that allows fully buffered transmission and receiving.

/*! \file src/ESP8266.cpp
* \brief ESP8266 library implementation.
* Copyright 2015 by Thomas Buck, Christian Högerle
* \author Thomas Buck
* \author Christian Högerle
*/
#define LOG
#define LOG_ALL_DATA
#include "debug.h"
#include "ESP8266.h"
#define RESPONSE_OK "OK"
#define RESPONSE_ERROR "ERROR"
#define RESPONSE_RESET "ready"
ESPIP::ESPIP(int _a, int _b, int _c, int _d) {
if ((_a < 0) || (_a > 255)) {
debugPrintf("ESPIP(%d, %d, %d, %d): invalid!\r\n", _a, _b, _c, _d);
a = 0;
} else {
a = _a;
}
if ((_b < 0) || (_b > 255)) {
debugPrintf("ESPIP(%d, %d, %d, %d): invalid!\r\n", _a, _b, _c, _d);
b = 0;
} else {
b = _b;
}
if ((_c < 0) || (_c > 255)) {
debugPrintf("ESPIP(%d, %d, %d, %d): invalid!\r\n", _a, _b, _c, _d);
c = 0;
} else {
c = _c;
}
if ((_d < 0) || (_d > 255)) {
debugPrintf("ESPIP(%d, %d, %d, %d): invalid!\r\n", _a, _b, _c, _d);
d = 0;
} else {
d = _d;
}
}
// ----------------------------------------------------------------------------
void ESP8266::checkAlive() {
// Clear UART buffer
com.rxBufferFlush();
// Wait until ESP is back and echoes sent characters
do {
com.putc('A');
while (!com.txBufferEmpty());
} while (com.getc() != 'A');
// Send dummy "AT" command
com.puts("T\r\n");
// Check for OK Response
while (true) {
std::string res = readLine();
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::checkAlive(): error!\r\n");
return;
} else if (res == RESPONSE_OK) {
debugPrintf("ESP::checkAlive(): done.\r\n");
return;
} else {
debugPrintf("ESP::checkAlive(): unexpected \"%s\"!\r\n", res.c_str());
}
}
}
void ESP8266::sendCommand(const char* cmd) {
#ifdef LOG_ALL_DATA
debug.printf("ESP::sendCommand(\"%s\")\r\n", cmd);
#endif
// Send command
com.printf("%s\r\n", cmd);
// Read echoed characters back
readLine();
}
std::string ESP8266::readLine() {
// Read until we get a non-empty line
static char lineBuffer[1024];
com.gets(lineBuffer, 1024);
std::string line(lineBuffer);
// Strip \r\n at the end
if ((line.length() < 2) || ((line.length() >= 2) && (line.length() <= 3) && (line[0] == '\r'))) {
#ifdef LOG_ALL_DATA
debug.printf("ESP::readLine(): empty line!\r\n");
#endif
return readLine();
} else if (line[line.length() - 3] == '\r') {
line.erase(line.length() - 3);
} else if (line[line.length() - 2] == '\r') {
line.erase(line.length() - 2);
}
#ifdef LOG_ALL_DATA
debug.printf("ESP::readLine(): \"%s\"\r\n", line.c_str());
#endif
return line;
}
void ESP8266::poll() {
if (!com.readable()) {
return;
}
bool shouldCallCallback = false;
int id = -1;
std::string data;
char first = com.getc();
if ((first >= '0') && (first <= '9')) {
// Connection opened or closed
std::string ans = readLine();
if (ans == ",CONNECT") {
debugPrintf("ESP::poll(): new connection %d!\r\n", (first - '0'));
} else if (ans == ",CLOSED") {
debugPrintf("ESP::poll(): closed connection %d!\r\n", (first - '0'));
} else {
debugPrintf("ESP::poll(): unexpected \"%c%s\"!\r\n", first, ans.c_str());
}
} else if (first == 'r') {
// The module has been reset
std::string ans = readLine();
if (ans == "eady") {
debugPrintf("ESP::poll(): reset detected!\r\n");
if (initFunc) {
initFunc(*this);
} else {
debugPrintf("ESP::poll(): no initialization given!\r\n");
}
} else {
debugPrintf("ESP::poll(): unexpected \"r%s\"!\r\n", ans.c_str());
}
} else if (first == '+') {
// Data has been received
std::string text;
for (int i = 0; i < 4; i++) text += com.getc();
if (text == "IPD,") {
// Read id and data length
std::string header;
while ((header.length() == 0) || (header[header.length() - 1] != ':')) {
header += com.getc();
}
// Parse id and length
int size = 0;
if (sscanf(header.c_str(), "%d,%d:", &id, &size) != 2) {
debugPrintf("ESP::poll(): malformed \"%s\"\r\n", header.c_str());
return;
}
if ((id >= 0) && (size > 0)) {
// Read data
for (int i = 0; i < size; i++) {
data += com.getc();
}
// Remember that we should call main program
shouldCallCallback = true;
}
debugPrintf("ESP::poll(): done.\r\n");
} else {
debugPrintf("ESP::poll(): unexpected \"+%s\"\r\n", text.c_str());
}
} else if ((first != '\r') && (first != '\n')) {
debugPrintf("ESP::poll(): unexpected first '%c'!\r\n", first);
}
if (shouldCallCallback) {
if (callback) {
callback(*this, id, data);
}
}
}
void ESP8266::dumbTerminal() {
debugPrintf("Now acting as ESP8266 terminal...\r\n");
debugPrintf("Press 3x 'q' to exit this mode!\r\n");
int n = 0;
while (n < 3) {
if (debug.readable()) {
char c = debug.getc();
if (c == 'q') {
n++;
} else {
n = 0;
}
com.putc(c);
}
if (com.readable()) {
debug.putc(com.getc());
}
wdt.kick();
}
com.printf("\r\n");
readLine();
readLine();
debugPrintf("\r\nBack to main-loop!\r\n");
}
void ESP8266::resetHardware() {
debugPrintf("ESP::resetHardware(): toggling pin...\r\n");
resetPin = 0;
wait_ms(50);
resetPin = 1;
wait_ms(50);
std::string res = readLine();
while (res != RESPONSE_RESET) {
if (res == RESPONSE_OK) {
debugPrintf("ESP::resetHardware(): going to reset...\r\n");
} else if (res == RESPONSE_ERROR) {
debugPrintf("ESP::resetHardware(): error!\r\n");
return;
}
res = readLine();
}
debugPrintf("ESP::resetHardware(): back alive!\r\n");
checkAlive();
}
void ESP8266::resetSoftware() {
// Send Reset command
debugPrintf("ESP::resetSoftware()\r\n");
sendCommand("AT+RST");
std::string res = readLine();
while (res != RESPONSE_RESET) {
if (res == RESPONSE_OK) {
debugPrintf("ESP::resetSoftware(): going to reset...\r\n");
} else if (res == RESPONSE_ERROR) {
debugPrintf("ESP::resetSoftware(): error!\r\n");
return;
}
res = readLine();
}
debugPrintf("ESP::resetSoftware(): back alive!\r\n");
checkAlive();
}
void ESP8266::setMode(ESPMode mode) {
if ((mode >= 1) && (mode <= 3)) {
// Send command and read echoed characters
debugPrintf("ESP::setMode(%d)\r\n", mode);
com.printf("AT+CWMODE=%d\r\n", mode);
readLine();
// Check response
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::setMode(%d): error!\r\n", mode);
return;
}
res = readLine();
}
} else {
debugPrintf("ESP::setMode(%d): invalid!\r\n", mode);
}
}
ESPMode ESP8266::getMode() {
sendCommand("AT+CWMODE?");
std::string answer = readLine();
// Check response
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::getMode(): error!\r\n");
return ESPMODE_UNKNOWN;
}
res = readLine();
}
// Interpret answer
if (answer.compare(0, 8, "+CWMODE:") == 0) {
std::string num = answer.substr(8);
if (num.length() == 1) {
int n = num[0] - '0';
if ((n >= 1) && (n <= 3)) {
return ESPMode(n);
}
}
}
return ESPMODE_UNKNOWN;
}
std::string ESP8266::getConnectedSSID() {
sendCommand("AT+CWJAP?");
std::string answer = readLine();
// Check response
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
if (answer != "No AP") {
debugPrintf("ESP::getConnectedSSID(): error!\r\n");
} else {
debugPrintf("ESP::getConnectedSSID(): not connected.\r\n");
}
return "";
}
res = readLine();
}
if (answer == "No AP") {
debugPrintf("ESP::getConnectedSSID(): not connected.\r\n");
return "";
}
// Interpret answer
if (answer.compare(0, 8, "+CWJAP:\"") == 0) {
std::string result = answer.substr(8, answer.length() - 9);
debugPrintf("ESP::getConnectedSSID(): \"%s\"\r\n", result.c_str());
return result;
}
debugPrintf("ESP::getConnectedSSID(): malformed answer \"%s\"\r\n", answer.c_str());
return "";
}
std::vector<ESPAccessPoint> ESP8266::listAccessPoints() {
sendCommand("AT+CWLAP");
std::vector<std::string> answers;
std::vector<ESPAccessPoint> ret;
while (1) {
std::string line = readLine();
if (line == RESPONSE_OK) {
break;
} else if (line == RESPONSE_ERROR) {
debugPrintf("ESP::listAccessPoints(): error!\r\n");
return ret;
} else if (line.length() > 0) {
answers.emplace_back(line);
}
}
for (std::string& str : answers) {
int enc, sig, chan;
char ssid[100], mac[100];
if (sscanf(str.c_str(),
"+CWLAP:(%d,\"%100[^\"]\",%d,\"%100[^\"]\",%d)",
&enc, ssid, &sig, mac, &chan) == 5) {
if ((enc >= 0) && (enc <= 4)) {
ret.emplace_back(ssid, mac, sig, chan, ESPEncryption(enc));
} else {
debugPrintf("ESP::listAccessPoints(): unknown encryption %d!\r\n", enc);
}
} else {
debugPrintf("ESP::listAccessPoints(): can't parse line \"%s\"!\r\n", str.c_str());
}
}
return ret;
}
void ESP8266::join(const char* ssid, const char* password) {
if ((ssid == nullptr) || (ssid[0] == '\0')) {
debugPrintf("ESP::join(nullptr, ...): invalid!\r\n");
return;
}
if ((password == nullptr) || (password[0] == '\0')) {
com.printf("AT+CWJAP=\"%s\"\r\n", ssid);
} else {
com.printf("AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, password);
}
readLine();
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::join(\"%s\", ...): error!\r\n", ssid);
return;
}
res = readLine();
}
debugPrintf("ESP::join(\"%s\", ...) done.\r\n", ssid);
}
void ESP8266::disconnectAP() {
sendCommand("AT+CWQAP");
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::disconnectAP(): error!\r\n");
return;
}
res = readLine();
}
debugPrintf("ESP::disconnectAP(): done.\r\n");
}
void ESP8266::setConnectionMode(bool multiple) {
debugPrintf("ESP::setConnectionMode(%s)\r\n", multiple ? "true" : "false");
com.printf("AT+CIPMUX=%d\r\n", multiple ? 1 : 0);
readLine();
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::setConnectionMode(%s): error!\r\n", multiple ? "true" : "false");
return;
}
res = readLine();
}
}
ESPIP ESP8266::getAccessPointAddress() {
int a = 0, b = 0, c = 0, d = 0;
sendCommand("AT+CIPAP?");
std::string answer = readLine();
// Check response
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::getAccessPointAddress(): error!\r\n");
return ESPIP(a, b, c, d);
}
res = readLine();
}
// Interpret answer
if (answer.compare(0, 8, "+CIPAP:\"") == 0) {
std::string result = answer.substr(8, answer.length() - 9);
if (sscanf(result.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) == 4) {
debugPrintf("ESP::getAccessPointAddress(): \"%s\"\r\n", result.c_str());
return ESPIP(a, b, c, d);
}
}
debugPrintf("ESP::getAccessPointAddress(): malformed IP \"%s\"!\r\n", answer.c_str());
return ESPIP(a, b, c, d);
}
ESPIP ESP8266::getStationAddress() {
int a = 0, b = 0, c = 0, d = 0;
sendCommand("AT+CIPSTA?");
std::string answer = readLine();
// Check response
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::getStationAddress(): error!\r\n");
return ESPIP(a, b, c, d);
}
res = readLine();
}
// Interpret answer
if (answer.compare(0, 9, "+CIPSTA:\"") == 0) {
std::string result = answer.substr(9, answer.length() - 10);
if (sscanf(result.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) == 4) {
debugPrintf("ESP::getStationAddress(): \"%s\"\r\n", result.c_str());
return ESPIP(a, b, c, d);
}
}
debugPrintf("ESP::getStationAddress(): malformed IP \"%s\"!\r\n", answer.c_str());
return ESPIP(a, b, c, d);
}
std::vector<ESPIP> ESP8266::getConnectedAddresses() {
std::vector<ESPIP> result;
// TODO
return result;
}
void ESP8266::setConnection(ESPIP ip, int port, int id, bool tcp) {
// TODO
}
void ESP8266::sendData(int length, const char* data, int id) {
if ((length <= 0) || (data == nullptr)) {
debugPrintf("ESP::sendData(%d, %s, %d): no data?!\r\n", length,
(data != nullptr) ? "..." : "nullptr", id);
return;
}
if (length > 2048) {
debugPrintf("ESP::sendData(%d, %s, %d): buffer too large!\r\n", length,
(data != nullptr) ? "..." : "nullptr", id);
length = 2048;
}
if ((id >= 0) && (id <= 4)) {
com.printf("AT+CIPSEND=%d,%d\r\n", id, length);
readLine();
} else {
com.printf("AT+CIPSEND=%d\r\n", length);
readLine();
}
std::string answer = readLine();
if (answer != RESPONSE_OK) {
debugPrintf("ESP::sendData(%d, %s, %d): unexpected \"%s\"!\r\n", length,
(data != nullptr) ? "..." : "nullptr", id, answer.c_str());
return;
}
char c1 = com.getc();
char c2 = com.getc();
if ((c1 == '>') && (c2 == ' ')) {
debugPrintf("ESP::sendData(%d, %s, %d): transmitting data...\r\n", length,
(data != nullptr) ? "..." : "nullptr", id);
for (int i = 0; i < length; i++) {
com.putc(data[i]);
}
std::string res = readLine();
while (res != "SEND OK") {
if (res == RESPONSE_OK) {
debugPrintf("ESP::sendData(%d, %s, %d): success?\r\n", length,
(data != nullptr) ? "..." : "nullptr", id);
return;
} else if (res == RESPONSE_ERROR) {
debugPrintf("ESP::sendData(%d, %s, %d): error!\r\n", length,
(data != nullptr) ? "..." : "nullptr", id);
return;
} else if (res == RESPONSE_RESET) {
debugPrintf("ESP::sendData(%d, %s, %d): reset detected!\r\n", length,
(data != nullptr) ? "..." : "nullptr", id);
if (initFunc) {
initFunc(*this);
}
return;
}
res = readLine();
}
} else {
debugPrintf("ESP::sendData(%d, %s, %d): unexpected \"%c%c\"!\r\n", length,
(data != nullptr) ? "..." : "nullptr", id, c1, c2);
}
}
void ESP8266::closeConnection(int id) {
if ((id >= 0) && (id <= 4)) {
com.printf("AT+CIPCLOSE=%d\r\n", id);
readLine();
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::closeConnection(%d): error!\r\n", id);
return;
} else if (res == "MUX=0") {
debugPrintf("ESP::closeConnection(%d): wrong mode!\r\n", id);
return;
} else if (res == "link is not") {
debugPrintf("ESP::closeConnection(%d): not open!\r\n", id);
}
res = readLine();
}
} else if (id < 0) {
sendCommand("AT+CIPCLOSE");
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::closeConnection(%d): error!\r\n");
return;
} else if (res == "MUX=1") {
debugPrintf("ESP::closeConnection(%d): wrong mode!\r\n");
return;
}
res = readLine();
}
} else {
debugPrintf("ESP::closeConnection(%d): invalid arg!\r\n", id);
}
}
void ESP8266::setServer(bool open, int port) {
if (!open) {
sendCommand("AT+CIPSERVER=0");
std::string res = readLine();
if (res != "no change") {
debugPrintf("ESP::setServer(%s, %d): need reset!\r\n", open ? "true" : "false", port);
resetHardware();
resetSoftware();
}
return;
}
if (port < 0) {
sendCommand("AT+CIPSERVER=1");
} else {
com.printf("AT+CIPSERVER=1,%d\r\n", port);
readLine();
}
std::string res = readLine();
while (res != RESPONSE_OK) {
if (res == RESPONSE_ERROR) {
debugPrintf("ESP::setServer(%s, %d): error!\r\n", open ? "true" : "false", port);
return;
} else if (res == "no change") {
break;
}
res = readLine();
}
}
void ESP8266::setAccessPointParameters(const char* ssid, const char* password,
int channel, ESPEncryption encryption) {
if ((encryption != ESPENC_NONE) && (encryption != ESPENC_WPA_PSK) && (encryption != ESPENC_WPA2_PSK) && (encryption != ESPENC_WPA_WPA2_PSK)) {
debugPrintf("ESP::setAccessPointParameters(\"%s\", \"%s\", %d, %d): invalid encryption!\r\n",
(ssid != nullptr) ? ssid : "nullptr", (password != nullptr) ? password : "nullptr",
channel, encryption);
return;
}
if ((ssid == nullptr) || (ssid[0] == '\0')) {
debugPrintf("ESP::setAccessPointParameters(\"%s\", \"%s\", %d, %d): missing SSID!\r\n",
(ssid != nullptr) ? ssid : "nullptr", (password != nullptr) ? password : "nullptr",
channel, encryption);
return;
}
if (((password == nullptr) || (password[0] == '\0')) && (encryption == ESPENC_NONE)) {
debugPrintf("ESP::setAccessPointParameters(\"%s\", \"%s\", %d, %d): password but no encryption!\r\n",
(ssid != nullptr) ? ssid : "nullptr", (password != nullptr) ? password : "nullptr",
channel, encryption);
return;
}
// TODO!
}
ESPStatus ESP8266::getStatus() {
sendCommand("AT+CIPSTATUS");
std::string text = readLine();
std::string ret = readLine();
while (ret != RESPONSE_OK) {
if (ret == RESPONSE_ERROR) {
debugPrintf("ESP::getStatus(): error!\r\n");
return ESPSTATUS_UNKNOWN;
}
}
int stat;
if (sscanf(text.c_str(), "STATUS:%d", &stat) == 1) {
if ((stat >= 2) && (stat <= 4)) {
return ESPStatus(stat);
} else {
debugPrintf("ESP::getStatus(): invalid status %d!\r\n", stat);
}
} else {
debugPrintf("ESP::getStatus(): invalid response \"%s\"!\r\n", text.c_str());
}
return ESPSTATUS_UNKNOWN;
}
std::vector<ESPConnection> ESP8266::getConnections() {
std::vector<ESPConnection> result;
sendCommand("AT+CIPSTATUS");
readLine(); // ignore status line
std::vector<std::string> answers;
while (true) {
std::string s = readLine();
if (s == RESPONSE_OK) {
break;
} else if (s == RESPONSE_ERROR) {
debugPrintf("ESP::getConnections(): error! Continuing...\r\n");
break;
} else {
answers.emplace_back(s);
}
}
for (auto& s : answers) {
int id, port, mode, a, b, c, d;
char type[100];
if (sscanf(s.c_str(), "+CIPSTATUS:%d,\"%100[^\"]\",\"%d.%d.%d.%d\",%d,%d",
&id, type, &a, &b, &c, &d, &port, &mode) == 8) {
std::string t(type);
ESPConnectionType nt = ESPCONTYPE_UNKNOWN;
if (t == "TCP") {
nt = ESPCONTYPE_TCP;
} else if (t == "UDP") {
nt = ESPCONTYPE_UDP;
} else {
debugPrintf("ESP::getConnections(): invalid type \"%s\"!\r\n", t.c_str());
}
ESPConnectionMode m = ESPCONMODE_UNKNOWN;
if (mode == 0) {
m = ESPCONMODE_CLIENT;
} else if (mode == 1) {
m = ESPCONMODE_SERVER;
} else {
debugPrintf("ESP::getConnections(): invalid mode %d!\r\n", mode);
}
result.emplace_back(id, nt, ESPIP(a, b, c, d), port, m);
} else {
debugPrintf("ESP::getConnections(): invalid line \"%s\"!\r\n", s.c_str());
}
}
return result;
}
void ESP8266::performFirmwareUpdate() {
sendCommand("AT+CIUPDATE");
int errorCount = 0;
do {
int step;
std::string res = readLine();
if (res == RESPONSE_RESET) {
debugPrintf("ESP::performFirmwareUpdate(): reset detected.\r\n");
if (initFunc) {
initFunc(*this);
}
return;
} else if (res == RESPONSE_OK) {
debugPrintf("ESP::performFirmwareUpdate(): success.\r\n");
return;
} else if (res == RESPONSE_ERROR) {
errorCount++;
if (errorCount >= 2) {
debugPrintf("ESP::performFirmwareUpdate(): error!\r\n");
return;
}
} else if (sscanf(res.c_str(), "+CIPUPDATE:%d", &step) == 1) {
if (step == 1) {
debugPrintf("ESP::performFirmwareUpdate(): found server...\r\n");
} else if (step == 2) {
debugPrintf("ESP::performFirmwareUpdate(): connected to server...\r\n");
} else if (step == 3) {
debugPrintf("ESP::performFirmwareUpdate(): loaded newest firmware...\r\n");
} else if (step == 4) {
debugPrintf("ESP::performFirmwareUpdate(): started firmware upgrade...\r\n");
} else {
debugPrintf("ESP::performFirmwareUpdate(): unexpected step %d!\r\n", step);
}
} else {
debugPrintf("ESP::performFirmwareUpdate(): unexpected \"%s\"!\r\n", res.c_str());
}
} while (true);
}
/*! \file include/ESP8266.h
* \brief ESP8266 library header. Depends on MODSERIAL.
* Copyright 2015 by Thomas Buck, Christian Högerle
* \author Thomas Buck
* \author Christian Högerle
*/
#ifndef ESP8266_H
#define ESP8266_H
#include <functional>
#include <string>
#include <vector>
#include "mbed.h"
#include "MODSERIAL/MODSERIAL.h"
//! \brief Simple IP Address representation.
struct ESPIP {
/*! \brief ESPIP constructor.
* Provides range-checking and debug output on error.
* \param _a first part of IP
* \param _b second part of IP
* \param _c third part of IP
* \param _d fourth part of IP
*/
ESPIP(int _a, int _b, int _c, int _d);
uint8_t a, b, c, d;
};
//! \brief Known ESP8266 modes.
typedef enum {
ESPMODE_STATION = 1, //!< Only connect to other Access Points
ESPMODE_ACCESS_POINT = 2, //!< Only be an Access Point ourselves.
ESPMODE_BOTH = 3, //!< Enable Station and Access Point mode.
ESPMODE_UNKNOWN //!< Unknown mode, do not use!
} ESPMode;
//! \brief Known ESP8266 security protocols.
typedef enum {
ESPENC_NONE = 0, //!< No encryption
ESPENC_WEP = 1, //!< WEP, insecure, not allowed for opening an AP!
ESPENC_WPA_PSK = 2, //!< WPA with pre-shared-key
ESPENC_WPA2_PSK = 3, //!< WPA2 with pre-shared-key
ESPENC_WPA_WPA2_PSK = 4, //!< WPA or WPA2 with pre-shared-key
ESPENC_UNKNOWN //!< Unknown encryption, do not use!
} ESPEncryption;
//! \brief Representing visible WiFi Access Points.
struct ESPAccessPoint {
ESPAccessPoint(std::string _ssid, std::string _mac, int sig, int ch, ESPEncryption enc)
: ssid(_ssid), mac(_mac), signal(sig), channel(ch), encryption(enc) { }
std::string ssid, mac;
int signal, channel;
ESPEncryption encryption;
};
//! \brief Known ESP8266 Connection status codes.
typedef enum {
ESPSTATUS_HAVE_IP = 2, //!< Have got an DHCP IP.
ESPSTATUS_CONNECTED = 3, //!< Connected.
ESPSTATUS_DISCONNECTED = 4, //!< Not connected.
ESPSTATUS_UNKNOWN //!< Unknown status, do not use!
} ESPStatus;
//! \brief Known ESP8266 Connection modes.
typedef enum {
ESPCONMODE_CLIENT = 0, //!< ESP8266 has opened the connection
ESPCONMODE_SERVER = 1, //!< ESP8266 has received a connection
ESPCONMODE_UNKNOWN //<! Unknown mode, do not use!
} ESPConnectionMode;
//! \brief Known ESP8266 Connection protocols.
typedef enum {
ESPCONTYPE_TCP, //!< TCP protocol
ESPCONTYPE_UDP, //!< UDP protocol
ESPCONTYPE_UNKNOWN //!< Unknown protocol, do not use!
} ESPConnectionType;
//! \brief Representing an opened TCP/UDP connection.
struct ESPConnection {
ESPConnection(int _id, ESPConnectionType t, ESPIP _ip, int p, ESPConnectionMode m)
: id(_id), type(t), ip(_ip), port(p), mode(m) { }
int id; //!< ESP8266 internal connection ID
ESPConnectionType type; //!< Connection Type (TCP/UDP)
ESPIP ip; //!< Remote IP Address
int port; //!< Port used on remote end
ESPConnectionMode mode; //!< Connection mode (Server/Client)
};
/* \brief ESP8266 interaction library.
* This class represents interaction with one specific ESP8266.
* Multiple instances can be used concurrently.
*/
class ESP8266 {
public:
/* \brief Construct an ESP8266 object.
* \param tx UART TX pin
* \param rx UART RX pin
* \param res Reset pin
* \param baudrate UART Baudrate
*/
ESP8266(PinName tx, PinName rx, PinName res, int baudrate = 115200) : com(tx, rx), resetPin(res) {
com.baud(baudrate);
resetPin = 1;
}
// Explicitly disable copy and move constructors
ESP8266(const ESP8266&) = delete;
ESP8266(ESP8266&&) = delete;
/* \brief Set ESP8266 initialization method.
* This function will be called everytime initialize() is called
* or a reset is detected by poll(). Use it to set ESP8266 config params.
* It will be given a reference to the ESP8266 object that received a reset.
* \param func function that will be stored and called later.
* \sa initialize
* \sa poll
*/
void setInitFunction(std::function<void(ESP8266&)> func) { initFunc = func; }
/* \brief Set ESP8266 data callback.
* This function will be called everytime a new data packet has been received
* by poll().
* \param cb function that will be stored and called later.
* \sa poll
*/
void registerCallback(std::function<void(ESP8266&, int, std::string)> cb) { callback = cb; }
//* \brief Call the already registered initialization function.
void initialize() { if (initFunc) initFunc(*this); }
/*! \brief Check for any new communication from the ESP8266.
* This will check for new Connection or Disconnection Events,
* listen for new data on open connections, and tries to detect
* unwanted ESP8266 resets and call the initialization function again.
*/
void poll();
/*! \brief Act as a dumb terminal between the ESP8266 and the USB
* debug UART. Pressing 'q' three times in a row will exit this mode again,
* but send a 'qq' command to the ESP8266 that will get an ERROR as response.
*/
void dumbTerminal();
/*! \brief Perform an ESP8266 Hardware Reset and check if it comes back.
* This will take approximately one second.
*/
void resetHardware();
/*! \brief Perform an ESP8266 Software Reset and check if it comes back.
* This will take approximately a half second.
*/
void resetSoftware();
/*! \brief Hard- and Software Reset.
* First runs the hardware reset, then the software reset.
* This will take approximately 1.5 seconds.
*/
void reset() { resetHardware(); resetSoftware(); }
/*! \brief Set the ESP8266 operating mode.
* \param mode the new mode, do not use ESPMODE_UNKNOWN!
*/
void setMode(ESPMode mode);
/*! \brief Get the current ESP8266 operating mode.
* \returns the current mode, or ESPMODE_UNKNOWN on error.
*/
ESPMode getMode();
/*! \brief Get the SSID of the currently connected Access Point.
* \returns name of AP or empty string on error/no connection.
*/
std::string getConnectedSSID();
/*! \brief List all visible Access Points.
* \returns Visible APs, or an empty vector on error.
*/
std::vector<ESPAccessPoint> listAccessPoints();
void join(const char* ssid, const char* password = "");
//! \brief Disconnect from the current Access Point.
void disconnectAP();
void setConnectionMode(bool multiple = true);
/*! \brief Get our own IP in the Access Point network.
* \returns IP address, or an IP with only zeros on error.
*/
ESPIP getAccessPointAddress();
/*! \brief Get our own IP in our own network.
* \returns IP address, or an IP with only zeros on error.
*/
ESPIP getStationAddress();
std::vector<ESPIP> getConnectedAddresses();
void setConnection(ESPIP ip, int port, int id = -1, bool tcp = true);
void sendData(int length, const char* data, int id = -1);
void closeConnection(int id = -1);
/*! \brief Enable or Disable the Server mode on a port.
* If required, this will automatically reset on closing of the server.
* There can only be one server at a time. You don't need to pass a port
* if open is false.
* \param open True if the server should be opened, closed otherwise.
* \param port Port that should be opened. Use -1 to not send this.
*/
void setServer(bool open, int port = -1);
void setAccessPointParameters(const char* ssid, const char* password = "",
int channel = 11, ESPEncryption encryption = ESPENC_NONE);
/*! \brief Read the current status from the ESP8266.
* \returns current ESPStatus, or ESPSTATUS_UNKNOWN on error.
*/
ESPStatus getStatus();
/*! \brief List all currently opened TCP/UDP connections.
* \returns All current connections, or an empty vector on error.
*/
std::vector<ESPConnection> getConnections();
/*! \brief Perform a Cloud Firmware Upgrade.
* Status updated will be printed on the debug USB UART.
*/
void performFirmwareUpdate();
/*! \brief Wait until ESP8266 is responding.
* This method sends 'A' characters until the ESP8266 starts
* echoing them back. Then we send a single 'T' to complete the
* 'AT' command that has no side-effects.
*/
void checkAlive();
private:
/*! \brief Send a command to the ESP8266.
* The echoed command will be read and discarded.
* \param cmd Command to send (without line-delimiter at the end!)
*/
void sendCommand(const char* cmd);
/*! \brief Read a line ending with "\r\n" from ESP8266.
* Empty lines will be ignored, instead waiting for next line.
* \returns non-empty line that has been read.
*/
std::string readLine();
MODSERIAL com; //!< Buffered Serial Port used to communicate with ESP8266.
DigitalOut resetPin; //!< Low-active reset pin of ESP8266.
std::function<void(ESP8266&, int, std::string)> callback; //!< Application data callback
std::function<void(ESP8266&)> initFunc; //!< Initialization callback
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment