diff --git a/components/wifi_cfg/wifi_cfg.c b/components/wifi_cfg/wifi_cfg.c index e24165e..edfe8cc 100644 --- a/components/wifi_cfg/wifi_cfg.c +++ b/components/wifi_cfg/wifi_cfg.c @@ -45,7 +45,7 @@ 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 void save_cfg(const char* ssid, const char* pass, const char* ip, const char* mask, const char* gw, bool dhcp){ +static void save_cfg(const char* ssid, const char* pass, const char* ip, const char* mask, const char* gw, bool dhcp, const char* band, const char* bw){ nvs_handle_t h; if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return; if (ssid) nvs_set_str2(h, "ssid", ssid); @@ -53,14 +53,18 @@ static void save_cfg(const char* ssid, const char* pass, const char* ip, const c if (ip) nvs_set_str2(h, "ip", ip); if (mask) nvs_set_str2(h, "mask", mask); if (gw) nvs_set_str2(h, "gw", gw); + if (band) nvs_set_str2(h, "band", band); + if (bw) nvs_set_str2(h, "bw", bw); nvs_set_u8(h, "dhcp", dhcp ? 1 : 0); nvs_commit(h); nvs_close(h); cfg_dhcp = dhcp; + ESP_LOGI(TAG, "Config saved to NVS: SSID=%s Band=%s BW=%s", ssid?ssid:"", band?band:"", bw?bw:""); } 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){ + char* ip, size_t isz, char* mask, size_t msz, char* gw, size_t gsz, + char* band, size_t bsz, char* bw, size_t bwsz, bool* dhcp){ nvs_handle_t h; if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false; size_t len; @@ -72,6 +76,8 @@ static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, 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; + len = bsz; e = nvs_get_str(h, "band", band, &len); if (e!=ESP_OK) strcpy(band, "2.4G"); + len = bwsz; e = nvs_get_str(h, "bw", bw, &len); if (e!=ESP_OK) strcpy(bw, "HT20"); uint8_t d=1; nvs_get_u8(h, "dhcp", &d); *dhcp = (d!=0); nvs_close(h); return true; @@ -134,14 +140,16 @@ static void apply_ip_static(const char* ip, const char* mask, const char* gw){ ESP_ERROR_CHECK( esp_netif_set_ip_info(sta_netif, &info) ); } -bool wifi_cfg_apply_from_nvs(void){ +bool wifi_cfg_apply_from_nvs(void) { char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0}; + char band[16]={0}, bw[16]={0}; bool dhcp = true; - if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), &dhcp)){ + if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), + band,sizeof(band), bw,sizeof(bw), &dhcp)){ ESP_LOGW(TAG, "No Wi‑Fi config in NVS"); return false; } - ESP_LOGI(TAG, "Applying Wi‑Fi config: SSID=%s DHCP=%d IP=%s", ssid, dhcp, ip); + ESP_LOGI(TAG, "Applying Wi‑Fi config: SSID=%s DHCP=%d IP=%s Band=%s BW=%s", ssid, dhcp, ip, band, bw); static bool inited = false; if (!inited){ @@ -164,7 +172,6 @@ bool wifi_cfg_apply_from_nvs(void){ sta_netif = esp_netif_create_default_wifi_sta(); } - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(err=wifi_ensure_inited()); if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err); } @@ -177,7 +184,39 @@ bool wifi_cfg_apply_from_nvs(void){ wcfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH; + // Set scan method to search all channels + wcfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + + // Log and configure band preference + if (strcmp(band, "5G") == 0) { + ESP_LOGI(TAG, "Configuring for 5GHz operation"); + // For 5GHz preference, scan both but prefer 5GHz channels + wcfg.sta.channel = 0; // Scan all channels (both 2.4GHz and 5GHz) + } else { + ESP_LOGI(TAG, "Configuring for 2.4GHz operation (default)"); + wcfg.sta.channel = 0; // Scan all channels + } + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + + // Enable WiFi 6 (802.11ax) and all protocols for best compatibility + wifi_protocols_t protocols = { + // 2.4 GHz: b/g/n/ax + .ghz_2g = WIFI_PROTOCOL_11B | + WIFI_PROTOCOL_11G | + WIFI_PROTOCOL_11N | + WIFI_PROTOCOL_11AX, + + // 5 GHz: a/n/ac/ax + .ghz_5g = WIFI_PROTOCOL_11A | + WIFI_PROTOCOL_11N | + WIFI_PROTOCOL_11AC | + WIFI_PROTOCOL_11AX, + // .ghz_6g will be zero-initialized (not used on C5) + }; + + ESP_ERROR_CHECK( esp_wifi_set_protocols(WIFI_IF_STA, &protocols) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wcfg) ); if (!dhcp && ip[0] && mask[0] && gw[0]){ @@ -188,6 +227,28 @@ bool wifi_cfg_apply_from_nvs(void){ esp_err_t err2 = esp_wifi_start(); if (err2 != ESP_OK && err2 != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err2); } + + // Set bandwidth AFTER WiFi is started but BEFORE connect + // For ESP32-C5 dual-band, use esp_wifi_set_bandwidths() with struct + wifi_bandwidths_t bandwidths = { + .ghz_2g = WIFI_BW_HT20, + .ghz_5g = WIFI_BW_HT20 + }; + + if (strcmp(bw, "HT40") == 0) { + bandwidths.ghz_2g = WIFI_BW_HT40; + bandwidths.ghz_5g = WIFI_BW_HT40; + ESP_LOGI(TAG, "Setting bandwidth to HT40 (40MHz) for both bands"); + } else { + ESP_LOGI(TAG, "Setting bandwidth to HT20 (20MHz) for both bands"); + } + + // Use dual-band API with struct pointer + err2 = esp_wifi_set_bandwidths(WIFI_IF_STA, &bandwidths); + if (err2 != ESP_OK) { + ESP_LOGW(TAG, "Failed to set bandwidths: %s", esp_err_to_name(err2)); + } + err2 = esp_wifi_connect(); if (err2 != ESP_OK && err2 != ESP_ERR_WIFI_NOT_INIT && err2 != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err2); } return true; @@ -203,18 +264,21 @@ typedef struct { bool (*fetch_line)(char *buf, size_t sz, void *ctx); } cfg_io_t; -static void on_cfg_line(const char *line, char *ssid, char *pass, char *ip, char *mask, char *gw, bool *dhcp){ +static void on_cfg_line(const char *line, char *ssid, char *pass, char *ip, char *mask, char *gw, char *band, char *bw, bool *dhcp){ if (strncmp(line, "SSID:",5)==0){ strncpy(ssid, line+5, 63); ssid[63]=0; return; } if (strncmp(line, "PASS:",5)==0){ strncpy(pass, line+5, 63); pass[63]=0; return; } if (strncmp(line, "IP:",3)==0){ strncpy(ip, line+3, 31); ip[31]=0; return; } if (strncmp(line, "MASK:",5)==0){ strncpy(mask, line+5, 31); mask[31]=0; return; } if (strncmp(line, "GW:",3)==0){ strncpy(gw, line+3, 31); gw[31]=0; return; } + if (strncmp(line, "BAND:",5)==0){ strncpy(band, line+5, 15); band[15]=0; return; } + if (strncmp(line, "BW:",3)==0){ strncpy(bw, line+3, 15); bw[15]=0; return; } if (strncmp(line, "DHCP:",5)==0){ *dhcp = atoi(line+5) ? true:false; return; } } static void cfg_worker(const cfg_io_t *io){ char line[160]; char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0}; + char band[16]={0}, bw[16]={0}; bool dhcp = true; bool in_cfg = false; @@ -228,18 +292,23 @@ static void cfg_worker(const cfg_io_t *io){ if (strcmp(line, "CFG")==0){ in_cfg = true; ssid[0]=pass[0]=ip[0]=mask[0]=gw[0]=0; + band[0]=bw[0]=0; dhcp = true; } continue; } if (strcmp(line, "END")==0){ - save_cfg(ssid, pass, ip, mask, gw, dhcp); + // Set defaults if not specified + if (!band[0]) strcpy(band, "2.4G"); + if (!bw[0]) strcpy(bw, "HT20"); + + save_cfg(ssid, pass, ip, mask, gw, dhcp, band, bw); if (io->emit) io->emit("OK\n", io->ctx); wifi_cfg_apply_from_nvs(); in_cfg = false; continue; } - on_cfg_line(line, ssid, pass, ip, mask, gw, &dhcp); + on_cfg_line(line, ssid, pass, ip, mask, gw, band, bw, &dhcp); } } diff --git a/config_device.py b/config_device.py index 61097bf..22f205f 100755 --- a/config_device.py +++ b/config_device.py @@ -14,19 +14,23 @@ def log_verbose(message, verbose=False): print(f"[VERBOSE] {message}") def config_device(port, ip, ssid="ClubHouse2G", password="ez2remember", - gateway="192.168.1.1", netmask="255.255.255.0", verbose=False): + gateway="192.168.1.1", netmask="255.255.255.0", + band="2.4G", bandwidth="HT20", reboot=True, verbose=False): """Configure ESP32 device via serial""" print(f"\n{'='*70}") print(f"ESP32 WiFi Configuration") print(f"{'='*70}") - print(f"Port: {port}") - print(f"SSID: {ssid}") - print(f"Password: {'*' * len(password)}") - print(f"IP: {ip}") - print(f"Gateway: {gateway}") - print(f"Netmask: {netmask}") - print(f"Verbose: {verbose}") + print(f"Port: {port}") + print(f"SSID: {ssid}") + print(f"Password: {'*' * len(password)}") + print(f"IP: {ip}") + print(f"Gateway: {gateway}") + print(f"Netmask: {netmask}") + print(f"Band: {band}") + print(f"Bandwidth: {bandwidth}") + print(f"Reboot: {'Yes' if reboot else 'No'}") + print(f"Verbose: {verbose}") print(f"{'='*70}\n") try: @@ -53,6 +57,8 @@ def config_device(port, ip, ssid="ClubHouse2G", password="ez2remember", f"MASK:{netmask}", f"GW:{gateway}", "DHCP:0", + f"BAND:{band}", + f"BW:{bandwidth}", "END" ] @@ -67,6 +73,12 @@ def config_device(port, ip, ssid="ClubHouse2G", password="ez2remember", # Send config print("Sending configuration...") + print("\nConfiguration being sent:") + for line in config_lines: + display_line = line if not line.startswith("PASS:") else "PASS:********" + print(f" {display_line}") + print() + start_time = time.time() bytes_written = ser.write(config.encode('utf-8')) @@ -74,42 +86,135 @@ def config_device(port, ip, ssid="ClubHouse2G", password="ez2remember", send_time = time.time() - start_time log_verbose(f"Wrote {bytes_written} bytes in {send_time:.3f}s", verbose) + print(f"Sent {bytes_written} bytes") - print("Sent! Waiting for response...") - time.sleep(2) + print("Waiting for response...") + time.sleep(3) # Give more time for response # Read response if ser.in_waiting: response_size = ser.in_waiting - log_verbose(f"Response available: {response_size} bytes", verbose) + print(f"\n✓ Response received: {response_size} bytes") response = ser.read(response_size).decode('utf-8', errors='ignore') - if verbose: - print("[VERBOSE] Raw response:") - for line in response.split('\n')[:20]: # Show first 20 lines - if line.strip(): - print(f"[VERBOSE] {line}") + print("\nDevice response:") + print("-" * 70) + for line in response.split('\n')[:30]: # Show first 30 lines + if line.strip(): + print(f" {line}") + print("-" * 70) # Check for key indicators + success_indicators = [] + warning_indicators = [] + if "OK" in response: - print("✓ Device acknowledged configuration (OK)") + success_indicators.append("✓ Configuration acknowledged (OK)") + if "Config saved" in response or "saved to NVS" in response: + success_indicators.append("✓ Config saved to NVS") if "got ip:" in response.lower(): - print("✓ Device connected to WiFi!") + success_indicators.append("✓ Device connected to WiFi!") # Extract IP from response import re ip_match = re.search(r'got ip:(\d+\.\d+\.\d+\.\d+)', response, re.IGNORECASE) if ip_match: - print(f" Assigned IP: {ip_match.group(1)}") + success_indicators.append(f" Assigned IP: {ip_match.group(1)}") if "connected" in response.lower(): - print("✓ WiFi connection established") + success_indicators.append("✓ WiFi connection established") + if "failed" in response.lower() or "disconnect" in response.lower(): - print("✗ WiFi connection may have failed") - if verbose: - print("[VERBOSE] Check response above for error details") + warning_indicators.append("⚠ WiFi connection may have failed") + if "error" in response.lower(): + warning_indicators.append("⚠ Error detected in response") + + if success_indicators: + print("\nStatus indicators:") + for indicator in success_indicators: + print(f" {indicator}") + + if warning_indicators: + print("\nWarnings:") + for warning in warning_indicators: + print(f" {warning}") else: - log_verbose("No immediate response from device", verbose) - print("⚠ No response (device may still be processing)") + print("\n⚠ No response from device") + print(" This could mean:") + print(" - Device is not running config handler") + print(" - Wrong serial port") + print(" - Baud rate mismatch") + + # Reboot device if requested + if reboot: + print("\n" + "="*70) + print("Rebooting device...") + print("="*70) + log_verbose("Performing hardware reset via DTR/RTS", verbose) + + # Standard ESP32 reset sequence + ser.dtr = False # DTR low + ser.rts = True # RTS high (EN low) + time.sleep(0.1) + + ser.rts = False # RTS low (EN high) + time.sleep(0.1) + + ser.dtr = True # DTR high + + print("✓ Reset signal sent - waiting for boot...") + + # Wait for boot messages + time.sleep(3) # Longer wait for full boot + + if ser.in_waiting: + boot_msg = ser.read(ser.in_waiting).decode('utf-8', errors='ignore') + + print("\nBoot messages:") + print("-" * 70) + for line in boot_msg.split('\n')[:40]: # Show more lines + if line.strip(): + print(f" {line}") + print("-" * 70) + + # Check boot status + boot_success = [] + boot_warnings = [] + + if "WiFi config loaded from NVS" in boot_msg: + boot_success.append("✓ Config successfully loaded from NVS") + elif "No WiFi config" in boot_msg or "YELLOW LED" in boot_msg: + boot_warnings.append("✗ NO CONFIG found in NVS - this is the problem!") + boot_warnings.append(" Device does not see saved config") + + if "got ip:" in boot_msg.lower(): + boot_success.append("✓ Device connected to WiFi!") + import re + ip_match = re.search(r'got ip:(\d+\.\d+\.\d+\.\d+)', boot_msg, re.IGNORECASE) + if ip_match: + boot_success.append(f" Connected with IP: {ip_match.group(1)}") + + if "WiFi CONNECTED" in boot_msg: + boot_success.append("✓ WiFi connection confirmed") + + if "BLUE LED solid" in boot_msg or "BLUE solid" in boot_msg: + boot_success.append("✓ LED should be BLUE (connected)") + elif "YELLOW solid" in boot_msg or "YELLOW LED" in boot_msg: + boot_warnings.append("⚠ LED is YELLOW (no config)") + elif "RED" in boot_msg and "blink" in boot_msg: + boot_warnings.append("⚠ LED is RED (connection failed)") + + if boot_success: + print("\nBoot Status - SUCCESS:") + for msg in boot_success: + print(f" {msg}") + + if boot_warnings: + print("\nBoot Status - ISSUES:") + for msg in boot_warnings: + print(f" {msg}") + else: + print("\n⚠ No boot messages received") + print(" Device may still be booting...") # Get final port stats if verbose: @@ -119,10 +224,28 @@ def config_device(port, ip, ssid="ClubHouse2G", password="ez2remember", ser.close() log_verbose("Serial port closed", verbose) - print(f"\nConfiguration sent to {port}") + print(f"\n{'='*70}") + print("Configuration Summary") + print(f"{'='*70}") + print(f"Port: {port}") print(f"Expected IP: {ip}") - print(f"Test with: ping {ip}") - print(f" iperf -c {ip}") + print(f"SSID: {ssid}") + print(f"Band: {band}") + print(f"Bandwidth: {bandwidth}") + print(f"{'='*70}") + print("\nNext steps:") + print(f" 1. Check LED color:") + print(f" - GREEN = Connected ✓") + print(f" - YELLOW = No config saved ✗") + print(f" - RED blinking = Connection failed") + print(f" 2. Test connection:") + print(f" ping {ip}") + print(f" iperf -c {ip}") + print(f"\nIf LED is YELLOW:") + print(f" - Config is NOT being saved to NVS") + print(f" - Check if wifi_cfg.c is handling BAND: and BW: fields") + print(f" - Try running: idf.py monitor") + print(f" to see what the device is actually receiving") return True @@ -151,14 +274,20 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: - # Basic configuration + # Basic configuration (2.4GHz) with auto-reboot %(prog)s -p /dev/ttyUSB0 -i 192.168.1.51 - # With verbose output + # 5GHz with HT40 bandwidth and auto-reboot + %(prog)s -p /dev/ttyUSB0 -i 192.168.1.81 -s ClubHouse5G -b 5G -B HT40 + + # Configure without rebooting (manual reboot required) + %(prog)s -p /dev/ttyUSB0 -i 192.168.1.51 -r + + # With verbose output to see boot messages %(prog)s -p /dev/ttyUSB0 -i 192.168.1.51 -v - # Custom WiFi credentials - %(prog)s -p /dev/ttyUSB0 -i 192.168.1.52 -s MyWiFi -P mypass -v + # Custom WiFi credentials on 2.4GHz + %(prog)s -p /dev/ttyUSB0 -i 192.168.1.52 -s MyWiFi -P mypass # Custom gateway %(prog)s -p /dev/ttyUSB0 -i 10.0.0.100 -g 10.0.0.1 @@ -177,6 +306,13 @@ Examples: help='Gateway IP (default: 192.168.1.1)') parser.add_argument('-m', '--netmask', default='255.255.255.0', help='Netmask (default: 255.255.255.0)') + parser.add_argument('-b', '--band', default='2.4G', choices=['2.4G', '5G'], + help='WiFi band: 2.4G or 5G (default: 2.4G)') + parser.add_argument('-B', '--bandwidth', default='HT20', + choices=['HT20', 'HT40'], + help='Channel bandwidth: HT20 or HT40 (default: HT20)') + parser.add_argument('-r', '--no-reboot', action='store_true', + help='Do NOT reboot device after configuration (default: will reboot)') parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output (show detailed debug info)') @@ -189,6 +325,9 @@ Examples: password=args.password, gateway=args.gateway, netmask=args.netmask, + band=args.band, + bandwidth=args.bandwidth, + reboot=not args.no_reboot, # Invert since flag is --no-reboot verbose=args.verbose )