build ok but has warnings

This commit is contained in:
Bob 2025-11-10 09:40:09 -08:00
parent db6d6b20c4
commit dec314ce6b
5 changed files with 434 additions and 233 deletions

203
flash_all_serial_config.py Normal file
View File

@ -0,0 +1,203 @@
#!/usr/bin/env python3
import argparse, os, sys, time, fnmatch
from pathlib import Path
from typing import List, Dict
import serial, serial.tools.list_ports as list_ports
import subprocess
KNOWN_VIDS = {0x0403, 0x10C4, 0x1A86, 0x067B, 0x303A}
def run(cmd, cwd=None, check=True):
print(">>", " ".join(cmd))
p = subprocess.run(cmd, cwd=cwd)
if check and p.returncode != 0:
raise RuntimeError(f"Command failed ({p.returncode}): {' '.join(cmd)}")
return p.returncode
def detect_chip_type(port: str) -> str:
try:
out = subprocess.check_output(["esptool.py", "--port", port, "chip_id"], stderr=subprocess.STDOUT, text=True, timeout=6)
if "ESP32-S3" in out.upper(): return "ESP32-S3"
if "ESP32-S2" in out.upper(): return "ESP32-S2"
if "ESP32-C3" in out.upper(): return "ESP32-C3"
if "ESP32-C6" in out.upper(): return "ESP32-C6"
if "ESP32" in out.upper(): return "ESP32"
except Exception:
pass
return "Unknown"
def map_chip_to_idf_target(chip: str) -> str:
s = chip.upper()
if s.startswith("ESP32-S3"): return "esp32s3"
if s.startswith("ESP32-S2"): return "esp32s2"
if s.startswith("ESP32-C3"): return "esp32c3"
if s.startswith("ESP32-C6"): return "esp32c6"
if s.startswith("ESP32-H2"): return "esp32h2"
if s.startswith("ESP32"): return "esp32"
return "unknown"
def list_ports_filtered(patterns: List[str] = None):
ports = list_ports.comports()
out = []
for p in ports:
dev = p.device
if patterns:
if not any(fnmatch.fnmatch(dev, pat) for pat in patterns):
continue
else:
if not (dev.startswith("/dev/ttyUSB") or dev.startswith("/dev/ttyACM")):
continue
vid = getattr(p, "vid", None)
if vid is not None and vid not in KNOWN_VIDS:
continue
out.append(p)
return out
def ensure_target(project_dir: str, target: str):
if not target or target == "unknown":
raise ValueError("Unknown IDF target; cannot set-target")
run(["idf.py", "set-target", target], cwd=project_dir, check=True)
def flash_device(project_dir: str, port: str, idf_target: str, baud: int = 460800) -> bool:
try:
ensure_target(project_dir, idf_target)
run(["idf.py", "-p", port, "-b", str(baud), "flash"], cwd=project_dir, check=True)
return True
except Exception as e:
print(f" Flash failed on {port}: {e}")
return False
def toggle_reset(ser):
try:
ser.dtr = False
ser.rts = True
time.sleep(0.05)
ser.dtr = True
ser.rts = False
time.sleep(0.05)
except Exception:
pass
def send_wifi_config(port: str, ssid: str, password: str, ip: str, mask: str, gw: str, dhcp: bool, baud: int = 115200, retries: int = 3) -> bool:
for attempt in range(1, retries+1):
try:
print(f" [{port}] opening serial @ {baud} (attempt {attempt}/{retries})")
with serial.Serial(port, baudrate=baud, timeout=2) as ser:
time.sleep(0.3)
toggle_reset(ser)
time.sleep(0.8)
ser.reset_input_buffer()
ser.reset_output_buffer()
lines = [
"CFG\n",
f"SSID:{ssid}\n" if ssid else "",
f"PASS:{password}\n" if password else "",
f"IP:{ip}\n" if ip else "",
f"MASK:{mask}\n" if mask else "",
f"GW:{gw}\n" if gw else "",
f"DHCP:{1 if dhcp else 0}\n",
"END\n",
]
payload = "".join([l for l in lines if l])
ser.write(payload.encode("utf-8"))
ser.flush()
t0 = time.time()
buf = b""
while time.time() - t0 < 3.0:
chunk = ser.read(64)
if chunk:
buf += chunk
if b"OK" in buf:
print(f" [{port}] config applied: {buf.decode(errors='ignore').strip()}")
return True
print(f" [{port}] no OK from device, got: {buf.decode(errors='ignore').strip()}")
except Exception as e:
print(f" [{port}] serial error: {e}")
time.sleep(0.6)
return False
def main():
ap = argparse.ArgumentParser(description="Mass flash ESP32 devices and push WiFi/IP config over serial.")
ap.add_argument("--project", required=True, help="Path to ESPIDF project")
ap.add_argument("--ssid", required=True, help="WiFi SSID")
ap.add_argument("--password", required=True, help="WiFi password")
ap.add_argument("--start-ip", default="192.168.1.50", help="Base IP (only used if --dhcp=0)")
ap.add_argument("--mask", default="255.255.255.0", help="Netmask for static IP")
ap.add_argument("--gw", default="192.168.1.1", help="Gateway for static IP")
ap.add_argument("--dhcp", type=int, choices=[0,1], default=1, help="1=use DHCP, 0=set static IPs")
ap.add_argument("--baud", type=int, default=460800, help="Flashing baud rate")
ap.add_argument("--cfg-baud", type=int, default=115200, help="Serial baud for config exchange")
ap.add_argument("--ports", help="Comma-separated globs to override port selection, e.g. '/dev/ttyUSB*,/dev/ttyACM*'")
ap.add_argument("--dry-run", action="store_true", help="Plan only; do not flash or configure")
args = ap.parse_args()
project_dir = os.path.abspath(args.project)
if not os.path.isdir(project_dir):
print(f"Project directory not found: {project_dir}")
sys.exit(1)
patterns = [p.strip() for p in args.ports.split(",")] if args.ports else None
devices = list_ports_filtered(patterns)
if not devices:
print("No candidate USB serial ports found.")
sys.exit(2)
print(f"Found {len(devices)} devices.")
base = args.start_ip.split(".")
try:
base = [int(x) for x in base]
except Exception:
base = [192,168,1,50]
plan = []
for idx, dev in enumerate(devices, 1):
port = dev.device
chip = detect_chip_type(port)
target = map_chip_to_idf_target(chip)
ip = f"{base[0]}.{base[1]}.{base[2]}.{base[3] + idx - 1}" if args.dhcp == 0 else None
plan.append(dict(idx=idx, port=port, chip=chip, target=target, ip=ip))
print("\nPlan:")
for d in plan:
print(f" {d['idx']:2d} {d['port']} {d['chip']} -> target {d['target']} IP:{d['ip'] or 'DHCP'}")
if args.dry_run:
print("\nDry run only.")
return
failed = []
for d in plan:
if d['target'] == 'unknown':
print(f"\nERROR: Unknown IDF target for {d['port']} (chip '{d['chip']}'). Skipping.")
failed.append(d['idx'])
continue
print(f"\nFlashing {d['port']} as {d['target']}...")
if not flash_device(project_dir, d['port'], d['target'], baud=args.baud):
failed.append(d['idx'])
continue
print(f"Configuring WiFi on {d['port']}...")
ok = send_wifi_config(
d['port'],
args.ssid,
args.password,
d['ip'],
args.mask if d['ip'] else None,
args.gw if d['ip'] else None,
dhcp=(args.dhcp == 1),
baud=args.cfg_baud,
)
if not ok:
print(f" WARN: config not acknowledged on {d['port']}")
if failed:
print(f"\nCompleted with flashing failures on: {failed}")
sys.exit(3)
print("\nAll done.")
if __name__ == "__main__":
main()

