Add Serial Terminal App & WebUi Theme
This commit is contained in:
@@ -45,233 +45,6 @@ static esp_err_t load_credentials(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static esp_err_t provisioning_scan(httpd_req_t *request)
|
||||
{
|
||||
wifi_scan_config_t scan_config = {
|
||||
.show_hidden = true,
|
||||
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
|
||||
};
|
||||
esp_err_t err = esp_wifi_scan_start(&scan_config, true);
|
||||
if (err != ESP_OK) {
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Wi-Fi scan failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
uint16_t count = 0;
|
||||
esp_wifi_scan_get_ap_num(&count);
|
||||
if (count > 32) count = 32;
|
||||
wifi_ap_record_t *records = calloc(count, sizeof(wifi_ap_record_t));
|
||||
if (count > 0 && !records) {
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Not enough memory for scan");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (count > 0) {
|
||||
err = esp_wifi_scan_get_ap_records(&count, records);
|
||||
if (err != ESP_OK) {
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not read scan results");
|
||||
free(records);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
httpd_resp_set_type(request, "application/json");
|
||||
httpd_resp_send_chunk(request, "[", 1);
|
||||
uint16_t visible_count = 0;
|
||||
for (uint16_t i = 0; i < count; i++) {
|
||||
if (records[i].ssid[0] == '\0') continue;
|
||||
|
||||
char prefix[32];
|
||||
int prefix_length = snprintf(prefix, sizeof(prefix), "%s{\"ssid\":\"",
|
||||
visible_count == 0 ? "" : ",");
|
||||
httpd_resp_send_chunk(request, prefix, prefix_length);
|
||||
append_json_string(request, records[i].ssid, sizeof(records[i].ssid));
|
||||
char suffix[48];
|
||||
int suffix_length = snprintf(suffix, sizeof(suffix), "\",\"rssi\":%d}", records[i].rssi);
|
||||
httpd_resp_send_chunk(request, suffix, suffix_length);
|
||||
visible_count++;
|
||||
}
|
||||
httpd_resp_send_chunk(request, "]", 1);
|
||||
free(records);
|
||||
return httpd_resp_send_chunk(request, NULL, 0);
|
||||
}
|
||||
|
||||
static esp_err_t provisioning_get(httpd_req_t *request)
|
||||
{
|
||||
const char *page =
|
||||
"<!doctype html><html><head><meta name=viewport content=\"width=device-width\">"
|
||||
"<title>ABrobot Wi-Fi</title><style>body{font:16px sans-serif;max-width:480px;margin:2em auto;padding:0 1em}"
|
||||
"input,select,button{font:inherit;padding:.5em;width:100%;box-sizing:border-box}button{margin-top:1em}"
|
||||
"#message{margin-top:1em}</style></head><body><h1>ABrobot Wi-Fi setup</h1>"
|
||||
"<form id=form method=post action=/configure><label>Network<br><select id=networks>"
|
||||
"<option value=\"\">Scanning...</option></select></label>"
|
||||
"<p><label><input type=checkbox id=hidden onchange=toggleManual()> Hidden network</label></p>"
|
||||
"<div id=manualBox style=display:none><label>Network name<br>"
|
||||
"<input id=manual name=ssid maxlength=32></label></div>"
|
||||
"<br><br><label>Password<br><input name=password type=password maxlength=64 required></label>"
|
||||
"<br><br><button type=button onclick=scan()>Refresh networks</button>"
|
||||
"<button type=submit>Connect</button></form><p id=message></p>"
|
||||
"<script>const list=document.getElementById('networks'),manual=document.getElementById('manual');"
|
||||
"function toggleManual(){let on=document.getElementById('hidden').checked;"
|
||||
"document.getElementById('manualBox').style.display=on?'block':'none';manual.required=on;"
|
||||
"if(on){manual.value='';manual.focus()}else if(list.value)manual.value=list.value}"
|
||||
"document.getElementById('form').onsubmit=()=>{if(!manual.value){message.textContent='Select a network or enable Hidden network';return false}return true};"
|
||||
"function scan(){fetch('/scan').then(r=>r.json()).then(a=>{list.innerHTML='';"
|
||||
"a.forEach(n=>{let o=document.createElement('option');o.value=n.ssid;o.textContent=n.ssid+' ('+n.rssi+' dBm)';list.appendChild(o)});"
|
||||
"if(!a.length)list.innerHTML='<option value=\"\">No networks found</option>';"
|
||||
"if(a.length&&!document.getElementById('hidden').checked)manual.value=list.value}).catch(()=>message.textContent='Scan failed')}"
|
||||
"list.onchange=()=>{if(!document.getElementById('hidden').checked)manual.value=list.value};scan();</script></body></html>";
|
||||
httpd_resp_set_type(request, "text/html");
|
||||
return httpd_resp_send(request, page, HTTPD_RESP_USE_STRLEN);
|
||||
}
|
||||
|
||||
static esp_err_t ota_get(httpd_req_t *request)
|
||||
{
|
||||
const char *page =
|
||||
"<!doctype html><html><head><meta name=viewport content=\"width=device-width\">"
|
||||
"<title>ABrobot OTA</title><style>body{font:16px sans-serif;max-width:480px;margin:2em auto;padding:0 1em}"
|
||||
"input,button{font:inherit;padding:.6em;width:100%;box-sizing:border-box}button{margin-top:1em}"
|
||||
"progress{width:100%;margin-top:1em}#message{margin-top:1em}</style></head><body>"
|
||||
"<h1>Firmware update</h1><form id=form><input id=file type=file accept=\".bin,application/octet-stream\" required>"
|
||||
"<button type=submit>Upload firmware</button></form><progress id=progress value=0 max=100 hidden></progress>"
|
||||
"<p id=message></p><script>const form=document.getElementById('form'),file=document.getElementById('file'),"
|
||||
"progress=document.getElementById('progress'),message=document.getElementById('message');"
|
||||
"form.onsubmit=async event=>{event.preventDefault();if(!file.files.length)return;"
|
||||
"let selected=file.files[0];progress.hidden=false;progress.value=0;message.textContent='Uploading...';"
|
||||
"try{let response=await fetch('/ota',{method:'POST',headers:{'Content-Type':'application/octet-stream'},body:selected});"
|
||||
"if(!response.ok)throw new Error(await response.text());progress.value=100;message.textContent='Update installed. Rebooting...';"
|
||||
"}catch(error){message.textContent='Update failed: '+error.message;progress.hidden=true}};</script></body></html>";
|
||||
httpd_resp_set_type(request, "text/html");
|
||||
return httpd_resp_send(request, page, HTTPD_RESP_USE_STRLEN);
|
||||
}
|
||||
|
||||
static esp_err_t ota_post(httpd_req_t *request)
|
||||
{
|
||||
const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
if (!update_partition || request->content_len == 0 || request->content_len > update_partition->size) {
|
||||
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Firmware image is too large or unavailable");
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
esp_ota_handle_t ota_handle;
|
||||
esp_err_t err = esp_ota_begin(update_partition, request->content_len, &ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not begin firmware update");
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t *buffer = malloc(4096);
|
||||
if (!buffer) {
|
||||
esp_ota_abort(ota_handle);
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Not enough memory for firmware update");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
size_t remaining = request->content_len;
|
||||
while (remaining > 0) {
|
||||
size_t chunk_size = remaining < 4096 ? remaining : 4096;
|
||||
int received = httpd_req_recv(request, (char *)buffer, chunk_size);
|
||||
if (received <= 0) {
|
||||
free(buffer);
|
||||
esp_ota_abort(ota_handle);
|
||||
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Firmware upload interrupted");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
err = esp_ota_write(ota_handle, buffer, received);
|
||||
if (err != ESP_OK) {
|
||||
free(buffer);
|
||||
esp_ota_abort(ota_handle);
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not write firmware image");
|
||||
return err;
|
||||
}
|
||||
remaining -= (size_t)received;
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
err = esp_ota_end(ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Invalid firmware image");
|
||||
return err;
|
||||
}
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
if (err != ESP_OK) {
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not select new firmware");
|
||||
return err;
|
||||
}
|
||||
|
||||
httpd_resp_sendstr(request, "Firmware update installed. Rebooting.");
|
||||
ESP_LOGI(TAG, "OTA image installed to %s; rebooting", update_partition->label);
|
||||
xTaskCreate(ota_reboot_task, "ota_reboot", 2048, NULL, 5, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t provisioning_post(httpd_req_t *request)
|
||||
{
|
||||
char body[160] = {0};
|
||||
char ssid[sizeof(s_sta_ssid)] = {0};
|
||||
char password[sizeof(s_sta_password)] = {0};
|
||||
int received = httpd_req_recv(request, body, sizeof(body) - 1);
|
||||
if (received <= 0 || !form_value(body, "ssid", ssid, sizeof(ssid)) ||
|
||||
!form_value(body, "password", password, sizeof(password))) {
|
||||
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "SSID and password are required");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t err = save_credentials(ssid, password);
|
||||
if (err != ESP_OK) {
|
||||
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not save credentials");
|
||||
return err;
|
||||
}
|
||||
|
||||
snprintf(s_sta_ssid, sizeof(s_sta_ssid), "%s", ssid);
|
||||
snprintf(s_sta_password, sizeof(s_sta_password), "%s", password);
|
||||
httpd_resp_sendstr(request, "Credentials saved. The device is connecting; you can close this page.");
|
||||
set_status(WIFI_STATE_CONNECTING, 0);
|
||||
err = esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to switch to station mode: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
wifi_config_t config = {0};
|
||||
copy_wifi_string(config.sta.ssid, sizeof(config.sta.ssid), s_sta_ssid);
|
||||
copy_wifi_string(config.sta.password, sizeof(config.sta.password), s_sta_password);
|
||||
err = esp_wifi_set_config(WIFI_IF_STA, &config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to apply station credentials: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
err = esp_wifi_connect();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start station connection: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Station connection started for '%s'", s_sta_ssid);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void start_provisioning_server(void)
|
||||
{
|
||||
if (s_http_server != NULL) return;
|
||||
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
if (httpd_start(&s_http_server, &config) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start provisioning web server");
|
||||
return;
|
||||
}
|
||||
const httpd_uri_t get_uri = {.uri = "/", .method = HTTP_GET, .handler = provisioning_get};
|
||||
const httpd_uri_t ota_get_uri = {.uri = "/ota", .method = HTTP_GET, .handler = ota_get};
|
||||
const httpd_uri_t ota_post_uri = {.uri = "/ota", .method = HTTP_POST, .handler = ota_post};
|
||||
const httpd_uri_t scan_uri = {.uri = "/scan", .method = HTTP_GET, .handler = provisioning_scan};
|
||||
const httpd_uri_t post_uri = {.uri = "/configure", .method = HTTP_POST, .handler = provisioning_post};
|
||||
httpd_register_uri_handler(s_http_server, &get_uri);
|
||||
httpd_register_uri_handler(s_http_server, &ota_get_uri);
|
||||
httpd_register_uri_handler(s_http_server, &ota_post_uri);
|
||||
httpd_register_uri_handler(s_http_server, &scan_uri);
|
||||
httpd_register_uri_handler(s_http_server, &post_uri);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void start_softap(void)
|
||||
{
|
||||
wifi_config_t ap_config = {0};
|
||||
|
||||
Reference in New Issue
Block a user