Skip to content

Commit c95b68f

Browse files
committed
New class: URLStreamESP32
1 parent 3a5773d commit c95b68f

File tree

2 files changed

+391
-0
lines changed

2 files changed

+391
-0
lines changed

src/AudioTools/CoreAudio/AudioHttp/HttpHeader.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,11 @@ class HttpHeader {
312312
temp_buffer.resize(bufferSize);
313313
}
314314

315+
/// Provides the http parameter lines
316+
List<HttpHeaderLine*> &getHeaderLines(){
317+
return lines;
318+
}
319+
315320
protected:
316321
int status_code = UNDEFINED;
317322
bool is_written = false;
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
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

Comments
 (0)