#pragma once #include // =========================================================================== // Compile-time power profile (PRD 4) // Exactly one of these is set by the PlatformIO environment. See platformio.ini. // =========================================================================== #if defined(POWER_PROFILE_MAINS) && defined(POWER_PROFILE_BATTERY) #error "Define only one of POWER_PROFILE_MAINS / POWER_PROFILE_BATTERY" #endif #if !defined(POWER_PROFILE_MAINS) && !defined(POWER_PROFILE_BATTERY) #error "No power profile selected. Build with -e mains or -e battery." #endif // =========================================================================== // Pin map -- VCC-GND Studio YD-ESP32-S3 (ESP32-S3-WROOM-1) // // Every sensor input sits on ADC1. This is not a style choice: ADC2 is owned // by the Wi-Fi radio on the ESP32-S3, and analogRead() on an ADC2 pin returns // garbage (or blocks) whenever the radio is active. ADC1 is GPIO1..GPIO10, // which is exactly enough for 6 sensors + a battery divider. // // Also avoided on this board: // GPIO0 strapping / BOOT button // GPIO3 strapping (JTAG source select) // GPIO19/20 native USB D-/D+ // GPIO33..37 octal flash/PSRAM on N*R8 variants // GPIO43/44 UART0 TX/RX (serial monitor) // GPIO45/46 strapping (VDD_SPI / boot log) // =========================================================================== constexpr uint8_t SENSOR_COUNT = 6; // Sensor 1..6 -> ADC1_CH3..CH8 constexpr uint8_t SENSOR_ADC_PINS[SENSOR_COUNT] = {4, 5, 6, 7, 8, 9}; // Battery sense via 2:1 divider -> ADC1_CH9. Battery profile only. constexpr uint8_t BATTERY_ADC_PIN = 10; // Gate of the P-channel MOSFET (or high-side load switch) feeding the 5V // sensor rail. HIGH = sensors powered. Battery profile only; on mains the // rail is simply left on. Needs an external pulldown/pullup so the rail is // OFF while the MCU is in deep sleep and the pin is floating. constexpr uint8_t SENSOR_POWER_GATE_PIN = 14; // Onboard addressable RGB LED. Used only for coarse boot/error feedback. constexpr uint8_t STATUS_LED_PIN = 48; // =========================================================================== // ADC sampling & smoothing (PRD 3.3) // =========================================================================== // Readings taken per sensor per cycle, before outlier rejection. constexpr uint8_t OVERSAMPLE_COUNT = 16; // Fraction discarded from each end of the sorted sample set (10% per PRD). // At 16 samples this trims 1 from the top and 1 from the bottom. constexpr uint8_t OVERSAMPLE_TRIM_PERCENT = 10; // Microseconds between individual reads within one sensor's burst. Spreads // the burst across mains-hum periods rather than sampling one point on it. constexpr uint32_t OVERSAMPLE_DELAY_US = 2000; // Time for the 5V rail and the sensors' XC6206 LDOs to settle after the // MOSFET turns on (PRD 4.2 step 3). constexpr uint32_t SENSOR_STABILIZE_MS = 500; // =========================================================================== // Two-point calibration (PRD 3.3) // // HW-390 output is INVERTED: dry/air reads HIGH, submerged reads LOW. // So AIR_MV > WATER_MV, and 0% is at AIR_MV. // // Per-sensor values because each probe's LDO and coating differ. Run // `docs/CALIBRATION.md` once per probe and paste the numbers here. // The defaults below are typical for a 3.3V HW-390 v2.0 and WILL be wrong // for your probes -- they exist so a fresh build compiles and reads // plausibly, not so you can skip calibration. // =========================================================================== constexpr uint16_t CAL_AIR_MV[SENSOR_COUNT] = {2600, 2600, 2600, 2600, 2600, 2600}; constexpr uint16_t CAL_WATER_MV[SENSOR_COUNT] = {1200, 1200, 1200, 1200, 1200, 1200}; // A probe reading outside [WATER_MV - margin, AIR_MV + margin] is reported as // unavailable rather than clamped -- that pattern means a cut Cat5 pair, a // dead LDO, or a probe pulled out of the ground, and silently clamping it to // 0%/100% would hide a fault behind a plausible number. constexpr uint16_t SENSOR_FAULT_MARGIN_MV = 400; // =========================================================================== // Battery monitoring (battery profile only) // =========================================================================== // Divider ratio: Vbat --[R1]--+--[R2]-- GND, ADC on the junction. // With R1 = R2 = 100k the measured voltage is half the battery voltage. constexpr float BATTERY_DIVIDER_RATIO = 2.0f; // Li-Ion 18650 discharge window used for the 0-100% mapping. constexpr uint16_t BATTERY_EMPTY_MV = 3300; constexpr uint16_t BATTERY_FULL_MV = 4200; // =========================================================================== // Timing // =========================================================================== #if defined(POWER_PROFILE_MAINS) // Publish cadence while awake (PRD 4.1). constexpr uint32_t PUBLISH_INTERVAL_MS = 60UL * 1000UL; #else // RTC wake interval (PRD 4.2). 15 minutes. constexpr uint64_t DEEP_SLEEP_INTERVAL_S = 15ULL * 60ULL; // Hard ceiling on one wake cycle. If Wi-Fi or the broker is unreachable we // sleep anyway instead of sitting at ~80mA until the cell is flat. constexpr uint32_t AWAKE_BUDGET_MS = 30UL * 1000UL; #endif constexpr uint32_t WIFI_CONNECT_TIMEOUT_MS = 15UL * 1000UL; constexpr uint32_t MQTT_CONNECT_TIMEOUT_MS = 10UL * 1000UL; // =========================================================================== // MQTT / Home Assistant // =========================================================================== // Prefix HA listens on for auto-discovery. Matches the HA MQTT integration's // "discovery prefix" setting; only change it if you changed it there. #define HA_DISCOVERY_PREFIX "homeassistant" // Root of this project's own state topics. #define MQTT_TOPIC_ROOT "soil" // Publish raw millivolts alongside the percentages as diagnostic entities. // Leave on: these are what you read off the HA dashboard when calibrating. #define PUBLISH_RAW_MV 1 #define FIRMWARE_VERSION "1.0.0"