Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ESP8266.cpp Source File

ESP8266.cpp

00001 /* ESP8266 Example
00002  * Copyright (c) 2015 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #if DEVICE_SERIAL && DEVICE_INTERRUPTIN && defined(MBED_CONF_EVENTS_PRESENT) && defined(MBED_CONF_NSAPI_PRESENT) && defined(MBED_CONF_RTOS_API_PRESENT)
00018 #ifndef __STDC_FORMAT_MACROS
00019 #define __STDC_FORMAT_MACROS
00020 #endif
00021 #include <inttypes.h>
00022 
00023 #include <string.h>
00024 #include <stdlib.h>
00025 
00026 #include "ESP8266.h"
00027 #include "features/netsocket/nsapi_types.h"
00028 #include "mbed_trace.h"
00029 #include "PinNames.h"
00030 #include "platform/Callback.h"
00031 #include "platform/mbed_error.h"
00032 #include "rtos/Kernel.h"
00033 
00034 #define TRACE_GROUP  "ESPA" // ESP8266 AT layer
00035 
00036 #define ESP8266_ALL_SOCKET_IDS      -1
00037 
00038 using namespace mbed;
00039 
00040 ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
00041     : _sdk_v(-1, -1, -1),
00042       _at_v(-1, -1, -1),
00043       _tcp_passive(false),
00044       _callback(0),
00045       _serial(tx, rx, MBED_CONF_ESP8266_SERIAL_BAUDRATE),
00046       _serial_rts(rts),
00047       _serial_cts(cts),
00048       _parser(&_serial),
00049       _packets(0),
00050       _packets_end(&_packets),
00051       _sock_active_id(-1),
00052       _heap_usage(0),
00053       _connect_error(0),
00054       _disconnect(false),
00055       _fail(false),
00056       _sock_already(false),
00057       _closed(false),
00058       _error(false),
00059       _busy(false),
00060       _reset_done(false),
00061       _conn_status(NSAPI_STATUS_DISCONNECTED )
00062 {
00063     _serial.set_baud(MBED_CONF_ESP8266_SERIAL_BAUDRATE);
00064     _parser.debug_on(debug);
00065     _parser.set_delimiter("\r\n");
00066     _parser.oob("+IPD", callback(this, &ESP8266::_oob_packet_hdlr));
00067     //Note: espressif at command document says that this should be +CWJAP_CUR:<error code>
00068     //but seems that at least current version is not sending it
00069     //https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
00070     //Also seems that ERROR is not sent, but FAIL instead
00071     _parser.oob("0,CLOSED", callback(this, &ESP8266::_oob_socket0_closed));
00072     _parser.oob("1,CLOSED", callback(this, &ESP8266::_oob_socket1_closed));
00073     _parser.oob("2,CLOSED", callback(this, &ESP8266::_oob_socket2_closed));
00074     _parser.oob("3,CLOSED", callback(this, &ESP8266::_oob_socket3_closed));
00075     _parser.oob("4,CLOSED", callback(this, &ESP8266::_oob_socket4_closed));
00076     _parser.oob("+CWJAP:", callback(this, &ESP8266::_oob_connect_err));
00077     _parser.oob("WIFI ", callback(this, &ESP8266::_oob_connection_status));
00078     _parser.oob("UNLINK", callback(this, &ESP8266::_oob_socket_close_err));
00079     _parser.oob("ALREADY CONNECTED", callback(this, &ESP8266::_oob_conn_already));
00080     _parser.oob("ERROR", callback(this, &ESP8266::_oob_err));
00081     _parser.oob("ready", callback(this, &ESP8266::_oob_ready));
00082     _parser.oob("+CWLAP:", callback(this, &ESP8266::_oob_scan_results));
00083     // Don't expect to find anything about the watchdog reset in official documentation
00084     //https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/
00085     _parser.oob("wdt reset", callback(this, &ESP8266::_oob_watchdog_reset));
00086     // Don't see a reason to make distiction between software(Software WDT reset) and hardware(wdt reset) watchdog treatment
00087     //https://github.com/esp8266/Arduino/blob/4897e0006b5b0123a2fa31f67b14a3fff65ce561/doc/faq/a02-my-esp-crashes.md#watchdog
00088     _parser.oob("Soft WDT reset", callback(this, &ESP8266::_oob_watchdog_reset));
00089     _parser.oob("busy ", callback(this, &ESP8266::_oob_busy));
00090     // NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds...
00091     _parser.oob("+CIPRECVDATA,", callback(this, &ESP8266::_oob_tcp_data_hdlr));
00092 
00093     for (int i = 0; i < SOCKET_COUNT; i++) {
00094         _sock_i[i].open = false;
00095         _sock_i[i].proto = NSAPI_UDP ;
00096         _sock_i[i].tcp_data = NULL;
00097         _sock_i[i].tcp_data_avbl = 0;
00098         _sock_i[i].tcp_data_rcvd = 0;
00099     }
00100 
00101     _scan_r.res = NULL;
00102     _scan_r.limit = 0;
00103     _scan_r.cnt = 0;
00104 }
00105 
00106 bool ESP8266::at_available()
00107 {
00108     bool ready = false;
00109 
00110     _smutex.lock();
00111     // Might take a while to respond after HW reset
00112     for (int i = 0; i < 5; i++) {
00113         ready = _parser.send("AT")
00114                 && _parser.recv("OK\n");
00115         if (ready) {
00116             break;
00117         }
00118         tr_debug("at_available(): Waiting AT response.");
00119     }
00120     _smutex.unlock();
00121 
00122     return ready;
00123 }
00124 
00125 bool ESP8266::echo_off()
00126 {
00127     _smutex.lock();
00128     bool ready = _parser.send("ATE0")
00129                  && _parser.recv("OK\n");
00130     _smutex.unlock();
00131 
00132     return ready;
00133 }
00134 
00135 struct ESP8266::fw_sdk_version ESP8266::sdk_version()
00136 {
00137     int major;
00138     int minor;
00139     int patch;
00140 
00141     _smutex.lock();
00142     bool done = _parser.send("AT+GMR")
00143                 && _parser.recv("SDK version:%d.%d.%d", &major, &minor, &patch)
00144                 && _parser.recv("OK\n");
00145     _smutex.unlock();
00146 
00147     if (done) {
00148         _sdk_v.major = major;
00149         _sdk_v.minor = minor;
00150         _sdk_v.patch = patch;
00151     }
00152     return _sdk_v;
00153 }
00154 
00155 struct ESP8266::fw_at_version ESP8266::at_version()
00156 {
00157     int major;
00158     int minor;
00159     int patch;
00160     int nused;
00161 
00162     _smutex.lock();
00163     bool done = _parser.send("AT+GMR")
00164                 && _parser.recv("AT version:%d.%d.%d.%d", &major, &minor, &patch, &nused)
00165                 && _parser.recv("OK\n");
00166     _smutex.unlock();
00167 
00168     if (done) {
00169         _at_v.major = major;
00170         _at_v.minor = minor;
00171         _at_v.patch = patch;
00172     }
00173     return _at_v;
00174 }
00175 
00176 bool ESP8266::stop_uart_hw_flow_ctrl(void)
00177 {
00178     bool done = true;
00179 #if DEVICE_SERIAL_FC
00180 
00181     if (_serial_rts != NC || _serial_cts != NC) {
00182         // Stop board's flow control
00183         _serial.set_flow_control(SerialBase::Disabled, _serial_rts, _serial_cts);
00184 
00185         // Stop ESP8266's flow control
00186         done = _parser.send("AT+UART_CUR=%u,8,1,0,0", MBED_CONF_ESP8266_SERIAL_BAUDRATE)
00187                && _parser.recv("OK\n");
00188     }
00189 
00190 #endif
00191     return done;
00192 }
00193 
00194 bool ESP8266::start_uart_hw_flow_ctrl(void)
00195 {
00196     bool done = true;
00197 
00198 #if DEVICE_SERIAL_FC
00199     _smutex.lock();
00200     if (_serial_rts != NC && _serial_cts != NC) {
00201         // Start ESP8266's flow control
00202         done = _parser.send("AT+UART_CUR=%u,8,1,0,3", MBED_CONF_ESP8266_SERIAL_BAUDRATE)
00203                && _parser.recv("OK\n");
00204 
00205         if (done) {
00206             // Start board's flow control
00207             _serial.set_flow_control(SerialBase::RTSCTS, _serial_rts, _serial_cts);
00208         }
00209 
00210     } else if (_serial_rts != NC) {
00211         _serial.set_flow_control(SerialBase::RTS, _serial_rts, NC);
00212 
00213         // Enable ESP8266's CTS pin
00214         done = _parser.send("AT+UART_CUR=%u,8,1,0,2", MBED_CONF_ESP8266_SERIAL_BAUDRATE)
00215                && _parser.recv("OK\n");
00216 
00217     } else if (_serial_cts != NC) {
00218         // Enable ESP8266's RTS pin
00219         done = _parser.send("AT+UART_CUR=%u,8,1,0,1", MBED_CONF_ESP8266_SERIAL_BAUDRATE)
00220                && _parser.recv("OK\n");
00221 
00222         if (done) {
00223             _serial.set_flow_control(SerialBase::CTS, NC, _serial_cts);
00224         }
00225     }
00226     _smutex.unlock();
00227 
00228     if (!done) {
00229         tr_debug("start_uart_hw_flow_ctrl(): Enable UART HW flow control: FAIL.");
00230     }
00231 #else
00232     if (_serial_rts != NC || _serial_cts != NC) {
00233         done = false;
00234     }
00235 #endif
00236     return done;
00237 }
00238 
00239 bool ESP8266::startup(int mode)
00240 {
00241     if (!(mode == WIFIMODE_STATION || mode == WIFIMODE_SOFTAP
00242             || mode == WIFIMODE_STATION_SOFTAP)) {
00243         return false;
00244     }
00245 
00246     _smutex.lock();
00247     set_timeout(ESP8266_CONNECT_TIMEOUT);
00248     bool done = _parser.send("AT+CWMODE_CUR=%d", mode)
00249                 && _parser.recv("OK\n")
00250                 && _parser.send("AT+CIPMUX=1")
00251                 && _parser.recv("OK\n");
00252     set_timeout(); //Restore default
00253     _smutex.unlock();
00254 
00255     return done;
00256 }
00257 
00258 bool ESP8266::reset(void)
00259 {
00260     static const int ESP8266_BOOTTIME = 10000; // [ms]
00261     bool done = false;
00262 
00263     _smutex.lock();
00264 
00265     unsigned long int start_time = rtos::Kernel::get_ms_count();
00266     _reset_done = false;
00267     set_timeout(ESP8266_RECV_TIMEOUT);
00268     for (int i = 0; i < 2; i++) {
00269         if (!_parser.send("AT+RST") || !_parser.recv("OK\n")) {
00270             tr_debug("reset(): AT+RST failed or no response.");
00271             continue;
00272         }
00273 
00274         while (!_reset_done) {
00275             _process_oob(ESP8266_RECV_TIMEOUT, true); // UART mutex claimed -> need to check for OOBs ourselves
00276             if (_reset_done || (rtos::Kernel::get_ms_count() - start_time >= ESP8266_BOOTTIME)) {
00277                 break;
00278             }
00279             rtos::ThisThread::sleep_for(100);
00280         }
00281 
00282         done = _reset_done;
00283         if (done) {
00284             break;
00285         }
00286     }
00287 
00288     tr_debug("reset(): Done: %s.", done ? "OK" : "FAIL");
00289 
00290     _clear_socket_packets(ESP8266_ALL_SOCKET_IDS);
00291     set_timeout();
00292     _smutex.unlock();
00293 
00294     return done;
00295 }
00296 
00297 bool ESP8266::dhcp(bool enabled, int mode)
00298 {
00299     //only 3 valid modes
00300     if (mode < 0 || mode > 2) {
00301         return false;
00302     }
00303 
00304     _smutex.lock();
00305     bool done = _parser.send("AT+CWDHCP_CUR=%d,%d", mode, enabled ? 1 : 0)
00306                 && _parser.recv("OK\n");
00307     _smutex.unlock();
00308 
00309     return done;
00310 }
00311 
00312 bool ESP8266::cond_enable_tcp_passive_mode()
00313 {
00314     bool done = true;
00315 
00316     if (FW_AT_LEAST_VERSION(_at_v.major, _at_v.minor, _at_v.patch, 0, ESP8266_AT_VERSION_TCP_PASSIVE_MODE)) {
00317         _smutex.lock();
00318         done = _parser.send("AT+CIPRECVMODE=1")
00319                && _parser.recv("OK\n");
00320         _smutex.unlock();
00321 
00322         _tcp_passive = done ? true : false;
00323     }
00324 
00325     return done;
00326 }
00327 
00328 
00329 nsapi_error_t ESP8266::connect(const char *ap, const char *passPhrase)
00330 {
00331     nsapi_error_t ret = NSAPI_ERROR_OK ;
00332 
00333     _smutex.lock();
00334     set_timeout(ESP8266_CONNECT_TIMEOUT);
00335 
00336     bool res = _parser.send("AT+CWJAP_CUR=\"%s\",\"%s\"", ap, passPhrase);
00337     if (!res || !_parser.recv("OK\n")) {
00338         if (_fail) {
00339             if (_connect_error == 1) {
00340                 ret = NSAPI_ERROR_CONNECTION_TIMEOUT ;
00341             } else if (_connect_error == 2) {
00342                 ret = NSAPI_ERROR_AUTH_FAILURE ;
00343             } else if (_connect_error == 3) {
00344                 ret = NSAPI_ERROR_NO_SSID ;
00345             } else {
00346                 ret = NSAPI_ERROR_NO_CONNECTION ;
00347             }
00348             _fail = false;
00349             _connect_error = 0;
00350         }
00351     }
00352 
00353     set_timeout();
00354     _smutex.unlock();
00355 
00356     return ret;
00357 }
00358 
00359 bool ESP8266::disconnect(void)
00360 {
00361     _smutex.lock();
00362     _disconnect = true;
00363     bool done = _parser.send("AT+CWQAP") && _parser.recv("OK\n");
00364     _smutex.unlock();
00365 
00366     return done;
00367 }
00368 
00369 const char *ESP8266::ip_addr(void)
00370 {
00371     _smutex.lock();
00372     set_timeout(ESP8266_CONNECT_TIMEOUT);
00373     if (!(_parser.send("AT+CIFSR")
00374             && _parser.recv("+CIFSR:STAIP,\"%15[^\"]\"", _ip_buffer)
00375             && _parser.recv("OK\n"))) {
00376         _smutex.unlock();
00377         return 0;
00378     }
00379     set_timeout();
00380     _smutex.unlock();
00381 
00382     return _ip_buffer;
00383 }
00384 
00385 const char *ESP8266::mac_addr(void)
00386 {
00387     _smutex.lock();
00388     if (!(_parser.send("AT+CIFSR")
00389             && _parser.recv("+CIFSR:STAMAC,\"%17[^\"]\"", _mac_buffer)
00390             && _parser.recv("OK\n"))) {
00391         _smutex.unlock();
00392         return 0;
00393     }
00394     _smutex.unlock();
00395 
00396     return _mac_buffer;
00397 }
00398 
00399 const char *ESP8266::gateway()
00400 {
00401     _smutex.lock();
00402     if (!(_parser.send("AT+CIPSTA_CUR?")
00403             && _parser.recv("+CIPSTA_CUR:gateway:\"%15[^\"]\"", _gateway_buffer)
00404             && _parser.recv("OK\n"))) {
00405         _smutex.unlock();
00406         return 0;
00407     }
00408     _smutex.unlock();
00409 
00410     return _gateway_buffer;
00411 }
00412 
00413 const char *ESP8266::netmask()
00414 {
00415     _smutex.lock();
00416     if (!(_parser.send("AT+CIPSTA_CUR?")
00417             && _parser.recv("+CIPSTA_CUR:netmask:\"%15[^\"]\"", _netmask_buffer)
00418             && _parser.recv("OK\n"))) {
00419         _smutex.unlock();
00420         return 0;
00421     }
00422     _smutex.unlock();
00423 
00424     return _netmask_buffer;
00425 }
00426 
00427 int8_t ESP8266::rssi()
00428 {
00429     int8_t rssi = 0;
00430     char bssid[18];
00431 
00432     _smutex.lock();
00433     set_timeout(ESP8266_CONNECT_TIMEOUT);
00434     if (!(_parser.send("AT+CWJAP_CUR?")
00435             && _parser.recv("+CWJAP_CUR:\"%*[^\"]\",\"%17[^\"]\"", bssid)
00436             && _parser.recv("OK\n"))) {
00437         _smutex.unlock();
00438         return 0;
00439     }
00440 
00441     set_timeout();
00442     _smutex.unlock();
00443 
00444     WiFiAccessPoint ap[1];
00445     _scan_r.res = ap;
00446     _scan_r.limit = 1;
00447     _scan_r.cnt = 0;
00448 
00449     _smutex.lock();
00450     set_timeout(ESP8266_CONNECT_TIMEOUT);
00451     if (!(_parser.send("AT+CWLAP=\"\",\"%s\",", bssid)
00452             && _parser.recv("OK\n"))) {
00453         rssi = 0;
00454     } else if (_scan_r.cnt == 1) {
00455         //All OK so read and return rssi
00456         rssi = ap[0].get_rssi();
00457     }
00458 
00459     _scan_r.cnt = 0;
00460     _scan_r.res = NULL;
00461     set_timeout();
00462     _smutex.unlock();
00463 
00464     return rssi;
00465 }
00466 
00467 int ESP8266::scan(WiFiAccessPoint *res, unsigned limit, scan_mode mode, unsigned t_max, unsigned t_min)
00468 {
00469     _smutex.lock();
00470 
00471     // Default timeout plus time spend scanning each channel
00472     set_timeout(ESP8266_MISC_TIMEOUT + 13 * (t_max ? t_max : ESP8266_SCAN_TIME_MAX_DEFAULT));
00473 
00474     _scan_r.res = res;
00475     _scan_r.limit = limit;
00476     _scan_r.cnt = 0;
00477 
00478     bool ret_parse_send = true;
00479 
00480     if (FW_AT_LEAST_VERSION(_at_v.major, _at_v.minor, _at_v.patch, 0, ESP8266_AT_VERSION_WIFI_SCAN_CHANGE)) {
00481         ret_parse_send = _parser.send("AT+CWLAP=,,,%u,%u,%u", (mode == SCANMODE_ACTIVE  ? 0 : 1), t_min, t_max);
00482     } else {
00483         ret_parse_send = _parser.send("AT+CWLAP");
00484     }
00485 
00486     if (!(ret_parse_send && _parser.recv("OK\n"))) {
00487         tr_warning("scan(): AP info parsing aborted.");
00488         // Lets be happy about partial success and not return NSAPI_ERROR_DEVICE_ERROR
00489         if (!_scan_r.cnt) {
00490             _scan_r.cnt = NSAPI_ERROR_DEVICE_ERROR ;
00491         }
00492     }
00493 
00494 
00495     int cnt = _scan_r.cnt;
00496     _scan_r.res = NULL;
00497 
00498     set_timeout();
00499     _smutex.unlock();
00500 
00501     return cnt;
00502 }
00503 
00504 nsapi_error_t ESP8266::open_udp(int id, const char *addr, int port, int local_port)
00505 {
00506     static const char *type = "UDP";
00507     bool done = false;
00508 
00509     _smutex.lock();
00510 
00511     // process OOB so that _sock_i reflects the correct state of the socket
00512     _process_oob(ESP8266_SEND_TIMEOUT, true);
00513 
00514     if (id >= SOCKET_COUNT || _sock_i[id].open) {
00515         _smutex.unlock();
00516         return NSAPI_ERROR_PARAMETER ;
00517     }
00518 
00519     for (int i = 0; i < 2; i++) {
00520         if (local_port) {
00521             done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, local_port);
00522         } else {
00523             done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port);
00524         }
00525 
00526         if (done) {
00527             if (!_parser.recv("OK\n")) {
00528                 if (_sock_already) {
00529                     _sock_already = false; // To be raised again by OOB msg
00530                     done = close(id);
00531                     if (!done) {
00532                         break;
00533                     }
00534                 }
00535                 if (_error) {
00536                     _error = false;
00537                     done = false;
00538                 }
00539                 continue;
00540             }
00541             _sock_i[id].open = true;
00542             _sock_i[id].proto = NSAPI_UDP ;
00543             break;
00544         }
00545     }
00546     _clear_socket_packets(id);
00547 
00548     _smutex.unlock();
00549 
00550     tr_debug("open_udp(): UDP socket %d opened: %s.", id, (_sock_i[id].open ? "true" : "false"));
00551 
00552     return done ? NSAPI_ERROR_OK  : NSAPI_ERROR_DEVICE_ERROR ;
00553 }
00554 
00555 nsapi_error_t ESP8266::open_tcp(int id, const char *addr, int port, int keepalive)
00556 {
00557     static const char *type = "TCP";
00558     bool done = false;
00559 
00560     _smutex.lock();
00561 
00562     // process OOB so that _sock_i reflects the correct state of the socket
00563     _process_oob(ESP8266_SEND_TIMEOUT, true);
00564 
00565     if (id >= SOCKET_COUNT || _sock_i[id].open) {
00566         _smutex.unlock();
00567         return NSAPI_ERROR_PARAMETER ;
00568     }
00569 
00570     for (int i = 0; i < 2; i++) {
00571         if (keepalive) {
00572             done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, keepalive);
00573         } else {
00574             done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port);
00575         }
00576 
00577         if (done) {
00578             if (!_parser.recv("OK\n")) {
00579                 if (_sock_already) {
00580                     _sock_already = false; // To be raised again by OOB msg
00581                     done = close(id);
00582                     if (!done) {
00583                         break;
00584                     }
00585                 }
00586                 if (_error) {
00587                     _error = false;
00588                     done = false;
00589                 }
00590                 continue;
00591             }
00592             _sock_i[id].open = true;
00593             _sock_i[id].proto = NSAPI_TCP ;
00594             break;
00595         }
00596     }
00597     _clear_socket_packets(id);
00598 
00599     _smutex.unlock();
00600 
00601     tr_debug("open_tcp: TCP socket %d opened: %s . ", id, (_sock_i[id].open ? "true" : "false"));
00602 
00603     return done ? NSAPI_ERROR_OK  : NSAPI_ERROR_DEVICE_ERROR ;
00604 }
00605 
00606 bool ESP8266::dns_lookup(const char *name, char *ip)
00607 {
00608     _smutex.lock();
00609     bool done = _parser.send("AT+CIPDOMAIN=\"%s\"", name) && _parser.recv("+CIPDOMAIN:%s%*[\r]%*[\n]", ip);
00610     _smutex.unlock();
00611 
00612     return done;
00613 }
00614 
00615 nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
00616 {
00617     nsapi_error_t ret = NSAPI_ERROR_DEVICE_ERROR ;
00618     // +CIPSEND supports up to 2048 bytes at a time
00619     // Data stream can be truncated
00620     if (amount > 2048 && _sock_i[id].proto == NSAPI_TCP ) {
00621         amount = 2048;
00622         // Datagram must stay intact
00623     } else if (amount > 2048 && _sock_i[id].proto == NSAPI_UDP ) {
00624         tr_debug("send(): UDP datagram maximum size is 2048 .");
00625         return NSAPI_ERROR_PARAMETER ;
00626     }
00627 
00628     _smutex.lock();
00629 RETRY:
00630     set_timeout(ESP8266_SEND_TIMEOUT);
00631     _busy = false;
00632     _error = false;
00633     if (!_parser.send("AT+CIPSEND=%d,%" PRIu32, id, amount)) {
00634         tr_debug("send(): AT+CIPSEND failed.");
00635         goto END;
00636     }
00637 
00638     //We might receive "busy s/p..." and "OK" from modem, so we need to check that also
00639     _ok_received = false;
00640     _parser.oob("OK", callback(this, &ESP8266::_oob_ok_received));
00641 
00642     if (!_parser.recv(">")) {
00643         _parser.remove_oob("OK");
00644         if (_busy) {
00645             if (_ok_received) {
00646                 goto RETRY;
00647             } else if (_parser.recv("OK")) {
00648                 goto RETRY;
00649             }
00650         }
00651         tr_debug("send(): Didn't get \">\"");
00652         ret = NSAPI_ERROR_WOULD_BLOCK ;
00653         goto END;
00654     }
00655     _ok_received = false;
00656     _parser.remove_oob("OK");
00657 
00658     if (_parser.write((char *)data, (int)amount) >= 0 && _parser.recv("SEND OK")) {
00659         ret = NSAPI_ERROR_OK ;
00660     }
00661 
00662 END:
00663     _process_oob(ESP8266_RECV_TIMEOUT, true); // Drain USART receive register to avoid data overrun
00664 
00665     // error hierarchy, from low to high
00666     if (_busy) {
00667         ret = NSAPI_ERROR_WOULD_BLOCK ;
00668         tr_debug("send(): Modem busy. ");
00669     }
00670 
00671     if (ret == NSAPI_ERROR_DEVICE_ERROR ) {
00672         ret = NSAPI_ERROR_WOULD_BLOCK ;
00673         tr_debug("send(): Send failed.");
00674     }
00675 
00676     if (_error) {
00677         ret = NSAPI_ERROR_CONNECTION_LOST ;
00678         tr_debug("send(): Connection disrupted.");
00679     }
00680 
00681     if (!_sock_i[id].open && ret != NSAPI_ERROR_OK ) {
00682         ret = NSAPI_ERROR_CONNECTION_LOST ;
00683         tr_debug("send(): Socket closed abruptly.");
00684     }
00685 
00686     set_timeout();
00687     _smutex.unlock();
00688 
00689     return ret;
00690 }
00691 
00692 void ESP8266::_oob_packet_hdlr()
00693 {
00694     int id;
00695     int amount;
00696     int pdu_len;
00697 
00698     // Get socket id
00699     if (!_parser.scanf(",%d,", &id)) {
00700         return;
00701     }
00702 
00703     if (_tcp_passive && _sock_i[id].open == true && _sock_i[id].proto == NSAPI_TCP ) {
00704         if (_parser.recv("%d\n", &amount)) {
00705             _sock_i[id].tcp_data_avbl = amount;
00706 
00707             // notify data is available
00708             if (_callback) {
00709                 _callback();
00710             }
00711         }
00712         return;
00713     } else if (!_parser.scanf("%d:", &amount)) {
00714         return;
00715     }
00716 
00717     pdu_len = sizeof(struct packet) + amount;
00718 
00719     if ((_heap_usage + pdu_len) > MBED_CONF_ESP8266_SOCKET_BUFSIZE) {
00720         tr_debug("\"esp8266.socket-bufsize\"-limit exceeded, packet dropped");
00721         return;
00722     }
00723 
00724     struct packet *packet = (struct packet *)malloc(pdu_len);
00725     if (!packet) {
00726         tr_debug("_oob_packet_hdlr(): Out of memory, unable to allocate memory for packet.");
00727         return;
00728     }
00729     _heap_usage += pdu_len;
00730 
00731     packet->id = id;
00732     packet->len = amount;
00733     packet->alloc_len = amount;
00734     packet->next = 0;
00735 
00736     if (_parser.read((char *)(packet + 1), amount) < amount) {
00737         free(packet);
00738         _heap_usage -= pdu_len;
00739         return;
00740     }
00741 
00742     // append to packet list
00743     *_packets_end = packet;
00744     _packets_end = &packet->next;
00745 }
00746 
00747 void ESP8266::_process_oob(uint32_t timeout, bool all)
00748 {
00749     set_timeout(timeout);
00750     // Poll for inbound packets
00751     while (_parser.process_oob() && all) {
00752     }
00753     set_timeout();
00754 }
00755 
00756 void ESP8266::bg_process_oob(uint32_t timeout, bool all)
00757 {
00758     _smutex.lock();
00759     _process_oob(timeout, all);
00760     _smutex.unlock();
00761 }
00762 
00763 int32_t ESP8266::_recv_tcp_passive(int id, void *data, uint32_t amount, uint32_t timeout)
00764 {
00765     int32_t ret = NSAPI_ERROR_WOULD_BLOCK ;
00766 
00767     _smutex.lock();
00768 
00769     _process_oob(timeout, true);
00770 
00771     if (_sock_i[id].tcp_data_avbl != 0) {
00772         _sock_i[id].tcp_data = (char *)data;
00773         _sock_i[id].tcp_data_rcvd = NSAPI_ERROR_WOULD_BLOCK ;
00774         _sock_active_id = id;
00775 
00776         // +CIPRECVDATA supports up to 2048 bytes at a time
00777         amount = amount > 2048 ? 2048 : amount;
00778 
00779         // NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds...
00780         bool done = _parser.send("AT+CIPRECVDATA=%d,%" PRIu32, id, amount)
00781                     && _parser.recv("OK\n");
00782 
00783         _sock_i[id].tcp_data = NULL;
00784         _sock_active_id = -1;
00785 
00786         if (!done) {
00787             goto BUSY;
00788         }
00789 
00790         // update internal variable tcp_data_avbl to reflect the remaining data
00791         if (_sock_i[id].tcp_data_rcvd > 0) {
00792             if (_sock_i[id].tcp_data_rcvd > (int32_t)amount) {
00793                 MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_EBADMSG), \
00794                            "ESP8266::_recv_tcp_passive() too much data from modem\n");
00795             }
00796             if (_sock_i[id].tcp_data_avbl > _sock_i[id].tcp_data_rcvd) {
00797                 _sock_i[id].tcp_data_avbl -= _sock_i[id].tcp_data_rcvd;
00798             } else {
00799                 _sock_i[id].tcp_data_avbl = 0;
00800             }
00801         }
00802 
00803         ret = _sock_i[id].tcp_data_rcvd;
00804     }
00805 
00806     if (!_sock_i[id].open && ret == NSAPI_ERROR_WOULD_BLOCK ) {
00807         ret = 0;
00808     }
00809 
00810     _smutex.unlock();
00811     return ret;
00812 
00813 BUSY:
00814     _process_oob(ESP8266_RECV_TIMEOUT, true);
00815     if (_busy) {
00816         tr_debug("_recv_tcp_passive(): Modem busy.");
00817         ret = NSAPI_ERROR_WOULD_BLOCK ;
00818     } else {
00819         tr_error("_recv_tcp_passive(): Unknown state.");
00820         ret = NSAPI_ERROR_DEVICE_ERROR ;
00821     }
00822     _smutex.unlock();
00823     return ret;
00824 }
00825 
00826 int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout)
00827 {
00828     if (_tcp_passive) {
00829         return _recv_tcp_passive(id, data, amount, timeout);
00830     }
00831 
00832     _smutex.lock();
00833 
00834     // No flow control, drain the USART receive register ASAP to avoid data overrun
00835     if (_serial_rts == NC) {
00836         _process_oob(timeout, true);
00837     }
00838 
00839     // check if any packets are ready for us
00840     for (struct packet **p = &_packets; *p; p = &(*p)->next) {
00841         if ((*p)->id == id) {
00842             struct packet *q = *p;
00843 
00844             if (q->len <= amount) { // Return and remove full packet
00845                 memcpy(data, q + 1, q->len);
00846 
00847                 if (_packets_end == &(*p)->next) {
00848                     _packets_end = p;
00849                 }
00850                 *p = (*p)->next;
00851 
00852                 _smutex.unlock();
00853 
00854                 uint32_t pdu_len = sizeof(struct packet) + q->alloc_len;
00855                 uint32_t len = q->len;
00856                 free(q);
00857                 _heap_usage -= pdu_len;
00858                 return len;
00859             } else { // return only partial packet
00860                 memcpy(data, q + 1, amount);
00861 
00862                 q->len -= amount;
00863                 memmove(q + 1, (uint8_t *)(q + 1) + amount, q->len);
00864 
00865                 _smutex.unlock();
00866                 return amount;
00867             }
00868         }
00869     }
00870     if (!_sock_i[id].open) {
00871         _smutex.unlock();
00872         return 0;
00873     }
00874 
00875     // Flow control, read from USART receive register only when no more data is buffered, and as little as possible
00876     if (_serial_rts != NC) {
00877         _process_oob(timeout, false);
00878     }
00879     _smutex.unlock();
00880 
00881     return NSAPI_ERROR_WOULD_BLOCK ;
00882 }
00883 
00884 int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount, uint32_t timeout)
00885 {
00886     _smutex.lock();
00887     set_timeout(timeout);
00888 
00889     // Process OOB data since this is
00890     // how UDP packets are received
00891     _process_oob(timeout, true);
00892 
00893     set_timeout();
00894 
00895     // check if any packets are ready for us
00896     for (struct packet **p = &_packets; *p; p = &(*p)->next) {
00897         if ((*p)->id == id) {
00898             struct packet *q = *p;
00899 
00900             // Return and remove packet (truncated if necessary)
00901             uint32_t len = q->len < amount ? q->len : amount;
00902             memcpy(data, q + 1, len);
00903 
00904             if (_packets_end == &(*p)->next) {
00905                 _packets_end = p;
00906             }
00907             *p = (*p)->next;
00908             _smutex.unlock();
00909 
00910             uint32_t pdu_len = sizeof(struct packet) + q->alloc_len;
00911             free(q);
00912             _heap_usage -= pdu_len;
00913             return len;
00914         }
00915     }
00916 
00917     // Flow control, read from USART receive register only when no more data is buffered, and as little as possible
00918     if (_serial_rts != NC) {
00919         _process_oob(timeout, false);
00920     }
00921 
00922     _smutex.unlock();
00923 
00924     return NSAPI_ERROR_WOULD_BLOCK ;
00925 }
00926 
00927 void ESP8266::_clear_socket_packets(int id)
00928 {
00929     struct packet **p = &_packets;
00930 
00931     while (*p) {
00932         if ((*p)->id == id || id == ESP8266_ALL_SOCKET_IDS) {
00933             struct packet *q = *p;
00934             int pdu_len = sizeof(struct packet) + q->alloc_len;
00935 
00936             if (_packets_end == &(*p)->next) {
00937                 _packets_end = p; // Set last packet next field/_packets
00938             }
00939             *p = (*p)->next;
00940             free(q);
00941             _heap_usage -= pdu_len;
00942         } else {
00943             // Point to last packet next field
00944             p = &(*p)->next;
00945         }
00946     }
00947     if (id == ESP8266_ALL_SOCKET_IDS) {
00948         for (int id = 0; id < 5; id++) {
00949             _sock_i[id].tcp_data_avbl = 0;
00950         }
00951     } else {
00952         _sock_i[id].tcp_data_avbl = 0;
00953     }
00954 }
00955 
00956 bool ESP8266::close(int id)
00957 {
00958     //May take a second try if device is busy
00959     for (unsigned i = 0; i < 2; i++) {
00960         _smutex.lock();
00961         if (_parser.send("AT+CIPCLOSE=%d", id)) {
00962             if (!_parser.recv("OK\n")) {
00963                 if (_closed) { // UNLINK ERROR
00964                     _closed = false;
00965                     _sock_i[id].open = false;
00966                     _clear_socket_packets(id);
00967                     _smutex.unlock();
00968                     // ESP8266 has a habit that it might close a socket on its own.
00969                     return true;
00970                 }
00971             } else {
00972                 // _sock_i[id].open set to false with an OOB
00973                 _clear_socket_packets(id);
00974                 _smutex.unlock();
00975                 return true;
00976             }
00977         }
00978         _smutex.unlock();
00979     }
00980 
00981     return false;
00982 }
00983 
00984 void ESP8266::set_timeout(uint32_t timeout_ms)
00985 {
00986     _parser.set_timeout(timeout_ms);
00987 }
00988 
00989 bool ESP8266::readable()
00990 {
00991     return _serial.FileHandle::readable();
00992 }
00993 
00994 bool ESP8266::writeable()
00995 {
00996     return _serial.FileHandle::writable();
00997 }
00998 
00999 void ESP8266::sigio(Callback<void()> func)
01000 {
01001     _serial.sigio(func);
01002     _callback = func;
01003 }
01004 
01005 void ESP8266::attach(Callback<void()> status_cb)
01006 {
01007     _conn_stat_cb = status_cb;
01008 }
01009 
01010 bool ESP8266::_recv_ap(nsapi_wifi_ap_t *ap)
01011 {
01012     int sec = NSAPI_SECURITY_UNKNOWN ;
01013     int dummy;
01014     int ret;
01015 
01016     if (FW_AT_LEAST_VERSION(_at_v.major, _at_v.minor, _at_v.patch, 0, ESP8266_AT_VERSION_WIFI_SCAN_CHANGE)) {
01017         ret = _parser.scanf("(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d,%d,%d,%d,%d)\n",
01018                             &sec,
01019                             ap->ssid,
01020                             &ap->rssi,
01021                             &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5],
01022                             &ap->channel,
01023                             &dummy,
01024                             &dummy,
01025                             &dummy,
01026                             &dummy,
01027                             &dummy,
01028                             &dummy);
01029     } else {
01030         ret = _parser.scanf("(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d)\n",
01031                             &sec,
01032                             ap->ssid,
01033                             &ap->rssi,
01034                             &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5],
01035                             &ap->channel,
01036                             &dummy,
01037                             &dummy);
01038     }
01039 
01040     if (ret < 0) {
01041         _parser.abort();
01042         tr_warning("_recv_ap(): AP info missing.");
01043     }
01044 
01045     ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN ;
01046 
01047     return ret < 0 ? false : true;
01048 }
01049 
01050 void ESP8266::_oob_watchdog_reset()
01051 {
01052     MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ETIME), \
01053                "_oob_watchdog_reset() modem watchdog reset triggered\n");
01054 }
01055 
01056 void ESP8266::_oob_ready()
01057 {
01058     _reset_done = true;
01059 
01060     for (int i = 0; i < SOCKET_COUNT; i++) {
01061         _sock_i[i].open = false;
01062     }
01063 
01064     // Makes possible to reinitialize
01065     _conn_status = NSAPI_STATUS_ERROR_UNSUPPORTED;
01066     _conn_stat_cb();
01067 
01068     tr_debug("_oob_reset(): Reset detected.");
01069 }
01070 
01071 void ESP8266::_oob_busy()
01072 {
01073     char status;
01074     if (_parser.scanf("%c...\n", &status)) {
01075         if (status == 's') {
01076             tr_debug("_oob_busy(): Busy s...");
01077         } else if (status == 'p') {
01078             tr_debug("_oob_busy(): Busy p...");
01079         } else {
01080             tr_error("_oob_busy(): unrecognized busy state '%c...'", status);
01081             MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_EBADMSG), \
01082                        "ESP8266::_oob_busy() unrecognized busy state\n");
01083         }
01084     } else {
01085         MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMSG), \
01086                    "ESP8266::_oob_busy() AT timeout\n");
01087     }
01088     _busy = true;
01089 }
01090 
01091 void ESP8266::_oob_tcp_data_hdlr()
01092 {
01093     int32_t len;
01094 
01095     MBED_ASSERT(_sock_active_id >= 0 && _sock_active_id < 5);
01096 
01097     if (!_parser.scanf("%" SCNd32 ":", &len)) {
01098         return;
01099     }
01100 
01101     if (_parser.read(_sock_i[_sock_active_id].tcp_data, len) == -1) {
01102         return;
01103     }
01104 
01105     _sock_i[_sock_active_id].tcp_data_rcvd = len;
01106 }
01107 
01108 void ESP8266::_oob_scan_results()
01109 {
01110     nsapi_wifi_ap_t ap;
01111 
01112     if (_recv_ap(&ap)) {
01113         if (_scan_r.res && _scan_r.cnt < _scan_r.limit) {
01114             _scan_r.res[_scan_r.cnt] = WiFiAccessPoint(ap);
01115         }
01116 
01117         _scan_r.cnt++;
01118     }
01119 }
01120 
01121 void ESP8266::_oob_connect_err()
01122 {
01123     _fail = false;
01124     _connect_error = 0;
01125 
01126     if (_parser.scanf("%d", &_connect_error) && _parser.scanf("FAIL")) {
01127         _fail = true;
01128         _parser.abort();
01129     }
01130 }
01131 
01132 
01133 void ESP8266::_oob_conn_already()
01134 {
01135     _sock_already = true;
01136     _parser.abort();
01137 }
01138 
01139 void ESP8266::_oob_err()
01140 {
01141     _error = true;
01142     _parser.abort();
01143 }
01144 
01145 void ESP8266::_oob_socket_close_err()
01146 {
01147     if (_error) {
01148         _error = false;
01149     }
01150     _closed = true; // Not possible to pinpoint to a certain socket
01151 }
01152 
01153 void ESP8266::_oob_socket0_closed()
01154 {
01155     static const int id = 0;
01156     _sock_i[id].open = false;
01157     tr_debug("_oob_socket0_closed(): Socket %d closed.", id);
01158 }
01159 
01160 void ESP8266::_oob_socket1_closed()
01161 {
01162     static const int id = 1;
01163     _sock_i[id].open = false;
01164     tr_debug("_oob_socket1_closed(): Socket %d closed.", id);
01165 }
01166 
01167 void ESP8266::_oob_socket2_closed()
01168 {
01169     static const int id = 2;
01170     _sock_i[id].open = false;
01171     tr_debug("_oob_socket2_closed(): Socket %d closed.", id);
01172 }
01173 
01174 void ESP8266::_oob_socket3_closed()
01175 {
01176     static const int id = 3;
01177     _sock_i[id].open = false;
01178     tr_debug("_oob_socket3_closed(): %d closed.", id);
01179 }
01180 
01181 void ESP8266::_oob_socket4_closed()
01182 {
01183     static const int id = 4;
01184     _sock_i[id].open = false;
01185     tr_debug("_oob_socket0_closed(): Socket %d closed.", id);
01186 }
01187 
01188 void ESP8266::_oob_connection_status()
01189 {
01190     char status[13];
01191     if (_parser.recv("%12[^\"]\n", status)) {
01192         if (strcmp(status, "GOT IP\n") == 0) {
01193             _conn_status = NSAPI_STATUS_GLOBAL_UP ;
01194         } else if (strcmp(status, "DISCONNECT\n") == 0) {
01195             if (_disconnect) {
01196                 _conn_status = NSAPI_STATUS_DISCONNECTED ;
01197                 _disconnect = false;
01198             } else {
01199                 _conn_status = NSAPI_STATUS_CONNECTING ;
01200             }
01201         } else if (strcmp(status, "CONNECTED\n") == 0) {
01202             _conn_status = NSAPI_STATUS_CONNECTING ;
01203         } else {
01204             tr_error("_oob_connection_status(): Invalid AT cmd \'%s\' .", status);
01205             MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_EBADMSG), \
01206                        "ESP8266::_oob_connection_status: invalid AT cmd\n");
01207         }
01208     } else {
01209         tr_error("_oob_connection_status(): Network status timeout, disconnecting.");
01210         if (!disconnect()) {
01211             tr_warning("_oob_connection_status(): Driver initiated disconnect failed.");
01212         } else {
01213             tr_debug("_oob_connection_status(): Disconnected.");
01214         }
01215         _conn_status = NSAPI_STATUS_ERROR_UNSUPPORTED;
01216     }
01217 
01218     MBED_ASSERT(_conn_stat_cb);
01219     _conn_stat_cb();
01220 }
01221 
01222 void ESP8266::_oob_ok_received()
01223 {
01224     tr_debug("_oob_ok_received called");
01225     _ok_received = true;
01226 }
01227 
01228 int8_t ESP8266::default_wifi_mode()
01229 {
01230     int8_t mode;
01231 
01232     _smutex.lock();
01233     if (_parser.send("AT+CWMODE_DEF?")
01234             && _parser.recv("+CWMODE_DEF:%hhd", &mode)
01235             && _parser.recv("OK\n")) {
01236         _smutex.unlock();
01237         return mode;
01238     }
01239     _smutex.unlock();
01240 
01241     return 0;
01242 }
01243 
01244 void ESP8266::flush()
01245 {
01246     _smutex.lock();
01247     _parser.flush();
01248     _smutex.unlock();
01249 }
01250 
01251 bool ESP8266::set_default_wifi_mode(const int8_t mode)
01252 {
01253     _smutex.lock();
01254     bool done = _parser.send("AT+CWMODE_DEF=%hhd", mode)
01255                 && _parser.recv("OK\n");
01256     _smutex.unlock();
01257 
01258     return done;
01259 }
01260 
01261 nsapi_connection_status_t ESP8266::connection_status() const
01262 {
01263     return _conn_status;
01264 }
01265 
01266 bool ESP8266::set_country_code_policy (bool track_ap, const char *country_code, int channel_start, int channels)
01267 {
01268     if (!(FW_AT_LEAST_VERSION(_at_v.major, _at_v.minor, _at_v.patch, 0, ESP8266_AT_VERSION_WIFI_SCAN_CHANGE))) {
01269         return true;
01270     }
01271 
01272     int t_ap = track_ap ? 0 : 1;
01273 
01274     _smutex.lock();
01275     bool done = _parser.send("AT+CWCOUNTRY_DEF=%d,\"%s\",%d,%d", t_ap, country_code, channel_start, channels)
01276                 && _parser.recv("OK\n");
01277 
01278     if (!done) {
01279         tr_error("\"AT+CWCOUNTRY_DEF=%d,\"%s\",%d,%d\" - FAIL", t_ap, country_code, channel_start, channels);
01280     }
01281 
01282     done &= _parser.send("AT+CWCOUNTRY_CUR=%d,\"%s\",%d,%d", t_ap, country_code, channel_start, channels)
01283             && _parser.recv("OK\n");
01284 
01285     if (!done) {
01286         tr_error("\"AT+CWCOUNTRY_CUR=%d,\"%s\",%d,%d\" - FAIL", t_ap, country_code, channel_start, channels);
01287     }
01288 
01289     _smutex.unlock();
01290 
01291     return done;
01292 }
01293 
01294 int ESP8266::uart_enable_input(bool enabled)
01295 {
01296     return _serial.enable_input(enabled);
01297 }
01298 
01299 #endif