ESP8266 uart wifi module via AT command

Dependents:   mbed_esp8266_demo

Files at this revision

API Documentation at this revision

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_ */