Ryan Malloy 4e19882d32 Harden firmware for dual-core concurrency and input validation
Address safety review findings for the dual-interface (WiFi + USB serial)
architecture running on the ESP32-S3's two Xtensa LX7 cores:

- Protect sweep state with std::atomic (acquire/release ordering)
- Add Attenuator::getSnapshot() for consistent multi-field reads
- Add advanceStep()/persistCurrent() to eliminate TOCTOU races
- Switch to StaticSemaphore_t (compile-time mutex, can't fail)
- Accumulate web server POST bodies before parsing (chunked TCP fix)
- Backport USB serial input validation to web server handlers
- Auto-stop sweep on manual set (prevents silent overwrite)
- Validate WiFi TX power against known-good levels
- Add OTA password authentication support
- Check NVS write return values, log failures
- Reset USB serial buffer on reconnect (stale overflow fix)
- Rename sweep.h to app.h (declares more than sweep functions)
2026-02-18 18:43:08 -07:00

89 lines
3.0 KiB
C

#pragma once
#include <Arduino.h>
// --- Firmware Version ---
#define FW_VERSION "2026-02-02"
#define FW_HOSTNAME "attenuator"
// --- WiFi Credentials ---
// Define WIFI_SSID and WIFI_PASS via build_flags in platformio_local.ini
#ifndef WIFI_SSID
#error "WIFI_SSID not defined — add build_flags to firmware/platformio_local.ini (see platformio_local.ini.example)"
#endif
#ifndef WIFI_PASS
#error "WIFI_PASS not defined — add build_flags to firmware/platformio_local.ini (see platformio_local.ini.example)"
#endif
#define WIFI_TIMEOUT_MS 15000
// WiFi TX power level (dBm)
// Lower = less current draw, less RF interference on bench
// Valid: WIFI_POWER_2dBm to WIFI_POWER_19_5dBm
// Using minimum (2 dBm / ~1.6 mW) to prevent brownout on USB power
#ifndef WIFI_TX_POWER_DBM
#define WIFI_TX_POWER_DBM WIFI_POWER_2dBm
#endif
// --- HMC472A Control Pins (active-low) ---
// Optimized mapping: GPIO number = step bit position + 1
// Enables single-instruction bitwise ops instead of loop
//
// Wiring (GPIO → HMC472A pin):
// GPIO1 → V6 (0.5 dB) = step bit 0 (LSB)
// GPIO2 → V5 (1 dB) = step bit 1
// GPIO3 → V4 (2 dB) = step bit 2
// GPIO4 → V3 (4 dB) = step bit 3
// GPIO5 → V2 (8 dB) = step bit 4
// GPIO6 → V1 (16 dB) = step bit 5 (MSB)
//
static constexpr uint8_t PIN_V6 = 1; // 0.5 dB (LSB) - step bit 0
static constexpr uint8_t PIN_V5 = 2; // 1 dB - step bit 1
static constexpr uint8_t PIN_V4 = 3; // 2 dB - step bit 2
static constexpr uint8_t PIN_V3 = 4; // 4 dB - step bit 3
static constexpr uint8_t PIN_V2 = 5; // 8 dB - step bit 4
static constexpr uint8_t PIN_V1 = 6; // 16 dB (MSB) - step bit 5
// Pin array ordered by attenuation value (V1=16dB first)
static constexpr uint8_t ATTEN_PINS[6] = {PIN_V1, PIN_V2, PIN_V3, PIN_V4, PIN_V5, PIN_V6};
static constexpr float ATTEN_DB[6] = {16.0f, 8.0f, 4.0f, 2.0f, 1.0f, 0.5f};
// Bitmask: 0b01111110 = bits 1-6 in GPIO register
static constexpr uint32_t ATTEN_PIN_MASK = 0x7E;
// --- Status LED ---
static constexpr uint8_t PIN_LED = 15; // Built-in LED, active HIGH
// --- OLED Display (SSD1306 128x64 I2C) ---
static constexpr uint8_t PIN_SDA = 8;
static constexpr uint8_t PIN_SCL = 9;
static constexpr uint8_t OLED_ADDR = 0x3C; // 7-bit address (0x78 >> 1)
static constexpr uint8_t OLED_WIDTH = 128;
static constexpr uint8_t OLED_HEIGHT = 64;
// --- Attenuator Limits ---
static constexpr float DB_MIN = 0.0f;
static constexpr float DB_MAX = 31.5f;
static constexpr float DB_STEP = 0.5f;
static constexpr uint8_t STEP_MIN = 0;
static constexpr uint8_t STEP_MAX = 63;
// --- Sweep Defaults ---
static constexpr uint32_t SWEEP_DWELL_MS_DEFAULT = 500;
static constexpr uint32_t SWEEP_DWELL_MS_MIN = 10;
static constexpr uint32_t SWEEP_DWELL_MS_MAX = 10000;
// --- NVS ---
#define NVS_NAMESPACE "atten"
#define NVS_KEY_STEP "step"
// --- Watchdog ---
#define WDT_TIMEOUT_S 120
// --- Web Server ---
#define WEB_PORT 80
// --- USB Serial Command Interface ---
#define USB_SERIAL_BAUD 115200
#define USB_SERIAL_BUF_LEN 256