takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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 #include "ESP8266.h"
00018 #include "mbed_debug.h"
00019 #include "nsapi_types.h"
00020 
00021 #include <cstring>
00022 
00023 #define ESP8266_DEFAULT_BAUD_RATE   115200
00024 #define ESP8266_ALL_SOCKET_IDS      -1
00025 
00026 ESP8266::ESP8266(PinName tx, PinName rx, bool debug)
00027     : _serial(tx, rx, ESP8266_DEFAULT_BAUD_RATE), 
00028       _parser(&_serial), 
00029       _packets(0), 
00030       _packets_end(&_packets),
00031       _connect_error(0),
00032       _fail(false),
00033       _closed(false),
00034       _socket_open(),
00035       _connection_status(NSAPI_STATUS_DISCONNECTED )
00036 {
00037     _serial.set_baud( ESP8266_DEFAULT_BAUD_RATE );
00038     _parser.debug_on(debug);
00039     _parser.set_delimiter("\r\n");
00040     _parser.oob("+IPD", callback(this, &ESP8266::_packet_handler));
00041     //Note: espressif at command document says that this should be +CWJAP_CUR:<error code>
00042     //but seems that at least current version is not sending it
00043     //https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
00044     //Also seems that ERROR is not sent, but FAIL instead
00045     _parser.oob("+CWJAP:", callback(this, &ESP8266::_connect_error_handler));
00046     _parser.oob("0,CLOSED", callback(this, &ESP8266::_oob_socket0_closed_handler));
00047     _parser.oob("1,CLOSED", callback(this, &ESP8266::_oob_socket1_closed_handler));
00048     _parser.oob("2,CLOSED", callback(this, &ESP8266::_oob_socket2_closed_handler));
00049     _parser.oob("3,CLOSED", callback(this, &ESP8266::_oob_socket3_closed_handler));
00050     _parser.oob("4,CLOSED", callback(this, &ESP8266::_oob_socket4_closed_handler));
00051     _parser.oob("WIFI ", callback(this, &ESP8266::_connection_status_handler));
00052     _parser.oob("UNLINK", callback(this, &ESP8266::_oob_socket_close_error));
00053 }
00054 
00055 int ESP8266::get_firmware_version()
00056 {
00057     int version;
00058 
00059     _smutex.lock();
00060     bool done = _parser.send("AT+GMR")
00061            && _parser.recv("SDK version:%d", &version)
00062            && _parser.recv("OK\n");
00063     _smutex.unlock();
00064 
00065     if(done) {
00066         return version;
00067     } else { 
00068         // Older firmware versions do not prefix the version with "SDK version: "
00069         return -1;
00070     }
00071 }
00072 
00073 bool ESP8266::startup(int mode)
00074 {
00075     if (!(mode == WIFIMODE_STATION || mode == WIFIMODE_SOFTAP
00076         || mode == WIFIMODE_STATION_SOFTAP)) {
00077         return false;
00078     }
00079 
00080     _smutex.lock();
00081     setTimeout(ESP8266_CONNECT_TIMEOUT);
00082     bool done = _parser.send("AT+CWMODE_CUR=%d", mode)
00083             && _parser.recv("OK\n")
00084             &&_parser.send("AT+CIPMUX=1")
00085             && _parser.recv("OK\n");
00086     setTimeout(); //Restore default
00087     _smutex.unlock();
00088 
00089     return done;
00090 }
00091 
00092 bool ESP8266::reset(void)
00093 {
00094     _smutex.lock();
00095     setTimeout(ESP8266_CONNECT_TIMEOUT);
00096 
00097     for (int i = 0; i < 2; i++) {
00098         if (_parser.send("AT+RST")
00099             && _parser.recv("OK\n")
00100             && _parser.recv("ready")) {
00101             _clear_socket_packets(ESP8266_ALL_SOCKET_IDS);
00102             _smutex.unlock();
00103             return true;
00104         }
00105     }
00106     setTimeout();
00107     _smutex.unlock();
00108 
00109     return false;
00110 }
00111 
00112 bool ESP8266::dhcp(bool enabled, int mode)
00113 {
00114     //only 3 valid modes
00115     if (mode < 0 || mode > 2) {
00116         return false;
00117     }
00118 
00119     _smutex.lock();
00120     bool done = _parser.send("AT+CWDHCP_CUR=%d,%d", mode, enabled?1:0)
00121                 && _parser.recv("OK\n");
00122     _smutex.unlock();
00123 
00124     return done;
00125 }
00126 
00127 nsapi_error_t ESP8266::connect(const char *ap, const char *passPhrase)
00128 {
00129     _smutex.lock();
00130     setTimeout(ESP8266_CONNECT_TIMEOUT);
00131     _connection_status = NSAPI_STATUS_CONNECTING ;
00132     if(_connection_status_cb)
00133         _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , _connection_status);
00134 
00135     _parser.send("AT+CWJAP_CUR=\"%s\",\"%s\"", ap, passPhrase);
00136     if (!_parser.recv("OK\n")) {
00137         if (_fail) {
00138             _smutex.unlock();
00139             nsapi_error_t ret;
00140             if (_connect_error == 1)
00141                 ret = NSAPI_ERROR_CONNECTION_TIMEOUT ;
00142             else if (_connect_error == 2)
00143                 ret = NSAPI_ERROR_AUTH_FAILURE ;
00144             else if (_connect_error == 3)
00145                 ret = NSAPI_ERROR_NO_SSID ;
00146             else
00147                 ret = NSAPI_ERROR_NO_CONNECTION ;
00148 
00149             _fail = false;
00150             _connect_error = 0;
00151             return ret;
00152         }
00153     }
00154     setTimeout();
00155     _smutex.unlock();
00156 
00157     return NSAPI_ERROR_OK ;
00158 }
00159 
00160 bool ESP8266::disconnect(void)
00161 {
00162     _smutex.lock();
00163     bool done = _parser.send("AT+CWQAP") && _parser.recv("OK\n");
00164     _smutex.unlock();
00165 
00166     return done;
00167 }
00168 
00169 const char *ESP8266::getIPAddress(void)
00170 {
00171     _smutex.lock();
00172     setTimeout(ESP8266_CONNECT_TIMEOUT);
00173     if (!(_parser.send("AT+CIFSR")
00174         && _parser.recv("+CIFSR:STAIP,\"%15[^\"]\"", _ip_buffer)
00175         && _parser.recv("OK\n"))) {
00176         _smutex.unlock();
00177         return 0;
00178     }
00179     setTimeout();
00180     _smutex.unlock();
00181 
00182     return _ip_buffer;
00183 }
00184 
00185 const char *ESP8266::getMACAddress(void)
00186 {
00187     _smutex.lock();
00188     if (!(_parser.send("AT+CIFSR")
00189         && _parser.recv("+CIFSR:STAMAC,\"%17[^\"]\"", _mac_buffer)
00190         && _parser.recv("OK\n"))) {
00191         _smutex.unlock();
00192         return 0;
00193     }
00194     _smutex.unlock();
00195 
00196     return _mac_buffer;
00197 }
00198 
00199 const char *ESP8266::getGateway()
00200 {
00201     _smutex.lock();
00202     if (!(_parser.send("AT+CIPSTA_CUR?")
00203         && _parser.recv("+CIPSTA_CUR:gateway:\"%15[^\"]\"", _gateway_buffer)
00204         && _parser.recv("OK\n"))) {
00205         _smutex.unlock();
00206         return 0;
00207     }
00208     _smutex.unlock();
00209 
00210     return _gateway_buffer;
00211 }
00212 
00213 const char *ESP8266::getNetmask()
00214 {
00215     _smutex.lock();
00216     if (!(_parser.send("AT+CIPSTA_CUR?")
00217         && _parser.recv("+CIPSTA_CUR:netmask:\"%15[^\"]\"", _netmask_buffer)
00218         && _parser.recv("OK\n"))) {
00219         _smutex.unlock();
00220         return 0;
00221     }
00222     _smutex.unlock();
00223 
00224     return _netmask_buffer;
00225 }
00226 
00227 int8_t ESP8266::getRSSI()
00228 {
00229     int8_t rssi;
00230     char bssid[18];
00231 
00232     _smutex.lock();
00233     setTimeout(ESP8266_CONNECT_TIMEOUT);
00234     if (!(_parser.send("AT+CWJAP_CUR?")
00235         && _parser.recv("+CWJAP_CUR:\"%*[^\"]\",\"%17[^\"]\"", bssid)
00236         && _parser.recv("OK\n"))) {
00237        _smutex.unlock();
00238         return 0;
00239     }
00240     setTimeout();
00241    _smutex.unlock();
00242 
00243    _smutex.lock();
00244    setTimeout(ESP8266_CONNECT_TIMEOUT);
00245     if (!(_parser.send("AT+CWLAP=\"\",\"%s\",", bssid)
00246         && _parser.recv("+CWLAP:(%*d,\"%*[^\"]\",%hhd,", &rssi)
00247         && _parser.recv("OK\n"))) {
00248         _smutex.unlock();
00249         return 0;
00250     }
00251     setTimeout();
00252     _smutex.unlock();
00253 
00254     return rssi;
00255 }
00256 
00257 int ESP8266::scan(WiFiAccessPoint *res, unsigned limit)
00258 {
00259     unsigned cnt = 0;
00260     nsapi_wifi_ap_t ap;
00261 
00262     _smutex.lock();
00263     setTimeout(ESP8266_CONNECT_TIMEOUT);
00264 
00265     if (!_parser.send("AT+CWLAP")) {
00266         _smutex.unlock();
00267         return NSAPI_ERROR_DEVICE_ERROR ;
00268     }
00269 
00270     while (recv_ap(&ap)) {
00271         if (cnt < limit) {
00272             res[cnt] = WiFiAccessPoint(ap);
00273         }
00274 
00275         cnt++;
00276         if (limit != 0 && cnt >= limit) {
00277             break;
00278         }
00279     }
00280     setTimeout();
00281     _smutex.unlock();
00282 
00283     return cnt;
00284 }
00285 
00286 nsapi_error_t ESP8266::open_udp(int id, const char* addr, int port, int local_port)
00287 {
00288     static const char *type = "UDP";
00289     bool done = false;
00290 
00291     if (id >= SOCKET_COUNT) {
00292         return NSAPI_ERROR_PARAMETER ;
00293     } else if (_socket_open[id]) {
00294         return NSAPI_ERROR_IS_CONNECTED ;
00295     }
00296 
00297     _smutex.lock();
00298     if(local_port) {
00299         done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, local_port)
00300                 && _parser.recv("OK\n");
00301     } else {
00302         done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port)
00303                && _parser.recv("OK\n");
00304     }
00305 
00306     if (done) {
00307         _socket_open[id] = 1;
00308     }
00309 
00310     _clear_socket_packets(id);
00311 
00312     _smutex.unlock();
00313 
00314     return done ? NSAPI_ERROR_OK  : NSAPI_ERROR_DEVICE_ERROR ;
00315 }
00316 
00317 bool ESP8266::open_tcp(int id, const char* addr, int port, int keepalive)
00318 {
00319     static const char *type = "TCP";
00320     bool done = false;
00321 
00322     if (id >= SOCKET_COUNT || _socket_open[id]) {
00323         return false;
00324     }
00325 
00326     _smutex.lock();
00327     if(keepalive) {
00328         done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, keepalive)
00329                 && _parser.recv("OK\n");
00330     } else {
00331         done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port)
00332                && _parser.recv("OK\n");
00333     }
00334 
00335     if (done) {
00336         _socket_open[id] = 1;
00337     }
00338 
00339     _clear_socket_packets(id);
00340 
00341     _smutex.unlock();
00342 
00343     return done;
00344 }
00345 
00346 bool ESP8266::dns_lookup(const char* name, char* ip)
00347 {
00348     _smutex.lock();
00349     bool done = _parser.send("AT+CIPDOMAIN=\"%s\"", name) && _parser.recv("+CIPDOMAIN:%s%*[\r]%*[\n]", ip);
00350     _smutex.unlock();
00351 
00352     return done;
00353 }
00354 
00355 nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
00356 {
00357     //May take a second try if device is busy
00358     for (unsigned i = 0; i < 2; i++) {
00359         _smutex.lock();
00360         setTimeout(ESP8266_SEND_TIMEOUT);
00361         if (_parser.send("AT+CIPSEND=%d,%lu", id, amount)
00362             && _parser.recv(">")
00363             && _parser.write((char*)data, (int)amount) >= 0) {
00364             while (_parser.process_oob()); // multiple sends in a row require this
00365             _smutex.unlock();
00366             return NSAPI_ERROR_OK ;
00367         }
00368         setTimeout();
00369         _smutex.unlock();
00370     }
00371 
00372     return NSAPI_ERROR_DEVICE_ERROR ;
00373 }
00374 
00375 void ESP8266::_packet_handler()
00376 {
00377     int id;
00378     int amount;
00379 
00380     // parse out the packet
00381     if (!_parser.recv(",%d,%d:", &id, &amount)) {
00382         return;
00383     }
00384 
00385     struct packet *packet = (struct packet*)malloc(
00386             sizeof(struct packet) + amount);
00387     if (!packet) {
00388         debug("ESP8266: could not allocate memory for RX data\n");
00389         return;
00390     }
00391 
00392     packet->id = id;
00393     packet->len = amount;
00394     packet->next = 0;
00395 
00396     if (_parser.read((char*)(packet + 1), amount) < amount) {
00397         free(packet);
00398         return;
00399     }
00400 
00401     // append to packet list
00402     *_packets_end = packet;
00403     _packets_end = &packet->next;
00404 }
00405 
00406 int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout)
00407 {
00408     _smutex.lock();
00409     setTimeout(timeout);
00410 
00411     // Poll for inbound packets
00412     while (_parser.process_oob()) {
00413     }
00414 
00415     setTimeout();
00416 
00417     // check if any packets are ready for us
00418     for (struct packet **p = &_packets; *p; p = &(*p)->next) {
00419         if ((*p)->id == id) {
00420             struct packet *q = *p;
00421 
00422             if (q->len <= amount) { // Return and remove full packet
00423                 memcpy(data, q+1, q->len);
00424 
00425                 if (_packets_end == &(*p)->next) {
00426                     _packets_end = p;
00427                 }
00428                 *p = (*p)->next;
00429                 _smutex.unlock();
00430 
00431                 uint32_t len = q->len;
00432                 free(q);
00433                 return len;
00434             } else { // return only partial packet
00435                 memcpy(data, q+1, amount);
00436 
00437                 q->len -= amount;
00438                 memmove(q+1, (uint8_t*)(q+1) + amount, q->len);
00439 
00440                 _smutex.unlock();
00441                 return amount;
00442             }
00443         }
00444     }
00445     if(!_socket_open[id]) {
00446         _smutex.unlock();
00447         return 0;
00448     }
00449     _smutex.unlock();
00450 
00451     return NSAPI_ERROR_WOULD_BLOCK ;
00452 }
00453 
00454 int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount, uint32_t timeout)
00455 {
00456     _smutex.lock();
00457     setTimeout(timeout);
00458 
00459     // Poll for inbound packets
00460     while (_parser.process_oob()) {
00461     }
00462 
00463     setTimeout();
00464 
00465     // check if any packets are ready for us
00466     for (struct packet **p = &_packets; *p; p = &(*p)->next) {
00467         if ((*p)->id == id) {
00468             struct packet *q = *p;
00469 
00470             // Return and remove packet (truncated if necessary)
00471             uint32_t len = q->len < amount ? q->len : amount;
00472             memcpy(data, q+1, len);
00473 
00474             if (_packets_end == &(*p)->next) {
00475                 _packets_end = p;
00476             }
00477             *p = (*p)->next;
00478             _smutex.unlock();
00479 
00480             free(q);
00481             return len;
00482         }
00483     }
00484     _smutex.unlock();
00485 
00486     return NSAPI_ERROR_WOULD_BLOCK ;
00487 }
00488 
00489 void ESP8266::_clear_socket_packets(int id)
00490 {
00491     struct packet **p = &_packets;
00492 
00493     while (*p) {
00494         if ((*p)->id == id || id == ESP8266_ALL_SOCKET_IDS) {
00495             struct packet *q = *p;
00496 
00497             if (_packets_end == &(*p)->next) {
00498                 _packets_end = p; // Set last packet next field/_packets
00499             }
00500             *p = (*p)->next;
00501 
00502             free(q);
00503         } else {
00504             // Point to last packet next field
00505             p = &(*p)->next;
00506         }
00507     }
00508 }
00509 
00510 bool ESP8266::close(int id)
00511 {
00512     //May take a second try if device is busy
00513     for (unsigned i = 0; i < 2; i++) {
00514         _smutex.lock();
00515         if (_parser.send("AT+CIPCLOSE=%d", id)) {
00516             if (!_parser.recv("OK\n")) {
00517                 if (_closed) { // UNLINK ERROR
00518                     _closed = false;
00519                     _socket_open[id] = 0;
00520                     _clear_socket_packets(id);
00521                     _smutex.unlock();
00522                     // ESP8266 has a habit that it might close a socket on its own.
00523                     //debug("ESP8266: socket %d already closed when calling close\n", id);
00524                     return true;
00525                 }
00526             } else {
00527                 _clear_socket_packets(id);
00528                 _smutex.unlock();
00529                 return true;
00530             }
00531         }
00532         _smutex.unlock();
00533     }
00534 
00535     return false;
00536 }
00537 
00538 void ESP8266::setTimeout(uint32_t timeout_ms)
00539 {
00540     _parser.set_timeout(timeout_ms);
00541 }
00542 
00543 bool ESP8266::readable()
00544 {
00545     return _serial.FileHandle::readable();
00546 }
00547 
00548 bool ESP8266::writeable()
00549 {
00550     return _serial.FileHandle::writable();
00551 }
00552 
00553 void ESP8266::sigio(Callback<void()> func)
00554 {
00555     _serial.sigio(func);
00556 }
00557 
00558 void ESP8266::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
00559 {
00560     _connection_status_cb = status_cb;
00561 }
00562 
00563 bool ESP8266::recv_ap(nsapi_wifi_ap_t *ap)
00564 {
00565     int sec;
00566     int dummy;
00567     bool ret = _parser.recv("+CWLAP:(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d)\n",
00568             &sec,
00569             ap->ssid,
00570             &ap->rssi,
00571             &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5],
00572             &ap->channel,
00573             &dummy,
00574             &dummy);
00575 
00576     ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN ;
00577 
00578     return ret;
00579 }
00580 
00581 void ESP8266::_connect_error_handler()
00582 {
00583     _fail = false;
00584     _connect_error = 0;
00585 
00586     if (_parser.recv("%d", &_connect_error) && _parser.recv("FAIL")) {
00587         _fail = true;
00588         _parser.abort();
00589     }
00590 }
00591 
00592 void ESP8266::_oob_socket_close_error()
00593 {
00594     if (_parser.recv("ERROR\n")) {
00595         _closed = true; // Not possible to pinpoint to a certain socket
00596         _parser.abort();
00597     }
00598 }
00599 
00600 void ESP8266::_oob_socket0_closed_handler()
00601 {
00602     _socket_open[0] = 0;
00603 }
00604 
00605 void ESP8266::_oob_socket1_closed_handler()
00606 {
00607     _socket_open[1] = 0;
00608 }
00609 
00610 void ESP8266::_oob_socket2_closed_handler()
00611 {
00612     _socket_open[2] = 0;
00613 }
00614 
00615 void ESP8266::_oob_socket3_closed_handler()
00616 {
00617     _socket_open[3] = 0;
00618 }
00619 
00620 void ESP8266::_oob_socket4_closed_handler()
00621 {
00622     _socket_open[4] = 0;
00623 }
00624 
00625 void ESP8266::_connection_status_handler()
00626 {
00627     char status[13];
00628     if (_parser.recv("%12[^\"]\n", status)) {
00629         if (strcmp(status, "GOT IP\n") == 0)
00630             _connection_status = NSAPI_STATUS_GLOBAL_UP ;
00631         else if (strcmp(status, "DISCONNECT\n") == 0)
00632             _connection_status = NSAPI_STATUS_DISCONNECTED ;
00633         else
00634             return;
00635 
00636         if(_connection_status_cb)
00637             _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , _connection_status);
00638     }
00639 }
00640 
00641 int8_t ESP8266::get_default_wifi_mode()
00642 {
00643     int8_t mode;
00644 
00645     _smutex.lock();
00646     if (_parser.send("AT+CWMODE_DEF?")
00647         && _parser.recv("+CWMODE_DEF:%hhd", &mode)
00648         && _parser.recv("OK\n")) {
00649         _smutex.unlock();
00650         return mode;
00651     }
00652     _smutex.unlock();
00653 
00654     return 0;
00655 }
00656 
00657 bool ESP8266::set_default_wifi_mode(const int8_t mode)
00658 {
00659     _smutex.lock();
00660     bool done = _parser.send("AT+CWMODE_DEF=%hhd", mode)
00661                 && _parser.recv("OK\n");
00662     _smutex.unlock();
00663 
00664     return done;
00665 }
00666 
00667 nsapi_connection_status_t ESP8266::get_connection_status() const
00668 {
00669     return _connection_status;
00670 }