From dac35fd80871c11e55aefb1b7aaabddd343d68fc Mon Sep 17 00:00:00 2001 From: Chris Snow Date: Fri, 20 Sep 2024 19:15:54 +0100 Subject: [PATCH] Add ESP-32 driver --- connectivity/drivers/wifi/CMakeLists.txt | 5 + .../COMPONENT_ESPRESSIF_ESP32/CMakeLists.txt | 16 + .../COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.cpp | 2077 +++++++++++++++++ .../COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.h | 767 ++++++ .../ESP32Interface.cpp | 376 +++ .../ESP32Interface.h | 278 +++ .../ESP32InterfaceAP.cpp | 253 ++ .../ESP32InterfaceAP.h | 259 ++ .../COMPONENT_ESPRESSIF_ESP32/ESP32Stack.cpp | 332 +++ .../COMPONENT_ESPRESSIF_ESP32/ESP32Stack.h | 179 ++ .../wifi/COMPONENT_ESPRESSIF_ESP32/README.md | 12 + .../COMPONENT_ESPRESSIF_ESP32/mbed_lib.json | 58 + targets/drivers.json5 | 4 + 13 files changed, 4616 insertions(+) create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/CMakeLists.txt create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.cpp create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.h create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.cpp create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.h create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.cpp create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.h create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.cpp create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.h create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/README.md create mode 100644 connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/mbed_lib.json diff --git a/connectivity/drivers/wifi/CMakeLists.txt b/connectivity/drivers/wifi/CMakeLists.txt index f729ed603d0..8b605448925 100644 --- a/connectivity/drivers/wifi/CMakeLists.txt +++ b/connectivity/drivers/wifi/CMakeLists.txt @@ -36,3 +36,8 @@ if("COMPONENT_ESPRESSIF_ESP8266=1" IN_LIST MBED_TARGET_DEFINITIONS) add_subdirectory(COMPONENT_ESPRESSIF_ESP8266) endif() +if("COMPONENT_ESPRESSIF_ESP32=1" IN_LIST MBED_TARGET_DEFINITIONS) + create_mbed_wifi_target() + add_subdirectory(COMPONENT_ESPRESSIF_ESP32) +endif() + diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/CMakeLists.txt b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/CMakeLists.txt new file mode 100644 index 00000000000..7c18dac6399 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2020 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +target_sources(mbed-wifi + PRIVATE + ./ESP32Interface.cpp + ./ESP32InterfaceAP.cpp + ./ESP32Stack.cpp + ./ESP32/ESP32.cpp +) + +target_include_directories(mbed-wifi + PUBLIC + ./ + ./ESP32/ +) diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.cpp b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.cpp new file mode 100644 index 00000000000..99843861941 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.cpp @@ -0,0 +1,2077 @@ +/* ESP32 Example + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if DEVICE_SERIAL && defined(MBED_CONF_EVENTS_PRESENT) && defined(MBED_CONF_NSAPI_PRESENT) && defined(MBED_CONF_RTOS_PRESENT) +#include "ESP32.h" +#include "platform/mbed_interface.h" + +#define ESP32_DEFAULT_BAUD_RATE 115200 +#define ESP32_ALL_SOCKET_IDS -1 + +using namespace mbed; +using namespace rtos; +using namespace std::chrono; +using std::milli; + +ESP32 * ESP32::instESP32 = NULL; + +ESP32 * ESP32::getESP32Inst(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) +{ + if (instESP32 == NULL) { + instESP32 = new ESP32(en, io0, tx, rx, debug, rts, cts, baudrate); + } else { + if (debug) { + instESP32->debugOn(debug); + } + } + return instESP32; +} + +ESP32 * ESP32::getESP32Inst(bool debug) +{ + return getESP32Inst(MBED_CONF_ESP32_WIFI_EN, MBED_CONF_ESP32_WIFI_IO0, + MBED_CONF_ESP32_WIFI_TX, MBED_CONF_ESP32_WIFI_RX, debug, + MBED_CONF_ESP32_WIFI_RTS, MBED_CONF_ESP32_WIFI_CTS, + MBED_CONF_ESP32_WIFI_BAUDRATE); +} + +ESP32::ESP32(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) + : _p_wifi_en(NULL), _p_wifi_io0(NULL), _init_end_common(false), _init_end_wifi(false) + , _serial(tx, rx, ESP32_DEFAULT_BAUD_RATE), _parser(&_serial, "\r\n") + , _packets(0), _packets_end(&_packets) + , _id_bits(0), _server_act(false) + , _wifi_status(STATUS_DISCONNECTED) + , _wifi_status_cb(NULL), _at_version(0) +#if defined(TARGET_ESP32AT_BLE) + , _ble_conn_cb(NULL), _ble_disconn_cb(NULL), _ble_write_cb(NULL), _ble_scan_cb(NULL) + , _ble_role(INIT_SERVER_ROLE), _init_end_ble(false) + , _primary_service_idx(0), _discovers_char_idx(0), _discovers_desc_idx(0) +#endif /* TARGET_ESP32AT_BLE */ +{ + if ((int)en != NC) { + _p_wifi_en = new DigitalOut(en); + } + if ((int)io0 != NC) { + _p_wifi_io0 = new DigitalOut(io0); + } + + _wifi_mode = WIFIMODE_STATION; + _baudrate = baudrate; + memset(_ids, 0, sizeof(_ids)); + memset(_cbs, 0, sizeof(_cbs)); +#if defined(TARGET_ESP32AT_BLE) + memset(&_cbs_ble, 0, sizeof(_cbs_ble)); +#endif /* TARGET_ESP32AT_BLE */ + + _rts = rts; + _cts = cts; + + if ((_rts != NC) && (_cts != NC)) { + _flow_control = 3; + } else if (_rts != NC) { + _flow_control = 1; + } else if (_cts != NC) { + _flow_control = 2; + } else { + _flow_control = 0; + } + + _serial.set_baud(ESP32_DEFAULT_BAUD_RATE); + debugOn(debug); + + _parser.oob("+IPD", callback(this, &ESP32::_packet_handler)); + _parser.oob("0,CONNECT", callback(this, &ESP32::_connect_handler_0)); + _parser.oob("1,CONNECT", callback(this, &ESP32::_connect_handler_1)); + _parser.oob("2,CONNECT", callback(this, &ESP32::_connect_handler_2)); + _parser.oob("3,CONNECT", callback(this, &ESP32::_connect_handler_3)); + _parser.oob("4,CONNECT", callback(this, &ESP32::_connect_handler_4)); + _parser.oob("0,CLOSED", callback(this, &ESP32::_closed_handler_0)); + _parser.oob("1,CLOSED", callback(this, &ESP32::_closed_handler_1)); + _parser.oob("2,CLOSED", callback(this, &ESP32::_closed_handler_2)); + _parser.oob("3,CLOSED", callback(this, &ESP32::_closed_handler_3)); + _parser.oob("4,CLOSED", callback(this, &ESP32::_closed_handler_4)); + _parser.oob("WIFI ", callback(this, &ESP32::_connection_status_handler)); +#if defined(TARGET_ESP32AT_BLE) + _parser.oob("+BLECONN:", callback(this, &ESP32::_ble_conn)); + _parser.oob("+BLEDISCONN:", callback(this, &ESP32::_ble_disconn)); + _parser.oob("+WRITE:", callback(this, &ESP32::_ble_write)); + _parser.oob("+BLESCAN:", callback(this, &ESP32::_ble_scan)); + _parser.oob("+BLEGATTCPRIMSRV:", callback(this, &ESP32::_ble_primsrv)); + _parser.oob("+BLEGATTCCHAR:", callback(this, &ESP32::_ble_discovers_char)); +#endif /* TARGET_ESP32AT_BLE */ + + _serial.sigio(Callback(this, &ESP32::event)); + + setTimeout(); +} + +void ESP32::debugOn(bool debug) +{ + _parser.debug_on((debug) ? 1 : 0); +} + +bool ESP32::get_version_info(char * ver_info, int buf_size) +{ + bool ret; + int idx = 0; + const char key_word[6] = "\nOK\r\n"; + char wk_buf[5]; + int wk_idx; + char c; + + if (ver_info == NULL) { + return false; + } + _smutex.lock(); + _startup_common(); + setTimeout(500); + ret = _parser.send("AT+GMR"); + if (!ret) { + setTimeout(); + _smutex.unlock(); + return false; + } + for (int i = 0; i < 10; i++) { + _parser.getc(); // dummy read + } + while (idx < buf_size) { + wk_idx = 0; + for (int i = 0; i < 5; i++) { + c = _parser.getc(); + wk_buf[wk_idx++] = c; + if (c != key_word[i]) { + break; + } + } + if (wk_idx >= 5) { + break; + } + for (int i = 0; (i < wk_idx) && (idx < buf_size); i++) { + ver_info[idx++] = wk_buf[i]; + } + } + setTimeout(); + _smutex.unlock(); + + ver_info[idx] = '\0'; + while (idx > 0) { + idx--; + if (ver_info[idx] != '\r' && ver_info[idx] != '\n') { + break; + } + ver_info[idx] = '\0'; + } + + return true; +} + +void ESP32::_startup_common() +{ + if (_init_end_common) { + return; + } + + _serial.set_baud(ESP32_DEFAULT_BAUD_RATE); + if (_p_wifi_io0 != NULL) { + _p_wifi_io0->write(1); + } + if (_p_wifi_en != NULL) { + _p_wifi_en->write(0); + ThisThread::sleep_for(10ms); + _p_wifi_en->write(1); + _parser.recv("ready"); + } else { + setTimeout(100); + _parser.recv("ready"); + } + + reset(); + + _init_end_common = true; + + return; +} + +bool ESP32::_startup_wifi() +{ + _startup_common(); + + if (_init_end_wifi) { + return true; + } + + bool success = _parser.send("AT+CWMODE=%d", _wifi_mode) + && _parser.recv("OK") + && _parser.send("AT+CIPMUX=1") + && _parser.recv("OK") + && _parser.send("AT+CWAUTOCONN=0") + && _parser.recv("OK") + && _parser.send("AT+CWQAP") + && _parser.recv("OK"); + if (success) { + _init_end_wifi = true; + } + + return success; +} + +bool ESP32::restart() +{ + bool success = true;; + bool ret; + + _smutex.lock(); + setTimeout(); + reset(); + if (_init_end_wifi) { + ret = _parser.send("AT+CWMODE=%d", _wifi_mode) + && _parser.recv("OK") + && _parser.send("AT+CIPMUX=1") + && _parser.recv("OK"); + if (!ret) { + success = false; + } + } +#if defined(TARGET_ESP32AT_BLE) + if (_init_end_ble) { + ret = _parser.send("AT+BLEINIT=%d", _ble_role) + && _parser.recv("OK"); + if (!ret) { + success = false; + } + } +#endif + _smutex.unlock(); + + return success; +} + +bool ESP32::set_mode(int mode) +{ + //only 3 valid modes + if (mode < 1 || mode > 3) { + return false; + } + if (_wifi_mode != mode) { + _wifi_mode = mode; + if (_init_end_wifi) { + return restart(); + } + } + return true; +} + +bool ESP32::cre_server(int port) +{ + if (_server_act) { + return false; + } + _smutex.lock(); + _startup_wifi(); + if (!(_parser.send("AT+CIPSERVER=1,%d", port) + && _parser.recv("OK"))) { + _smutex.unlock(); + return false; + } + _server_act = true; + _smutex.unlock(); + return true; +} + +bool ESP32::del_server() +{ + _smutex.lock(); + _startup_wifi(); + if (!(_parser.send("AT+CIPSERVER=0") + && _parser.recv("OK"))) { + _smutex.unlock(); + return false; + } + _server_act = false; + _smutex.unlock(); + return true; +} + +void ESP32::socket_handler(bool connect, int id) +{ + _cbs[id].Notified = 0; + if (connect) { + _id_bits |= (1 << id); + if (_server_act) { + _accept_id.push_back(id); + } + } else { + _id_bits &= ~(1 << id); + if (_server_act) { + for (size_t i = 0; i < _accept_id.size(); i++) { + if (id == _accept_id[i]) { + _accept_id.erase(_accept_id.begin() + i); + } + } + } + } +} + +bool ESP32::accept(int * p_id) +{ + bool ret = false; + + while (!ret) { + if (!_server_act) { + break; + } + + _smutex.lock(); + _startup_wifi(); + if (!_accept_id.empty()) { + ret = true; + } else { + _parser.process_oob(); // Poll for inbound packets + if (!_accept_id.empty()) { + ret = true; + } + } + if (ret) { + *p_id = _accept_id[0]; + _accept_id.erase(_accept_id.begin()); + } + _smutex.unlock(); + if (!ret) { + ThisThread::sleep_for(5ms); + } + } + + return ret; +} + +bool ESP32::reset(void) +{ + for (int i = 0; i < 2; i++) { + if (_parser.send("AT+RST") + && _parser.recv("OK")) { + _serial.set_baud(ESP32_DEFAULT_BAUD_RATE); +#if DEVICE_SERIAL_FC + _serial.set_flow_control(SerialBase::Disabled); +#endif + _parser.recv("ready"); + _clear_socket_packets(ESP32_ALL_SOCKET_IDS); + + if (_parser.send("AT+UART_CUR=%d,8,1,0,%d", _baudrate, _flow_control) + && _parser.recv("OK")) { + _serial.set_baud(_baudrate); +#if DEVICE_SERIAL_FC + switch (_flow_control) { + case 1: + _serial.set_flow_control(SerialBase::RTS, _rts); + break; + case 2: + _serial.set_flow_control(SerialBase::CTS, _cts); + break; + case 3: + _serial.set_flow_control(SerialBase::RTSCTS, _rts, _cts); + break; + case 0: + default: + // do nothing + break; + } +#endif + } + + ThisThread::sleep_for(5ms); + + uint8_t wk_ver[4+1]; /* It needs 1 byte extra. */ + + if (_parser.send("AT+GMR") + && _parser.recv("AT version:%hhx.%hhx.%hhx.%hhx", &wk_ver[0], &wk_ver[1], &wk_ver[2], &wk_ver[3]) + && _parser.recv("OK") + ) { + _at_version = (wk_ver[0] << 24) + | (wk_ver[1] << 16) + | (wk_ver[2] << 8) + | (wk_ver[3] << 0); + } + + return true; + } + } + + return false; +} + +bool ESP32::dhcp(bool enabled, int mode) +{ + //only 3 valid modes + if (mode < 0 || mode > 2) { + return false; + } + + _smutex.lock(); + _startup_wifi(); + bool done = _parser.send("AT+CWDHCP=%d,%d", enabled?1:0, mode) + && _parser.recv("OK"); + _smutex.unlock(); + + return done; +} + +bool ESP32::connect(const char *ap, const char *passPhrase) +{ + bool ret; + + _wifi_status = STATUS_DISCONNECTED; + + _smutex.lock(); + _startup_wifi(); + + setTimeout(ESP32_CONNECT_TIMEOUT); + ret = _parser.send("AT+CWJAP=\"%s\",\"%s\"", ap, passPhrase) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + return ret; +} + +bool ESP32::config_soft_ap(const char *ap, const char *passPhrase, uint8_t chl, uint8_t ecn) +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CWSAP=\"%s\",\"%s\",%hhu,%hhu", ap, passPhrase, chl, ecn) + && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +bool ESP32::get_ssid(char *ap) +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CWJAP?") + && _parser.recv("+CWJAP:\"%33[^\"]\",", ap) + && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +bool ESP32::disconnect(void) +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CWQAP") && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +const char *ESP32::getIPAddress(void) +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:STAIP,\"%15[^\"]\"", _ip_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + if (!ret) { + return 0; + } + return _ip_buffer; +} + +const char *ESP32::getIPAddress_ap(void) +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:APIP,\"%15[^\"]\"", _ip_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + if (!ret) { + return 0; + } + return _ip_buffer_ap; +} + +const char *ESP32::getMACAddress(void) +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:STAMAC,\"%17[^\"]\"", _mac_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _mac_buffer; +} + +const char *ESP32::getMACAddress_ap(void) +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:APMAC,\"%17[^\"]\"", _mac_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _mac_buffer_ap; +} + +const char *ESP32::getGateway() +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIPSTA?") + && _parser.recv("+CIPSTA:gateway:\"%15[^\"]\"", _gateway_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _gateway_buffer; +} + +const char *ESP32::getGateway_ap() +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIPAP?") + && _parser.recv("+CIPAP:gateway:\"%15[^\"]\"", _gateway_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _gateway_buffer_ap; +} + +const char *ESP32::getNetmask() +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIPSTA?") + && _parser.recv("+CIPSTA:netmask:\"%15[^\"]\"", _netmask_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _netmask_buffer; +} + +const char *ESP32::getNetmask_ap() +{ + bool ret; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CIPAP?") + && _parser.recv("+CIPAP:netmask:\"%15[^\"]\"", _netmask_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _netmask_buffer_ap; +} + +int8_t ESP32::getRSSI() +{ + bool ret; + int8_t rssi[2]; /* It needs 1 byte extra. */ + char ssid[33]; + char bssid[18]; + + _smutex.lock(); + _startup_wifi(); + ret = _parser.send("AT+CWJAP?") + && _parser.recv("+CWJAP:\"%32[^\"]\",\"%17[^\"]\"", ssid, bssid) + && _parser.recv("OK"); + if (!ret) { + _smutex.unlock(); + return 0; + } + + ret = _parser.send("AT+CWLAP=\"%s\",\"%s\"", ssid, bssid) + && _parser.recv("+CWLAP:(%*d,\"%*[^\"]\",%hhd,", &rssi[0]) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + + return rssi[0]; +} + +int ESP32::scan(WiFiAccessPoint *res, unsigned limit) +{ + unsigned cnt = 0; + nsapi_wifi_ap_t ap; + + if (!_init_end_wifi) { + _smutex.lock(); + _startup_wifi(); + _smutex.unlock(); + ThisThread::sleep_for(1500ms); + } + + _smutex.lock(); + setTimeout(5000); + if (!_parser.send("AT+CWLAP")) { + _smutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } + + while (recv_ap(&ap)) { + if (cnt < limit) { + res[cnt] = WiFiAccessPoint(ap); + } + + cnt++; + if ((limit != 0) && (cnt >= limit)) { + setTimeout(10); + _parser.recv("OK"); + break; + } + setTimeout(500); + } + setTimeout(); + _smutex.unlock(); + + return cnt; +} + +bool ESP32::isConnected(void) +{ + return getIPAddress() != 0; +} + +bool ESP32::open(const char *type, int id, const char* addr, int port, int opt) +{ + bool ret; + + if (id >= SOCKET_COUNT) { + return false; + } + _cbs[id].Notified = 0; + + _smutex.lock(); + _startup_wifi(); + setTimeout(ESP32_SEND_TIMEOUT); + if (opt != 0) { + ret = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, opt) + && _parser.recv("OK"); + } else { + ret = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port) + && _parser.recv("OK"); + } + setTimeout(); + _clear_socket_packets(id); + _smutex.unlock(); + + return ret; +} + +bool ESP32::send(int id, const void *data, uint32_t amount) +{ + int send_size; + int max_send_size = 2048; + bool ret; + int error_cnt = 0; + int index = 0; + + _cbs[id].Notified = 0; + if (amount == 0) { + return true; + } + + if (_cts == NC) { + max_send_size = 512; + } + + //May take a second try if device is busy + while (error_cnt < 2) { + _smutex.lock(); + if ((_id_bits & (1 << id)) == 0) { + _smutex.unlock(); + return false; + } + send_size = amount; + if (send_size > max_send_size) { + send_size = max_send_size; + } + _startup_wifi(); + setTimeout(ESP32_SEND_TIMEOUT); + ret = _parser.send("AT+CIPSEND=%d,%d", id, send_size) + && _parser.recv(">") + && (_parser.write((char*)data + index, (int)send_size) >= 0) + && _parser.recv("SEND OK"); + setTimeout(); + _smutex.unlock(); + if (ret) { + amount -= send_size; + index += send_size; + error_cnt = 0; + if (amount == 0) { + return true; + } + } else { + error_cnt++; + } + } + + return false; +} + +void ESP32::_packet_handler() +{ + int id; + int amount; + uint32_t tmp_timeout; + + // parse out the packet + if (!_parser.recv(",%d,%d:", &id, &amount)) { + return; + } + + struct packet *packet = (struct packet*)malloc( + sizeof(struct packet) + amount); + if (!packet) { + return; + } + + packet->id = id; + packet->len = amount; + packet->next = 0; + packet->index = 0; + + tmp_timeout = last_timeout_ms; + setTimeout(500); + if (!(_parser.read((char*)(packet + 1), amount))) { + free(packet); + setTimeout(tmp_timeout); + return; + } + + // append to packet list + *_packets_end = packet; + _packets_end = &packet->next; +} + +int32_t ESP32::recv(int id, void *data, uint32_t amount, uint32_t timeout) +{ + struct packet **p; + uint32_t idx = 0; + + _cbs[id].Notified = 0; + + _smutex.lock(); + setTimeout(timeout); + if (_rts == NC) { + while (_parser.process_oob()); // Poll for inbound packets + } else { + _parser.process_oob(); // Poll for inbound packets + } + setTimeout(); + + // check if any packets are ready for us + p = &_packets; + while (*p) { + if ((*p)->id == id) { + struct packet *q = *p; + + if (q->len <= amount) { // Return and remove full packet + memcpy(&(((uint8_t *)data)[idx]), (uint8_t*)(q+1) + q->index, q->len); + if (_packets_end == &(*p)->next) { + _packets_end = p; + } + *p = (*p)->next; + idx += q->len; + amount -= q->len; + free(q); + } else { // return only partial packet + memcpy(&(((uint8_t *)data)[idx]), (uint8_t*)(q+1) + q->index, amount); + q->len -= amount; + q->index += amount; + idx += amount; + } + break; + } else { + p = &(*p)->next; + } + } + _smutex.unlock(); + + if (idx > 0) { + return idx; + } else if ((_id_bits & (1 << id)) == 0) { + return 0; + } else { + _cbs[id].Notified = 0; + return -1; + } +} + +void ESP32::_clear_socket_packets(int id) +{ + struct packet **p = &_packets; + + while (*p) { + if ((*p)->id == id || id == ESP32_ALL_SOCKET_IDS) { + struct packet *q = *p; + + if (_packets_end == &(*p)->next) { + _packets_end = p; // Set last packet next field/_packets + } + *p = (*p)->next; + + free(q); + } else { + // Point to last packet next field + p = &(*p)->next; + } + } +} + +bool ESP32::close(int id, bool wait_close) +{ + if (wait_close) { + _smutex.lock(); + for (int j = 0; j < 2; j++) { + if ((_id_bits & (1 << id)) == 0) { + _ids[id] = false; + _clear_socket_packets(id); + _smutex.unlock(); + return true; + } + _startup_wifi(); + setTimeout(500); + _parser.process_oob(); // Poll for inbound packets + setTimeout(); + } + _smutex.unlock(); + } + + //May take a second try if device is busy + for (unsigned i = 0; i < 2; i++) { + _smutex.lock(); + if ((_id_bits & (1 << id)) == 0) { + _ids[id] = false; + _clear_socket_packets(id); + _smutex.unlock(); + return true; + } + _startup_wifi(); + setTimeout(500); + if (_parser.send("AT+CIPCLOSE=%d", id) + && _parser.recv("OK")) { + setTimeout(); + _clear_socket_packets(id); + _ids[id] = false; + _smutex.unlock(); + return true; + } + setTimeout(); + _smutex.unlock(); + } + + _ids[id] = false; + return false; +} + +void ESP32::setTimeout(uint32_t timeout_ms) +{ + last_timeout_ms = timeout_ms; + _parser.set_timeout(timeout_ms); +} + +bool ESP32::readable() +{ + return _serial.FileHandle::readable(); +} + +bool ESP32::writeable() +{ + return _serial.FileHandle::writable(); +} + +void ESP32::socket_attach(int id, void (*callback)(void *), void *data) +{ + _cbs[id].callback = callback; + _cbs[id].data = data; + _cbs[id].Notified = 0; +} + +bool ESP32::recv_ap(nsapi_wifi_ap_t *ap) +{ + bool ret; + int c; + const char keyword_0[8] = "+CWLAP:"; + const char keyword_1[6] = "\nOK\r\n"; + int idx_0 = 0; + int idx_1 = 0; + + while (true) { + c = _parser.getc(); + if (c < 0) { + ret = false; + break; + } + if ((char)c == keyword_0[idx_0]) { + idx_0++; + } else { + idx_0 = 0; + } + if ((char)c == keyword_1[idx_1]) { + idx_1++; + } else { + idx_1 = 0; + } + + // "+CWLAP:" + if (idx_0 >= (int)(sizeof(keyword_0) - 1)) { + int sec; + uint8_t work_buf[6+1]; /* It needs 1 byte extra. */ + int8_t work_rssi[2]; /* It needs 1 byte extra. */ + + ret = _parser.recv("(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu)", &sec, ap->ssid, + &work_rssi[0], &work_buf[0], &work_buf[1], &work_buf[2], &work_buf[3], &work_buf[4], + &work_buf[5], &ap->channel); + ap->rssi = work_rssi[0]; + memcpy(ap->bssid, work_buf, 6); + ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN; + break; + } + + // "\nOK\r\n" + if (idx_1 >= (int)(sizeof(keyword_1) - 1)) { + ret = false; + break; + } + } + + return ret; +} + +void ESP32::_connect_handler_0() { socket_handler(true, 0); } +void ESP32::_connect_handler_1() { socket_handler(true, 1); } +void ESP32::_connect_handler_2() { socket_handler(true, 2); } +void ESP32::_connect_handler_3() { socket_handler(true, 3); } +void ESP32::_connect_handler_4() { socket_handler(true, 4); } +void ESP32::_closed_handler_0() { socket_handler(false, 0); } +void ESP32::_closed_handler_1() { socket_handler(false, 1); } +void ESP32::_closed_handler_2() { socket_handler(false, 2); } +void ESP32::_closed_handler_3() { socket_handler(false, 3); } +void ESP32::_closed_handler_4() { socket_handler(false, 4); } + +void ESP32::_connection_status_handler() +{ + char status[13]; + if (_parser.recv("%12[^\"]\n", status)) { + if (strcmp(status, "CONNECTED\n") == 0) { + _wifi_status = STATUS_CONNECTED; + } else if (strcmp(status, "GOT IP\n") == 0) { + _wifi_status = STATUS_GOT_IP; + } else if (strcmp(status, "DISCONNECT\n") == 0) { + _wifi_status = STATUS_DISCONNECTED; + } else { + return; + } + + if(_wifi_status_cb) { + _wifi_status_cb(_wifi_status); + } + } +} + +int ESP32::get_free_id() +{ + // Look for an unused socket + int id = -1; + + for (int i = 0; i < SOCKET_COUNT; i++) { + if ((!_ids[i]) && ((_id_bits & (1 << i)) == 0)) { + id = i; + _ids[i] = true; + break; + } + } + + return id; +} + +void ESP32::event() { +#if defined(TARGET_ESP32AT_BLE) + if ((_cbs_ble.callback) && (_cbs_ble.Notified == 0)) { + _cbs_ble.Notified = 1; + _cbs_ble.callback(); + } +#endif /* TARGET_ESP32AT_BLE */ + for (int i = 0; i < SOCKET_COUNT; i++) { + if ((_cbs[i].callback) && (_cbs[i].Notified == 0)) { + _cbs[i].Notified = 1; + _cbs[i].callback(_cbs[i].data); + } + } +} + +bool ESP32::set_network(const char *ip_address, const char *netmask, const char *gateway) +{ + bool ret; + + if (ip_address == NULL) { + return false; + } + + _smutex.lock(); + if ((netmask != NULL) && (gateway != NULL)) { + ret = _parser.send("AT+CIPSTA=\"%s\",\"%s\",\"%s\"", ip_address, gateway, netmask) + && _parser.recv("OK"); + } else { + ret = _parser.send("AT+CIPSTA=\"%s\"", ip_address) + && _parser.recv("OK"); + } + _smutex.unlock(); + + return ret; +} + +bool ESP32::set_network_ap(const char *ip_address, const char *netmask, const char *gateway) +{ + bool ret; + + if (ip_address == NULL) { + return false; + } + + _smutex.lock(); + if ((netmask != NULL) && (gateway != NULL)) { + ret = _parser.send("AT+CIPAP=\"%s\",\"%s\",\"%s\"", ip_address, gateway, netmask) + && _parser.recv("OK"); + } else { + ret = _parser.send("AT+CIPAP=\"%s\"", ip_address) + && _parser.recv("OK"); + } + _smutex.unlock(); + + return ret; +} + +void ESP32::attach_wifi_status(mbed::Callback status_cb) +{ + _wifi_status_cb = status_cb; +} + +int8_t ESP32::get_wifi_status() const +{ + return _wifi_status; +} + +void ESP32::flush() +{ + _smutex.lock(); + _parser.flush(); + _smutex.unlock(); +} + + +#if defined(TARGET_ESP32AT_BLE) +bool ESP32::ble_set_role(int role) +{ + if ((role != INIT_CLIENT_ROLE) && (role != INIT_SERVER_ROLE)) { + return false; + } + if (_ble_role != role) { + _ble_role = role; + if (_init_end_ble) { + return restart(); + } + } + return true; +} + +bool ESP32::ble_get_role(int * role) +{ + *role = _ble_role; + return true; +} + +bool ESP32::ble_set_device_name(const char * name) +{ + bool ret; + + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLENAME=\"%s\"", name) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_get_device_name(char * name) +{ + bool ret; + + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLENAME?") + && _parser.recv("+BLENAME:%s\n", name); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + + +bool ESP32::ble_start_services() +{ + bool ret; + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEGATTSSRVCRE") + && _parser.recv("OK") + && _parser.send("AT+BLEGATTSSRVSTART") + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_set_scan_response(const uint8_t * data, int len) +{ + bool ret; + int size = 0; + char * wk_buf = new char[1024]; + + if (wk_buf == NULL) { + return false; + } + + sprintf(&wk_buf[size], "AT+BLESCANRSPDATA=\""); + size = strlen(wk_buf); + _set_char(&wk_buf[size], data, len); + strcat(wk_buf, "\""); + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("%s", wk_buf) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + + delete [] wk_buf; + + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_start_advertising() +{ + bool ret; + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEADVSTART") + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_stop_advertising() +{ + bool ret; + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEADVSTOP") + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_set_addr(int addr_type, const uint8_t * random_addr) +{ + bool ret; + + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + if ((addr_type == 1) && (random_addr != NULL)) { + ret = _parser.send("AT+BLEADDR=1,\"%02x:%02x:%02x:%02x:%02x:%02x\"", + random_addr[0], random_addr[1], random_addr[2], random_addr[3], random_addr[4], random_addr[5]) + && _parser.recv("OK"); + } else { + ret = _parser.send("AT+BLEADDR=%d", addr_type) + && _parser.recv("OK"); + } + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_get_addr(uint8_t * public_addr) +{ + bool ret; + uint8_t work_buf[6+1]; /* It needs 1 byte extra. */ + + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEADDR?") + && _parser.recv("+BLEADDR:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n", + &work_buf[0], &work_buf[1], &work_buf[2], &work_buf[3], &work_buf[4], &work_buf[5]) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + memcpy(public_addr, work_buf, 6); + + return true; +} + +bool ESP32::ble_set_advertising_param(const advertising_param_t * param) +{ + bool ret; + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEADVPARAM=%d,%d,%d,%d,%d,%d,%d,\"%02x:%02x:%02x:%02x:%02x:%02x\"", + param->adv_int_min, param->adv_int_max, param->adv_type, param->own_addr_type, + param->channel_map, param->adv_filter_policy, param->peer_addr_type, + param->peer_addr[0], param->peer_addr[1], param->peer_addr[2], + param->peer_addr[3], param->peer_addr[4], param->peer_addr[5]) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_set_advertising_data(const uint8_t * data, int len) +{ + bool ret; + int size = 0; + char * wk_buf = new char[1024]; + + if (wk_buf == NULL) { + return false; + } + + sprintf(&wk_buf[size], "AT+BLEADVDATA=\""); + size = strlen(wk_buf); + _set_char(&wk_buf[size], data, len); + strcat(wk_buf, "\""); + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("%s", wk_buf) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + + delete [] wk_buf; + + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_set_service(const gatt_service_t * service_list, int num) +{ + uint8_t header[17] = {0x9D,0x10,0x27,0x95,0x7B,0x22,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x22,0x3A,0x20,0x5B}; + bool ret; + int idx = 0; + int size = 0; + uint8_t wk_data[4]; + char * wk_buf = new char[1024]; + + if (wk_buf == NULL) { + return false; + } + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + + size = sizeof(header); + ret = _parser.send("AT+SYSFLASH=0,\"ble_data\"") + && _parser.recv("OK") + && _parser.send("AT+SYSFLASH=1,\"ble_data\",0,%d", size) + && _parser.recv(">") + && (_parser.write((char*)header, size) >= 0) + && _parser.recv("OK"); + idx += size; + if (!ret) { + _smutex.unlock(); + delete [] wk_buf; + return false; + } + + for (int i = 0; i < num; i++) { + size = 0; + sprintf(&wk_buf[size], "{\"index\": %d, \"uuid\": \"", i); + size = strlen(wk_buf); + if ((service_list->uuid_type != 0) && (service_list->uuid_size <= 4)) { + for (int j = 0; j < service_list->uuid_size; j++) { + wk_data[j] = (service_list->uuid.data >> (8 * (service_list->uuid_size - 1 - j))) & 0x00FF; + } + _set_char(&wk_buf[size], wk_data, service_list->uuid_size); + } else { + _set_char(&wk_buf[size], service_list->uuid.addr, service_list->uuid_size); + } + size = strlen(wk_buf); + + sprintf(&wk_buf[size], "\", \"uuid_len\": %d, \"val_max_len\": %d, \"value\": \"", + service_list->uuid_size * 8, service_list->val_max_len); + size = strlen(wk_buf); + + if ((service_list->value_type != 0) && (service_list->value_size <= 4)) { + for (int j = 0; j < service_list->value_size; j++) { + wk_data[j] = (service_list->value.data >> (8 * (service_list->value_size - 1 - j))) & 0x00FF; + } + _set_char(&wk_buf[size], wk_data, service_list->value_size); + } else { + _set_char(&wk_buf[size], service_list->value.addr, service_list->value_size); + } + size = strlen(wk_buf); + + sprintf(&wk_buf[size], "\", \"perm\": %d, \"val_cur_len\": %d}", + service_list->permissions, service_list->value_size); + if ((i + 1) == num) { + strcat(wk_buf, "]}"); + } else { + strcat(wk_buf, ", "); + } + size = strlen(wk_buf); + + ret = _parser.send("AT+SYSFLASH=1,\"ble_data\",%d,%d", idx, size) + && _parser.recv(">") + && (_parser.write((char*)wk_buf, size) >= 0) + && _parser.recv("OK"); + idx += size; + if (!ret) { + break; + } + service_list++; + } + delete [] wk_buf; + + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_set_characteristic(int srv_index, int char_index, const uint8_t * data, int len) +{ + bool ret; + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEGATTSSETATTR=%d,%d,,%d", srv_index, char_index, len) + && _parser.recv(">") + && (_parser.write((char*)data, len) >= 0) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_notifies_characteristic(int srv_index, int char_index, const uint8_t * data, int len) +{ + bool ret; + + ble_set_role(INIT_SERVER_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEGATTSNTFY=0,%d,%d,%d", srv_index, char_index, len) + && _parser.recv(">") + && (_parser.write((char*)data, len) >= 0) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_set_scan_param(int scan_type, int own_addr_type, int filter_policy, int scan_interval, int scan_window) +{ + bool ret; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + ret = _parser.send("AT+BLESCANPARAM=%d,%d,%d,%d,%d", + scan_type, own_addr_type, filter_policy, scan_interval, scan_window) + && _parser.recv("OK"); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_start_scan(int interval) +{ + bool ret; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + ret = _parser.send("AT+BLESCAN=1,%d", interval); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_stop_scan() +{ + bool ret; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLESCAN=0") + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_connect(int conn_index, const uint8_t * remote_addr) +{ + bool ret; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLECONN=%d,\"%02x:%02x:%02x:%02x:%02x:%02x\"", conn_index, + remote_addr[0], remote_addr[1], remote_addr[2], + remote_addr[3], remote_addr[4], remote_addr[5]) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_disconnect(int conn_index) +{ + bool ret; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEDISCONN=%d", conn_index) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_discovery_service(int conn_index, ble_primary_service_t * service, int * num) +{ + bool ret; + int cpy_num; + + if ((service == NULL) || (num == NULL)) { + return false; + } + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + _primary_service_idx = 0; + ret = _parser.send("AT+BLEGATTCPRIMSRV=%d", conn_index) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + *num = 0; + return false; + } + + cpy_num = *num; + if (cpy_num > _primary_service_idx) { + cpy_num = _primary_service_idx; + } + for (int i = 0; i < cpy_num; i++) { + service[i] = _primary_service[i]; + } + *num = cpy_num; + + return true; +} + +bool ESP32::ble_discovery_characteristics( + int conn_index, int srv_index, + ble_discovers_char_t * discovers_char, int * char_num, + ble_discovers_desc_t * discovers_desc, int * desc_num +) +{ + bool ret; + int cpy_num; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + _discovers_char_idx = 0; + _discovers_desc_idx = 0; + ret = _parser.send("AT+BLEGATTCCHAR=%d,%d", conn_index, srv_index) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + if (char_num != NULL) { + *char_num = 0; + } + if (desc_num != NULL) { + *desc_num = 0; + } + return false; + } + + if ((discovers_char != NULL) && (char_num != NULL)) { + cpy_num = *char_num; + if (cpy_num > _discovers_char_idx) { + cpy_num = _discovers_char_idx; + } + for (int i = 0; i < cpy_num; i++) { + discovers_char[i] = _discovers_char[i]; + } + *char_num = cpy_num; + } + + if ((discovers_desc != NULL) && (desc_num != NULL)) { + cpy_num = *desc_num; + if (cpy_num > _discovers_desc_idx) { + cpy_num = _discovers_desc_idx; + } + for (int i = 0; i < cpy_num; i++) { + discovers_desc[i] = _discovers_desc[i]; + } + *desc_num = cpy_num; + } + + return true; +} + +int32_t ESP32::ble_read_characteristic(int conn_index, int srv_index, int char_index, uint8_t * data, int amount) +{ + bool ret; + int wk_conn_index; + int data_len; + int idx = 0;; + char c; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEGATTCRD=%d,%d,%d", conn_index, srv_index, char_index) + && _parser.recv("+BLEGATTCRD:%d,%d,", &wk_conn_index, &data_len); + if (!ret) { + setTimeout(); + _smutex.unlock(); + return -1; + } + for (int i = 0; i < data_len; i++) { + c = _parser.getc(); + if (idx < amount) { + data[idx++] = (uint8_t)c; + } + } + _parser.recv("OK"); + + setTimeout(); + _smutex.unlock(); + + return idx; +} + +int32_t ESP32::ble_read_descriptor(int conn_index, int srv_index, int char_index, int desc_index, uint8_t * data, int amount) +{ + bool ret; + int wk_conn_index; + int data_len; + int idx = 0;; + char c; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEGATTCRD=%d,%d,%d,%d", conn_index, srv_index, char_index, desc_index) + && _parser.recv("+BLEGATTCRD:%d,%d,", &wk_conn_index, &data_len); + if (!ret) { + setTimeout(); + _smutex.unlock(); + return 0; + } + for (int i = 0; i < data_len; i++) { + c = _parser.getc(); + if (idx < amount) { + data[idx++] = (uint8_t)c; + } + } + _parser.recv("OK"); + + setTimeout(); + _smutex.unlock(); + + return idx; +} + +bool ESP32::ble_write_characteristic(int conn_index, int srv_index, int char_index, const uint8_t * data, int amount) +{ + bool ret; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEGATTCWR=%d,%d,%d,,%d", conn_index, srv_index, char_index, amount) + && _parser.recv(">") + && (_parser.write((char*)data, amount) >= 0) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +bool ESP32::ble_write_descriptor(int conn_index, int srv_index, int char_index, int desc_index, const uint8_t * data, int amount) +{ + bool ret; + + ble_set_role(INIT_CLIENT_ROLE); + _smutex.lock(); + _startup_ble(); + setTimeout(ESP32_MISC_TIMEOUT); + ret = _parser.send("AT+BLEGATTCWR=%d,%d,%d,%d,%d", conn_index, srv_index, char_index, desc_index, amount) + && _parser.recv(">") + && (_parser.write((char*)data, amount) >= 0) + && _parser.recv("OK"); + setTimeout(); + _smutex.unlock(); + if (!ret) { + return false; + } + return true; +} + +void ESP32::ble_process_oob(uint32_t timeout, bool all) +{ + _smutex.lock(); + _cbs_ble.Notified = 0; + setTimeout(timeout); + // Poll for inbound packets + while (_parser.process_oob() && all) { + } + setTimeout(); + _smutex.unlock(); +} + +void ESP32::ble_attach_sigio(mbed::Callback cb_func) +{ + _smutex.lock(); + _cbs_ble.Notified = 0; + _cbs_ble.callback = cb_func; + _smutex.unlock(); +} + +void ESP32::ble_attach_conn(mbed::Callback cb_func) +{ + _smutex.lock(); + _ble_conn_cb = cb_func; + _smutex.unlock(); +} + +void ESP32::ble_attach_disconn(mbed::Callback cb_func) +{ + _smutex.lock(); + _ble_disconn_cb = cb_func; + _smutex.unlock(); +} + +void ESP32::ble_attach_write(mbed::Callback cb_func) +{ + _smutex.lock(); + _ble_write_cb = cb_func; + _smutex.unlock(); +} + +void ESP32::ble_attach_scan(mbed::Callback cb_func) +{ + _smutex.lock(); + _ble_scan_cb = cb_func; + _smutex.unlock(); +} + +bool ESP32::_startup_ble() +{ + _startup_common(); + + if (_init_end_ble) { + return true; + } + + if (_at_version < 0x01010300) { + printf("Please update ESP32 FW \"AT version:1.1.3.0\" or later.\r\n"); + mbed_die(); + } + + setTimeout(ESP32_MISC_TIMEOUT); + bool success = _parser.send("AT+BLEINIT=%d", _ble_role) + && _parser.recv("OK"); + setTimeout(); + if (success) { + _init_end_ble = true; + } + + return success; +} + +void ESP32::_ble_conn() +{ + int conn_index; + uint8_t remote_addr[6+1]; /* It needs 1 byte extra. */ + + _parser.recv("%d,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\"", &conn_index, + &remote_addr[0], &remote_addr[1], &remote_addr[2], &remote_addr[3], &remote_addr[4], &remote_addr[5]); + if(_ble_conn_cb) { + _ble_conn_cb(conn_index, remote_addr); + } +} + +void ESP32::_ble_disconn() +{ + int conn_index; + + _parser.recv("%d", &conn_index); + if(_ble_disconn_cb) { + _ble_disconn_cb(conn_index); + } +} + +void ESP32::_ble_write() +{ + int conn_index; + int srv_index; + int char_index; + int desc_index = -1; + int amount; + char c; + uint32_t tmp_timeout; + + if(!_ble_write_cb) { + return; + } + + _parser.recv("%d,%d,%d,", &conn_index, &srv_index, &char_index); + (void)conn_index; + c = _parser.getc(); + if (c != ',') { + desc_index = c; + _parser.getc(); // to read ',' after desc_index. + } + _parser.recv("%d,", &amount); + + ble_packet_t *ble_packet = (ble_packet_t*)malloc( + sizeof(ble_packet_t) + amount); + if (!ble_packet) { + return; + } + + ble_packet->srv_index = srv_index; + ble_packet->char_index = char_index; + ble_packet->desc_index = desc_index; + ble_packet->len = amount; + ble_packet->data = ble_packet + 1; + + tmp_timeout = last_timeout_ms; + setTimeout(500); + if (!(_parser.read((char*)(ble_packet + 1), amount))) { + free(ble_packet); + setTimeout(tmp_timeout); + return; + } + if(_ble_write_cb) { + _ble_write_cb(ble_packet); + } + free(ble_packet); +} + +void ESP32::_ble_scan() +{ + char c; + int idx; + uint8_t work_buf[7]; /* It needs 1 byte extra. */ + int8_t work_rssi[2]; /* It needs 1 byte extra. */ + + if(!_ble_scan_cb) { + return; + } + + ble_scan_t *ble_scan = (ble_scan_t*)malloc(sizeof(ble_scan_t)); + if (!ble_scan) { + return; + } + + _parser.recv("%hhx:%hhx:%hhx:%hhx:%hhx:%hhx,%hhd,", + &work_buf[0], &work_buf[1], &work_buf[2], + &work_buf[3], &work_buf[4], &work_buf[5], + &work_rssi[0]); + + memcpy(ble_scan->addr, work_buf, 6); + ble_scan->rssi = work_rssi[0]; + + idx = 0; + for (int i = 0; i < (31 * 2); i++) { + c = _parser.getc(); + if (c == ',') { + break; + } + if ((i & 0x01) == 0) { + ble_scan->adv_data[idx] = (_char2int(c) << 4); + } else { + ble_scan->adv_data[idx] += _char2int(c); + idx++; + } + } + ble_scan->adv_data_len = idx; + + if (c != ',') { + c = _parser.getc(); + } + + idx = 0; + for (int i = 0; i < (31 * 2); i++) { + c = _parser.getc(); + if (c == ',') { + break; + } + if ((i & 0x01) == 0) { + ble_scan->scan_rsp_data[idx] = (_char2int(c) << 4); + } else { + ble_scan->scan_rsp_data[idx] += _char2int(c); + idx++; + } + } + ble_scan->scan_rsp_data_len = idx; + + if (c != ',') { + c = _parser.getc(); + } + + _parser.recv("%hhd\n", &work_buf[0]); + ble_scan->addr_type = work_buf[0]; + + if(_ble_scan_cb) { + _ble_scan_cb(ble_scan); + } + free(ble_scan); +} + +void ESP32::_ble_primsrv() +{ + // fix me (supports only short uuid) + int conn_index; + ble_primary_service_t * p_service = &_primary_service[_primary_service_idx]; + + if (_primary_service_idx < PRIMARY_SERVICE_BUF_NUM) { + if (_parser.recv("%d,%d,%hx,%d\n", &conn_index, &p_service->srv_index, + &p_service->srv_uuid, &p_service->srv_type)) { + _primary_service_idx++; + } + } +} + +void ESP32::_ble_discovers_char() +{ + // fix me (supports only short uuid) + int conn_index; + int srv_index; + char type[4]; + uint8_t work_buf[2]; /* It needs 1 byte extra. */ + + _parser.getc(); // skip '"' + _parser.read(type, 4); + _parser.getc(); // skip '"' + + if (_parser.recv(",%d,%d,", &conn_index, &srv_index)) { + if (memcmp(type, "char", 4) == 0) { + if (_discovers_char_idx < DISCOVERS_CHAR_BUF_NUM) { + ble_discovers_char_t * p_char = &_discovers_char[_discovers_char_idx]; + if (_parser.recv("%d,%hx,%hhx\n", &p_char->char_index, &p_char->char_uuid, &work_buf[0])) { + p_char->char_prop = work_buf[0]; + _discovers_char_idx++; + } + } + } else if (memcmp(type, "desc", 4) == 0) { + if (_discovers_desc_idx < DISCOVERS_DESC_BUF_NUM) { + ble_discovers_desc_t * p_desc = &_discovers_desc[_discovers_desc_idx]; + if (_parser.recv("%d,%d,%hx\n", &p_desc->char_index, &p_desc->desc_index, &p_desc->desc_uuid)) { + _discovers_desc_idx++; + } + } + } else { + // do nothing + } + } +} + +char ESP32::_int2char(int data) +{ + if ((data >= 0) && (data <= 9)) { + return data + '0'; + } else if ((data >= 0xA) && (data <= 0xF)) { + return data - 0xA + 'A'; + } else { + return 0; + } +} + +int ESP32::_char2int(char c) +{ + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } else if ((c >= 'A') && (c <= 'F')) { + return c - 'A' + 0xA; + } else if ((c >= 'a') && (c <= 'f')) { + return c - 'a' + 0xA; + } else { + return 0; + } +} + +int ESP32::_set_char(char * buf1, const uint8_t * buf2, int size) +{ + int idx = 0; + + for (int i = 0; i < size; i++) { + buf1[idx++] = _int2char((buf2[i]>> 4) & 0x0F); + buf1[idx++] = _int2char(buf2[i] & 0x0F); + } + buf1[idx] = '\0'; + + return idx; +} +#endif /* TARGET_ESP32AT_BLE */ + +#endif + diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.h b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.h new file mode 100644 index 00000000000..e6460dd4b7e --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32/ESP32.h @@ -0,0 +1,767 @@ +/* ESP32Interface Example + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP32_H +#define ESP32_H + +#if DEVICE_SERIAL && defined(MBED_CONF_EVENTS_PRESENT) && defined(MBED_CONF_NSAPI_PRESENT) && defined(MBED_CONF_RTOS_PRESENT) +#include +#include +#include +#include "drivers/DigitalOut.h" +#include "drivers/SerialBase.h" +#include "netsocket/nsapi_types.h" +#include "netsocket/WiFiAccessPoint.h" +#include "PinNames.h" +#include "platform/ATCmdParser.h" +#include "platform/Callback.h" +#include "platform/mbed_error.h" +#include "platform/mbed_version.h" +#include "rtos/Mutex.h" +#include "rtos/ThisThread.h" + +#if (MBED_MAJOR_VERSION < 6) +#include "drivers/UARTSerial.h" +#else +#include "drivers/BufferedSerial.h" +#endif + +#ifndef ESP32_CONNECT_TIMEOUT +#define ESP32_CONNECT_TIMEOUT 15000 +#endif +#ifndef ESP32_SEND_TIMEOUT +#define ESP32_SEND_TIMEOUT 2000 +#endif +#ifndef ESP32_RECV_TIMEOUT +#define ESP32_RECV_TIMEOUT 2000 +#endif +#ifndef ESP32_MISC_TIMEOUT +#define ESP32_MISC_TIMEOUT 2000 +#endif + +/** ESP32Interface class. + This is an interface to a ESP32 radio. + */ +class ESP32 +{ +public: + /** + * Static method to create or retrieve the single ESP32 instance + */ + static ESP32 * getESP32Inst(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate); + static ESP32 * getESP32Inst(bool debug = false); + + ESP32(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate); + + /** + * Checks Version Information + * + * @param ver_info buffer address for storing version information + * @param buf_size buffer size + * @return String of Version Information + */ + bool get_version_info(char * ver_info, int buf_size); + + /** + * Sets the Wi-Fi Mode + * + * @param mode mode of WIFI 1-client, 2-host, 3-both + * @return true only if ESP32 was setup correctly + */ + bool set_mode(int mode); + + /** + * Enable/Disable DHCP + * + * @param enabled DHCP enabled when true + * @param mode mode of DHCP 0-softAP, 1-station, 2-both + * @return true only if ESP32 enables/disables DHCP successfully + */ + bool dhcp(bool enabled, int mode); + + /** + * Connect ESP32 to AP + * + * @param ap the name of the AP + * @param passPhrase the password of AP + * @return true only if ESP32 is connected successfully + */ + bool connect(const char *ap, const char *passPhrase); + + /** + * Disconnect ESP32 from AP + * + * @return true only if ESP32 is disconnected successfully + */ + bool disconnect(void); + + /** + * Get the IP address of ESP32 + * + * @return null-teriminated IP address or null if no IP address is assigned + */ + const char *getIPAddress(void); + const char *getIPAddress_ap(void); + + /** + * Get the MAC address of ESP32 + * + * @return null-terminated MAC address or null if no MAC address is assigned + */ + const char *getMACAddress(void); + const char *getMACAddress_ap(void); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been recieved + */ + const char *getGateway(); + const char *getGateway_ap(); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been recieved + */ + const char *getNetmask(); + const char *getNetmask_ap(); + + /* Return RSSI for active connection + * + * @return Measured RSSI + */ + int8_t getRSSI(); + + /** + * Check if ESP32 is conenected + * + * @return true only if the chip has an IP address + */ + bool isConnected(void); + + /** Scan for available networks + * + * @param ap Pointer to allocated array to store discovered AP + * @param limit Size of allocated @a res array, or 0 to only count available AP + * @return Number of entries in @a res, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + int scan(WiFiAccessPoint *res, unsigned limit); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "UDP" or "TCP" + * @param id id to give the new socket, valid 0-4 + * @param port port to open connection with + * @param addr the IP address of the destination + * @param addr the IP address of the destination + * @param opt type=" UDP" : UDP socket's local port, zero means any + * type=" TCP" : TCP connection's keep alive time, zero means disabled + * @return true only if socket opened successfully + */ + bool open(const char *type, int id, const char* addr, int port, int opt = 0); + + /** + * Sends data to an open socket + * + * @param id id of socket to send to + * @param data data to be sent + * @param amount amount of data to be sent - max 1024 + * @return true only if data sent successfully + */ + bool send(int id, const void *data, uint32_t amount); + + /** + * Receives data from an open socket + * + * @param id id to receive from + * @param data placeholder for returned information + * @param amount number of bytes to be received + * @return the number of bytes received + */ + int32_t recv(int id, void *data, uint32_t amount, uint32_t timeout = ESP32_RECV_TIMEOUT); + + /** + * Closes a socket + * + * @param id id of socket to close, valid only 0-4 + * @param wait_close + * @return true only if socket is closed successfully + */ + bool close(int id, bool wait_close = false); + + /** + * Allows timeout to be changed between commands + * + * @param timeout_ms timeout of the connection + */ + void setTimeout(uint32_t timeout_ms = ESP32_MISC_TIMEOUT); + + /** + * Checks if data is available + */ + bool readable(); + + /** + * Checks if data can be written + */ + bool writeable(); + + void socket_attach(int id, void (*callback)(void *), void *data); + int get_free_id(); + + bool config_soft_ap(const char *ap, const char *passPhrase, uint8_t chl, uint8_t ecn); + + bool restart(); + bool get_ssid(char *ap); + bool cre_server(int port); + bool del_server(); + bool accept(int * p_id); + + bool set_network(const char *ip_address, const char *netmask, const char *gateway); + bool set_network_ap(const char *ip_address, const char *netmask, const char *gateway); + + /** + * Attach a function to call whenever network state has changed + * + * @param func A pointer to a void function, or 0 to set as none + */ + void attach_wifi_status(mbed::Callback status_cb); + + /** Get the connection status + * + * @return The connection status according to ConnectionStatusType + */ + int8_t get_wifi_status() const; + + /** + * Flush the serial port input buffers. + * + * If you do HW reset for ESP module, you should + * flush the input buffers from existing responses + * from the device. + */ + void flush(); + + static const int8_t WIFIMODE_STATION = 1; + static const int8_t WIFIMODE_SOFTAP = 2; + static const int8_t WIFIMODE_STATION_SOFTAP = 3; + static const int8_t SOCKET_COUNT = 5; + + static const int8_t STATUS_DISCONNECTED = 0; + static const int8_t STATUS_CONNECTED = 1; + static const int8_t STATUS_GOT_IP = 2; + +private: + mbed::DigitalOut * _p_wifi_en; + mbed::DigitalOut * _p_wifi_io0; + bool _init_end_common; + bool _init_end_wifi; +#if (MBED_MAJOR_VERSION < 6) + mbed::UARTSerial _serial; +#else + mbed::BufferedSerial _serial; +#endif + mbed::ATCmdParser _parser; + struct packet { + struct packet *next; + int id; + uint32_t len; + uint32_t index; + // data follows + } *_packets, **_packets_end; + int _wifi_mode; + int _baudrate; + PinName _rts; + PinName _cts; + int _flow_control; + uint32_t last_timeout_ms; + + std::vector _accept_id; + uint32_t _id_bits; + bool _server_act; + rtos::Mutex _smutex; // Protect serial port access + static ESP32 * instESP32; + int8_t _wifi_status; + mbed::Callback _wifi_status_cb; + uint32_t _at_version; + + bool _ids[SOCKET_COUNT]; + struct { + void (*callback)(void *); + void *data; + int Notified; + } _cbs[SOCKET_COUNT]; + + void _startup_common(); + bool _startup_wifi(); + bool reset(void); + void debugOn(bool debug); + void socket_handler(bool connect, int id); + void _connect_handler_0(); + void _connect_handler_1(); + void _connect_handler_2(); + void _connect_handler_3(); + void _connect_handler_4(); + void _closed_handler_0(); + void _closed_handler_1(); + void _closed_handler_2(); + void _closed_handler_3(); + void _closed_handler_4(); + void _connection_status_handler(); + void _packet_handler(); + void _clear_socket_packets(int id); + void event(); + bool recv_ap(nsapi_wifi_ap_t *ap); + + char _ip_buffer[16]; + char _gateway_buffer[16]; + char _netmask_buffer[16]; + char _mac_buffer[18]; + + char _ip_buffer_ap[16]; + char _gateway_buffer_ap[16]; + char _netmask_buffer_ap[16]; + char _mac_buffer_ap[18]; + +#if defined(TARGET_ESP32AT_BLE) +public: + typedef struct { + int srv_index; /**< service's index starting from 1 */ + int char_index; /**< characteristic's index starting from 1 */ + int desc_index; /**< descriptor's index */ + void * data; /**< data buffer address */ + uint32_t len; /**< data len */ + } ble_packet_t; + + typedef union { + const uint8_t * addr; /**< buffer address */ + uint32_t data; /**< data */ + } union_data_t; + + typedef struct { + union_data_t uuid; /**< UUID value */ + uint8_t uuid_type; /**< UUID type 0: addr 1:data */ + uint16_t uuid_size; /**< UUID size */ + uint16_t val_max_len; /**< max allow value length (the max length when you dynamic set value) */ + union_data_t value; /**< initial value */ + uint8_t value_type; /**< initial value type 0: addr 1:data */ + uint16_t value_size; /**< initial value size */ + uint8_t permissions; /**< permission (refer to BLE Spec for definition) */ + } gatt_service_t; + + typedef struct { + uint16_t adv_int_min; /**< minimum value of advertising interval; range: 0x0020 ~ 0x4000 */ + uint16_t adv_int_max; /**< maximum value of advertising interval; range: 0x0020 ~ 0x4000 */ + uint8_t adv_type; /**< 0:FADV_TYPE_IND, 2:FADV_TYPE_SCAN_IND, 3:FADV_TYPE_NONCONN_IND */ + uint8_t own_addr_type; /**< own BLE address type; 0:FBLE_ADDR_TYPE_PUBLIC, 1:FBLE_ADDR_TYPE_RANDOM */ + uint8_t channel_map; /**< channel of advertising; ADV_CHNL_~ */ + uint8_t adv_filter_policy; /**< filter policy of advertising; ADV_FILTER_ALLOW_SCAN_~ */ + uint8_t peer_addr_type; /**< remote BLE address type; 0:FPUBLIC, 1:FRANDOM */ + uint8_t peer_addr[6]; /**< remote BLE address */ + } advertising_param_t; + + typedef struct { + uint8_t addr[6]; /**< BLE address */ + int8_t rssi; /**< signal strength */ + uint8_t adv_data[31]; /**< advertising data */ + uint8_t adv_data_len; /**< advertising data length */ + uint8_t scan_rsp_data[31]; /**< scan response data */ + uint8_t scan_rsp_data_len; /**< scan response data length */ + uint8_t addr_type; /**< the address type of broadcasters */ + } ble_scan_t; + + typedef struct { + int srv_index; /**< service's index starting from 1 */ + uint16_t srv_uuid; /**< service's UUID */ + int srv_type; /**< service's type */ + } ble_primary_service_t; + + typedef struct { + int srv_index; /**< service's index starting from 1 */ + uint16_t srv_uuid; /**< service's UUID */ + int srv_type; /**< service's type */ + } ble_characteristics_t; + + typedef struct { + int char_index; /**< Characteristic's index starting from 1 */ + uint16_t char_uuid; /**< Characteristic's UUID */ + uint8_t char_prop; /**< Characteristic's properties */ + } ble_discovers_char_t; + + typedef struct { + int char_index; /**< Characteristic's index starting from 1 */ + int desc_index; /**< Descriptor's index */ + uint16_t desc_uuid; /**< Descriptor's UUID */ + } ble_discovers_desc_t; + + // advertising_param_t:adv_type + #define ADV_TYPE_IND 0 + #define ADV_TYPE_SCAN_IND 2 + #define ADV_TYPE_NONCONN_IND 3 + + // advertising_param_t:own_addr_type and peer_addr_type + #define BLE_ADDR_TYPE_PUBLIC 0 + #define BLE_ADDR_TYPE_RANDOM 1 + + // advertising_param_t:channel_map + #define ADV_CHNL_37 0x01 + #define ADV_CHNL_38 0x02 + #define ADV_CHNL_39 0x04 + #define ADV_CHNL_ALL 0x07 + + // advertising_param_t:adv_filter_policy + #define ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY 0 + #define ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY 1 + #define ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST 2 + #define ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST 3 + + // ble_set_role: role + #define INIT_CLIENT_ROLE 1 + #define INIT_SERVER_ROLE 2 + + /** Sets BLE Role + * + * @param role INIT_CLIENT_ROLE: client role, INIT_SERVER_ROLE: server role + * @return true: success, false: failure + */ + bool ble_set_role(int role); + + /** Gets BLE Role + * + * @param role INIT_CLIENT_ROLE: client role, INIT_SERVER_ROLE: server role + * @return true: success, false: failure + */ + bool ble_get_role(int * role); + + /** Sets BLE Device's Name + * + * @param name The BLE device name + * @return true: success, false: failure + */ + bool ble_set_device_name(const char * name); + + /** Gets BLE Device's Name + * + * @param name The BLE device name + * @return true: success, false: failure + */ + bool ble_get_device_name(char * name); + + /** GATTS Creates and Starts Services + * + * @return true: success, false: failure + */ + bool ble_start_services(); + + /** Sets BLE Scan Response + * + * @param data Scan response data + * @param len Data len + * @return true: success, false: failure + */ + bool ble_set_scan_response(const uint8_t * data, int len); + + /** Starts Advertising + * + * @return true: success, false: failure + */ + bool ble_start_advertising(); + + /** Stops Advertising + * + * @return true: success, false: failure + */ + bool ble_stop_advertising(); + + /** Sets BLE Device's Address + * + * @param addr_type 0: public address, 1: random address + * @param addr Random address data. Valid only when addr_type is 1. + * @return true: success, false: failure + */ + bool ble_set_addr(int addr_type, const uint8_t * random_addr = NULL); + + /** Gets BLE Device's Address + * + * @param public_addr BLE public address + * @return true: success, false: failure + */ + bool ble_get_addr(uint8_t * public_addr); + + /** Sets Parameters of Advertising + * + * @param param Parameters. See advertising_param_t. + * @return true: success, false: failure + */ + bool ble_set_advertising_param(const advertising_param_t * param); + + /** Sets Advertising Data + * + * @param data Advertising data + * @param len Data len + * @return true: success, false: failure + */ + bool ble_set_advertising_data(const uint8_t * data, int len); + + /** GATT Sets Service + * + * @param service_list GATT service list. see gatt_service_t. + * @param num Number of GATT service list + * @return true: success, false: failure + */ + bool ble_set_service(const gatt_service_t * service_list, int num); + + /** GATTS Sets Characteristic + * + * @param srv_index Service's index starting from 1 + * @param char_index Characteristic's index starting from 1 + * @param data Data buffer address + * @param len Data len + * @return true: success, false: failure + */ + bool ble_set_characteristic(int srv_index, int char_index, const uint8_t * data, int len); + + /** GATTS Notifies of Characteristics + * + * @param srv_index Service's index starting from 1 + * @param char_index Characteristic's index starting from 1 + * @param data Data buffer address + * @param len Data len + * @return true: success, false: failure + */ + bool ble_notifies_characteristic(int srv_index, int char_index, const uint8_t * data, int len); + + /** Sets Parameters of BLE Scanning + * + * @param scan_type 0: passive scan 1: active scan + * @param own_addr_type 0: public address 1: random address 2: RPA public address 3: RPA random address + * @param filter_policy 0: BLE_SCAN_FILTER_ALLOW_ALL + * 1: BLE_SCAN_FILTER_ALLOW_ONLY_WLST + * 2: BLE_SCAN_FILTER_ALLOW_UND_RPA_DIR + * 3: BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR + * @param scan_interval scan interval + * @param scan_window scan window + * @return rue: success, false: failure + */ + bool ble_set_scan_param(int scan_type, int own_addr_type, int filter_policy, int scan_interval, int scan_window); + + /** Enables BLE Scanning + * + * @param interval 0: scanning is continuous + * !0: scanning should last for seconds and then stop automatically + * @return true: success, false: failure + */ + bool ble_start_scan(int interval = 0); + + /** Disables BLE scan + * + * @return true: success, false: failure + */ + bool ble_stop_scan(); + + /** Establishes BLE connection + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @param remote_addr Remote BLE address + * @return true: success, false: failure + */ + bool ble_connect(int conn_index, const uint8_t * remote_addr); + + /** Ends BLE connection + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @return true: success, false: failure + */ + bool ble_disconnect(int conn_index); + + /** GATTC Discovers Primary Services + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @param service Service info + * @param num Number of service info + * @return true: success, false: failure + */ + bool ble_discovery_service(int conn_index, ble_primary_service_t * service, int * num); + + /** GATTC Discovers Characteristics + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @param srv_index Service's index. It can be fetched with "ble_discovery_service()" + * @param discovers_char Characteristic info + * @param char_num Number of characteristic info + * @param discovers_desc Descriptor info + * @param desc_num Number of descriptor info + * @return true: success, false: failure + */ + bool ble_discovery_characteristics( + int conn_index, int srv_index, + ble_discovers_char_t * discovers_char, int * char_num, + ble_discovers_desc_t * discovers_desc, int * desc_num + ); + + /** GATTC Reads a Characteristic + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @param srv_index Service's index. It can be fetched with "ble_discovery_service()" + * @param char_index Characteristic's index. It can be fetched with "ble_discovery_characteristics()" + * @param data Read data buffer + * @param amount Amount of bytes to be received + * @return Data size of received + */ + int32_t ble_read_characteristic(int conn_index, int srv_index, int char_index, uint8_t * data, int amount); + + /** GATTC Reads a Descriptor + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @param srv_index Service's index. It can be fetched with "ble_discovery_service()" + * @param char_index Characteristic's index. It can be fetched with "ble_discovery_characteristics()" + * @param desc_index Descriptor's index. It can be fetched with "ble_discovery_characteristics()" + * @param data Read data buffer + * @param amount Amount of bytes to be received + * @return true: success, false: failure + */ + int32_t ble_read_descriptor(int conn_index, int srv_index, int char_index, int desc_index, uint8_t * data, int amount); + + /** GATTC Writes Characteristic + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @param srv_index Service's index. It can be fetched with "ble_discovery_service()" + * @param char_index Characteristic's index. It can be fetched with "ble_discovery_characteristics()" + * @param data Write data buffer + * @param amount Amount of data to be written + * @return true: success, false: failure + */ + bool ble_write_characteristic(int conn_index, int srv_index, int char_index, const uint8_t * data, int amount); + + /** GATTC Writes Descriptor + * + * @param conn_index Index of BLE connection; only 0 is supported for the single connection right now, + * but multiple BLE connections will be supported in the future. + * @param srv_index Service's index. It can be fetched with "ble_discovery_service()" + * @param char_index Characteristic's index. It can be fetched with "ble_discovery_characteristics()" + * @param desc_index Descriptor's index. It can be fetched with "ble_discovery_characteristics()" + * @param data Write data buffer + * @param amount Amount of data to be written + * @return true: success, false: failure + */ + bool ble_write_descriptor(int conn_index, int srv_index, int char_index, int desc_index, const uint8_t * data, int amount); + + /** For executing OOB processing on background + * + * @param timeout AT parser receive timeout + * @param all if TRUE, process all OOBs instead of only one + */ + void ble_process_oob(uint32_t timeout, bool all); + + /** Register a callback on state change. + * + * The specified callback will be called on state changes. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations. + * + * Note! This is not intended as an attach-like asynchronous api, but rather + * as a building block for constructing such functionality. + * + * The exact timing of when the registered function + * is called is not guaranteed and susceptible to change. It should be used + * as a cue to make ble_process_oobl calls to find the current state. + * + * @param cb_func function to call on state change + */ + void ble_attach_sigio(mbed::Callback cb_func); + + /** + * Attach a function to call whenever the BLE connection establishes + * + * @param cb_func Pointer to the function to be calledt + * cb_func argument 0: disconnect, 1: connect + */ + void ble_attach_conn(mbed::Callback cb_func); + + /** + * Attach a function to call whenever the BLE connection ends + * + * @param cb_func Pointer to the function to be calledt + * cb_func argument 0: disconnect, 1: connect + */ + void ble_attach_disconn(mbed::Callback cb_func); + + /** + * Attach a function to call whenever characteristic data is written + * + * @param cb_func Pointer to the function to be called + */ + void ble_attach_write(mbed::Callback cb_func); + + /** + * Attach a function to call whenever scan data is received + * + * @param cb_func Pointer to the function to be called + */ + void ble_attach_scan(mbed::Callback cb_func); + +private: + #define PRIMARY_SERVICE_BUF_NUM 16 + #define DISCOVERS_CHAR_BUF_NUM 16 + #define DISCOVERS_DESC_BUF_NUM 16 + + struct { + mbed::Callback callback; + int Notified; + } _cbs_ble; + + mbed::Callback _ble_conn_cb; + mbed::Callback _ble_disconn_cb; + mbed::Callback _ble_write_cb; + mbed::Callback _ble_scan_cb; + int _ble_role; + bool _init_end_ble; + int _primary_service_idx; + int _discovers_char_idx; + int _discovers_desc_idx; + ble_primary_service_t _primary_service[PRIMARY_SERVICE_BUF_NUM]; + ble_discovers_char_t _discovers_char[DISCOVERS_CHAR_BUF_NUM]; + ble_discovers_desc_t _discovers_desc[DISCOVERS_DESC_BUF_NUM]; + + bool _startup_ble(); + void _ble_conn(); + void _ble_disconn(); + void _ble_write(); + void _ble_scan(); + void _ble_primsrv(); + void _ble_discovers_char(); + char _int2char(int data); + int _char2int(char c); + int _set_char(char * buf1, const uint8_t * buf2, int size); +#endif /* TARGET_ESP32AT_BLE */ + +}; +#endif +#endif /* ESP32_H */ diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.cpp b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.cpp new file mode 100644 index 00000000000..f9755736361 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.cpp @@ -0,0 +1,376 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "ESP32Interface.h" + +// ESP32Interface implementation +ESP32Interface::ESP32Interface() : + ESP32Stack(MBED_CONF_ESP32_WIFI_EN, MBED_CONF_ESP32_WIFI_IO0, MBED_CONF_ESP32_WIFI_TX, MBED_CONF_ESP32_WIFI_RX, MBED_CONF_ESP32_WIFI_DEBUG, + MBED_CONF_ESP32_WIFI_RTS, MBED_CONF_ESP32_WIFI_CTS, MBED_CONF_ESP32_WIFI_BAUDRATE, 0), + _rst_pin(MBED_CONF_ESP32_WIFI_EN), + _initialized(false), + _dhcp(true), + _ap_ssid(), + _ap_pass(), + _ap_sec(NSAPI_SECURITY_NONE), + _ip_address(), + _netmask(), + _gateway(), + _connection_status(NSAPI_STATUS_DISCONNECTED), + _connection_status_cb(NULL) +{ + memset(_ap_ssid, 0, sizeof(_ap_ssid)); + _esp->attach_wifi_status(callback(this, &ESP32Interface::wifi_status_cb)); +} + +ESP32Interface::ESP32Interface(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) : + ESP32Stack(en, io0, tx, rx, debug, rts, cts, baudrate, 0), + _rst_pin(en), + _initialized(false), + _dhcp(true), + _ap_ssid(), + _ap_pass(), + _ap_sec(NSAPI_SECURITY_NONE), + _ip_address(), + _netmask(), + _gateway(), + _connection_status(NSAPI_STATUS_DISCONNECTED), + _connection_status_cb(NULL) +{ + memset(_ap_ssid, 0, sizeof(_ap_ssid)); + _esp->attach_wifi_status(callback(this, &ESP32Interface::wifi_status_cb)); +} + +ESP32Interface::ESP32Interface(PinName tx, PinName rx, bool debug) : + ESP32Stack(NC, NC, tx, rx, debug, NC, NC, 230400, 0), + _rst_pin(MBED_CONF_ESP32_WIFI_EN), + _initialized(false), + _dhcp(true), + _ap_ssid(), + _ap_pass(), + _ap_sec(NSAPI_SECURITY_NONE), + _ip_address(), + _netmask(), + _gateway(), + _connection_status(NSAPI_STATUS_DISCONNECTED), + _connection_status_cb(NULL) +{ + memset(_ap_ssid, 0, sizeof(_ap_ssid)); + _esp->attach_wifi_status(callback(this, &ESP32Interface::wifi_status_cb)); +} + +nsapi_error_t ESP32Interface::set_network(const SocketAddress &ip_address, const SocketAddress &netmask, const SocketAddress &gateway) +{ + _dhcp = false; + _ip_address = ip_address; + _netmask = netmask; + _gateway = gateway; + + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP32Interface::set_network(const char *ip_address, const char *netmask, const char *gateway) +{ + _dhcp = false; + + // Don't check return values, so user can clear the addresses by passing empty strings. + _ip_address.set_ip_address(ip_address); + _netmask.set_ip_address(netmask); + _gateway.set_ip_address(gateway); + + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP32Interface::set_dhcp(bool dhcp) +{ + _dhcp = dhcp; + + return NSAPI_ERROR_OK; +} + +int ESP32Interface::connect(const char *ssid, const char *pass, nsapi_security_t security, + uint8_t channel) +{ + if (channel != 0) { + return NSAPI_ERROR_UNSUPPORTED; + } + + int ret = set_credentials(ssid, pass, security); + if (ret != NSAPI_ERROR_OK) { + return ret; + } + + _init(); + return connect(); +} + +int ESP32Interface::connect() +{ + if (_ap_ssid[0] == 0) { + return NSAPI_ERROR_NO_SSID; + } + + if (!_esp->dhcp(_dhcp, 1)) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + if (!_dhcp) { + if (!_esp->set_network(_ip_address.get_ip_address(), _netmask.get_ip_address(), _gateway.get_ip_address())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + } + + set_connection_status(NSAPI_STATUS_CONNECTING); + if (!_esp->connect(_ap_ssid, _ap_pass)) { + set_connection_status(NSAPI_STATUS_DISCONNECTED); + return NSAPI_ERROR_NO_CONNECTION; + } + + return NSAPI_ERROR_OK; +} + +int ESP32Interface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security) +{ + int ret = NSAPI_ERROR_OK; + size_t pass_len; + + if ((ssid == NULL) || (ssid[0] == 0)) { + return NSAPI_ERROR_PARAMETER; + } + + if ((pass == NULL) || (pass[0] == 0)) { + pass_len = 0; + } else { + pass_len = strlen(pass); + } + + switch (security) { + case NSAPI_SECURITY_NONE: + if (pass_len != 0) { + ret = NSAPI_ERROR_PARAMETER; + } + break; + case NSAPI_SECURITY_WEP: + if ((pass_len < 5) || (pass_len > 26)) { + ret = NSAPI_ERROR_PARAMETER; + } + break; + case NSAPI_SECURITY_WPA: + case NSAPI_SECURITY_WPA2: + case NSAPI_SECURITY_WPA_WPA2: + if ((pass_len < 8) || (pass_len > 63)) { + ret = NSAPI_ERROR_PARAMETER; + } + break; + case NSAPI_SECURITY_UNKNOWN: + // do nothing + break; + default: + ret = NSAPI_ERROR_UNSUPPORTED; + break; + } + + if (ret != NSAPI_ERROR_OK) { + return ret; + } + + memset(_ap_ssid, 0, sizeof(_ap_ssid)); + strncpy(_ap_ssid, ssid, sizeof(_ap_ssid)); + + memset(_ap_pass, 0, sizeof(_ap_pass)); + if (pass_len != 0) { + strncpy(_ap_pass, pass, pass_len); + } + + _ap_sec = security; + + return NSAPI_ERROR_OK; +} + +int ESP32Interface::set_channel(uint8_t channel) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ESP32Interface::disconnect() +{ + _initialized = false; + if (_connection_status == NSAPI_STATUS_DISCONNECTED) { + return NSAPI_ERROR_NO_CONNECTION; + } + + if (!_esp->disconnect()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP32Interface::get_ip_address(SocketAddress* sockAddr) +{ + if (sockAddr->set_ip_address(_esp->getIPAddress())) { + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_NO_ADDRESS; +} + +const char *ESP32Interface::get_ip_address() +{ + return _esp->getIPAddress(); +} + +const char *ESP32Interface::get_mac_address() +{ + return _esp->getMACAddress(); +} + +nsapi_error_t ESP32Interface::get_gateway(SocketAddress* sockAddr) +{ + if (sockAddr->set_ip_address(_esp->getGateway())) { + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_NO_ADDRESS; +} + +const char *ESP32Interface::get_gateway() +{ + return _esp->getGateway(); +} + +nsapi_error_t ESP32Interface::get_netmask(SocketAddress* sockAddr) +{ + if (sockAddr->set_ip_address(_esp->getNetmask())) { + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_NO_ADDRESS; +} + +const char *ESP32Interface::get_netmask() +{ + return _esp->getNetmask(); +} + +int8_t ESP32Interface::get_rssi() +{ + return _esp->getRSSI(); +} + +int ESP32Interface::scan(WiFiAccessPoint *res, unsigned count) +{ + _init(); + return _esp->scan(res, count); +} + +void ESP32Interface::attach(mbed::Callback status_cb) +{ + _connection_status_cb = status_cb; +} + +nsapi_connection_status_t ESP32Interface::get_connection_status() const +{ + return _connection_status; +} + +void ESP32Interface::set_connection_status(nsapi_connection_status_t connection_status) +{ + if (_connection_status != connection_status) { + _connection_status = connection_status; + if (_connection_status_cb) { + _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connection_status); + } + } +} + +void ESP32Interface::wifi_status_cb(int8_t wifi_status) +{ + switch (wifi_status) { + case ESP32::STATUS_DISCONNECTED: + set_connection_status(NSAPI_STATUS_DISCONNECTED); + break; + case ESP32::STATUS_GOT_IP: + set_connection_status(NSAPI_STATUS_GLOBAL_UP); + break; + case ESP32::STATUS_CONNECTED: + default: + // do nothing + break; + } +} + +#if MBED_CONF_ESP32_PROVIDE_DEFAULT + +WiFiInterface *WiFiInterface::get_default_instance() { + static ESP32Interface esp32; + return &esp32; +} + +#endif + +ESP32Interface::~ESP32Interface() +{ + // Power down the modem + _rst_pin.rst_assert(); +} + +ESP32Interface::ResetPin::ResetPin(PinName rst_pin) : _rst_pin(mbed::DigitalOut(rst_pin, 1)) +{ +} + +void ESP32Interface::ResetPin::rst_assert() +{ + if (_rst_pin.is_connected()) { + _rst_pin = 0; + //tr_debug("rst_assert(): HW reset asserted."); + } +} + +void ESP32Interface::ResetPin::rst_deassert() +{ + if (_rst_pin.is_connected()) { + _rst_pin = 1; + //tr_debug("rst_deassert(): HW reset deasserted."); + } +} + +bool ESP32Interface::ResetPin::is_connected() +{ + return _rst_pin.is_connected(); +} + +nsapi_error_t ESP32Interface::_init(void) +{ + if (!_initialized) { + if (_reset() != NSAPI_ERROR_OK) { + return NSAPI_ERROR_DEVICE_ERROR; + } + _initialized = true; + } + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP32Interface::_reset(void) +{ + if (_rst_pin.is_connected()) { + _rst_pin.rst_assert(); + rtos::ThisThread::sleep_for(2ms); + _esp->flush(); + _rst_pin.rst_deassert(); + } + + return NSAPI_ERROR_OK; +} diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.h b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.h new file mode 100644 index 00000000000..d5909ce33ff --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Interface.h @@ -0,0 +1,278 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP32_INTERFACE_H +#define ESP32_INTERFACE_H + +#include "mbed.h" +#include "ESP32Stack.h" + +/** ESP32Interface class + * Implementation of the NetworkStack for the ESP32 + */ +class ESP32Interface : public ESP32Stack, public WiFiInterface +{ +public: + /** ESP32Interface lifetime + * Configuration defined in mbed_lib.json + */ + ESP32Interface(); + + /** ESP32Interface lifetime + * @param en EN pin (If not used this pin, please set "NC") + * @param io0 IO0 pin (If not used this pin, please set "NC") + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + * @param rts RTS pin + * @param cts CTS pin + * @param baudrate The baudrate of the serial port (default = 230400). + */ + ESP32Interface(PinName en, PinName io0, PinName tx, PinName rx, bool debug = false, + PinName rts = NC, PinName cts = NC, int baudrate = 230400); + + /** + * @brief ESP32Interface default destructor + */ + virtual ~ESP32Interface(); + + /** ESP32Interface lifetime + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + */ + ESP32Interface(PinName tx, PinName rx, bool debug = false); + + /** Set a static IP address + * + * Configures this network interface to use a static IP address. + * Implicitly disables DHCP, which can be enabled in set_dhcp. + * Requires that the network is disconnected. + * + * @param ip_address SocketAddress representation of the local IP address + * @param netmask SocketAddress representation of the local network mask + * @param gateway SocketAddress representation of the local gateway + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t set_network( + const SocketAddress &ip_address, const SocketAddress &netmask, + const SocketAddress &gateway) override; + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual nsapi_error_t set_network( + const char *ip_address, const char *netmask, const char *gateway); + + /** Enable or disable DHCP on the network + * + * Enables DHCP on connecting the network. Defaults to enabled unless + * a static IP address has been assigned. Requires that the network is + * disconnected. + * + * @param dhcp True to enable DHCP + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t set_dhcp(bool dhcp); + + /** Start the interface + * + * Attempts to connect to a WiFi network. Requires ssid and passphrase to be set. + * If passphrase is invalid, NSAPI_ERROR_AUTH_ERROR is returned. + * + * @return 0 on success, negative error code on failure + */ + virtual int connect(); + + /** Start the interface + * + * Attempts to connect to a WiFi network. + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE) + * @param channel This parameter is not supported, setting it to anything else than 0 will result in NSAPI_ERROR_UNSUPPORTED + * @return 0 on success, or error code on failure + */ + virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE, + uint8_t channel = 0); + + /** Set the WiFi network credentials + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * (defaults to NSAPI_SECURITY_NONE) + * @return 0 on success, or error code on failure + */ + virtual int set_credentials(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE); + + /** Set the WiFi network channel - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param channel Channel on which the connection is to be made, or 0 for any (Default: 0) + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual int set_channel(uint8_t channel); + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual int disconnect(); + + /** Get the internally stored IP address + * @param sockAddr SocketAddress pointer to store the local IP address + * @retval NSAPI_ERROR_OK on success + * @retval NSAPI_ERROR_UNSUPPORTED if this feature is not supported + * @retval NSAPI_ERROR_PARAMETER if the provided pointer is invalid + * @retval NSAPI_ERROR_NO_ADDRESS if the address cannot be obtained from stack + */ + virtual nsapi_error_t get_ip_address(SocketAddress *sockAddr); + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual const char *get_ip_address(); + + /** Get the internally stored MAC address + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Get the local gateway + * + * @param sockAddr SocketAddress representation of gateway address + * @retval NSAPI_ERROR_OK on success + * @retval NSAPI_ERROR_UNSUPPORTED if this feature is not supported + * @retval NSAPI_ERROR_PARAMETER if the provided pointer is invalid + * @retval NSAPI_ERROR_NO_ADDRESS if the address cannot be obtained from stack + */ + virtual nsapi_error_t get_gateway(SocketAddress *sockAddr); + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual const char *get_gateway(); + + /** Get the local network mask + * + * @param sockAddr SocketAddress representation of netmask + * @retval NSAPI_ERROR_OK on success + * @retval NSAPI_ERROR_UNSUPPORTED if this feature is not supported + * @retval NSAPI_ERROR_PARAMETER if the provided pointer is invalid + * @retval NSAPI_ERROR_NO_ADDRESS if the address cannot be obtained from stack + */ + virtual nsapi_error_t get_netmask(SocketAddress *sockAddr); + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual const char *get_netmask(); + + /** Gets the current radio signal strength for active connection + * + * @return Connection strength in dBm (negative value) + */ + virtual int8_t get_rssi(); + + /** Scan for available networks + * + * This function will block. + * + * @param ap Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @param timeout Timeout in milliseconds; 0 for no timeout (Default: 0) + * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + virtual int scan(WiFiAccessPoint *res, unsigned count); + + /** Translates a hostname to an IP address with specific version + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param address Destination for the host SocketAddress + * @param host Hostname to resolve + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::gethostbyname; + + /** Add a domain name server to list of servers to query + * + * @param addr Destination for the host address + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::add_dns_server; + + /** Register callback for status reporting + * + * The specified status callback function will be called on status changes + * on the network. The parameters on the callback are the event type and + * event-type dependent reason parameter. + * + * In ESP32 the callback will be called when processing OOB-messages via + * AT-parser. Do NOT call any ESP8266Interface -functions or do extensive + * processing in the callback. + * + * @param status_cb The callback for status changes + */ + virtual void attach(mbed::Callback status_cb); + + /** Get the connection status + * + * @return The connection status according to ConnectionStatusType + */ + virtual nsapi_connection_status_t get_connection_status() const; + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() + { + return this; + } + +private: + + // HW reset pin + class ResetPin { + public: + ResetPin(PinName rst_pin); + void rst_assert(); + void rst_deassert(); + bool is_connected(); + private: + mbed::DigitalOut _rst_pin; + } _rst_pin; + + int _initialized; + bool _dhcp; + char _ap_ssid[33]; /* 32 is what 802.11 defines as longest possible name; +1 for the \0 */ + char _ap_pass[64]; /* The longest allowed passphrase */ + nsapi_security_t _ap_sec; + SocketAddress _ip_address; + SocketAddress _netmask; + SocketAddress _gateway; + nsapi_connection_status_t _connection_status; + mbed::Callback _connection_status_cb; + + void set_connection_status(nsapi_connection_status_t connection_status); + void wifi_status_cb(int8_t wifi_status); + nsapi_error_t _init(void); + nsapi_error_t _reset(void); +}; + +#endif diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.cpp b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.cpp new file mode 100644 index 00000000000..cb8fefd2f18 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.cpp @@ -0,0 +1,253 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "ESP32InterfaceAP.h" + +// ESP32InterfaceAP implementation +ESP32InterfaceAP::ESP32InterfaceAP() : + ESP32Stack(MBED_CONF_ESP32_WIFI_EN, MBED_CONF_ESP32_WIFI_IO0, MBED_CONF_ESP32_WIFI_TX, MBED_CONF_ESP32_WIFI_RX, MBED_CONF_ESP32_WIFI_DEBUG, + MBED_CONF_ESP32_WIFI_RTS, MBED_CONF_ESP32_WIFI_CTS, MBED_CONF_ESP32_WIFI_BAUDRATE, 1), + _dhcp(true), + _own_ch(1), + _own_ssid(), + _own_pass(), + _own_sec(NSAPI_SECURITY_NONE), + _ip_address(), + _netmask(), + _gateway(), + _connection_status(NSAPI_STATUS_DISCONNECTED), + _connection_status_cb(NULL) +{ +} + +ESP32InterfaceAP::ESP32InterfaceAP(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) : + ESP32Stack(en, io0, tx, rx, debug, rts, cts, baudrate, 1), + _dhcp(true), + _own_ch(1), + _own_ssid(), + _own_pass(), + _own_sec(NSAPI_SECURITY_NONE), + _ip_address(), + _netmask(), + _gateway(), + _connection_status(NSAPI_STATUS_DISCONNECTED), + _connection_status_cb(NULL) +{ +} + +ESP32InterfaceAP::ESP32InterfaceAP(PinName tx, PinName rx, bool debug) : + ESP32Stack(NC, NC, tx, rx, debug, NC, NC, 230400, 1), + _dhcp(true), + _own_ch(1), + _own_ssid(), + _own_pass(), + _own_sec(NSAPI_SECURITY_NONE), + _ip_address(), + _netmask(), + _gateway(), + _connection_status(NSAPI_STATUS_DISCONNECTED), + _connection_status_cb(NULL) +{ +} + +nsapi_error_t ESP32InterfaceAP::set_network(const SocketAddress &ip_address, const SocketAddress &netmask, const SocketAddress &gateway) +{ + _dhcp = false; + _ip_address = ip_address; + _netmask = netmask; + _gateway = gateway; + + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP32InterfaceAP::set_network(const char *ip_address, const char *netmask, const char *gateway) +{ + _dhcp = false; + + _ip_address.set_ip_address(ip_address); + _netmask.set_ip_address(netmask); + _gateway.set_ip_address(gateway); + + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP32InterfaceAP::set_dhcp(bool dhcp) +{ + _dhcp = dhcp; + + return NSAPI_ERROR_OK; +} + +int ESP32InterfaceAP::connect(const char *ssid, const char *pass, nsapi_security_t security, + uint8_t channel) +{ + int ret; + + ret = set_credentials(ssid, pass, security); + if (ret != 0) { + return ret; + } + + ret = set_channel(channel); + if (ret != 0) { + return ret; + } + + return connect(); +} + +int ESP32InterfaceAP::connect() +{ + if (!_esp->set_mode(ESP32::WIFIMODE_STATION_SOFTAP)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + if (!_esp->dhcp(_dhcp, 1)) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + if (!_dhcp) { + if (!_esp->set_network_ap(_ip_address.get_ip_address(), _netmask.get_ip_address(), _gateway.get_ip_address())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + } + + if (!_esp->config_soft_ap(_own_ssid, _own_pass, _own_ch, (uint8_t)_own_sec)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + _connection_status = NSAPI_STATUS_GLOBAL_UP; + if (_connection_status_cb) { + _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connection_status); + } + + return NSAPI_ERROR_OK; +} + +int ESP32InterfaceAP::set_credentials(const char *ssid, const char *pass, nsapi_security_t security) +{ + switch (security) { + case NSAPI_SECURITY_NONE: + case NSAPI_SECURITY_WPA: + case NSAPI_SECURITY_WPA2: + case NSAPI_SECURITY_WPA_WPA2: + _own_sec = security; + break; + case NSAPI_SECURITY_UNKNOWN: + case NSAPI_SECURITY_WEP: + default: + return NSAPI_ERROR_UNSUPPORTED; + } + + memset(_own_ssid, 0, sizeof(_own_ssid)); + strncpy(_own_ssid, ssid, sizeof(_own_ssid)); + + memset(_own_pass, 0, sizeof(_own_pass)); + strncpy(_own_pass, pass, sizeof(_own_pass)); + + return 0; +} + +int ESP32InterfaceAP::set_channel(uint8_t channel) +{ + if (channel != 0) { + _own_ch = channel; + } + + return 0; +} + +int ESP32InterfaceAP::disconnect() +{ + if (!_esp->set_mode(ESP32::WIFIMODE_STATION)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + _connection_status = NSAPI_STATUS_DISCONNECTED; + if (_connection_status_cb) { + _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connection_status); + } + + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP32InterfaceAP::get_ip_address(SocketAddress* sockAddr) +{ + if (sockAddr->set_ip_address(_esp->getIPAddress_ap())) { + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_NO_ADDRESS; +} + +const char *ESP32InterfaceAP::get_ip_address() +{ + return _esp->getIPAddress_ap(); +} + +const char *ESP32InterfaceAP::get_mac_address() +{ + return _esp->getMACAddress_ap(); +} + +nsapi_error_t ESP32InterfaceAP::get_gateway(SocketAddress* sockAddr) +{ + if (sockAddr->set_ip_address(_esp->getGateway_ap())) { + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_NO_ADDRESS; +} + +const char *ESP32InterfaceAP::get_gateway() +{ + return _esp->getGateway_ap(); +} + +nsapi_error_t ESP32InterfaceAP::get_netmask(SocketAddress* sockAddr) +{ + if (sockAddr->set_ip_address(_esp->getNetmask_ap())) { + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_NO_ADDRESS; +} + + +const char *ESP32InterfaceAP::get_netmask() +{ + return _esp->getNetmask_ap(); +} + +int8_t ESP32InterfaceAP::get_rssi() +{ + return 0; +} + +int ESP32InterfaceAP::scan(WiFiAccessPoint *res, unsigned count) +{ + return _esp->scan(res, count); +} + +void ESP32InterfaceAP::attach(mbed::Callback status_cb) +{ + _connection_status_cb = status_cb; +} + +nsapi_connection_status_t ESP32InterfaceAP::get_connection_status() const +{ + return _connection_status; +} + diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.h b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.h new file mode 100644 index 00000000000..8a6aecf4002 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32InterfaceAP.h @@ -0,0 +1,259 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP32_INTERFACE_AP_H +#define ESP32_INTERFACE_AP_H + +#include "mbed.h" +#include "ESP32Stack.h" + + +/** ESP32Interface class + * Implementation of the NetworkStack for the ESP32 + */ +class ESP32InterfaceAP : public ESP32Stack, public WiFiInterface +{ +public: + /** ESP32InterfaceAP lifetime + * Configuration defined in mbed_lib.json + */ + ESP32InterfaceAP(); + + /** ESP32InterfaceAP lifetime + * @param en EN pin (If not used this pin, please set "NC") + * @param io0 IO0 pin (If not used this pin, please set "NC") + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + * @param rts RTS pin + * @param cts CTS pin + * @param baudrate The baudrate of the serial port (default = 230400). + */ + ESP32InterfaceAP(PinName en, PinName io0, PinName tx, PinName rx, bool debug = false, + PinName rts = NC, PinName cts = NC, int baudrate = 230400); + + /** ESP32InterfaceAP lifetime + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + */ + ESP32InterfaceAP(PinName tx, PinName rx, bool debug = false); + + /** Set a static IP address + * + * Configures this network interface to use a static IP address. + * Implicitly disables DHCP, which can be enabled in set_dhcp. + * Requires that the network is disconnected. + * + * @param ip_address SocketAddress representation of the local IP address + * @param netmask SocketAddress representation of the local network mask + * @param gateway SocketAddress representation of the local gateway + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t set_network( + const SocketAddress &ip_address, const SocketAddress &netmask, + const SocketAddress &gateway) override; + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual nsapi_error_t set_network( + const char *ip_address, const char *netmask, const char *gateway); + + /** Enable or disable DHCP on the network + * + * Enables DHCP on connecting the network. Defaults to enabled unless + * a static IP address has been assigned. Requires that the network is + * disconnected. + * + * @param dhcp True to enable DHCP + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t set_dhcp(bool dhcp); + + /** Start the interface + * + * Attempts to connect to a WiFi network. Requires ssid and passphrase to be set. + * If passphrase is invalid, NSAPI_ERROR_AUTH_ERROR is returned. + * + * @return 0 on success, negative error code on failure + */ + virtual int connect(); + + /** Start the interface + * + * Attempts to connect to a WiFi network. + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE) + * @param channel This parameter is not supported, setting it to anything else than 0 will result in NSAPI_ERROR_UNSUPPORTED + * @return 0 on success, or error code on failure + */ + virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE, + uint8_t channel = 0); + + /** Set the WiFi network credentials + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * (defaults to NSAPI_SECURITY_NONE) + * @return 0 on success, or error code on failure + */ + virtual int set_credentials(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE); + + /** Set the WiFi network channel - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param channel Channel on which the connection is to be made, or 0 for any (Default: 0) + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual int set_channel(uint8_t channel); + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual int disconnect(); + + /** Get the internally stored IP address + * + * @param address SocketAddress pointer to store the local IP address + * @retval NSAPI_ERROR_OK on success + * @retval NSAPI_ERROR_UNSUPPORTED if this feature is not supported + * @retval NSAPI_ERROR_PARAMETER if the provided pointer is invalid + * @retval NSAPI_ERROR_NO_ADDRESS if the address cannot be obtained from stack + */ + virtual nsapi_error_t get_ip_address(SocketAddress *address); + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual const char *get_ip_address(); + + /** Get the internally stored MAC address + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Get the local gateway + * + * @param address SocketAddress representation of gateway address + * @retval NSAPI_ERROR_OK on success + * @retval NSAPI_ERROR_UNSUPPORTED if this feature is not supported + * @retval NSAPI_ERROR_PARAMETER if the provided pointer is invalid + * @retval NSAPI_ERROR_NO_ADDRESS if the address cannot be obtained from stack + */ + virtual nsapi_error_t get_gateway(SocketAddress *address); + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual const char *get_gateway(); + + /** Get the local network mask + * + * @param address SocketAddress representation of netmask + * @retval NSAPI_ERROR_OK on success + * @retval NSAPI_ERROR_UNSUPPORTED if this feature is not supported + * @retval NSAPI_ERROR_PARAMETER if the provided pointer is invalid + * @retval NSAPI_ERROR_NO_ADDRESS if the address cannot be obtained from stack + */ + virtual nsapi_error_t get_netmask(SocketAddress *address); + + MBED_DEPRECATED_SINCE("mbed-os-5.15", "String-based APIs are deprecated") + virtual const char *get_netmask(); + + /** Gets the current radio signal strength for active connection + * + * @return Connection strength in dBm (negative value) + */ + virtual int8_t get_rssi(); + + /** Scan for available networks + * + * This function will block. + * + * @param ap Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @param timeout Timeout in milliseconds; 0 for no timeout (Default: 0) + * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + virtual int scan(WiFiAccessPoint *res, unsigned count); + + /** Translates a hostname to an IP address with specific version + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param address Destination for the host SocketAddress + * @param host Hostname to resolve + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::gethostbyname; + + /** Add a domain name server to list of servers to query + * + * @param addr Destination for the host address + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::add_dns_server; + + /** Register callback for status reporting + * + * The specified status callback function will be called on status changes + * on the network. The parameters on the callback are the event type and + * event-type dependent reason parameter. + * + * In ESP32 the callback will be called when processing OOB-messages via + * AT-parser. Do NOT call any ESP8266Interface -functions or do extensive + * processing in the callback. + * + * @param status_cb The callback for status changes + */ + virtual void attach(mbed::Callback status_cb); + + /** Get the connection status + * + * @return The connection status according to ConnectionStatusType + */ + virtual nsapi_connection_status_t get_connection_status() const; + + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() + { + return this; + } + +private: + bool _dhcp; + uint8_t _own_ch; + char _own_ssid[33]; /* 32 is what 802.11 defines as longest possible name; +1 for the \0 */ + char _own_pass[64]; /* The longest allowed passphrase */ + nsapi_security_t _own_sec; + SocketAddress _ip_address; + SocketAddress _netmask; + SocketAddress _gateway; + nsapi_connection_status_t _connection_status; + Callback _connection_status_cb; +}; + +#endif diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.cpp b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.cpp new file mode 100644 index 00000000000..06d94fdceb3 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.cpp @@ -0,0 +1,332 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "ESP32Stack.h" + +// ESP32Stack implementation +ESP32Stack::ESP32Stack(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate, int is_ap) : _is_ap(is_ap) +{ + _esp = ESP32::getESP32Inst(en, io0, tx, rx, debug, rts, cts, baudrate); + memset(_local_ports, 0, sizeof(_local_ports)); +} + +struct esp32_socket { + int id; + nsapi_protocol_t proto; + bool connected; + SocketAddress addr; + int keepalive; // TCP + bool accept_id; + bool tcp_server; +}; + +int ESP32Stack::socket_open(void **handle, nsapi_protocol_t proto) +{ + // Look for an unused socket + int id = _esp->get_free_id(); + + if (id == -1) { + return NSAPI_ERROR_NO_SOCKET; + } + + struct esp32_socket *socket = new struct esp32_socket; + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + socket->id = id; + socket->proto = proto; + socket->connected = false; + socket->keepalive = 0; + socket->accept_id = false; + socket->tcp_server = false; + *handle = socket; + _local_ports[socket->id] = 0; + return 0; +} + +int ESP32Stack::socket_close(void *handle) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + int err = 0; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (!_esp->close(socket->id, socket->accept_id)) { + err = NSAPI_ERROR_DEVICE_ERROR; + } + + if (socket->tcp_server) { + _esp->del_server(); + } + _local_ports[socket->id] = 0; + + delete socket; + return err; +} + +int ESP32Stack::socket_bind(void *handle, const SocketAddress &address) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (address.get_ip_address() != NULL) { + if (_is_ap == 0) { + if (strncmp(address.get_ip_address(), _esp->getIPAddress(), NSAPI_IPv6_SIZE) != 0) { + return NSAPI_ERROR_PARAMETER; + } + } else { + if (strncmp(address.get_ip_address(), _esp->getIPAddress_ap(), NSAPI_IPv6_SIZE) != 0) { + return NSAPI_ERROR_PARAMETER; + } + } + } + + if(address.get_addr().version != NSAPI_UNSPEC) { + return NSAPI_ERROR_UNSUPPORTED; + } + + for(int id = 0; id < ESP32::SOCKET_COUNT; id++) { + if(_local_ports[id] == address.get_port() && id != socket->id) { // Port already reserved by another socket + return NSAPI_ERROR_PARAMETER; + } else if (id == socket->id && socket->connected) { + return NSAPI_ERROR_PARAMETER; + } + } + _local_ports[socket->id] = address.get_port(); + socket->addr = address; + return 0; +} + +int ESP32Stack::socket_listen(void *handle, int backlog) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + (void)backlog; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (socket->proto != NSAPI_TCP) { + return NSAPI_ERROR_UNSUPPORTED; + } + + if (!_esp->cre_server(socket->addr.get_port())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + socket->tcp_server = true; + return 0; +} + +int ESP32Stack::socket_connect(void *handle, const SocketAddress &addr) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (socket->proto == NSAPI_UDP) { + if (!_esp->open("UDP", socket->id, addr.get_ip_address(), addr.get_port(), _local_ports[socket->id])) { + return NSAPI_ERROR_DEVICE_ERROR; + } + } else { + if (!_esp->open("TCP", socket->id, addr.get_ip_address(), addr.get_port(), socket->keepalive)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + } + + socket->connected = true; + return 0; +} + +int ESP32Stack::socket_accept(void *server, void **socket, SocketAddress *addr) +{ + struct esp32_socket *socket_new = new struct esp32_socket; + int id; + + if (!socket_new) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (!_esp->accept(&id)) { + delete socket_new; + return NSAPI_ERROR_NO_SOCKET; + } + + socket_new->id = id; + socket_new->proto = NSAPI_TCP; + socket_new->connected = true; + socket_new->accept_id = true; + socket_new->tcp_server = false; + *socket = socket_new; + + return 0; +} + +int ESP32Stack::socket_send(void *handle, const void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (!_esp->send(socket->id, data, size)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return size; +} + +int ESP32Stack::socket_recv(void *handle, void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int32_t recv = _esp->recv(socket->id, data, size); + if (recv == -1) { + return NSAPI_ERROR_WOULD_BLOCK; + } + + return recv; +} + +int ESP32Stack::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (socket->connected && socket->addr != addr) { + if (!_esp->close(socket->id, socket->accept_id)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + socket->connected = false; + } + + if (!socket->connected) { + int err = socket_connect(socket, addr); + if (err < 0) { + return err; + } + socket->addr = addr; + } + + return socket_send(socket, data, size); +} + +int ESP32Stack::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int ret = socket_recv(socket, data, size); + if (ret >= 0 && addr) { + *addr = socket->addr; + } + + return ret; +} + +void ESP32Stack::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!socket) { + return; + } + + _esp->socket_attach(socket->id, callback, data); +} + +nsapi_error_t ESP32Stack::setsockopt(nsapi_socket_t handle, int level, + int optname, const void *optval, unsigned optlen) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!optlen) { + return NSAPI_ERROR_PARAMETER; + } else if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (level == NSAPI_SOCKET && socket->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_KEEPALIVE: { + if(socket->connected) {// ESP32 limitation, keepalive needs to be given before connecting + return NSAPI_ERROR_UNSUPPORTED; + } + + if (optlen == sizeof(int)) { + int secs = *(int *)optval; + if (secs >= 0 && secs <= 7200) { + socket->keepalive = secs; + return NSAPI_ERROR_OK; + } + } + return NSAPI_ERROR_PARAMETER; + } + } + } + + return NSAPI_ERROR_UNSUPPORTED; +} + +nsapi_error_t ESP32Stack::getsockopt(nsapi_socket_t handle, int level, + int optname, void *optval, unsigned *optlen) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!optval || !optlen) { + return NSAPI_ERROR_PARAMETER; + } else if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (level == NSAPI_SOCKET && socket->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_KEEPALIVE: { + if(*optlen > sizeof(int)) { + *optlen = sizeof(int); + } + memcpy(optval, &(socket->keepalive), *optlen); + return NSAPI_ERROR_OK; + } + } + } + + return NSAPI_ERROR_UNSUPPORTED; +} + diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.h b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.h new file mode 100644 index 00000000000..e3d8a67f633 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/ESP32Stack.h @@ -0,0 +1,179 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP32_STACK_H +#define ESP32_STACK_H + +#include "mbed.h" +#include "ESP32.h" + +/** ESP32Stack class + * Implementation of the NetworkStack for the ESP32 + */ +class ESP32Stack : public NetworkStack +{ +protected: + /** ESP32Stack lifetime + * @param en EN pin + * @param io0 IO0 pin + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + * @param rts RTS pin + * @param cts CTS pin + * @param baudrate The baudrate of the serial port. + * @param is_ap 0:Terminal 1:Access point. + */ + ESP32Stack(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate, int is_ap); + +protected: + /** Open a socket + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative on failure + */ + virtual int socket_open(void **handle, nsapi_protocol_t proto); + + /** Close the socket + * @param handle Socket handle + * @return 0 on success, negative on failure + * @note On failure, any memory associated with the socket must still + * be cleaned up + */ + virtual int socket_close(void *handle); + + /** Bind a server socket to a specific port + * @param handle Socket handle + * @param address Local address to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + virtual int socket_bind(void *handle, const SocketAddress &address); + + /** Start listening for incoming connections + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return 0 on success, negative on failure + */ + virtual int socket_listen(void *handle, int backlog); + + /** Connects this TCP socket to the server + * @param handle Socket handle + * @param address SocketAddress to connect to + * @return 0 on success, negative on failure + */ + virtual int socket_connect(void *handle, const SocketAddress &address); + + /** Accept a new connection. + * @param handle Handle in which to store new socket + * @param server Socket handle to server to accept from + * @return 0 on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_accept(void *handle, void **socket, SocketAddress *address); + + /** Send data to the remote host + * @param handle Socket handle + * @param data The buffer to send to the host + * @param size The length of the buffer to send + * @return Number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_send(void *handle, const void *data, unsigned size); + + /** Receive data from the remote host + * @param handle Socket handle + * @param data The buffer in which to store the data received from the host + * @param size The maximum length of the buffer + * @return Number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recv(void *handle, void *data, unsigned size); + + /** Send a packet to a remote endpoint + * @param handle Socket handle + * @param address The remote SocketAddress + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return The number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + + /** Receive a packet from a remote endpoint + * @param handle Socket handle + * @param address Destination for the remote SocketAddress or null + * @param buffer The buffer for storing the incoming packet data + * If a packet is too long to fit in the supplied buffer, + * excess bytes are discarded + * @param size The length of the buffer + * @return The number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + * @note Callback may be called in an interrupt context. + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + + /** Set stack-specific socket options + * The setsockopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * + * @param handle Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t setsockopt(nsapi_socket_t handle, int level, + int optname, const void *optval, unsigned optlen); + + /** Get stack-specific socket options + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param handle Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t getsockopt(nsapi_socket_t handle, int level, + int optname, void *optval, unsigned *optlen); + +protected: + ESP32 *_esp; + uint16_t _local_ports[ESP32::SOCKET_COUNT]; + int _is_ap; +}; + +#endif diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/README.md b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/README.md new file mode 100644 index 00000000000..b27739ae61a --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/README.md @@ -0,0 +1,12 @@ +# The ESP32 WiFi driver for Mbed OS +The Mbed OS driver for the ESP32 WiFi module. + +## Firmware version +How to write mbed-os compatible firmware for GR-LCYHEE and GR-PEACH. +https://github.com/d-kato/GR-Boards_ESP32_Serial_Bridge + +## Mbed OS BLE stack using ESP32 +You can use the Mbed OS BLE stack with ESP32 by using [esp32-at-ble-stack.lib](https://github.com/d-kato/esp32-at-ble-stack). + +## Restrictions +- Setting up an UDP server is not possible diff --git a/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/mbed_lib.json b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/mbed_lib.json new file mode 100644 index 00000000000..7270acfce78 --- /dev/null +++ b/connectivity/drivers/wifi/COMPONENT_ESPRESSIF_ESP32/mbed_lib.json @@ -0,0 +1,58 @@ +{ + "name": "esp32", + "config": { + "wifi-en": { + "help": "Enable pin", + "value": "NC" + }, + "wifi-io0": { + "help": "IO0 pin", + "value": "NC" + }, + "wifi-tx": { + "help": "Serial TX pin", + "value": "NC" + }, + "wifi-rx": { + "help": "Serial RX pin", + "value": "NC" + }, + "wifi-debug": { + "help": "Enable debugging", + "value": false + }, + "wifi-rts": { + "help": "Serial RTS pin", + "value": "NC" + }, + "wifi-cts": { + "help": "Serial CTS pin", + "value": "NC" + }, + "wifi-baudrate": { + "help": "Serial baudrate", + "value": "230400" + }, + "provide-default": { + "help": "Provide default WifiInterface. [true/false]", + "value": false + } + }, + "target_overrides": { + "GR_LYCHEE": { + "esp32.wifi-en" : "P5_3", + "esp32.wifi-io0": "P3_14", + "esp32.wifi-tx" : "P7_1", + "esp32.wifi-rx" : "P0_1", + "esp32.wifi-rts": "P7_7", + "esp32.wifi-cts": "P7_6", + "esp32.provide-default": true + }, + "RZ_A1H": { + "esp32.wifi-en" : "P3_10", + "esp32.wifi-io0": "P3_9", + "esp32.wifi-tx" : "P2_14", + "esp32.wifi-rx" : "P2_15" + } + } +} diff --git a/targets/drivers.json5 b/targets/drivers.json5 index 55b648a2b8e..9a46677b4af 100644 --- a/targets/drivers.json5 +++ b/targets/drivers.json5 @@ -49,6 +49,10 @@ "description": "Wifi 802.11b/g/n MCU running ESP8266-IDF-AT AT command firmware", "friendly_name": "Espressif ESP8366" }, + "COMPONENT_ESPRESSIF_ESP32": { + "description": "Wifi 802.11b/g/n MCU running ESP32-IDF-AT AT command firmware", + "friendly_name": "Espressif ESP32" + }, // Bluetooth only modules ----------------------------------------------------------- "COMPONENT_BlueNRG_2": {