diff --git a/src/network/netutils.cpp b/src/network/netutils.cpp new file mode 100644 index 000000000..b07b8706a --- /dev/null +++ b/src/network/netutils.cpp @@ -0,0 +1,282 @@ +/* + Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - + Copyright (c) 2019-2023 Fabrizio Di Vittorio. + All rights reserved. + + +* Please contact fdivitto2013@gmail.com if you need a commercial license. + + +* This library and related software is available under GPL v3. + + FabGL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FabGL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FabGL. If not, see . + */ + + + +#include + +#include "esp_event.h" +#include "esp_wifi.h" +#include "esp_wifi_default.h" + +#include "fabutils.h" + +#include "netutils.h" + + + +namespace fabgl { + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// WiFiScanner + + +WiFiScanner::WiFiScanner() + : m_items(nullptr), m_count(0) +{ +} + + +WiFiScanner::~WiFiScanner() +{ + cleanUp(); +} + + +// if justCount = true, maxItems is ignored +bool WiFiScanner::scan(int maxItems, bool justCount) +{ + esp_event_loop_create_default(); + + // init with reduced memory footprint + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + cfg.static_rx_buf_num = 2; + cfg.static_tx_buf_num = 1; + cfg.ampdu_rx_enable = cfg.ampdu_tx_enable = cfg.amsdu_tx_enable = 0; + esp_wifi_init(&cfg); + + esp_wifi_set_mode(WIFI_MODE_STA); + esp_wifi_start(); + + auto r = esp_wifi_scan_start(nullptr, true); + + if (r == ESP_OK) { + esp_wifi_scan_get_ap_num(&m_count); + if (!justCount) { + m_count = std::min(m_count, maxItems); + m_items = new wifi_ap_record_t[m_count]; + uint16_t tempCount = m_count; + esp_wifi_scan_get_ap_records(&tempCount, m_items); // use tempCount because esp_wifi_scan_get_ap_records() changes it on exit (standing to the docs) + } + } + + esp_wifi_clear_ap_list(); // needed if esp_wifi_scan_get_ap_records() is not used + esp_wifi_stop(); + esp_wifi_deinit(); + esp_event_loop_delete_default(); + + return r == ESP_OK; +} + + +void WiFiScanner::cleanUp() +{ + if (m_items) + delete [] m_items; + m_items = nullptr; +} + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// WiFiConnection + + +WiFiConnection::WiFiConnection() + : m_netif(nullptr), m_state(WiFiConnectionState::Disconnected) +{ +} + + +WiFiConnection::~WiFiConnection() +{ + disconnect(); +} + + +WiFiConnectionState WiFiConnection::connect(char const * ssid, char const * password, int waitConnectionTimeOutMS) +{ + if (m_state == WiFiConnectionState::Disconnected) { + + esp_netif_init(); + esp_event_loop_create_default(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_wifi_init(&cfg); + + m_state = WiFiConnectionState::ConnectingWiFi; + + esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_WIFI_STA(); + m_netif = esp_netif_new(&netif_config); + + esp_netif_attach_wifi_station(m_netif); + esp_wifi_set_default_wifi_sta_handlers(); + + esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifiEventStaDisconnected, this); + esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &wifiEventStaConnected, this); + esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ipEventStaGotIP, this); + + esp_wifi_set_storage(WIFI_STORAGE_RAM); + + wifi_config_t wifi_config; + memset(&wifi_config, 0, sizeof(wifi_config)); + strncpy((char *)wifi_config.sta.ssid, ssid, strlen(ssid)); + if (password) + strncpy((char *)wifi_config.sta.password, password, strlen(password)); + + esp_wifi_set_mode(WIFI_MODE_STA); + esp_wifi_set_config(WIFI_IF_STA, &wifi_config); + esp_wifi_start(); + esp_wifi_connect(); + + } + + TimeOut timeout; + while (m_state != WiFiConnectionState::Connected && !timeout.expired(waitConnectionTimeOutMS)) + vTaskDelay(100 / portTICK_PERIOD_MS); + + return m_state; +} + + +void WiFiConnection::disconnect() +{ + if (m_state != WiFiConnectionState::Disconnected) { + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifiEventStaDisconnected); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &wifiEventStaConnected); + esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &ipEventStaGotIP); + esp_wifi_stop(); + esp_wifi_clear_default_wifi_driver_and_handlers(m_netif); + esp_netif_destroy(m_netif); + m_netif = nullptr; + esp_wifi_deinit(); + esp_netif_deinit(); + esp_event_loop_delete_default(); + m_state = WiFiConnectionState::Disconnected; + } +} + + +void WiFiConnection::wifiEventStaConnected(void * arg, esp_event_base_t event_base, int32_t event_id, void * event_data) +{ + auto obj = (WiFiConnection *)arg; + if (obj->m_state == WiFiConnectionState::ConnectingWiFi) + obj->m_state = WiFiConnectionState::WaitingIP; +} + + +void WiFiConnection::wifiEventStaDisconnected(void * arg, esp_event_base_t event_base, int32_t event_id, void * event_data) +{ + auto obj = (WiFiConnection *)arg; + if (obj->m_state == WiFiConnectionState::ConnectingWiFi || obj->m_state == WiFiConnectionState::Connected) { + // tries to reconnect + esp_wifi_connect(); + } +} + + +void WiFiConnection::ipEventStaGotIP(void * arg, esp_event_base_t event_base, int32_t event_id, void * event_data) +{ + auto obj = (WiFiConnection *)arg; + if (obj->m_state == WiFiConnectionState::WaitingIP) { + auto event = (ip_event_got_ip_t *)event_data; + obj->m_IP = event->ip_info.ip; + obj->m_netmask = event->ip_info.netmask; + obj->m_gateway = event->ip_info.gw; + obj->m_state = WiFiConnectionState::Connected; + } +} + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// HTTPRequest + + +HTTPRequest::HTTPRequest() + : m_client(nullptr) +{ +} + + +HTTPRequest::~HTTPRequest() +{ + close(); +} + + +esp_err_t HTTPRequest::httpEventHandler(esp_http_client_event_t * evt) +{ + auto obj = (HTTPRequest *) evt->user_data; + + switch(evt->event_id) { + case HTTP_EVENT_ERROR: + break; + case HTTP_EVENT_ON_CONNECTED: + break; + case HTTP_EVENT_ON_DATA: + obj->onData(evt->data, evt->data_len); + break; + case HTTP_EVENT_ON_FINISH: + break; + case HTTP_EVENT_DISCONNECTED: + break; + default: + break; + } + + return ESP_OK; +} + + +int HTTPRequest::GET(char const * URL) +{ + if (m_client) { + esp_http_client_set_url(m_client, URL); + } else { + esp_http_client_config_t config = { + .url = URL, + .event_handler = httpEventHandler, + .user_data = this, + }; + m_client = esp_http_client_init(&config); + } + return esp_http_client_perform(m_client) == ESP_OK ? esp_http_client_get_status_code(m_client) : 0; +} + + +void HTTPRequest::close() +{ + if (m_client) { + esp_http_client_close(m_client); + esp_http_client_cleanup(m_client); + m_client = nullptr; + } +} + + +} // end of namespace diff --git a/src/network/netutils.h b/src/network/netutils.h new file mode 100644 index 000000000..95289d780 --- /dev/null +++ b/src/network/netutils.h @@ -0,0 +1,298 @@ +/* + Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - + Copyright (c) 2019-2023 Fabrizio Di Vittorio. + All rights reserved. + + +* Please contact fdivitto2013@gmail.com if you need a commercial license. + + +* This library and related software is available under GPL v3. + + FabGL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FabGL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FabGL. If not, see . + */ + + +#pragma once + + + +/** + * @file + * + * @brief This file contains fabgl::WiFiScanner, fabgl::WiFiConnection, fabgl::HTTPRequest definitions. + */ + + +#include +#include + +#include "esp_system.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_http_client.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "fabglconf.h" +#include "fabutils.h" + + + +namespace fabgl { + + + +/////////////////////////////////////////////////////////////////////////////////// +// WiFiScanner + +/** +* @brief WiFi scanner helper class +* +* WiFi scanner allows to scan for nearby WiFi networks +* +* Example: +* +* fabgl::WiFiScanner scanner; +* if (scanner.scan()) +* for (int i = 0; i < scanner.count(); ++i) { +* auto item = scanner.get(i); +* Term.printf("#%d %s %d dBm\r\n", i, item->ssid, item->rssi); +* } +*/ +class WiFiScanner { + +public: + + WiFiScanner(); + ~WiFiScanner(); + + /** + * @brief Performs WiFi scan + * + * @param maxItems Maximum number of networks to return + * @param justCount If true just count number of networks (WiFiScanner::get() will returns nullptr and maxItems is ignored) + * + * @return True on success + */ + bool scan(int maxItems = 8, bool justCount = false); + + /** + * @brief Returns number of found or maximum requested networks + * + * @return Number of found or maximum requested networks + */ + int count() { return m_count; } + + /** + * @brief Returns specified network info + * + * @param index Index of network to get (0 = first network + * + * @return WiFi network info or nullptr in case justCount parameter of scan() is True + */ + wifi_ap_record_t * get(int index) { return m_items ? m_items + index : nullptr; } + + /** + * @brief Free resources used to scan WiFi networks + */ + void cleanUp(); + +private: + + wifi_ap_record_t * m_items; + uint16_t m_count; +}; + + + +/////////////////////////////////////////////////////////////////////////////////// +// WiFiConnection + + +/** \ingroup Enumerations +* @brief This enum defines fabgl::WiFiConnection state +*/ +enum class WiFiConnectionState { + Disconnected, /**< Disconnected or unable to connect */ + ConnectingWiFi, /**< Connecting in progress */ + WaitingIP, /**< Connecting success, waiting for IP */ + Connected, /**< Successfully connected */ +}; + + +/** +* @brief WiFi connection helper class +* +* WiFiConnection allows to establish and maintain a connection with a WiFi network +* +* Example: +* +* auto nc = fabgl::WiFiConnection(); +* if (nc.connect("MySSID", "MyPassword")) { +* Term.printf("Connected. IP:%d.%d.%d.%d NMASK:%d.%d.%d.%d GATEW:%d.%d.%d.%d\r\n", IP2STR(&nc.IP()), IP2STR(&nc.netmask()), IP2STR(&nc.gateway())); +* +* ...to some stuff with the network (ie using fabgl::HTTPRequest) +* +* // now disconnect +* nc.disconnect(); +* } +*/ +class WiFiConnection { + +public: + + WiFiConnection(); + ~WiFiConnection(); + + /** + * @brief Tries to connect to a WiFi network. + * + * @param ssid SSID of required WiFi network + * @param password Password of the WiFi network + * @param waitConnectionTimeOutMS Timeout in milliseconds + * + * @return Returns WiFiConnectionState::Connected on success + */ + WiFiConnectionState connect(char const * ssid, char const * password, int waitConnectionTimeOutMS = 10000); + + /** + * @brief Disconnects from the WiFi + */ + void disconnect(); + + /** + * @brief Returns connection state + * + * @return Returns current connection state + */ + WiFiConnectionState state() { return m_state; } + + /** + * @brief Returns IP address + * + * @return Returns acquired IP address + */ + esp_ip4_addr_t const & IP() { return m_IP; } + + /** + * @brief Returns netmask + * + * @return Returns acquired netmask + */ + esp_ip4_addr_t const & netmask() { return m_netmask; } + + /** + * @brief Returns gateway address + * + * @return Returns gateway address + */ + esp_ip4_addr_t const & gateway() { return m_gateway; } + + +private: + + static void wifiEventStaConnected(void * arg, esp_event_base_t event_base, int32_t event_id, void * event_data); + static void wifiEventStaDisconnected(void * arg, esp_event_base_t event_base, int32_t event_id, void * event_data); + static void ipEventStaGotIP(void * arg, esp_event_base_t event_base, int32_t event_id, void * event_data); + + esp_netif_t * m_netif; + WiFiConnectionState m_state; + esp_ip4_addr_t m_IP; + esp_ip4_addr_t m_netmask; + esp_ip4_addr_t m_gateway; +}; + + + +/////////////////////////////////////////////////////////////////////////////////// +// HTTPRequest + + +/** +* @brief HTTP connection and request helper +* +* HTTPRequest allows to connect to a website and get a page or a file +* +* Example: +* +* auto nc = fabgl::WiFiConnection(); +* if (nc.connect("MySSID", "MyPassword")) { +* fabgl::HTTPRequest req; +* // just count how many bytes we have got +* int received = 0; +* req.onData = [&](void const * data, int len) { +* received += len; +* }; +* // start GET method +* if (req.GET("http://www.fabglib.org/schema_audio.png") == 200) +* Term.printf("Received %d of %d bytes\r\n", received, req.contentLength()); +* } +*/ +class HTTPRequest { + +public: + + HTTPRequest(); + ~HTTPRequest(); + + /** + * @brief Performs the GET method + * + * Multiple consecutive requestes can be issued by the same fabgl::HTTPRequest object. + * + * @param URL The page or file to retrieve + * + * @return Returns the HTTP error or success code (0 = connection failure) + */ + int GET(char const * URL); + + /** + * @brief Gets page content length + * + * @return Returns page content length as returned by the web server + */ + int contentLength() { return esp_http_client_get_content_length(m_client); } + + /** + * @brief Closes HTTP connection + */ + void close(); + + + // delegates + + /** + * @brief Data received delegate + * + * This delegate is called whenever a block of data has been received. + * First parameter specifies a pointer to the received buffer. + * Second parameter specifies received buffer length in bytes. + */ + Delegate onData; // (data, len) + + +private: + + static esp_err_t httpEventHandler(esp_http_client_event_t * evt); + + esp_http_client_handle_t m_client; + +}; + + + +} // end of namespace +