View File

@ -1,2 +1,8 @@
idf_component_register(SRCS "main.c" "iperf.c" idf_component_register(
INCLUDE_DIRS ".") SRCS
"main.c"
"iperf.c"
"wifi_cfg.c"
INCLUDE_DIRS
"."
)

View File

@ -1,137 +1,41 @@
// main.c — Add support for wifi_cfg and ip address assignment
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h> #include <inttypes.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h" #include "esp_event.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_mac.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "esp_console.h" #include "esp_console.h"
#include "argtable3/argtable3.h" #include "argtable3/argtable3.h"
#include "esp_netif.h" #include "esp_netif.h"
#include "lwip/inet.h" #include "lwip/inet.h"
#include "iperf.h"
#define WIFI_SSID CONFIG_WIFI_SSID #include "iperf.h"
#define WIFI_PASS CONFIG_WIFI_PASSWORD #include "wifi_cfg.h"
#define WIFI_MAXIMUM_RETRY CONFIG_WIFI_MAXIMUM_RETRY
static const char *TAG = "main"; static const char *TAG = "main";
static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static void event_handler(void* arg, esp_event_base_t event_base, static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) int32_t event_id, void* event_data)
{ {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
wifi_event_sta_connected_t* event = (wifi_event_sta_connected_t*) event_data;
// Get RSSI
wifi_ap_record_t ap_info;
esp_wifi_sta_get_ap_info(&ap_info);
ESP_LOGI(TAG, "Connected to AP SSID:%.*s, BSSID:" MACSTR " Channel:%d RSSI:%d dBm",
event->ssid_len, (const char *)event->ssid, MAC2STR(event->bssid), event->channel, ap_info.rssi);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < WIFI_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR " gw:" IPSTR " netmask:" IPSTR, ESP_LOGI(TAG, "got ip:" IPSTR " gw:" IPSTR " netmask:" IPSTR,
IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.ip),
IP2STR(&event->ip_info.gw), IP2STR(&event->ip_info.gw),
IP2STR(&event->ip_info.netmask)); IP2STR(&event->ip_info.netmask));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
} }
} }
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
#ifdef CONFIG_USE_STATIC_IP
// Configure static IP
ESP_LOGI(TAG, "Configuring static IP: %s", CONFIG_STATIC_IP_ADDR);
esp_netif_dhcpc_stop(sta_netif);
esp_netif_ip_info_t ip_info;
memset(&ip_info, 0, sizeof(esp_netif_ip_info_t));
ip_info.ip.addr = ipaddr_addr(CONFIG_STATIC_IP_ADDR);
ip_info.gw.addr = ipaddr_addr(CONFIG_STATIC_GATEWAY_ADDR);
ip_info.netmask.addr = ipaddr_addr(CONFIG_STATIC_NETMASK_ADDR);
ESP_ERROR_CHECK(esp_netif_set_ip_info(sta_netif, &ip_info));
#endif
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "WiFi connection successful");
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s", WIFI_SSID);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
/* Console command structure for iperf */
static struct { static struct {
struct arg_str *ip; struct arg_str *ip;
struct arg_lit *server; struct arg_lit *server;
@ -145,74 +49,6 @@ static struct {
struct arg_end *end; struct arg_end *end;
} iperf_args; } iperf_args;
/* Console command structure for wifi config */
static struct {
struct arg_str *ssid;
struct arg_str *password;
struct arg_end *end;
} wifi_args;
static int cmd_wifi(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&wifi_args);
if (nerrors != 0) {
arg_print_errors(stderr, wifi_args.end, argv[0]);
return 1;
}
if (wifi_args.ssid->count == 0) {
ESP_LOGE(TAG, "Please provide SSID with -s option");
return 1;
}
wifi_config_t wifi_config;
memset(&wifi_config, 0, sizeof(wifi_config_t));
// Set SSID
strncpy((char *)wifi_config.sta.ssid, wifi_args.ssid->sval[0], sizeof(wifi_config.sta.ssid) - 1);
// Set password if provided
if (wifi_args.password->count > 0) {
strncpy((char *)wifi_config.sta.password, wifi_args.password->sval[0], sizeof(wifi_config.sta.password) - 1);
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
}
ESP_LOGI(TAG, "Updating WiFi configuration...");
ESP_LOGI(TAG, "SSID: %s", wifi_config.sta.ssid);
// Stop WiFi
esp_wifi_disconnect();
esp_wifi_stop();
// Update config
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
// Restart WiFi
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
ESP_LOGI(TAG, "WiFi configuration updated. Attempting to connect...");
return 0;
}
static void register_wifi(void)
{
wifi_args.ssid = arg_str1("s", "ssid", "<ssid>", "WiFi SSID");
wifi_args.password = arg_str0("p", "password", "<password>", "WiFi password");
wifi_args.end = arg_end(1);
const esp_console_cmd_t wifi_cmd = {
.command = "wifi",
.help = "Configure WiFi credentials (wifi -s <ssid> -p <password>)",
.hint = NULL,
.func = &cmd_wifi,
.argtable = &wifi_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&wifi_cmd));
}
static int cmd_iperf(int argc, char **argv) static int cmd_iperf(int argc, char **argv)
{ {
int nerrors = arg_parse(argc, argv, (void **)&iperf_args); int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
@ -221,7 +57,6 @@ static int cmd_iperf(int argc, char **argv)
return 1; return 1;
} }
/* Check if needs to abort */
if (iperf_args.abort->count != 0) { if (iperf_args.abort->count != 0) {
iperf_stop(); iperf_stop();
return 0; return 0;
@ -230,10 +65,8 @@ static int cmd_iperf(int argc, char **argv)
iperf_cfg_t cfg; iperf_cfg_t cfg;
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
/* Set protocol type */
cfg.flag |= (iperf_args.udp->count == 0) ? IPERF_FLAG_TCP : IPERF_FLAG_UDP; cfg.flag |= (iperf_args.udp->count == 0) ? IPERF_FLAG_TCP : IPERF_FLAG_UDP;
/* Set server/client mode */
if (iperf_args.server->count != 0) { if (iperf_args.server->count != 0) {
cfg.flag |= IPERF_FLAG_SERVER; cfg.flag |= IPERF_FLAG_SERVER;
cfg.sport = IPERF_DEFAULT_PORT; cfg.sport = IPERF_DEFAULT_PORT;
@ -258,84 +91,61 @@ static int cmd_iperf(int argc, char **argv)
} }
} }
/* Set test time */
cfg.time = IPERF_DEFAULT_TIME; cfg.time = IPERF_DEFAULT_TIME;
if (iperf_args.time->count != 0) { if (iperf_args.time->count != 0) {
cfg.time = iperf_args.time->ival[0]; cfg.time = iperf_args.time->ival[0];
} }
/* Set bandwidth limit for UDP */
cfg.bw_lim = 0; cfg.bw_lim = 0;
if (iperf_args.bw->count != 0) { if (iperf_args.bw->count != 0) {
cfg.bw_lim = iperf_args.bw->ival[0]; cfg.bw_lim = iperf_args.bw->ival[0];
} }
ESP_LOGI(TAG, "mode=%s-%s sport=%u, dip=%u.%u.%u.%u:%u, interval=%" PRIu32 ", time=%" PRIu32, if (iperf_args.interval->count != 0) {
(cfg.flag & IPERF_FLAG_TCP) ? "tcp" : "udp", cfg.interval = iperf_args.interval->ival[0];
(cfg.flag & IPERF_FLAG_SERVER) ? "server" : "client", }
cfg.sport,
cfg.dip & 0xFF, (cfg.dip >> 8) & 0xFF, (cfg.dip >> 16) & 0xFF, (cfg.dip >> 24) & 0xFF, cfg.dport,
cfg.interval, cfg.time);
iperf_start(&cfg); iperf_start(&cfg);
return 0; return 0;
} }
static void register_iperf(void) static void register_iperf(void)
{ {
iperf_args.ip = arg_str0("c", "client", "<ip>", "run in client mode, connecting to <host>"); iperf_args.ip = arg_str0("c", "client_ip", "<ip>", "Destination IP for client mode");
iperf_args.server = arg_lit0("s", "server", "run in server mode"); iperf_args.server = arg_lit0("s", "server", "Run as server");
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP"); iperf_args.udp = arg_lit0("u", "udp", "Use UDP (default TCP)");
iperf_args.port = arg_int0("p", "port", "<port>", "server port to listen on/connect to"); iperf_args.client = arg_lit0("c", "client", "Run as client");
iperf_args.interval = arg_int0("i", "interval", "<interval>", "seconds between periodic bandwidth reports"); iperf_args.port = arg_int0("p", "port", "<n>", "Port (default 5001)");
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 30 secs)"); iperf_args.interval = arg_int0("i", "interval", "<s>", "Report interval");
iperf_args.bw = arg_int0("b", "bandwidth", "<bandwidth>", "bandwidth to send at in Mbits/sec"); iperf_args.time = arg_int0("t", "time", "<s>", "Test duration");
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf"); iperf_args.bw = arg_int0("b", "bandwidth", "<kbps>", "UDP bandwidth");
iperf_args.end = arg_end(1); iperf_args.abort = arg_lit0(NULL, "abort", "Abort iperf");
iperf_args.end = arg_end(2);
const esp_console_cmd_t iperf_cmd = { const esp_console_cmd_t cmd = {
.command = "iperf", .command = "iperf",
.help = "iperf command", .help = "iperf traffic test (server/client, TCP/UDP)",
.hint = NULL, .hint = NULL,
.func = &cmd_iperf, .func = &cmd_iperf,
.argtable= &iperf_args .argtable= &iperf_args
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
}
static void initialize_console(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
repl_config.prompt = "iperf>";
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
register_iperf();
register_wifi();
ESP_ERROR_CHECK(esp_console_start_repl(repl));
} }
void app_main(void) void app_main(void)
{ {
ESP_LOGI(TAG, "ESP32 iperf starting..."); ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Initialize NVS ESP_ERROR_CHECK(esp_event_handler_instance_register(
esp_err_t ret = nvs_flash_init(); IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL));
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase()); wifi_cfg_init();
ret = nvs_flash_init(); wifi_cfg_apply_from_nvs();
}
ESP_ERROR_CHECK(ret); register_iperf();
// Initialize WiFi ESP_LOGI(TAG, "System init complete. Waiting for serial config or console commands.");
wifi_init_sta();
// Initialize console
initialize_console();
ESP_LOGI(TAG, "Ready. Type 'iperf --help' for usage");
} }

