1+ #pragma once
2+
3+ #include " AbstractURLStream.h"
4+ #include " esp_crt_bundle.h"
5+ #include " esp_http_client.h"
6+ #include " esp_system.h"
7+ #include " esp_wifi.h"
8+ #include " nvs_flash.h"
9+
10+ namespace audio_tools {
11+
12+ /* *
13+ * @brief Login to Wifi using the ESP32 IDF functionality
14+ * @author Phil Schatzmann
15+ * @ingroup http
16+ * @copyright GPLv3
17+ */
18+ class WiFiESP32 {
19+ public:
20+ bool begin (const char * ssid, const char * password) {
21+ TRACEI ();
22+ if (is_open) return true ;
23+ if (!setupWIFI (ssid, password)) {
24+ LOGE (" setupWIFI failed" );
25+ return false ;
26+ }
27+
28+ // wait for connection
29+ Serial.print (" Waiting for connection " );
30+ while (!is_open) {
31+ delay (200 );
32+ Serial.print (" ." );
33+ }
34+ Serial.println ();
35+
36+ return is_open;
37+ }
38+
39+ void end () {
40+ TRACED ();
41+ if (is_open) {
42+ TRACEI ();
43+ esp_wifi_stop ();
44+ esp_wifi_deinit ();
45+ }
46+ is_open = false ;
47+ }
48+
49+ // / Register a new URLStream
50+ int doRegister () {
51+ int count = numbers.size ();
52+ int next_number;
53+ if (count == 0 ) {
54+ // provide first number
55+ next_number = 1 ;
56+ } else {
57+ next_number = numbers[count - 1 ];
58+ }
59+ numbers.push_back (next_number);
60+ return next_number;
61+ }
62+
63+ // / Deregister the URLStream: when all are removed we close the wifi
64+ void deRegister (int id) {
65+ for (int j = 0 ; j < numbers.size (); j++) {
66+ if (numbers[j] == id) {
67+ numbers.erase (j);
68+ break ;
69+ }
70+ }
71+ if (numbers.size () == 0 ) {
72+ end ();
73+ }
74+ }
75+
76+ void setPowerSave (wifi_ps_type_t powerSave){
77+ power_save = powerSave;
78+ }
79+
80+ protected:
81+ volatile bool is_open = false ;
82+ esp_ip4_addr_t ip = {0 };
83+ Vector<int > numbers;
84+ wifi_ps_type_t power_save = WIFI_PS_NONE;
85+
86+ bool setupWIFI (const char * ssid, const char * password) {
87+ assert (ssid != nullptr );
88+ assert (password != nullptr );
89+ LOGI (" setupWIFI: %s" , ssid);
90+ esp_err_t ret = nvs_flash_init ();
91+ if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
92+ ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
93+ nvs_flash_erase ();
94+ ret = nvs_flash_init ();
95+ }
96+
97+ if (esp_netif_init () != ESP_OK) {
98+ LOGE (" esp_netif_init" );
99+ return false ;
100+ };
101+
102+ if (esp_event_loop_create_default () != ESP_OK) {
103+ LOGE (" esp_event_loop_create_default" );
104+ return false ;
105+ };
106+
107+ esp_netif_t * itf = esp_netif_create_default_wifi_sta ();
108+ if (itf == nullptr ) {
109+ LOGE (" esp_netif_create_default_wifi_sta" );
110+ return false ;
111+ };
112+
113+ esp_event_handler_instance_register (WIFI_EVENT, ESP_EVENT_ANY_ID,
114+ &wifi_sta_event_handler, this , NULL );
115+ esp_event_handler_instance_register (IP_EVENT, IP_EVENT_STA_GOT_IP,
116+ &wifi_sta_event_handler, this , NULL );
117+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT ();
118+ if (esp_wifi_init (&cfg) != ESP_OK) {
119+ LOGE (" esp_wifi_init" );
120+ return false ;
121+ }
122+
123+ esp_wifi_set_mode (WIFI_MODE_STA);
124+ esp_wifi_set_ps (power_save);
125+
126+
127+ wifi_config_t sta_config;
128+ memset (&sta_config, 0 , sizeof (wifi_config_t ));
129+ strncpy ((char *)sta_config.sta .ssid , ssid, 32 );
130+ strncpy ((char *)sta_config.sta .password , password, 32 );
131+ sta_config.sta .threshold .authmode = WIFI_AUTH_WPA_WPA2_PSK;
132+ esp_wifi_set_config (WIFI_IF_STA, &sta_config);
133+
134+ // start wifi
135+ bool rc = esp_wifi_start () == ESP_OK;
136+ if (!rc) {
137+ LOGE (" esp_wifi_start" );
138+ }
139+ return rc;
140+ }
141+
142+ static void wifi_sta_event_handler (void * arg, esp_event_base_t event_base,
143+ int32_t event_id, void * event_data) {
144+ WiFiESP32* self = (WiFiESP32*)arg;
145+
146+ if (event_base == WIFI_EVENT) {
147+ switch (event_id) {
148+ case WIFI_EVENT_STA_START:
149+ LOGI (" WIFI_EVENT_STA_START" );
150+ esp_wifi_connect ();
151+ break ;
152+ case WIFI_EVENT_STA_DISCONNECTED:
153+ LOGI (" WIFI_EVENT_STA_DISCONNECTED" );
154+ esp_wifi_connect ();
155+ break ;
156+ }
157+ } else if (event_base == IP_EVENT) {
158+ switch (event_id) {
159+ case IP_EVENT_STA_GOT_IP: {
160+ ip_event_got_ip_t * event = (ip_event_got_ip_t *)event_data;
161+ self->ip = event->ip_info .ip ;
162+ self->is_open = true ;
163+ LOGI (" ==> Station connected with IP: " IPSTR " , GW: " IPSTR
164+ " , Mask: " IPSTR " ." ,
165+ IP2STR (&event->ip_info .ip ), IP2STR (&event->ip_info .gw ),
166+ IP2STR (&event->ip_info .netmask ));
167+ break ;
168+ }
169+ }
170+ }
171+ }
172+
173+ } static esp32_wifi;
174+
175+ /* *
176+ * @brief URLStream using the ESP32 IDF API.
177+ *
178+ * For Https you need to provide the certificate.
179+ * Execute: openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
180+ *
181+ * To completely disable the certificate check, you will need to go to ESP-TLS
182+ * in menuconfig, enable "Allow potentially insecure options" and then enable
183+ * "Skip server certificate verification by default" (accepting risks).
184+ *
185+ * This is unfortunately not an option when using Arduino!
186+ *
187+ * @author Phil Schatzmann
188+ * @ingroup http
189+ * @copyright GPLv3
190+ */
191+
192+ class URLStreamESP32 : public AbstractURLStream {
193+ public:
194+ URLStreamESP32 (const char * ssid, const char * pwd) {
195+ setSSID (ssid);
196+ setPassword (pwd);
197+ id = esp32_wifi.doRegister ();
198+ _timeout = 8000 ;
199+ }
200+ URLStreamESP32 () : URLStreamESP32(nullptr , nullptr ) {}
201+ ~URLStreamESP32 () {
202+ end ();
203+ esp32_wifi.deRegister (id);
204+ }
205+ // executes the URL request
206+ virtual bool begin (const char * urlStr, const char * acceptMime = " " ,
207+ MethodID action = GET, const char * reqMime = " " ,
208+ const char * reqData = " " ) {
209+ TRACED ();
210+ // start wifi if necessary and possible
211+ if (ssid != nullptr ) {
212+ if (!esp32_wifi.begin (ssid, password)) {
213+ LOGE (" Wifi failed" );
214+ return false ;
215+ }
216+ }
217+
218+ // determine wifi country
219+ wifi_country_t cntry;
220+ memset (&cntry, 0 , sizeof (wifi_country_t ));
221+ esp_wifi_get_country (&cntry);
222+ char text[4 ] = {0 };
223+ strncpy (text, cntry.cc , 3 );
224+ LOGI (" wifi country: %s" , text);
225+
226+ // fill http_config
227+ esp_http_client_config_t http_config;
228+ memset (&http_config, 0 , sizeof (http_config));
229+ http_config.url = urlStr;
230+ http_config.user_agent = DEFAULT_AGENT;
231+ http_config.event_handler = http_event_handler;
232+ http_config.buffer_size = buffer_size;
233+ http_config.timeout_ms = _timeout;
234+ // for SSL
235+ if (pem_cert != nullptr ) {
236+ http_config.cert_pem = (const char *)pem_cert;
237+ http_config.cert_len = pem_cert_len;
238+ } else {
239+ http_config.crt_bundle_attach = esp_crt_bundle_attach;
240+ }
241+
242+ switch (action) {
243+ case GET:
244+ http_config.method = HTTP_METHOD_GET;
245+ break ;
246+ case POST:
247+ http_config.method = HTTP_METHOD_POST;
248+ break ;
249+ case PUT:
250+ http_config.method = HTTP_METHOD_PUT;
251+ break ;
252+ case DELETE:
253+ http_config.method = HTTP_METHOD_DELETE;
254+ break ;
255+ }
256+
257+ // Init only the first time
258+ if (client_handle == nullptr ) {
259+ client_handle = esp_http_client_init (&http_config);
260+ }
261+
262+ // process header parameters
263+ if (!StrView (acceptMime).isEmpty ()) addRequestHeader (ACCEPT, acceptMime);
264+ if (!StrView (reqMime).isEmpty ()) addRequestHeader (CONTENT_TYPE, reqMime);
265+ List<HttpHeaderLine*>& lines = request.header ().getHeaderLines ();
266+ for (auto it = lines.begin (); it != lines.end (); ++it) {
267+ if ((*it)->active ) {
268+ esp_http_client_set_header (client_handle, (*it)->key .c_str (),
269+ (*it)->value .c_str ());
270+ }
271+ }
272+
273+ // Open http
274+ if (esp_http_client_open (client_handle, 0 ) != ESP_OK) {
275+ LOGE (" esp_http_client_open" );
276+ return false ;
277+ }
278+
279+ // Determine the result
280+ int content_length = esp_http_client_fetch_headers (client_handle);
281+ int status_code = esp_http_client_get_status_code (client_handle);
282+ LOGI (" status_code: %d / content_length: %d" , status_code, content_length);
283+
284+ // Process post/put data
285+ StrView data (reqData);
286+ if (!data.isEmpty ()) {
287+ write ((const uint8_t *)reqData, data.length ());
288+ }
289+
290+ return status_code == 200 ;
291+ }
292+ // ends the request
293+ virtual void end () override {
294+ esp_http_client_close (client_handle);
295+ esp_http_client_cleanup (client_handle);
296+ }
297+ // / provides access to the HttpRequest
298+ virtual HttpRequest& httpRequest () { return request; }
299+ // / Writes are not supported
300+ int availableForWrite () override { return 1024 ; }
301+
302+ // / (Re-)defines the client
303+ virtual void setClient (Client& clientPar) {};
304+
305+ // / Sets the ssid that will be used for logging in (when calling begin)
306+ virtual void setSSID (const char * ssid) { this ->ssid = ssid; }
307+
308+ // / Sets the password that will be used for logging in (when calling begin)
309+ virtual void setPassword (const char * password) { this ->password = password; }
310+
311+ // / Sets the power save mode (default false)!
312+ virtual void setPowerSave (bool ps) { esp32_wifi.setPowerSave (ps?WIFI_PS_MAX_MODEM: WIFI_PS_NONE); }
313+
314+ size_t write (const uint8_t * data, size_t len) override {
315+ TRACED ();
316+ return esp_http_client_write (client_handle, (const char *)data, len);
317+ }
318+
319+ size_t readBytes (uint8_t * data, size_t len) override {
320+ TRACED ();
321+ return esp_http_client_read (client_handle, (char *)data, len);
322+ }
323+
324+ // / Adds/Updates a request header
325+ void addRequestHeader (const char * header, const char * value) {
326+ TRACED ();
327+ request.header ().put (header, value);
328+ }
329+
330+ void setBufferSize (int size) { buffer_size = size; }
331+
332+ // / Define the Root PEM Certificate for SSL
333+ void setSSLCertificate (const uint8_t * cert, int len) {
334+ pem_cert_len = len;
335+ pem_cert = cert;
336+ }
337+
338+ protected:
339+ int id = 0 ;
340+ HttpRequest request;
341+ esp_http_client_handle_t client_handle = nullptr ;
342+ bool is_power_save = false ;
343+ const char * ssid = nullptr ;
344+ const char * password = nullptr ;
345+ int buffer_size = DEFAULT_BUFFER_SIZE;
346+ const uint8_t * pem_cert = nullptr ;
347+ int pem_cert_len = 0 ;
348+
349+ static esp_err_t http_event_handler (esp_http_client_event_t * evt) {
350+ switch (evt->event_id ) {
351+ case HTTP_EVENT_ERROR:
352+ ESP_LOGI (TAG, " HTTP_EVENT_ERROR" );
353+ break ;
354+ case HTTP_EVENT_ON_CONNECTED:
355+ ESP_LOGI (TAG, " HTTP_EVENT_ON_CONNECTED" );
356+ break ;
357+ case HTTP_EVENT_HEADER_SENT:
358+ ESP_LOGI (TAG, " HTTP_EVENT_HEADER_SENT" );
359+ break ;
360+ case HTTP_EVENT_ON_HEADER:
361+ ESP_LOGI (TAG, " HTTP_EVENT_ON_HEADER, key=%s, value=%s" , evt->header_key ,
362+ evt->header_value );
363+ break ;
364+ case HTTP_EVENT_ON_DATA:
365+ ESP_LOGI (TAG, " HTTP_EVENT_ON_DATA, len=%d" , evt->data_len );
366+ break ;
367+ case HTTP_EVENT_ON_FINISH:
368+ ESP_LOGI (TAG, " HTTP_EVENT_ON_FINISH" );
369+ break ;
370+ case HTTP_EVENT_DISCONNECTED:
371+ ESP_LOGI (TAG, " HTTP_EVENT_DISCONNECTED" );
372+ break ;
373+ case HTTP_EVENT_REDIRECT:
374+ ESP_LOGI (TAG, " HTTP_EVENT_REDIRECT" );
375+ break ;
376+ }
377+ return ESP_OK;
378+ }
379+ };
380+
381+ // Support URLStream w/o Arduino
382+ #ifndef ARDUINO
383+ using URLStream = URLStreamESP32;
384+ #endif
385+
386+ } // namespace audio_tools
0 commit comments