ESP8266 uart wifi module via AT command
Revision 0:ee86ca9e494d, committed 2015-05-03
- Comitter:
- steeven
- Date:
- Sun May 03 14:45:51 2015 +0000
- Commit message:
- ESP8266 driver module
Changed in this revision
Esp8266.cpp | Show annotated file Show diff for this revision Revisions of this file |
Esp8266.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r ee86ca9e494d Esp8266.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Esp8266.cpp Sun May 03 14:45:51 2015 +0000 @@ -0,0 +1,153 @@ +#include <ESP8266/Esp8266.h> + +namespace steeven { + +void Esp8266::init() { + _ch_pd = 1; + _rst = 1; + wait_rst(); + cmd_data("AT+GMR"); //get version + +} + +void Esp8266::wait_rst() { + cmd_data(NULL, NULL, 0, 10000, "\r\nready\r\n"); +} + +// 1: client, 2: AP, 3: mixed +void Esp8266::switchClientMode(int mode) { + int reset = 0; + + //remembered in flash, don't change and restart if not changed. + if (query_int("AT+CWMODE?") != mode) { + sprintf(_buf, "AT+CWMODE=%d", mode); + cmd_data(_buf); + reset = 1; + } + if (query_int("AT+CIPMUX?") != 0) { + cmd_data("AT+CIPMUX=0", NULL, 0, 3000, "OK\r"); // no multiple connection + reset = 1; + } + if (reset) { + cmd_data("AT+RST"); + wait_rst(); + } +} + +int Esp8266::connect(const char *ap, const char *pswd) { + char buf[100]; + int n = 0; + const char *noip = "+CIFSR:STAIP,\"0.0.0.0\""; + + if (strcmp(ap, query("AT+CWJAP?")) != 0) { + sprintf(buf, "AT+SWJAP=\"%s\",\"%s\"", ap, pswd); + cmd_data(buf); + } + + while (cmd_data("AT+CIFSR", buf, sizeof(buf)) && n++ < 10) { + if (memcmp(buf, noip, strlen(noip)) != 0) + return 1; + wait(0.3); + } + return 0; +} + +int Esp8266::send(const char *host, int port, const char *tx, + void (*cb)(char *, int)) { + char buf[100]; + int r; + + ASSERT(cmd_data("AT+CIPMODE=0")); //none-transparent data xfer mode + //TODO + cmd_data("AT+CIPCLOSE"); + // AT+CIPSTART="TCP","www.baidu.com",80 + sprintf(buf, "AT+CIPSTART=\"TCP\",\"%s\",%d", host, port); + ASSERT(cmd_data(buf)); + sprintf(buf, "AT+CIPSEND=%d", strlen(tx)); + // AT+CIPSEND=4 + ASSERT(cmd_data(buf, NULL, 0, 5000, "OK\r\n", NULL)); +// _dbg = 2; + ASSERT(cmd_data(tx, NULL, 0, 5000, "SEND OK\r\n\r\n", "")); +// _dbg = 0; + _rcv_cnt = 0; + _rcv_exp = 0; + _rcv_start = 0; + _rcv_cb = cb; + _uart.attach(this, &Esp8266::ipd_rx, SerialBase::RxIrq); + return 1; +} + +//Esp8266::~Esp8266() { +// _uart.close(); +//} + +//"+IPD,xxx:" +void Esp8266::ipd_dect() { + int i; + DBG("testing IPD...\n"); + if (memcmp("+IPD,", _buf, 5) == 0) { + DBG("IPD found...\n"); + for (i = 5; i < _rcv_cnt; ++i) { + if (_buf[i] >= '0' && _buf[i] <= '9') + continue; + if (_buf[i] == ':') { + DBG("':' found...\n"); + _buf[i] = 0; + break; + } + } + if (i == _rcv_cnt) //not found + return; + _rcv_exp = atoi(&_buf[5]); + _rcv_start = i + 1; + log('['); + DBG("IPD:%d %d\n", _rcv_start, _rcv_exp); + } else if (memcmp(_buf, "CLOSED", 5) == 0) { + DBG("connection closed\n"); + if (_rcv_cb != NULL) + _rcv_cb(NULL, 0); //end of rcv + _rcv_cb = NULL; + //notify receiver + _uart.attach(NULL, SerialBase::RxIrq); + } +} + +void Esp8266::ipd_rx() { + char ch; + int i = 0; + while (_uart.readable()) { + ch = _uart.getc(); + log(ch); + if (_rcv_exp == 0 && (ch == '\r' || ch == '\n')) { + DBG("ignore leading blank"); + continue; + } + _buf[_rcv_cnt++] = ch; + i++; + if (_rcv_exp == 0) { + if (_rcv_cnt > 5 && _rcv_cnt <= 11) { // expecting "+IPD:xxx" or "CLOSED" + ipd_dect(); + } + if (_rcv_cnt>11 && _rcv_exp==0) { //failed to detect IPD + _uart.attach(NULL, SerialBase::RxIrq); + _buf[_rcv_cnt] = 0; + LOG("\nFailed to detect IPD: %s", _buf); + return; + } + } + if (_rcv_cnt >= (int) sizeof(_buf)) { + error("rx buffer overflow size\n"); + } + if (_rcv_exp > 0 && _rcv_exp + _rcv_start == _rcv_cnt) { //reach end of pkt + if (_rcv_cb != NULL) + _rcv_cb(&_buf[_rcv_start], _rcv_exp); //end of IPD + _rcv_exp = 0; + _rcv_cnt = 0; + _rcv_start = 0; + log(']'); + } + } + DBG(" %d %d\n", i, _rcv_cnt); +} + +} // namespace steeven
diff -r 000000000000 -r ee86ca9e494d Esp8266.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Esp8266.h Sun May 03 14:45:51 2015 +0000 @@ -0,0 +1,328 @@ +#ifndef ESP8266_H_ +#define ESP8266_H_ + +#include "mbed.h" +#include "BufferedSerial/BufferedSerial.h" + +#define WIFI_TIME_OUT 5000 +#define _DBG 1 +#if _DBG==1 +#define DBG(msg...) do{if(_logger && _dbg) _logger->printf(msg);}while(0) +#define LOG(msg...) do{if(_logger) _logger->printf(msg);}while(0) +#else +#define DBG(msg...) +#define LOG(msg...) +#endif +#define ASSERT(eval,msg...) if((r=eval)<=0) { LOG("%s():%d result: %d\n", __FUNCTION__, __LINE__, r); return r;} + +namespace steeven { + +/** Esp8266 WiFi serial module. + * Warnning: + * CH_PD pin has to connect VCC to boot from internal flash! + * Command has to end with "\r\n" + * If you can't get full AT output, set console same baudrate or higher, or set wifi baudrate low!!! + * DO NOT print console if you want get a long result from internet. + * Some bad data at beginning because the baudrate changed to 74880 + * \r: 0xd \n: 0xa + */ +class Esp8266 { + +public: + /** + * + * @param pin LED pin with PWM support + * @param time Time for the brightness transition + * @param hold Time for holding the brightness before next transition. + * Effective for loop mode. + */ + Esp8266(PinName tx, PinName rx, PinName ch_pd, PinName rst, int baudrate = + 115200) : + _ch_pd(ch_pd), _rst(rst), _uart(tx, rx) { + + _uart.baud(baudrate); +// _uart.attach(this, &Esp8266::on_rx, SerialBase::RxIrq); + + _timer.start(); + _timeout_ms = WIFI_TIME_OUT; + + _ch_pd = 0; //uart mode + _rst = 0; //reset + } + + void init(); + + void wait_rst(); + + // 1: client, 2: AP, 3: mixed + void switchClientMode(int mode); + + int connect(const char *ap, const char *pswd); + + int send(const char *host, int port, const char *tx, + void (*cb)(char *, int) = NULL); + + void set_log(BufferedSerial* logger, int dbg) { + _logger = logger; + _dbg = dbg; + } + + void log(char ch) { + if (_logger) { + _logger->putc(ch); + if (_dbg > 1) { + _logger->printf("[%x]", ch); + } + } + } + + void dbg(char ch) { + if (_logger == NULL || _dbg == 0) + return; + _logger->putc(ch); + } + + void dbg(const char *msg) { + if (_logger == NULL || _dbg == 0) + return; + _logger->puts(msg); + } + + void dbg(const char * type, const char *msg) { + if (_logger == NULL || _dbg == 0) + return; + _logger->printf("::wifi:: %s: %s\n", type, msg); + } + + void clear() { + while (_uart.readable()) { + log(_uart.getc()); + } + } + + /* + * query result of a command, command must end with '?' + * >>>AT+CWMODE? + * <<<+CWMODE:3 + * <<<OK + * cmd CWMODE, result is 3. + */ + char* query(const char *cmd) { + int r; + int len = strlen(cmd) - 3; + + r = cmd_data(cmd, _buf, sizeof(_buf)); + if (r < 0) { + dbg("query", "failed"); + return NULL; + } + dbg("query result", _buf); + if (r < len + 2) { // has to be: +CMD:xxx + dbg("query", "invalid result length"); + return NULL; + } + if (memcmp(cmd + 2, _buf, len) != 0) { //check: +CMD + dbg("query", "length not match"); + return NULL; + } + if (_buf[len] != ':') { + dbg("query", "\":\" not found"); + return NULL; + } + return _buf + len + 1; + } + + int query_int(const char *cmd) { + return atoi(query(cmd)); + } + + int query_match(const char *cmd, const char *r) { + return strcmp(query(cmd), r); + } + + /** + * Read data until match or reach max size + * return + * size of data + * -3: timeout + * -1: get error pattern + */ + int read_until(char *buf, int max, const char *p_ok = "OK\r\n", + const char *p_error = "ERROR\r\n") { + int i = 0, i_ok = 0, i_err = 0; + char ch; + + int ok_size = strlen(p_ok); + int err_size = strlen(p_error); + + while (1) { + while (_uart.readable()) { + ch = _uart.getc(); + if (buf) + buf[i] = ch; + log(ch); + i++; + + //check ok pattern + if (p_ok) { + if (p_ok[i_ok] != ch) { + if (i_ok > 0 && _logger && _dbg > 0) + _logger->putc('*'); + i_ok = 0; + } + if (p_ok[i_ok] == ch) { + i_ok++; + if (i_ok > 0) + if (_logger && _dbg > 1) + _logger->putc('_'); + if (i_ok == ok_size) { //found ok pattern + if (buf) + buf[i - ok_size] = 0; + dbg("match", "done\n"); + return i - ok_size; + } + } + } + + //check error pattern + if (p_error) { + if (p_error[i_err] != ch) + i_err = 0; + if (p_error[i_err] == ch) { + i_err++; + if (i_err == err_size) { //match error + if (buf) + buf[i - err_size] = 0; + dbg("match", "error\n"); + return -1; + } + } + } + + if (buf) { + if (i >= max) { + dbg("buffer", "overflow\n"); +// buf[i-1] = 0; + return i; + } + } else { + // Just continue check ok/error + } + } + if (_timer.read_ms() > _timeout_ms) { + dbg("cmd", "timeout\n"); + return -3; + } + } + + } + + int cmd_send(const char* cmd, const char *echo = NULL) { + int r = 0; + + _uart.attach(NULL, SerialBase::RxIrq); + + _uart.puts(cmd); + _uart.puts("\r\n"); + dbg("cmd", "sent"); + + //check echo + if (echo == NULL) + echo = cmd; + + if (echo && strlen(echo) > 0) { + r = read_until(NULL, 0, echo, NULL); + if (r < 0) { + dbg("cmd", "no echo"); + return r; + } + + r = read_until(NULL, 0, "\r\r\n", NULL); + if (r < 0) { + dbg("cmd", "no echo end"); + return r; + } + } + + dbg("cmd", "sent"); + return r; + } + + /** every command has to end with "\r\n" + * + * >>>cmd + * <<<cmd + * <<<data + * <<<OK/ERROR + * <<< + * + * return size of result + * 0: ok + * -1: if error + * -2: if buffer overflow + * -3: timeout + * others: size of content + */ + int cmd_data(const char *cmd, char* ret = NULL, int size = 0, + int timeout_ms = + WIFI_TIME_OUT, const char *end = "OK\r\n", + const char *echo = NULL) { + int r = 0; + + _timer.reset(); + _timeout_ms = timeout_ms; + + dbg("cmd", cmd); + + clear(); + dbg("cmd", "cleared"); + + if (cmd) { + if ((r = cmd_send(cmd, echo)) < 0) { + dbg("cmd", "send failed"); + return r; + } + } + + if (end != NULL) { + /* check OK or ERROR */ + dbg("cmd", "matching end..."); + if ((r = read_until(ret, size, end)) < 0) + dbg("cmd", "check end failed"); + return r; + } + + clear(); + dbg("cmd", "cleared"); + + dbg("cmd", "complete"); + return r; + } + + void ipd_rx(); + +// virtual ~Esp8266(); + + BufferedSerial *_logger; + int _dbg; + +protected: + void ipd_dect(); + + DigitalOut _ch_pd; //ESP8266 CH_PD pin + DigitalOut _rst; //ESP8266 reset pin + Serial _uart; + + char _buf[1600]; + Timer _timer; + int _timeout_ms; + + int _rcv_start; //"+IPD,xxx:" + int _rcv_cnt; // total buffer used + int _rcv_exp; // expected size + void (*_rcv_cb)(char *buf, int len); + +} +; +} +#endif /* ESP8266_H_ */