arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
URLStreamESP32.h
1#pragma once
2
3#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h"
4#include "AudioTools/CoreAudio/AudioHttp/HttpRequest.h"
5#include "AudioTools/CoreAudio/AudioHttp/ICYStreamT.h"
6#include "AudioTools/CoreAudio/AudioHttp/URLStreamBufferedT.h"
7#include "esp_http_client.h"
8#include "esp_idf_version.h"
9#include "esp_system.h"
10#include "esp_wifi.h"
11#include "nvs_flash.h"
12
13namespace audio_tools {
14
22class WiFiESP32 {
23 public:
24 bool begin(const char* ssid, const char* password) {
25 TRACEI();
26 if (is_open) return true;
27 if (!setupWIFI(ssid, password)) {
28 LOGE("setupWIFI failed");
29 return false;
30 }
31
32 return true;
33 }
34
35 void end() {
36 TRACED();
37 if (is_open) {
38 TRACEI();
39 esp_wifi_stop();
40 esp_wifi_deinit();
41 }
42 is_open = false;
43 }
44
45 void setPowerSave(wifi_ps_type_t powerSave) { power_save = powerSave; }
46
47 bool isConnected() { return is_open; }
48
49 protected:
50 volatile bool is_open = false;
51 esp_ip4_addr_t ip = {0};
52 wifi_ps_type_t power_save = WIFI_PS_NONE;
53
54 bool setupWIFI(const char* ssid, const char* password) {
55 assert(ssid != nullptr);
56 assert(password != nullptr);
57 LOGI("setupWIFI: %s", ssid);
58 esp_err_t ret = nvs_flash_init();
59 if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
60 ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
61 nvs_flash_erase();
62 ret = nvs_flash_init();
63 }
64
65 if (esp_netif_init() != ESP_OK) {
66 LOGE("esp_netif_init");
67 return false;
68 };
69
70 if (esp_event_loop_create_default() != ESP_OK) {
71 LOGE("esp_event_loop_create_default");
72 return false;
73 };
74
75 esp_netif_t* itf = esp_netif_create_default_wifi_sta();
76 if (itf == nullptr) {
77 LOGE("esp_netif_create_default_wifi_sta");
78 return false;
79 };
80
81 esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
82 &wifi_sta_event_handler, this, NULL);
83 esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
84 &wifi_sta_event_handler, this, NULL);
85 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
86 if (esp_wifi_init(&cfg) != ESP_OK) {
87 LOGE("esp_wifi_init");
88 return false;
89 }
90
91 esp_wifi_set_mode(WIFI_MODE_STA);
92 esp_wifi_set_ps(power_save);
93
94 wifi_config_t sta_config;
95 memset(&sta_config, 0, sizeof(wifi_config_t));
96 strncpy((char*)sta_config.sta.ssid, ssid, 32);
97 strncpy((char*)sta_config.sta.password, password, 32);
98 sta_config.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK;
99 esp_wifi_set_config(WIFI_IF_STA, &sta_config);
100
101 // start wifi
102 bool rc = esp_wifi_start() == ESP_OK;
103 if (!rc) {
104 LOGE("esp_wifi_start");
105 }
106 return rc;
107 }
108
109 static void wifi_sta_event_handler(void* arg, esp_event_base_t event_base,
110 int32_t event_id, void* event_data) {
111 WiFiESP32* self = (WiFiESP32*)arg;
112
113 if (event_base == WIFI_EVENT) {
114 switch (event_id) {
115 case WIFI_EVENT_STA_START:
116 LOGI("WIFI_EVENT_STA_START");
117 esp_wifi_connect();
118 break;
119 case WIFI_EVENT_STA_DISCONNECTED:
120 LOGI("WIFI_EVENT_STA_DISCONNECTED");
121 esp_wifi_connect();
122 break;
123 }
124 } else if (event_base == IP_EVENT) {
125 switch (event_id) {
126 case IP_EVENT_STA_GOT_IP: {
127 ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;
128 self->ip = event->ip_info.ip;
129 self->is_open = true;
130 LOGI("==> Station connected with IP: " IPSTR ", GW: " IPSTR
131 ", Mask: " IPSTR ".",
132 IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.gw),
133 IP2STR(&event->ip_info.netmask));
134 break;
135 }
136 }
137 }
138 }
139
140} static IDF_WIFI;
141
142class URLStreamESP32;
143static URLStreamESP32* actualURLStreamESP32 = nullptr;
144
164 public:
165 URLStreamESP32(const char* ssid, const char* pwd) {
166 setSSID(ssid);
167 setPassword(pwd);
168 _timeout = 8000;
169 }
170 URLStreamESP32() : URLStreamESP32(nullptr, nullptr) {}
171 ~URLStreamESP32() { end(); }
172 // executes the URL request
173 virtual bool begin(const char* urlStr, const char* acceptMime = "",
174 MethodID action = GET, const char* reqMime = "",
175 const char* reqData = "") {
176 TRACED();
177 total_read = 0;
178 url_str = urlStr;
179 content_length = 0;
180 // start wifi if necessary and possible
181 if (ssid != nullptr) {
182 if (!IDF_WIFI.begin(ssid, password)) {
183 LOGE("Wifi failed");
184 return false;
185 }
186 if (!IDF_WIFI.isConnected()) {
187 // wait for connection
188 Serial.print("Waiting for connection ");
189 while (!IDF_WIFI.isConnected()) {
190 delay(200);
191 Serial.print(".");
192 }
193 Serial.println();
194 }
195 }
196
197 // for headers
198 actualURLStreamESP32 = this;
199
200 // determine wifi country
201 wifi_country_t cntry;
202 memset(&cntry, 0, sizeof(wifi_country_t));
203 esp_wifi_get_country(&cntry);
204 char text[4] = {0};
205 strncpy(text, cntry.cc, 3);
206 LOGI("wifi country: %s", text);
207
208 // fill http_config
209 esp_http_client_config_t http_config;
210 memset(&http_config, 0, sizeof(http_config));
211 http_config.url = urlStr;
212 http_config.user_agent = DEFAULT_AGENT;
213 http_config.event_handler = http_event_handler;
214 http_config.buffer_size = buffer_size;
215 http_config.timeout_ms = _timeout;
216 http_config.user_data = this;
217 // for SSL certificate
218 if (pem_cert != nullptr) {
219 http_config.cert_pem = (const char*)pem_cert;
220 http_config.cert_len = pem_cert_len;
221 }
222 // for SSL (use of a bundle for certificate verification)
223 if (crt_bundle_attach != nullptr) {
224 http_config.crt_bundle_attach = crt_bundle_attach;
225 }
226
227 switch (action) {
228 case GET:
229 http_config.method = HTTP_METHOD_GET;
230 break;
231 case POST:
232 http_config.method = HTTP_METHOD_POST;
233 break;
234 case PUT:
235 http_config.method = HTTP_METHOD_PUT;
236 break;
237 case DELETE:
238 http_config.method = HTTP_METHOD_DELETE;
239 break;
240 default:
241 LOGE("Unsupported action: %d", action);
242 break;
243 }
244
245 // Init only the first time
246 if (client_handle == nullptr) {
247 client_handle = esp_http_client_init(&http_config);
248 }
249
250 // process header parameters
251 if (!StrView(acceptMime).isEmpty()) addRequestHeader(ACCEPT, acceptMime);
252 if (!StrView(reqMime).isEmpty()) addRequestHeader(CONTENT_TYPE, reqMime);
253 List<HttpHeaderLine*>& lines = request.header().getHeaderLines();
254 for (auto it = lines.begin(); it != lines.end(); ++it) {
255 if ((*it)->active) {
256 esp_http_client_set_header(client_handle, (*it)->key.c_str(),
257 (*it)->value.c_str());
258 }
259 }
260
261 // Open http
262 if (esp_http_client_open(client_handle, 0) != ESP_OK) {
263 LOGE("esp_http_client_open");
264 return false;
265 }
266
267 // Determine the result
268 content_length = esp_http_client_fetch_headers(client_handle);
269 int status_code = esp_http_client_get_status_code(client_handle);
270 LOGI("status_code: %d / content_length: %d", status_code, content_length);
271
272 // Process post/put data
273 StrView data(reqData);
274 if (!data.isEmpty()) {
275 write((const uint8_t*)reqData, data.length());
276 }
277
278 return status_code == 200;
279 }
280 // ends the request
281 virtual void end() override {
282 esp_http_client_close(client_handle);
283 esp_http_client_cleanup(client_handle);
284 }
285
287 int availableForWrite() override { return 1024; }
288
290 virtual void setSSID(const char* ssid) { this->ssid = ssid; }
291
293 virtual void setPassword(const char* password) { this->password = password; }
294
296 virtual void setPowerSave(bool ps) {
297 IDF_WIFI.setPowerSave(ps ? WIFI_PS_MAX_MODEM : WIFI_PS_NONE);
298 }
299
300 size_t write(const uint8_t* data, size_t len) override {
301 TRACED();
302 return esp_http_client_write(client_handle, (const char*)data, len);
303 }
304
305 size_t readBytes(uint8_t* data, size_t len) override {
306 TRACED();
307 size_t read = esp_http_client_read(client_handle, (char*)data, len);
308 total_read += read;
309 return read;
310 }
311
313 void addRequestHeader(const char* key, const char* value) override {
314 TRACED();
315 request.addRequestHeader(key, value);
316 }
318 const char* getReplyHeader(const char* key) override {
319 return request.getReplyHeader(key);
320 }
321
324 void setCACert(const char* cert) override {
325 int len = strlen(cert);
326 setCACert((const uint8_t*)cert, len + 1);
327 }
328
331 void setCACert(esp_err_t (*cb)(void* conf)) { crt_bundle_attach = cb; }
332
334 void setReadBufferSize(int size) { buffer_size = size; }
335
337 HttpRequest& httpRequest() override { return request; }
338
340 void setClient(Client& client) override {}
341
343 const char* urlStr() override { return (char*)url_str; }
344
346 size_t totalRead() override { return total_read; }
347
349 int contentLength() override { return content_length; }
350
352 virtual bool waitForData(int timeout) override{ return false; }
354 void setConnectionClose(bool flag) override {}
355
356 protected:
357 int id = 0;
358 HttpRequest request;
359 esp_http_client_handle_t client_handle = nullptr;
360 bool is_power_save = false;
361 const char* ssid = nullptr;
362 const char* password = nullptr;
363 int buffer_size = DEFAULT_BUFFER_SIZE;
364 const uint8_t* pem_cert = nullptr;
365 int pem_cert_len = 0;
366 esp_err_t (*crt_bundle_attach)(void* conf) = nullptr;
367 size_t total_read = 0;
368 const char* url_str = nullptr;
369 int content_length = 0;
370
373 void setCACert(const uint8_t* cert, int len) {
374 pem_cert_len = len;
375 pem_cert = cert;
376 // certificate must end with traling null
377 assert(cert[len - 1] == 0);
378 }
379
380 static esp_err_t http_event_handler(esp_http_client_event_t* evt) {
381 switch (evt->event_id) {
382 case HTTP_EVENT_ERROR:
383 LOGI("HTTP_EVENT_ERROR");
384 break;
385 case HTTP_EVENT_ON_CONNECTED:
386 LOGD("HTTP_EVENT_ON_CONNECTED");
387 break;
388 case HTTP_EVENT_HEADER_SENT:
389 LOGD("HTTP_EVENT_HEADER_SENT");
390 break;
391 case HTTP_EVENT_ON_HEADER:
392 LOGI("HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key,
393 evt->header_value);
394 // store reply headers
395 actualURLStreamESP32->request.reply().put(evt->header_key,
396 evt->header_value);
397 break;
398 case HTTP_EVENT_ON_DATA:
399 LOGD("HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
400 break;
401 case HTTP_EVENT_ON_FINISH:
402 LOGI("HTTP_EVENT_ON_FINISH");
403 break;
404 case HTTP_EVENT_DISCONNECTED:
405 LOGI("HTTP_EVENT_DISCONNECTED");
406 break;
407#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 3, 7)
408 case HTTP_EVENT_REDIRECT:
409 LOGI("HTTP_EVENT_REDIRECT");
410 break;
411#endif
412 }
413 return ESP_OK;
414 }
415};
416
421
423#if !defined(ARDUINO)
427#endif
428
429} // namespace audio_tools
Abstract Base class for all URLStream implementations.
Definition AbstractURLStream.h:17
Definition NoArduino.h:169
List< HttpHeaderLine * > & getHeaderLines()
Provides the http parameter lines.
Definition HttpHeader.h:308
Simple API to process get, put, post, del http requests I tried to use Arduino HttpClient,...
Definition HttpRequest.h:25
void addRequestHeader(const char *key, const char *value)
Adds/Updates a request header.
Definition HttpRequest.h:191
virtual HttpRequestHeader & header()
provides access to the request header
Definition HttpRequest.h:160
Icecast/Shoutcast Audio Stream which splits the data into metadata and audio data....
Definition ICYStreamT.h:26
Double linked list.
Definition List.h:18
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual int length()
Definition StrView.h:383
virtual bool isEmpty()
checks if the string is empty
Definition StrView.h:386
URLStream implementation for the ESP32 based on a separate FreeRTOS task: the.
Definition URLStreamBufferedT.h:154
URLStream using the ESP32 IDF API.
Definition URLStreamESP32.h:163
size_t totalRead() override
Total amout of data that was consumed so far.
Definition URLStreamESP32.h:346
virtual void setPowerSave(bool ps)
Sets the power save mode (default false)!
Definition URLStreamESP32.h:296
void setCACert(esp_err_t(*cb)(void *conf))
Definition URLStreamESP32.h:331
virtual void setSSID(const char *ssid)
Sets the ssid that will be used for logging in (when calling begin)
Definition URLStreamESP32.h:290
virtual void setPassword(const char *password)
Sets the password that will be used for logging in (when calling begin)
Definition URLStreamESP32.h:293
virtual bool waitForData(int timeout) override
Not used.
Definition URLStreamESP32.h:352
HttpRequest & httpRequest() override
Used for request and reply header parameters.
Definition URLStreamESP32.h:337
void setCACert(const uint8_t *cert, int len)
Definition URLStreamESP32.h:373
int availableForWrite() override
Writes are not supported.
Definition URLStreamESP32.h:287
void addRequestHeader(const char *key, const char *value) override
Adds/Updates a request header.
Definition URLStreamESP32.h:313
const char * urlStr() override
Provides the url as string.
Definition URLStreamESP32.h:343
void setConnectionClose(bool flag) override
Not used.
Definition URLStreamESP32.h:354
void setCACert(const char *cert) override
Definition URLStreamESP32.h:324
int contentLength() override
Provides the reported data size from the http reply.
Definition URLStreamESP32.h:349
void setClient(Client &client) override
Does nothing.
Definition URLStreamESP32.h:340
void setReadBufferSize(int size)
Defines the read buffer size.
Definition URLStreamESP32.h:334
const char * getReplyHeader(const char *key) override
Provides a header entry.
Definition URLStreamESP32.h:318
Represents the content of a URL as Stream. We use the WiFi.h API. If you run into performance issues,...
Definition URLStream.h:32
Login to Wifi using the ESP32 IDF functionality. This can be accessed with the global object IDF_WIFI...
Definition URLStreamESP32.h:22
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10