Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
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
Generated on Tue Jul 12 2022 13:54:18 by