167
main/wifi_cfg.c Normal file
View File

@ -0,0 +1,167 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "driver/uart.h"
#include "esp_vfs_dev.h"
#include "driver/usb_serial_jtag.h"
static const char *TAG = "wifi_cfg";
static void trim(char *s){
int n = strlen(s);
while(n>0 && (s[n-1]=='\r' || s[n-1]=='\n' || isspace((unsigned char)s[n-1]))) s[--n]=0;
while(*s && isspace((unsigned char)*s)) memmove(s, s+1, strlen(s));
}
static esp_err_t nvs_set_str2(nvs_handle_t h, const char *key, const char *val){
return val ? nvs_set_str(h, key, val) : nvs_erase_key(h, key);
}
static bool cfg_dhcp = true;
static void save_cfg(const char* ssid, const char* pass, const char* ip, const char* mask, const char* gw, bool dhcp){
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return;
if (ssid) nvs_set_str2(h, "ssid", ssid);
if (pass) nvs_set_str2(h, "pass", pass);
if (ip) nvs_set_str2(h, "ip", ip);
if (mask) nvs_set_str2(h, "mask", mask);
if (gw) nvs_set_str2(h, "gw", gw);
nvs_set_u8(h, "dhcp", dhcp ? 1 : 0);
nvs_commit(h);
nvs_close(h);
cfg_dhcp = dhcp;
}
static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, char* ip, size_t isz, char* mask, size_t msz, char* gw, size_t gsz, bool* dhcp){
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false;
size_t len;
esp_err_t e;
if ((e = nvs_get_str(h, "ssid", NULL, &len)) != ESP_OK){ nvs_close(h); return false; }
if (len >= ssz){ nvs_close(h); return false; }
nvs_get_str(h, "ssid", ssid, &len);
len = psz; e = nvs_get_str(h, "pass", pass, &len); if (e!=ESP_OK) pass[0]=0;
len = isz; e = nvs_get_str(h, "ip", ip, &len); if (e!=ESP_OK) ip[0]=0;
len = msz; e = nvs_get_str(h, "mask", mask, &len); if (e!=ESP_OK) mask[0]=0;
len = gsz; e = nvs_get_str(h, "gw", gw, &len); if (e!=ESP_OK) gw[0]=0;
uint8_t d=1; nvs_get_u8(h, "dhcp", &d); *dhcp = (d!=0);
nvs_close(h);
return true;
}
void wifi_cfg_force_dhcp(bool enable){ cfg_dhcp = enable; }
static esp_netif_t *sta_netif = NULL;
static void apply_ip_static(const char* ip, const char* mask, const char* gw){
if (!sta_netif) return;
esp_netif_ip_info_t info = {0};
esp_netif_dhcpc_stop(sta_netif);
info.ip.addr = esp_ip4addr_aton(ip);
info.netmask.addr = esp_ip4addr_aton(mask);
info.gw.addr = esp_ip4addr_aton(gw);
ESP_ERROR_CHECK( esp_netif_set_ip_info(sta_netif, &info) );
}
bool wifi_cfg_apply_from_nvs(void){
char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
bool dhcp = true;
if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), &dhcp)){
ESP_LOGW(TAG, "No WiFi config in NVS");
return false;
}
ESP_LOGI(TAG, "Applying WiFi config: SSID=%s DHCP=%d IP=%s", ssid, dhcp, ip);
static bool inited = false;
if (!inited){
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
sta_netif = esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
inited = true;
}
wifi_config_t wcfg = {0};
strncpy((char*)wcfg.sta.ssid, ssid, sizeof(wcfg.sta.ssid)-1);
strncpy((char*)wcfg.sta.password, pass, sizeof(wcfg.sta.password)-1);
wcfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wcfg) );
if (!dhcp && ip[0] && mask[0] && gw[0]){
apply_ip_static(ip, mask, gw);
}else{
if (sta_netif) esp_netif_dhcpc_start(sta_netif);
}
ESP_ERROR_CHECK( esp_wifi_start() );
ESP_ERROR_CHECK( esp_wifi_connect() );
return true;
}
static void cfg_listener_task(void *arg){
// Install low-level USB-Serial/JTAG driver
usb_serial_jtag_driver_config_t dcfg = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&dcfg));
// Bind stdio to that driver (deprecated name, but available on IDF v5.3.1)
esp_vfs_usb_serial_jtag_use_driver();
// Now stdio is safe to use
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
char line[160];
char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
bool dhcp = true;
bool in_cfg = false;
while (1){
if (!fgets(line, sizeof(line), stdin)){
vTaskDelay(pdMS_TO_TICKS(50));
continue;
}
// ... rest unchanged ...
trim(line);
if (!in_cfg){
if (strcmp(line, "CFG")==0){
in_cfg = true;
ssid[0]=pass[0]=ip[0]=mask[0]=gw[0]=0;
dhcp = true;
}
continue;
}
if (strcmp(line, "END")==0){
save_cfg(ssid, pass, ip, mask, gw, dhcp);
printf("OK\n");
wifi_cfg_apply_from_nvs();
in_cfg = false;
continue;
}
if (strncmp(line, "SSID:",5)==0){ strncpy(ssid, line+5, sizeof(ssid)-1); continue; }
if (strncmp(line, "PASS:",5)==0){ strncpy(pass, line+5, sizeof(pass)-1); continue; }
if (strncmp(line, "IP:",3)==0){ strncpy(ip, line+3, sizeof(ip)-1); continue; }
if (strncmp(line, "MASK:",5)==0){ strncpy(mask, line+5, sizeof(mask)-1); continue; }
if (strncmp(line, "GW:",3)==0){ strncpy(gw, line+3, sizeof(gw)-1); continue; }
if (strncmp(line, "DHCP:",5)==0){ dhcp = atoi(line+5) ? true:false; continue; }
}
}
void wifi_cfg_init(void){
nvs_flash_init();
xTaskCreatePinnedToCore(cfg_listener_task, "cfg_listener", 6144, NULL, 5, NULL, tskNO_AFFINITY);
}

15
main/wifi_cfg.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
void wifi_cfg_init(void); // starts serial listener task
bool wifi_cfg_apply_from_nvs(void); // reads saved config and connects WiFi
void wifi_cfg_force_dhcp(bool enable); // for testing
#ifdef __cplusplus
}
#endif