diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 5cc8513..e3b4450 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRCS "eis4.c" "eis.c" "echem.c" "protocol.c" "wifi_transport.c" "temp.c" "refs.c" - "wifi_cfg.c" "ble_prov.c" + "wifi_cfg.c" "captive_portal.c" INCLUDE_DIRS "." - REQUIRES ad5941 ad5941_port nvs_flash esp_wifi esp_netif esp_event esp_timer bt) + REQUIRES ad5941 ad5941_port nvs_flash esp_wifi esp_netif esp_event esp_timer esp_http_server) diff --git a/main/ble_prov.c b/main/ble_prov.c deleted file mode 100644 index abcd815..0000000 --- a/main/ble_prov.c +++ /dev/null @@ -1,391 +0,0 @@ -#include "ble_prov.h" -#include "wifi_cfg.h" -#include "protocol.h" -#include -#include -#include "nimble/nimble_port.h" -#include "nimble/nimble_port_freertos.h" -#include "host/ble_hs.h" -#include "host/ble_uuid.h" -#include "host/util/util.h" -#include "services/gap/ble_svc_gap.h" -#include "services/gatt/ble_svc_gatt.h" -#include "esp_mac.h" - -/* wifi_transport provides these */ -extern esp_err_t wifi_transport_reconnect_sta(const char *ssid, const char *pass); -extern void wifi_transport_get_sta_state(uint8_t *state, uint32_t *ip); - -#define DEVICE_NAME "EIS4" -#define FW_VERSION "4.0" -#define RSP_BUF_MAX 128 - -/* EIS4 provisioning service UUID: 4e455334-a001-4801-b947-001122334455 */ -static const ble_uuid128_t svc_uuid = - BLE_UUID128_INIT(0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x47, 0xb9, - 0x01, 0x48, 0x01, 0xa0, 0x34, 0x53, 0x45, 0x4e); - -/* EIS4 provisioning characteristic UUID: 4e455334-a002-4801-b947-001122334455 */ -static const ble_uuid128_t chr_uuid = - BLE_UUID128_INIT(0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x47, 0xb9, - 0x01, 0x48, 0x02, 0xa0, 0x34, 0x53, 0x45, 0x4e); - -static bool s_ble_active; -static uint16_t s_chr_handle; -static uint8_t s_rsp_buf[RSP_BUF_MAX]; -static uint16_t s_rsp_len; -static uint8_t s_own_addr_type; - -static void start_advertise(void); - -/* -- response builders -- */ - -static void build_wifi_cfg_rsp(void) -{ - char ssid[WIFI_CFG_SSID_MAX], pass[WIFI_CFG_PASS_MAX]; - uint8_t *p = s_rsp_buf; - - *p++ = 0xF0; - *p++ = 0x7D; - *p++ = RSP_WIFI_CFG; - - /* STA SSID */ - if (wifi_cfg_get_sta(ssid, sizeof(ssid), pass, sizeof(pass)) == ESP_OK) { - uint8_t slen = (uint8_t)strlen(ssid); - *p++ = slen; - memcpy(p, ssid, slen); p += slen; - /* STA status */ - uint8_t state; uint32_t ip; - wifi_transport_get_sta_state(&state, &ip); - *p++ = state; - } else { - *p++ = 0; - *p++ = WIFI_STATE_DISCONNECTED; - } - - /* AP SSID */ - wifi_cfg_get_ap(ssid, sizeof(ssid), pass, sizeof(pass)); - { - uint8_t slen = (uint8_t)strlen(ssid); - *p++ = slen; - memcpy(p, ssid, slen); p += slen; - } - - *p++ = 0xF7; - s_rsp_len = (uint16_t)(p - s_rsp_buf); -} - -static void build_set_result(uint8_t ok) -{ - uint8_t *p = s_rsp_buf; - *p++ = 0xF0; - *p++ = 0x7D; - *p++ = RSP_SET_WIFI_RESULT; - *p++ = ok ? 0 : 1; - *p++ = 0xF7; - s_rsp_len = (uint16_t)(p - s_rsp_buf); -} - -static void build_device_info_rsp(void) -{ - uint8_t mac[6]; - esp_read_mac(mac, ESP_MAC_WIFI_STA); - - uint8_t *p = s_rsp_buf; - *p++ = 0xF0; - *p++ = 0x7D; - *p++ = RSP_DEVICE_INFO; - - /* version */ - uint8_t vlen = (uint8_t)strlen(FW_VERSION); - *p++ = vlen; - memcpy(p, FW_VERSION, vlen); p += vlen; - - /* name */ - uint8_t nlen = (uint8_t)strlen(DEVICE_NAME); - *p++ = nlen; - memcpy(p, DEVICE_NAME, nlen); p += nlen; - - /* MAC (6 bytes, each masked to 7-bit halves) */ - for (int i = 0; i < 6; i++) { - *p++ = mac[i] & 0x7F; - *p++ = (mac[i] >> 7) & 0x01; - } - - *p++ = 0xF7; - s_rsp_len = (uint16_t)(p - s_rsp_buf); -} - -static void build_wifi_status_rsp(void) -{ - uint8_t state; - uint32_t ip; - wifi_transport_get_sta_state(&state, &ip); - - uint8_t *p = s_rsp_buf; - *p++ = 0xF0; - *p++ = 0x7D; - *p++ = RSP_WIFI_STATUS; - *p++ = state; - if (state == WIFI_STATE_CONNECTED) { - *p++ = (ip >> 0) & 0x7F; - *p++ = (ip >> 7) & 0x7F; - *p++ = (ip >> 14) & 0x7F; - *p++ = (ip >> 21) & 0x7F; - *p++ = (ip >> 28) & 0x0F; - } - *p++ = 0xF7; - s_rsp_len = (uint16_t)(p - s_rsp_buf); -} - -/* -- command processing -- */ - -static void process_command(const uint8_t *data, uint16_t len) -{ - if (len < 4 || data[0] != 0xF0 || data[1] != 0x7D) - return; - - uint16_t end = 0; - for (uint16_t i = 2; i < len; i++) { - if (data[i] == 0xF7) { end = i; break; } - } - if (!end) return; - - uint8_t cmd = data[2]; - - switch (cmd) { - case CMD_GET_WIFI_CFG: - build_wifi_cfg_rsp(); - break; - - case CMD_SET_WIFI_STA: { - if (end < 6) { build_set_result(0); break; } - uint8_t ssid_len = data[3]; - if (3 + 1 + ssid_len + 1 >= end) { build_set_result(0); break; } - - char ssid[WIFI_CFG_SSID_MAX] = {0}; - memcpy(ssid, &data[4], ssid_len); - - uint8_t pass_len = data[4 + ssid_len]; - char pass[WIFI_CFG_PASS_MAX] = {0}; - if (pass_len > 0 && (4 + ssid_len + 1 + pass_len) <= end) - memcpy(pass, &data[5 + ssid_len], pass_len); - - esp_err_t err = wifi_cfg_set_sta(ssid, pass); - if (err == ESP_OK) - err = wifi_transport_reconnect_sta(ssid, pass); - build_set_result(err == ESP_OK); - break; - } - - case CMD_SET_WIFI_AP: { - if (end < 6) { build_set_result(0); break; } - uint8_t ssid_len = data[3]; - if (3 + 1 + ssid_len + 1 >= end) { build_set_result(0); break; } - - char ssid[WIFI_CFG_SSID_MAX] = {0}; - memcpy(ssid, &data[4], ssid_len); - - uint8_t pass_len = data[4 + ssid_len]; - char pass[WIFI_CFG_PASS_MAX] = {0}; - if (pass_len > 0 && (4 + ssid_len + 1 + pass_len) <= end) - memcpy(pass, &data[5 + ssid_len], pass_len); - - esp_err_t err = wifi_cfg_set_ap(ssid, pass); - build_set_result(err == ESP_OK); - break; - } - - case CMD_GET_DEVICE_INFO: - build_device_info_rsp(); - break; - - case CMD_WIFI_STATUS: - build_wifi_status_rsp(); - break; - - default: - return; - } -} - -/* -- GATT -- */ - -static int chr_access_cb(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - (void)conn_handle; - (void)attr_handle; - (void)arg; - - switch (ctxt->op) { - case BLE_GATT_ACCESS_OP_READ_CHR: - if (s_rsp_len > 0) - os_mbuf_append(ctxt->om, s_rsp_buf, s_rsp_len); - return 0; - - case BLE_GATT_ACCESS_OP_WRITE_CHR: { - uint8_t buf[RSP_BUF_MAX]; - uint16_t out_len = 0; - uint16_t om_len = OS_MBUF_PKTLEN(ctxt->om); - if (om_len > sizeof(buf)) om_len = sizeof(buf); - ble_hs_mbuf_to_flat(ctxt->om, buf, sizeof(buf), &out_len); - process_command(buf, out_len); - return 0; - } - - default: - return BLE_ATT_ERR_UNLIKELY; - } -} - -static const struct ble_gatt_svc_def gatt_svr_svcs[] = { - { - .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = &svc_uuid.u, - .characteristics = (struct ble_gatt_chr_def[]) { - { - .uuid = &chr_uuid.u, - .access_cb = chr_access_cb, - .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, - .val_handle = &s_chr_handle, - }, - { 0 }, - }, - }, - { 0 }, -}; - -static int gatt_svr_init(void) -{ - ble_svc_gap_init(); - ble_svc_gatt_init(); - - int rc = ble_gatts_count_cfg(gatt_svr_svcs); - if (rc != 0) return rc; - - return ble_gatts_add_svcs(gatt_svr_svcs); -} - -/* -- GAP -- */ - -static int gap_event_cb(struct ble_gap_event *event, void *arg) -{ - (void)arg; - switch (event->type) { - case BLE_GAP_EVENT_CONNECT: - if (event->connect.status != 0) - start_advertise(); - return 0; - - case BLE_GAP_EVENT_DISCONNECT: - if (s_ble_active) - start_advertise(); - return 0; - - case BLE_GAP_EVENT_ADV_COMPLETE: - if (s_ble_active) - start_advertise(); - return 0; - - default: - return 0; - } -} - -static void start_advertise(void) -{ - struct ble_hs_adv_fields fields = {0}; - fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; - fields.tx_pwr_lvl_is_present = 1; - fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; - - const char *name = ble_svc_gap_device_name(); - fields.name = (uint8_t *)name; - fields.name_len = strlen(name); - fields.name_is_complete = 1; - - ble_gap_adv_set_fields(&fields); - - struct ble_gap_adv_params params = {0}; - params.conn_mode = BLE_GAP_CONN_MODE_UND; - params.disc_mode = BLE_GAP_DISC_MODE_GEN; - - ble_gap_adv_start(s_own_addr_type, NULL, BLE_HS_FOREVER, - ¶ms, gap_event_cb, NULL); -} - -static void on_sync(void) -{ - ble_hs_util_ensure_addr(0); - ble_hs_id_infer_auto(0, &s_own_addr_type); - start_advertise(); - printf("BLE: advertising\n"); -} - -static void on_reset(int reason) -{ - printf("BLE: host reset, reason=%d\n", reason); -} - -static void ble_host_task(void *param) -{ - (void)param; - nimble_port_run(); - nimble_port_freertos_deinit(); -} - -/* -- public API -- */ - -esp_err_t ble_prov_start(void) -{ - if (s_ble_active) - return ESP_OK; - - esp_err_t err = nimble_port_init(); - if (err != ESP_OK) { - printf("BLE: nimble_port_init failed: %d\n", (int)err); - return err; - } - - ble_hs_cfg.reset_cb = on_reset; - ble_hs_cfg.sync_cb = on_sync; - ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; - - int rc = gatt_svr_init(); - if (rc != 0) { - printf("BLE: GATT init failed: %d\n", rc); - nimble_port_deinit(); - return ESP_FAIL; - } - - ble_svc_gap_device_name_set(DEVICE_NAME); - nimble_port_freertos_init(ble_host_task); - - s_ble_active = true; - s_rsp_len = 0; - printf("BLE: provisioning started\n"); - return ESP_OK; -} - -esp_err_t ble_prov_stop(void) -{ - if (!s_ble_active) - return ESP_OK; - - s_ble_active = false; - - if (ble_gap_adv_active()) - ble_gap_adv_stop(); - - nimble_port_stop(); - nimble_port_deinit(); - - printf("BLE: provisioning stopped\n"); - return ESP_OK; -} - -bool ble_prov_is_active(void) -{ - return s_ble_active; -} diff --git a/main/ble_prov.h b/main/ble_prov.h deleted file mode 100644 index 31f28b8..0000000 --- a/main/ble_prov.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef BLE_PROV_H -#define BLE_PROV_H - -#include "esp_err.h" -#include - -esp_err_t ble_prov_start(void); -esp_err_t ble_prov_stop(void); -bool ble_prov_is_active(void); - -#endif diff --git a/main/captive_portal.c b/main/captive_portal.c new file mode 100644 index 0000000..0e89ac7 --- /dev/null +++ b/main/captive_portal.c @@ -0,0 +1,427 @@ +#include "captive_portal.h" +#include "wifi_cfg.h" +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_http_server.h" +#include "esp_wifi.h" +#include "esp_system.h" +#include "lwip/sockets.h" + +#define DNS_PORT 53 +#define HTTP_PORT 80 +#define MAX_SCAN_AP 20 + +static httpd_handle_t s_httpd; +static TaskHandle_t s_dns_task; +static int s_dns_sock = -1; +static bool s_active; + +static const uint8_t AP_IP[4] = {192, 168, 4, 1}; + +/* ------------------------------------------------------------------ */ +/* Config page HTML */ +/* ------------------------------------------------------------------ */ + +static const char CONFIG_PAGE[] = +"" +"" +"" +"EIS4 Setup" +"" +"

