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