Test version

Revision:
0:4be500de690c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ESP8266.cpp	Tue Mar 20 02:09:21 2018 +0000
@@ -0,0 +1,562 @@
+/* ESP8266 Example
+ * Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ESP8266.h"
+#include "mbed_debug.h"
+#include "nsapi_types.h"
+
+#include <cstring>
+
+
+#define TRACE_GROUP "Wifi"
+#define ESP8266_DEFAULT_BAUD_RATE   115200
+
+
+ESP8266::ESP8266(PinName tx, PinName rx, bool debug)
+    : _serial(tx, rx, ESP8266_DEFAULT_BAUD_RATE), 
+      _parser(&_serial), 
+      _packets(0), 
+      _packets_end(&_packets),
+      _connect_error(0),
+      _fail(false),
+      _socket_open()
+{
+    _serial.set_baud( ESP8266_DEFAULT_BAUD_RATE );
+    _parser.debug_on(debug);
+    _parser.set_delimiter("\r\n");
+    _parser.oob("+IPD", callback(this, &ESP8266::_packet_handler));
+    //Note: espressif at command document says that this should be +CWJAP_CUR:<error code>
+    //but seems that at least current version is not sending it
+    //https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
+    //Also seems that ERROR is not sent, but FAIL instead
+    _parser.oob("+CWJAP:", callback(this, &ESP8266::_connect_error_handler));
+    _parser.oob("0,CLOSED", callback(this, &ESP8266::_oob_socket0_closed_handler));
+    _parser.oob("1,CLOSED", callback(this, &ESP8266::_oob_socket1_closed_handler));
+    _parser.oob("2,CLOSED", callback(this, &ESP8266::_oob_socket2_closed_handler));
+    _parser.oob("3,CLOSED", callback(this, &ESP8266::_oob_socket3_closed_handler));
+    _parser.oob("4,CLOSED", callback(this, &ESP8266::_oob_socket4_closed_handler));
+}
+
+int ESP8266::get_firmware_version()
+{
+    int version;
+
+    _smutex.lock();
+    bool done = _parser.send("AT+GMR")
+           && _parser.recv("SDK version:%d", &version)
+           && _parser.recv("OK\n");
+    _smutex.unlock();
+
+    if(done) {
+        return version;
+    } else { 
+        // Older firmware versions do not prefix the version with "SDK version: "
+        return -1;
+    }
+}
+
+bool ESP8266::startup(int mode)
+{
+    if (!(mode == WIFIMODE_STATION || mode == WIFIMODE_SOFTAP
+        || mode == WIFIMODE_STATION_SOFTAP)) {
+        return false;
+    }
+
+    _smutex.lock();
+    bool done = _parser.send("AT+CWMODE_CUR=%d", mode)
+            && _parser.recv("OK\n")
+            &&_parser.send("AT+CIPMUX=1")
+            && _parser.recv("OK\n");
+    _smutex.unlock();
+
+    return done;
+}
+
+bool ESP8266::reset(void)
+{
+    _smutex.lock();
+    for (int i = 0; i < 2; i++) {
+        if (_parser.send("AT+RST")
+            && _parser.recv("OK\n")
+            && _parser.recv("ready")) {
+            _smutex.unlock();
+            return true;
+        }
+    }
+    _smutex.unlock();
+
+    return false;
+}
+
+bool ESP8266::dhcp(bool enabled, int mode)
+{
+    //only 3 valid modes
+    if (mode < 0 || mode > 2) {
+        return false;
+    }
+
+    _smutex.lock();
+    bool done = _parser.send("AT+CWDHCP_CUR=%d,%d", mode, enabled?1:0)
+                && _parser.recv("OK\n");
+    _smutex.unlock();
+
+    return done;
+}
+
+nsapi_error_t ESP8266::connect(const char *ap, const char *passPhrase)
+{
+    _smutex.lock();
+    _parser.send("AT+CWJAP_CUR=\"%s\",\"%s\"", ap, passPhrase);
+    if (!_parser.recv("OK\n")) {
+        if (_fail) {
+            _smutex.unlock();
+            nsapi_error_t ret;
+            if (_connect_error == 1)
+                ret = NSAPI_ERROR_CONNECTION_TIMEOUT;
+            else if (_connect_error == 2)
+                ret = NSAPI_ERROR_AUTH_FAILURE;
+            else if (_connect_error == 3)
+                ret = NSAPI_ERROR_NO_SSID;
+            else
+                ret = NSAPI_ERROR_NO_CONNECTION;
+
+            _fail = false;
+            _connect_error = 0;
+            return ret;
+        }
+    }
+    _smutex.unlock();
+
+    return NSAPI_ERROR_OK;
+}
+
+bool ESP8266::disconnect(void)
+{
+    _smutex.lock();
+    bool done = _parser.send("AT+CWQAP") && _parser.recv("OK\n");
+    _smutex.unlock();
+
+    return done;
+}
+
+const char *ESP8266::getIPAddress(void)
+{
+    _smutex.lock();
+    if (!(_parser.send("AT+CIFSR")
+        && _parser.recv("+CIFSR:STAIP,\"%15[^\"]\"", _ip_buffer)
+        && _parser.recv("OK\n"))) {
+        _smutex.unlock();
+        return 0;
+    }
+    _smutex.unlock();
+
+    return _ip_buffer;
+}
+
+const char *ESP8266::getMACAddress(void)
+{
+    _smutex.lock();
+    if (!(_parser.send("AT+CIFSR")
+        && _parser.recv("+CIFSR:STAMAC,\"%17[^\"]\"", _mac_buffer)
+        && _parser.recv("OK\n"))) {
+        _smutex.unlock();
+        return 0;
+    }
+    _smutex.unlock();
+
+    return _mac_buffer;
+}
+
+const char *ESP8266::getGateway()
+{
+    _smutex.lock();
+    if (!(_parser.send("AT+CIPSTA_CUR?")
+        && _parser.recv("+CIPSTA_CUR:gateway:\"%15[^\"]\"", _gateway_buffer)
+        && _parser.recv("OK\n"))) {
+        _smutex.unlock();
+        return 0;
+    }
+    _smutex.unlock();
+
+    return _gateway_buffer;
+}
+
+const char *ESP8266::getNetmask()
+{
+    _smutex.lock();
+    if (!(_parser.send("AT+CIPSTA_CUR?")
+        && _parser.recv("+CIPSTA_CUR:netmask:\"%15[^\"]\"", _netmask_buffer)
+        && _parser.recv("OK\n"))) {
+        _smutex.unlock();
+        return 0;
+    }
+    _smutex.unlock();
+
+    return _netmask_buffer;
+}
+
+int8_t ESP8266::getRSSI()
+{
+    int8_t rssi;
+    char bssid[18];
+
+    _smutex.lock();
+   if (!(_parser.send("AT+CWJAP_CUR?")
+        && _parser.recv("+CWJAP_CUR:\"%*[^\"]\",\"%17[^\"]\"", bssid)
+        && _parser.recv("OK\n"))) {
+       _smutex.unlock();
+        return 0;
+    }
+   _smutex.unlock();
+
+   _smutex.lock();
+    if (!(_parser.send("AT+CWLAP=\"\",\"%s\",", bssid)
+        && _parser.recv("+CWLAP:(%*d,\"%*[^\"]\",%hhd,", &rssi)
+        && _parser.recv("OK\n"))) {
+        _smutex.unlock();
+        return 0;
+    }
+    _smutex.unlock();
+
+    return rssi;
+}
+
+int ESP8266::scan(WiFiAccessPoint *res, unsigned limit)
+{
+    unsigned cnt = 0;
+    nsapi_wifi_ap_t ap;
+
+    _smutex.lock();
+    if (!_parser.send("AT+CWLAP")) {
+        _smutex.unlock();
+        return NSAPI_ERROR_DEVICE_ERROR;
+    }
+
+    while (recv_ap(&ap)) {
+        if (cnt < limit) {
+            res[cnt] = WiFiAccessPoint(ap);
+        }
+
+        cnt++;
+        if (limit != 0 && cnt >= limit) {
+            break;
+        }
+    }
+    _smutex.unlock();
+
+    return cnt;
+}
+
+bool ESP8266::open_udp(int id, const char* addr, int port, int local_port)
+{
+    static const char *type = "UDP";
+    bool done = false;
+
+    if (id >= SOCKET_COUNT || _socket_open[id]) {
+        return false;
+    }
+
+    _smutex.lock();
+    if(local_port) {
+        done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, local_port)
+                && _parser.recv("OK\n");
+    } else {
+        done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port)
+               && _parser.recv("OK\n");
+    }
+
+    if (done) {
+        _socket_open[id] = 1;
+    }
+
+    _smutex.unlock();
+
+    return done;
+}
+
+bool ESP8266::open_tcp(int id, const char* addr, int port, int keepalive)
+{
+    static const char *type = "TCP";
+    bool done = false;
+
+    if (id >= SOCKET_COUNT || _socket_open[id]) {
+        return false;
+    }
+
+    _smutex.lock();
+
+    if(keepalive) {
+        done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, keepalive)
+                && _parser.recv("OK\n");
+    } else {
+        done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port)
+               && _parser.recv("OK\n");
+    }
+
+    if (done) {
+        _socket_open[id] = 1;
+    }
+
+    _smutex.unlock();
+
+    return done;
+}
+
+bool ESP8266::dns_lookup(const char* name, char* ip)
+{
+    _smutex.lock();
+    bool done = _parser.send("AT+CIPDOMAIN=\"%s\"", name) && _parser.recv("+CIPDOMAIN:%s%*[\r]%*[\n]", ip);
+    _smutex.unlock();
+
+    return done;
+}
+
+nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
+{
+    //May take a second try if device is busy
+    for (unsigned i = 0; i < 2; i++) {
+        _smutex.lock();
+        if (_parser.send("AT+CIPSEND=%d,%lu", id, amount)
+            && _parser.recv(">")
+            && _parser.write((char*)data, (int)amount) >= 0) {
+            while (_parser.process_oob()); // multiple sends in a row require this
+            _smutex.unlock();
+            return NSAPI_ERROR_OK;
+        }
+        _smutex.unlock();
+    }
+
+    return NSAPI_ERROR_DEVICE_ERROR;
+}
+
+void ESP8266::_packet_handler()
+{
+    int id;
+    uint32_t amount;
+
+    // parse out the packet
+    if (!_parser.recv(",%d,%lu:", &id, &amount)) {
+        return;
+    }
+
+    struct packet *packet = (struct packet*)malloc(
+            sizeof(struct packet) + amount);
+    if (!packet) {
+        debug("Could not allocate memory for RX data");
+        return;
+    }
+
+    packet->id = id;
+    packet->len = amount;
+    packet->next = 0;
+
+    if (!(_parser.read((char*)(packet + 1), amount))) {
+        free(packet);
+        return;
+    }
+
+    // append to packet list
+    *_packets_end = packet;
+    _packets_end = &packet->next;
+}
+
+int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount)
+{
+    _smutex.lock();
+    // Poll for inbound packets
+    while (_parser.process_oob()) {
+    }
+
+    // check if any packets are ready for us
+    for (struct packet **p = &_packets; *p; p = &(*p)->next) {
+        if ((*p)->id == id) {
+            struct packet *q = *p;
+
+            if (q->len <= amount) { // Return and remove full packet
+                memcpy(data, q+1, q->len);
+
+                if (_packets_end == &(*p)->next) {
+                    _packets_end = p;
+                }
+                *p = (*p)->next;
+                _smutex.unlock();
+
+                uint32_t len = q->len;
+                free(q);
+                return len;
+            } else { // return only partial packet
+                memcpy(data, q+1, amount);
+
+                q->len -= amount;
+                memmove(q+1, (uint8_t*)(q+1) + amount, q->len);
+
+                _smutex.unlock();
+                return amount;
+            }
+        }
+    }
+    if(!_socket_open[id]) {
+        _smutex.unlock();
+        return 0;
+    }
+
+    _smutex.unlock();
+
+    return NSAPI_ERROR_WOULD_BLOCK;
+}
+
+int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount)
+{
+    _smutex.lock();
+    // Poll for inbound packets
+    while (_parser.process_oob()) {
+    }
+
+    // check if any packets are ready for us
+    for (struct packet **p = &_packets; *p; p = &(*p)->next) {
+        if ((*p)->id == id) {
+            struct packet *q = *p;
+
+            // Return and remove packet (truncated if necessary)
+            uint32_t len = q->len < amount ? q->len : amount;
+            memcpy(data, q+1, len);
+
+            if (_packets_end == &(*p)->next) {
+                _packets_end = p;
+            }
+            *p = (*p)->next;
+            _smutex.unlock();
+
+            free(q);
+            return len;
+        }
+    }
+
+    _smutex.unlock();
+
+    return NSAPI_ERROR_WOULD_BLOCK;
+}
+
+bool ESP8266::close(int id)
+{
+    //May take a second try if device is busy
+    for (unsigned i = 0; i < 2; i++) {
+        _smutex.lock();
+        if (_parser.send("AT+CIPCLOSE=%d", id) && _parser.recv("OK\n")) {
+            if (!_socket_open[id]) { // recv(processing OOBs) needs to be done first
+                _smutex.unlock();
+                return true;
+            }
+        }
+        _smutex.unlock();
+    }
+
+    return false;
+}
+
+void ESP8266::setTimeout(uint32_t timeout_ms)
+{
+    _parser.set_timeout(timeout_ms);
+}
+
+bool ESP8266::readable()
+{
+    return _serial.FileHandle::readable();
+}
+
+bool ESP8266::writeable()
+{
+    return _serial.FileHandle::writable();
+}
+
+void ESP8266::attach(Callback<void()> func)
+{
+    _serial.sigio(func);
+}
+
+bool ESP8266::recv_ap(nsapi_wifi_ap_t *ap)
+{
+    int sec;
+    bool ret = _parser.recv("+CWLAP:(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu", &sec, ap->ssid,
+                            &ap->rssi, &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4],
+                            &ap->bssid[5], &ap->channel);
+
+    ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN;
+
+    return ret;
+}
+
+void ESP8266::_connect_error_handler()
+{
+    _fail = false;
+    _connect_error = 0;
+
+    if (_parser.recv("%d", &_connect_error) && _parser.recv("FAIL")) {
+        _fail = true;
+        _parser.abort();
+    }
+}
+
+void ESP8266::_oob_socket0_closed_handler()
+{
+    _socket_open[0] = 0;
+}
+
+void ESP8266::_oob_socket1_closed_handler()
+{
+    _socket_open[1] = 0;
+}
+
+void ESP8266::_oob_socket2_closed_handler()
+{
+    _socket_open[2] = 0;
+}
+
+void ESP8266::_oob_socket3_closed_handler()
+{
+    _socket_open[3] = 0;
+}
+
+void ESP8266::_oob_socket4_closed_handler()
+{
+    _socket_open[4] = 0;
+}
+
+int8_t ESP8266::get_default_wifi_mode()
+{
+    int8_t mode;
+
+    _smutex.lock();
+    if (_parser.send("AT+CWMODE_DEF?")
+        && _parser.recv("+CWMODE_DEF:%hhd", &mode)
+        && _parser.recv("OK\n")) {
+        _smutex.unlock();
+        return mode;
+    }
+    _smutex.unlock();
+
+    return 0;
+}
+
+bool ESP8266::set_default_wifi_mode(const int8_t mode)
+{
+    _smutex.lock();
+    bool done = _parser.send("AT+CWMODE_DEF=%hhd", mode)
+                && _parser.recv("OK\n");
+    _smutex.unlock();
+
+    return done;
+}