EIS4 Setup

" +"
" +"" +"
  • Scanning...
" +"" +"" +"" +"" +"" +"" +"" +"

Minimum 8 characters, or leave blank for open network

" +"" +"
" +"
" +""; + +/* ------------------------------------------------------------------ */ +/* URL parameter helpers */ +/* ------------------------------------------------------------------ */ + +static void url_decode(char *dst, const char *src, size_t dst_sz) +{ + size_t di = 0; + while (*src && di < dst_sz - 1) { + if (*src == '%' && src[1] && src[2]) { + char hex[3] = {src[1], src[2], 0}; + dst[di++] = (char)strtol(hex, NULL, 16); + src += 3; + } else if (*src == '+') { + dst[di++] = ' '; + src++; + } else { + dst[di++] = *src++; + } + } + dst[di] = '\0'; +} + +static void get_param(const char *body, const char *key, + char *val, size_t val_sz) +{ + val[0] = '\0'; + size_t klen = strlen(key); + const char *p = body; + while ((p = strstr(p, key)) != NULL) { + if ((p == body || *(p - 1) == '&') && p[klen] == '=') { + p += klen + 1; + const char *end = strchr(p, '&'); + size_t len = end ? (size_t)(end - p) : strlen(p); + char raw[128]; + if (len >= sizeof(raw)) len = sizeof(raw) - 1; + memcpy(raw, p, len); + raw[len] = '\0'; + url_decode(val, raw, val_sz); + return; + } + p++; + } +} + +/* ------------------------------------------------------------------ */ +/* JSON escape for SSIDs */ +/* ------------------------------------------------------------------ */ + +static int json_escape(char *dst, size_t dst_sz, const char *src) +{ + size_t di = 0; + while (*src && di < dst_sz - 2) { + if (*src == '"' || *src == '\\') { + if (di + 2 >= dst_sz) break; + dst[di++] = '\\'; + } + dst[di++] = *src++; + } + dst[di] = '\0'; + return (int)di; +} + +/* ------------------------------------------------------------------ */ +/* HTTP handlers */ +/* ------------------------------------------------------------------ */ + +static esp_err_t handle_root(httpd_req_t *req) +{ + httpd_resp_set_type(req, "text/html"); + httpd_resp_send(req, CONFIG_PAGE, HTTPD_RESP_USE_STRLEN); + return ESP_OK; +} + +static esp_err_t handle_scan(httpd_req_t *req) +{ + wifi_scan_config_t scan_cfg = {0}; + esp_wifi_scan_start(&scan_cfg, true); + + uint16_t count = 0; + esp_wifi_scan_get_ap_num(&count); + if (count > MAX_SCAN_AP) count = MAX_SCAN_AP; + + wifi_ap_record_t *aps = calloc(count ? count : 1, sizeof(wifi_ap_record_t)); + if (!aps) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, NULL); + return ESP_FAIL; + } + esp_wifi_scan_get_ap_records(&count, aps); + + char json[2048]; + int off = 0; + off += snprintf(json + off, sizeof(json) - off, "["); + + for (int i = 0; i < count && off < (int)sizeof(json) - 80; i++) { + if (!aps[i].ssid[0]) continue; + + /* skip duplicates (scan is sorted by rssi, first is strongest) */ + bool dup = false; + for (int j = 0; j < i; j++) { + if (strcmp((char *)aps[i].ssid, (char *)aps[j].ssid) == 0) { + dup = true; + break; + } + } + if (dup) continue; + + char escaped[66]; + json_escape(escaped, sizeof(escaped), (char *)aps[i].ssid); + if (off > 1) off += snprintf(json + off, sizeof(json) - off, ","); + off += snprintf(json + off, sizeof(json) - off, + "{\"s\":\"%s\",\"r\":%d}", escaped, aps[i].rssi); + } + + off += snprintf(json + off, sizeof(json) - off, "]"); + free(aps); + + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, json, off); + return ESP_OK; +} + +static esp_err_t handle_save(httpd_req_t *req) +{ + char buf[256]; + int n = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (n <= 0) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL); + return ESP_FAIL; + } + buf[n] = '\0'; + + char ssid[WIFI_CFG_SSID_MAX] = {0}; + char pass[WIFI_CFG_PASS_MAX] = {0}; + char ap_pass[WIFI_CFG_PASS_MAX] = {0}; + + get_param(buf, "ssid", ssid, sizeof(ssid)); + get_param(buf, "pass", pass, sizeof(pass)); + get_param(buf, "ap_pass", ap_pass, sizeof(ap_pass)); + + if (!ssid[0]) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing SSID"); + return ESP_FAIL; + } + + if (ap_pass[0] && strlen(ap_pass) < 8) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, + "AP password must be 8+ characters or empty"); + return ESP_FAIL; + } + + printf("Portal: saving STA \"%s\", AP pass %s\n", + ssid, ap_pass[0] ? "set" : "open"); + + wifi_cfg_set_sta(ssid, pass); + wifi_cfg_set_ap("EIS4", ap_pass); + + httpd_resp_sendstr(req, "OK"); + + vTaskDelay(pdMS_TO_TICKS(1000)); + esp_restart(); + return ESP_OK; +} + +static esp_err_t handle_catchall(httpd_req_t *req) +{ + httpd_resp_set_status(req, "302 Found"); + httpd_resp_set_hdr(req, "Location", "http://192.168.4.1/"); + httpd_resp_send(req, NULL, 0); + return ESP_OK; +} + +/* ------------------------------------------------------------------ */ +/* DNS redirect server */ +/* ------------------------------------------------------------------ */ + +static void dns_task(void *arg) +{ + (void)arg; + + s_dns_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s_dns_sock < 0) { + printf("DNS: socket failed\n"); + vTaskDelete(NULL); + return; + } + + struct sockaddr_in bind_addr = { + .sin_family = AF_INET, + .sin_port = htons(DNS_PORT), + .sin_addr.s_addr = htonl(INADDR_ANY), + }; + if (bind(s_dns_sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0) { + printf("DNS: bind failed\n"); + close(s_dns_sock); + s_dns_sock = -1; + vTaskDelete(NULL); + return; + } + + printf("DNS: redirecting all queries to " "%d.%d.%d.%d\n", + AP_IP[0], AP_IP[1], AP_IP[2], AP_IP[3]); + + uint8_t buf[256]; + struct sockaddr_in src; + socklen_t slen; + + for (;;) { + slen = sizeof(src); + int n = recvfrom(s_dns_sock, buf, sizeof(buf), 0, + (struct sockaddr *)&src, &slen); + if (n < 12) { + if (n < 0) break; + continue; + } + + /* walk question section to find its end */ + int qend = 12; + bool valid = true; + while (qend < n && buf[qend] != 0) { + uint8_t ll = buf[qend]; + if (ll > 63 || qend + ll + 1 > n) { valid = false; break; } + qend += ll + 1; + } + if (!valid) continue; + qend += 5; /* null terminator + QTYPE(2) + QCLASS(2) */ + if (qend > n) continue; + + /* build response: header + question (copied) + one A answer */ + uint8_t rsp[280]; + if (qend + 16 > (int)sizeof(rsp)) continue; + memcpy(rsp, buf, qend); + + rsp[2] = 0x81; rsp[3] = 0x80; /* flags: standard response */ + rsp[6] = 0x00; rsp[7] = 0x01; /* 1 answer RR */ + + int off = qend; + rsp[off++] = 0xC0; rsp[off++] = 0x0C; /* name pointer */ + rsp[off++] = 0x00; rsp[off++] = 0x01; /* type A */ + rsp[off++] = 0x00; rsp[off++] = 0x01; /* class IN */ + rsp[off++] = 0x00; rsp[off++] = 0x00; + rsp[off++] = 0x00; rsp[off++] = 0x01; /* TTL 1s */ + rsp[off++] = 0x00; rsp[off++] = 0x04; /* rdlength */ + rsp[off++] = AP_IP[0]; rsp[off++] = AP_IP[1]; + rsp[off++] = AP_IP[2]; rsp[off++] = AP_IP[3]; + + sendto(s_dns_sock, rsp, off, 0, (struct sockaddr *)&src, slen); + } + + vTaskDelete(NULL); +} + +/* ------------------------------------------------------------------ */ +/* Public API */ +/* ------------------------------------------------------------------ */ + +esp_err_t captive_portal_start(void) +{ + if (s_active) return ESP_OK; + + httpd_config_t cfg = HTTPD_DEFAULT_CONFIG(); + cfg.server_port = HTTP_PORT; + cfg.stack_size = 6144; + cfg.uri_match_fn = httpd_uri_match_wildcard; + cfg.max_uri_handlers = 8; + + esp_err_t err = httpd_start(&s_httpd, &cfg); + if (err != ESP_OK) { + printf("Portal: HTTP start failed: %d\n", err); + return err; + } + + const httpd_uri_t uri_scan = {"/scan", HTTP_GET, handle_scan, NULL}; + const httpd_uri_t uri_save = {"/save", HTTP_POST, handle_save, NULL}; + const httpd_uri_t uri_root = {"/", HTTP_GET, handle_root, NULL}; + const httpd_uri_t uri_catch = {"/*", HTTP_GET, handle_catchall, NULL}; + + httpd_register_uri_handler(s_httpd, &uri_scan); + httpd_register_uri_handler(s_httpd, &uri_save); + httpd_register_uri_handler(s_httpd, &uri_root); + httpd_register_uri_handler(s_httpd, &uri_catch); + + xTaskCreate(dns_task, "dns", 3072, NULL, 5, &s_dns_task); + + s_active = true; + printf("Portal: active on port %d\n", HTTP_PORT); + return ESP_OK; +} + +esp_err_t captive_portal_stop(void) +{ + if (!s_active) return ESP_OK; + + if (s_httpd) { + httpd_stop(s_httpd); + s_httpd = NULL; + } + + if (s_dns_sock >= 0) { + close(s_dns_sock); + s_dns_sock = -1; + } + + s_active = false; + printf("Portal: stopped\n"); + return ESP_OK; +} + +bool captive_portal_is_active(void) +{ + return s_active; +} diff --git a/main/captive_portal.h b/main/captive_portal.h new file mode 100644 index 0000000..46d9510 --- /dev/null +++ b/main/captive_portal.h @@ -0,0 +1,11 @@ +#ifndef CAPTIVE_PORTAL_H +#define CAPTIVE_PORTAL_H + +#include "esp_err.h" +#include + +esp_err_t captive_portal_start(void); +esp_err_t captive_portal_stop(void); +bool captive_portal_is_active(void); + +#endif diff --git a/main/echem.c b/main/echem.c index 964029f..b115bcf 100644 --- a/main/echem.c +++ b/main/echem.c @@ -1,4 +1,5 @@ #include "echem.h" +#include "eis.h" #include "ad5940.h" #include "protocol.h" #include @@ -623,11 +624,18 @@ int echem_ph_ocp(const PhConfig *cfg, PhResult *result) float v_re0 = sum_re0 / PH_AVG_N; float ocp = v_se0 - v_re0; - float t_k = cfg->temp_c + 273.15f; - float slope = 0.1984f * t_k; /* mV/pH at temperature */ + float ocp_corrected = ocp; + float tc_cold = eis_get_ph_temp_slope_cold(); + float tc_hot = eis_get_ph_temp_slope_hot(); + if (tc_cold != 0.0f || tc_hot != 0.0f) { + float dt = cfg->temp_c - 25.0f; + float alpha = (dt < 0.0f) ? tc_cold : tc_hot; + if (alpha != 0.0f) + ocp_corrected = ocp - alpha * dt; + } result->v_ocp_mv = ocp; - result->ph = 7.0f - ocp / slope; + result->ph = eis_get_ph_slope() * ocp_corrected + eis_get_ph_offset(); result->temp_c = cfg->temp_c; printf("pH: SE0=%.1f mV, RE0=%.1f mV, OCP=%.1f mV, pH=%.2f\n", diff --git a/main/eis.c b/main/eis.c index 78b7d5d..0b3aeea 100644 --- a/main/eis.c +++ b/main/eis.c @@ -651,30 +651,168 @@ void eis_load_cl_factor(void) nvs_close(h); } -void eis_set_ph_cal(float slope, float offset) +float eis_get_ph_slope(void) { return ph_slope_cached; } +float eis_get_ph_offset(void) { return ph_offset_cached; } + +/* ---- 3-buffer × 3-temperature pH calibration ---- */ + +static const float PH_BUFFERS[PH_CAL_BUFFERS] = {4.0f, 6.86f, 9.0f}; + +#define NVS_PH_CAL_PTS_KEY "ph_cal9" + +typedef struct { + float ocp_mv; + float temp_c; +} PhCalSample; + +static struct { + PhCalSample s[PH_CAL_BUFFERS][PH_CAL_TEMPS]; + uint16_t valid; +} ph_cal; + +static float ph_temp_slope_cold; +static float ph_temp_slope_hot; + +static void ph_cal_recalculate(void) +{ + /* baseline slope/offset from the 3 baseline (tslot=1) points */ + int n = 0; + float sx = 0, sy = 0, sxx = 0, sxy = 0; + for (int i = 0; i < PH_CAL_BUFFERS; i++) { + int bit = i * PH_CAL_TEMPS + PH_TEMP_BASE; + if (!(ph_cal.valid & (1 << bit))) continue; + float x = ph_cal.s[i][PH_TEMP_BASE].ocp_mv; + float y = PH_BUFFERS[i]; + sx += x; sy += y; sxx += x * x; sxy += x * y; + n++; + } + if (n < 2) { + ph_slope_cached = 0; + ph_offset_cached = 0; + } else { + float d = (float)n * sxx - sx * sx; + if (fabsf(d) < 1e-10f) { + ph_slope_cached = 0; + ph_offset_cached = 0; + } else { + ph_slope_cached = ((float)n * sxy - sx * sy) / d; + ph_offset_cached = (sy - ph_slope_cached * sx) / (float)n; + } + } + printf("pH cal: baseline slope=%.6f offset=%.4f (%d pts)\n", + ph_slope_cached, ph_offset_cached, n); + + /* temperature drift from off-temperature points */ + ph_temp_slope_cold = 0; + ph_temp_slope_hot = 0; + int nc = 0, nh = 0; + for (int i = 0; i < PH_CAL_BUFFERS; i++) { + int base_bit = i * PH_CAL_TEMPS + PH_TEMP_BASE; + if (!(ph_cal.valid & (1 << base_bit))) continue; + float ocp_base = ph_cal.s[i][PH_TEMP_BASE].ocp_mv; + + int cold_bit = i * PH_CAL_TEMPS + PH_TEMP_BELOW; + if (ph_cal.valid & (1 << cold_bit)) { + float dt = ph_cal.s[i][PH_TEMP_BELOW].temp_c - 25.0f; + if (fabsf(dt) > 0.5f) { + ph_temp_slope_cold += (ph_cal.s[i][PH_TEMP_BELOW].ocp_mv - ocp_base) / dt; + nc++; + } + } + + int hot_bit = i * PH_CAL_TEMPS + PH_TEMP_ABOVE; + if (ph_cal.valid & (1 << hot_bit)) { + float dt = ph_cal.s[i][PH_TEMP_ABOVE].temp_c - 25.0f; + if (fabsf(dt) > 0.5f) { + ph_temp_slope_hot += (ph_cal.s[i][PH_TEMP_ABOVE].ocp_mv - ocp_base) / dt; + nh++; + } + } + } + if (nc > 0) ph_temp_slope_cold /= nc; + if (nh > 0) ph_temp_slope_hot /= nh; + + if (nc > 0 || nh > 0) + printf("pH cal: temp drift cold=%.4f hot=%.4f mV/C\n", + ph_temp_slope_cold, ph_temp_slope_hot); +} + +static void ph_cal_save(void) { - ph_slope_cached = slope; - ph_offset_cached = offset; nvs_handle_t h; if (nvs_open(NVS_OCAL_NS, NVS_READWRITE, &h) != ESP_OK) return; - nvs_set_blob(h, NVS_PH_SLOPE_KEY, &slope, sizeof(slope)); - nvs_set_blob(h, NVS_PH_OFFSET_KEY, &offset, sizeof(offset)); + nvs_set_blob(h, NVS_PH_CAL_PTS_KEY, &ph_cal, sizeof(ph_cal)); nvs_commit(h); nvs_close(h); } -float eis_get_ph_slope(void) { return ph_slope_cached; } -float eis_get_ph_offset(void) { return ph_offset_cached; } +float eis_get_ph_temp_slope_cold(void) { return ph_temp_slope_cold; } +float eis_get_ph_temp_slope_hot(void) { return ph_temp_slope_hot; } void eis_load_ph_cal(void) { nvs_handle_t h; if (nvs_open(NVS_OCAL_NS, NVS_READONLY, &h) != ESP_OK) return; - size_t len = sizeof(ph_slope_cached); - if (nvs_get_blob(h, NVS_PH_SLOPE_KEY, &ph_slope_cached, &len) != ESP_OK || len != sizeof(ph_slope_cached)) - ph_slope_cached = 0.0f; - len = sizeof(ph_offset_cached); - if (nvs_get_blob(h, NVS_PH_OFFSET_KEY, &ph_offset_cached, &len) != ESP_OK || len != sizeof(ph_offset_cached)) - ph_offset_cached = 0.0f; + size_t len = sizeof(ph_cal); + if (nvs_get_blob(h, NVS_PH_CAL_PTS_KEY, &ph_cal, &len) != ESP_OK + || len != sizeof(ph_cal)) { + memset(&ph_cal, 0, sizeof(ph_cal)); + } nvs_close(h); + ph_cal_recalculate(); +} + +int eis_ph_cal_set_point(uint8_t buf, uint8_t tslot, float ocp_mv, float temp_c) +{ + if (buf >= PH_CAL_BUFFERS || tslot >= PH_CAL_TEMPS) return -1; + ph_cal.s[buf][tslot].ocp_mv = ocp_mv; + ph_cal.s[buf][tslot].temp_c = temp_c; + ph_cal.valid |= (1 << (buf * PH_CAL_TEMPS + tslot)); + ph_cal_recalculate(); + ph_cal_save(); + return 0; +} + +int eis_ph_cal_clear_point(uint8_t buf, uint8_t tslot) +{ + if (buf >= PH_CAL_BUFFERS || tslot >= PH_CAL_TEMPS) return -1; + int bit = buf * PH_CAL_TEMPS + tslot; + ph_cal.valid &= ~(1 << bit); + memset(&ph_cal.s[buf][tslot], 0, sizeof(PhCalSample)); + ph_cal_recalculate(); + ph_cal_save(); + return 0; +} + +void eis_ph_cal_clear_all(void) +{ + memset(&ph_cal, 0, sizeof(ph_cal)); + ph_slope_cached = 0; + ph_offset_cached = 0; + ph_temp_slope_cold = 0; + ph_temp_slope_hot = 0; + ph_cal_save(); +} + +bool eis_ph_cal_get_point(uint8_t buf, uint8_t tslot, float *ocp_mv, float *temp_c) +{ + if (buf >= PH_CAL_BUFFERS || tslot >= PH_CAL_TEMPS) return false; + if (!(ph_cal.valid & (1 << (buf * PH_CAL_TEMPS + tslot)))) return false; + if (ocp_mv) *ocp_mv = ph_cal.s[buf][tslot].ocp_mv; + if (temp_c) *temp_c = ph_cal.s[buf][tslot].temp_c; + return true; +} + +int eis_ph_cal_count(void) +{ + int n = 0; + for (int i = 0; i < PH_CAL_BUFFERS * PH_CAL_TEMPS; i++) + if (ph_cal.valid & (1 << i)) n++; + return n; +} + +float eis_ph_cal_buffer_ph(uint8_t buf) +{ + if (buf >= PH_CAL_BUFFERS) return 0; + return PH_BUFFERS[buf]; } diff --git a/main/eis.h b/main/eis.h index 76c4bb2..34a50e9 100644 --- a/main/eis.h +++ b/main/eis.h @@ -2,6 +2,7 @@ #define EIS_H #include "ad5940.h" +#include #define EIS_MAX_POINTS 100 @@ -71,9 +72,23 @@ void eis_set_cl_factor(float f); float eis_get_cl_factor(void); void eis_load_cl_factor(void); -void eis_set_ph_cal(float slope, float offset); float eis_get_ph_slope(void); float eis_get_ph_offset(void); +float eis_get_ph_temp_slope_cold(void); +float eis_get_ph_temp_slope_hot(void); void eis_load_ph_cal(void); +#define PH_CAL_BUFFERS 3 +#define PH_CAL_TEMPS 3 +#define PH_TEMP_BELOW 0 +#define PH_TEMP_BASE 1 +#define PH_TEMP_ABOVE 2 + +int eis_ph_cal_set_point(uint8_t buf, uint8_t tslot, float ocp_mv, float temp_c); +int eis_ph_cal_clear_point(uint8_t buf, uint8_t tslot); +void eis_ph_cal_clear_all(void); +bool eis_ph_cal_get_point(uint8_t buf, uint8_t tslot, float *ocp_mv, float *temp_c); +int eis_ph_cal_count(void); +float eis_ph_cal_buffer_ph(uint8_t buf); + #endif diff --git a/main/eis4.c b/main/eis4.c index 5c3db94..334423e 100644 --- a/main/eis4.c +++ b/main/eis4.c @@ -250,10 +250,50 @@ void app_main(void) send_cl_factor(eis_get_cl_factor()); break; - case CMD_SET_PH_CAL: - eis_set_ph_cal(cmd.ph_cal.slope, cmd.ph_cal.offset); - send_ph_cal(cmd.ph_cal.slope, cmd.ph_cal.offset); - printf("pH cal set: slope=%.4f offset=%.4f\n", cmd.ph_cal.slope, cmd.ph_cal.offset); + case CMD_PH_CAL_POINT: { + uint8_t bid = cmd.ph_cal_point.buffer_id; + uint8_t tsl = cmd.ph_cal_point.temp_slot; + if (bid >= PH_CAL_BUFFERS || tsl >= PH_CAL_TEMPS) break; + float buf_ph = eis_ph_cal_buffer_ph(bid); + printf("pH cal: buffer %u slot %u (pH %.2f)\n", bid, tsl, buf_ph); + + PhConfig ph_cfg; + ph_cfg.stabilize_s = cmd.ph_cal_point.stabilize_s; + ph_cfg.temp_c = temp_get(); + + PhResult ph_result; + echem_ph_ocp(&ph_cfg, &ph_result); + + eis_ph_cal_set_point(bid, tsl, ph_result.v_ocp_mv, ph_result.temp_c); + + int baseline_n = 0; + for (int i = 0; i < PH_CAL_BUFFERS; i++) + if (eis_ph_cal_get_point(i, PH_TEMP_BASE, NULL, NULL)) baseline_n++; + + printf("pH cal: [%u][%u] OCP=%.1f mV T=%.1f C (%d/%d)\n", + bid, tsl, ph_result.v_ocp_mv, ph_result.temp_c, + baseline_n, eis_ph_cal_count()); + send_ph_cal_point(bid, tsl, ph_result.v_ocp_mv, ph_result.temp_c, + buf_ph, (uint8_t)baseline_n); + break; + } + + case CMD_PH_CAL_CLEAR: { + uint8_t bid = cmd.ph_cal_clear.buffer_id; + uint8_t tsl = cmd.ph_cal_clear.temp_slot; + if (bid == 0x7F) { + eis_ph_cal_clear_all(); + printf("pH cal: all points cleared\n"); + } else if (bid < PH_CAL_BUFFERS && tsl < PH_CAL_TEMPS) { + eis_ph_cal_clear_point(bid, tsl); + printf("pH cal: [%u][%u] cleared\n", bid, tsl); + } + send_ph_cal_status(); + break; + } + + case CMD_PH_CAL_STATUS: + send_ph_cal_status(); break; case CMD_GET_PH_CAL: diff --git a/main/protocol.c b/main/protocol.c index 17069f7..cddea1d 100644 --- a/main/protocol.c +++ b/main/protocol.c @@ -1,4 +1,5 @@ #include "protocol.h" +#include "eis.h" #include "wifi_transport.h" #include #include @@ -357,6 +358,44 @@ int send_ph_cal(float slope, float offset) return send_sysex(sx, p); } +int send_ph_cal_point(uint8_t buf, uint8_t tslot, float ocp_mv, float temp_c, + float buffer_ph, uint8_t baseline_count) +{ + uint8_t sx[24]; + uint16_t p = 0; + sx[p++] = 0xF0; sx[p++] = 0x7D; sx[p++] = RSP_PH_CAL_POINT; + sx[p++] = buf & 0x7F; + sx[p++] = tslot & 0x7F; + encode_float(ocp_mv, &sx[p]); p += 5; + encode_float(temp_c, &sx[p]); p += 5; + encode_float(buffer_ph, &sx[p]); p += 5; + sx[p++] = baseline_count & 0x7F; + sx[p++] = 0xF7; + return send_sysex(sx, p); +} + +int send_ph_cal_status(void) +{ + uint8_t sx[32]; + uint16_t p = 0; + sx[p++] = 0xF0; sx[p++] = 0x7D; sx[p++] = RSP_PH_CAL_STATUS; + + uint16_t mask = 0; + for (int b = 0; b < PH_CAL_BUFFERS; b++) + for (int t = 0; t < PH_CAL_TEMPS; t++) + if (eis_ph_cal_get_point(b, t, NULL, NULL)) + mask |= (1 << (b * PH_CAL_TEMPS + t)); + + sx[p++] = mask & 0x7F; + sx[p++] = (mask >> 7) & 0x7F; + encode_float(eis_get_ph_slope(), &sx[p]); p += 5; + encode_float(eis_get_ph_offset(), &sx[p]); p += 5; + encode_float(eis_get_ph_temp_slope_cold(), &sx[p]); p += 5; + encode_float(eis_get_ph_temp_slope_hot(), &sx[p]); p += 5; + sx[p++] = 0xF7; + return send_sysex(sx, p); +} + /* ---- outbound: pH ---- */ int send_ph_result(float v_ocp_mv, float ph, float temp_c, diff --git a/main/protocol.h b/main/protocol.h index edbd946..67abd42 100644 --- a/main/protocol.h +++ b/main/protocol.h @@ -28,8 +28,10 @@ #define CMD_CLEAR_REFS 0x32 #define CMD_SET_CL_FACTOR 0x33 #define CMD_GET_CL_FACTOR 0x34 -#define CMD_SET_PH_CAL 0x35 #define CMD_GET_PH_CAL 0x36 +#define CMD_PH_CAL_POINT 0x37 +#define CMD_PH_CAL_CLEAR 0x38 +#define CMD_PH_CAL_STATUS 0x39 /* Session sync commands (0x4x) */ #define CMD_SESSION_CREATE 0x40 @@ -38,13 +40,6 @@ #define CMD_SESSION_RENAME 0x43 #define CMD_HEARTBEAT 0x44 -/* Provisioning commands (0x6x, over BLE) */ -#define CMD_GET_WIFI_CFG 0x60 -#define CMD_SET_WIFI_STA 0x61 -#define CMD_SET_WIFI_AP 0x62 -#define CMD_GET_DEVICE_INFO 0x63 -#define CMD_WIFI_STATUS 0x64 - /* Responses: Firmware -> Client (0x0x, 0x2x) */ #define RSP_SWEEP_START 0x01 #define RSP_DATA_POINT 0x02 @@ -68,7 +63,9 @@ #define RSP_REFS_DONE 0x22 #define RSP_REF_STATUS 0x23 #define RSP_CL_FACTOR 0x24 -#define RSP_PH_CAL 0x25 +#define RSP_PH_CAL 0x25 +#define RSP_PH_CAL_POINT 0x26 +#define RSP_PH_CAL_STATUS 0x27 #define RSP_KEEPALIVE 0x50 /* Session sync responses (0x4x) */ @@ -78,13 +75,7 @@ #define RSP_SESSION_RENAMED 0x43 #define RSP_CLIENT_LIST 0x44 -/* Provisioning responses (0x6x, over BLE) */ -#define RSP_WIFI_CFG 0x60 -#define RSP_SET_WIFI_RESULT 0x61 -#define RSP_DEVICE_INFO 0x63 -#define RSP_WIFI_STATUS 0x64 - -/* WiFi STA state values (for RSP_WIFI_STATUS) */ +/* WiFi STA state values */ #define WIFI_STATE_DISCONNECTED 0 #define WIFI_STATE_CONNECTING 1 #define WIFI_STATE_CONNECTED 2 @@ -107,7 +98,8 @@ typedef struct { struct { float v_mv; float duration_s; } clean; float cell_k; float cl_factor; - struct { float slope; float offset; } ph_cal; + struct { uint8_t buffer_id; uint8_t temp_slot; float stabilize_s; } ph_cal_point; + struct { uint8_t buffer_id; uint8_t temp_slot; } ph_cal_clear; struct { uint8_t name_len; char name[MAX_SESSION_NAME]; } session_create; struct { uint8_t id; } session_switch; struct { uint8_t id; uint8_t name_len; char name[MAX_SESSION_NAME]; } session_rename; @@ -169,6 +161,9 @@ int send_cl_factor(float f); /* outbound: pH calibration */ int send_ph_cal(float slope, float offset); +int send_ph_cal_point(uint8_t buf, uint8_t tslot, float ocp_mv, float temp_c, + float buffer_ph, uint8_t baseline_count); +int send_ph_cal_status(void); /* outbound: reference collection */ int send_ref_frame(uint8_t mode, uint8_t rtia_idx); diff --git a/main/wifi_cfg.c b/main/wifi_cfg.c index 53bfc37..53ccd85 100644 --- a/main/wifi_cfg.c +++ b/main/wifi_cfg.c @@ -10,7 +10,7 @@ #define KEY_AP_PASS "ap_pass" #define DEFAULT_AP_SSID "EIS4" -#define DEFAULT_AP_PASS "eis4data" +#define DEFAULT_AP_PASS "" static char sta_ssid[WIFI_CFG_SSID_MAX]; static char sta_pass[WIFI_CFG_PASS_MAX]; diff --git a/main/wifi_transport.c b/main/wifi_transport.c index e885b71..04d1f75 100644 --- a/main/wifi_transport.c +++ b/main/wifi_transport.c @@ -1,6 +1,6 @@ #include "wifi_transport.h" #include "wifi_cfg.h" -#include "ble_prov.h" +#include "captive_portal.h" #include "protocol.h" #include #include @@ -46,6 +46,7 @@ static SemaphoreHandle_t client_mutex; static uint8_t s_sta_state = WIFI_STATE_DISCONNECTED; static uint32_t s_sta_ip; static int s_sta_retries; +static bool s_sta_give_up; static EventGroupHandle_t s_reconnect_eg; static void client_touch(const struct sockaddr_in *addr) @@ -185,10 +186,16 @@ static void parse_udp_sysex(const uint8_t *data, uint16_t len) if (len < 8) return; cmd.cl_factor = decode_float(&data[3]); break; - case CMD_SET_PH_CAL: - if (len < 13) return; - cmd.ph_cal.slope = decode_float(&data[3]); - cmd.ph_cal.offset = decode_float(&data[8]); + case CMD_PH_CAL_POINT: + if (len < 10) return; + cmd.ph_cal_point.buffer_id = data[3] & 0x7F; + cmd.ph_cal_point.temp_slot = data[4] & 0x7F; + cmd.ph_cal_point.stabilize_s = decode_float(&data[5]); + break; + case CMD_PH_CAL_CLEAR: + if (len < 5) return; + cmd.ph_cal_clear.buffer_id = data[3] & 0x7F; + cmd.ph_cal_clear.temp_slot = data[4] & 0x7F; break; case CMD_SESSION_CREATE: if (len < 5) return; @@ -218,6 +225,7 @@ static void parse_udp_sysex(const uint8_t *data, uint16_t len) case CMD_GET_CELL_K: case CMD_GET_CL_FACTOR: case CMD_GET_PH_CAL: + case CMD_PH_CAL_STATUS: case CMD_START_REFS: case CMD_GET_REFS: case CMD_CLEAR_REFS: @@ -320,6 +328,25 @@ static void wifi_event_handler(void *arg, esp_event_base_t base, } } +static void reconfigure_ap_open(void) +{ + char ap_ssid[WIFI_CFG_SSID_MAX]; + char ap_pass[WIFI_CFG_PASS_MAX]; + wifi_cfg_get_ap(ap_ssid, sizeof(ap_ssid), ap_pass, sizeof(ap_pass)); + + wifi_config_t ap_cfg = { + .ap = { + .channel = WIFI_CHANNEL, + .max_connection = WIFI_MAX_CONN, + .authmode = WIFI_AUTH_OPEN, + }, + }; + strncpy((char *)ap_cfg.ap.ssid, ap_ssid, sizeof(ap_cfg.ap.ssid) - 1); + ap_cfg.ap.ssid_len = strlen(ap_ssid); + esp_wifi_set_config(WIFI_IF_AP, &ap_cfg); + printf("WiFi: AP reconfigured as open\n"); +} + static void sta_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) { @@ -333,10 +360,13 @@ static void sta_event_handler(void *arg, esp_event_base_t base, if (s_sta_retries >= STA_RETRY_MAX) { if (s_reconnect_eg) xEventGroupSetBits(s_reconnect_eg, RECONNECT_BIT_FAIL); - if (!ble_prov_is_active()) { - printf("WiFi: STA retries exhausted, starting BLE provisioning\n"); - ble_prov_start(); + if (!s_sta_give_up) { + s_sta_give_up = true; + printf("WiFi: STA retries exhausted, starting captive portal\n"); + reconfigure_ap_open(); + captive_portal_start(); } + return; } s_sta_state = WIFI_STATE_CONNECTING; @@ -346,13 +376,14 @@ static void sta_event_handler(void *arg, esp_event_base_t base, s_sta_state = WIFI_STATE_CONNECTED; s_sta_ip = evt->ip_info.ip.addr; s_sta_retries = 0; + s_sta_give_up = false; printf("WiFi: STA connected, IP " IPSTR "\n", IP2STR(&evt->ip_info.ip)); if (s_reconnect_eg) xEventGroupSetBits(s_reconnect_eg, RECONNECT_BIT_OK); - if (ble_prov_is_active()) - ble_prov_stop(); + if (captive_portal_is_active()) + captive_portal_stop(); } } @@ -365,6 +396,7 @@ void wifi_transport_get_sta_state(uint8_t *state, uint32_t *ip) esp_err_t wifi_transport_reconnect_sta(const char *ssid, const char *pass) { s_sta_retries = 0; + s_sta_give_up = false; s_sta_state = WIFI_STATE_CONNECTING; esp_wifi_disconnect(); @@ -415,12 +447,16 @@ static int wifi_ap_init(void) .ap = { .channel = WIFI_CHANNEL, .max_connection = WIFI_MAX_CONN, - .authmode = WIFI_AUTH_WPA2_PSK, }, }; strncpy((char *)ap_cfg.ap.ssid, ap_ssid, sizeof(ap_cfg.ap.ssid) - 1); ap_cfg.ap.ssid_len = strlen(ap_ssid); - strncpy((char *)ap_cfg.ap.password, ap_pass, sizeof(ap_cfg.ap.password) - 1); + if (ap_pass[0]) { + strncpy((char *)ap_cfg.ap.password, ap_pass, sizeof(ap_cfg.ap.password) - 1); + ap_cfg.ap.authmode = WIFI_AUTH_WPA2_PSK; + } else { + ap_cfg.ap.authmode = WIFI_AUTH_OPEN; + } esp_wifi_set_mode(WIFI_MODE_APSTA); esp_wifi_set_config(WIFI_IF_AP, &ap_cfg); @@ -448,11 +484,12 @@ static int wifi_ap_init(void) esp_wifi_connect(); printf("WiFi: STA connecting to \"%s\"\n", sta_ssid); } else { - printf("WiFi: no STA credentials, starting BLE provisioning\n"); - ble_prov_start(); + printf("WiFi: no STA credentials, starting captive portal\n"); + captive_portal_start(); } - printf("WiFi: AP \"%s\" on channel %d\n", ap_ssid, WIFI_CHANNEL); + printf("WiFi: AP \"%s\" %s on channel %d\n", + ap_ssid, ap_pass[0] ? "(secured)" : "(open)", WIFI_CHANNEL); return 0; } diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 6e992f6..17103c4 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -1,6 +1,5 @@ CONFIG_IDF_TARGET="esp32s3" CONFIG_LWIP_DHCPS_MAX_STATION_NUM=12 CONFIG_ESP_WIFI_SLP_IRAM_OPT=n -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_ENABLED=n CONFIG_ESP_WIFI_IRAM_OPT=n