|
| 1 | +# TankSync Hardware Wiring & Pin Connections |
| 2 | + |
| 3 | +Authoritative wiring reference for the TankSync receiver (RX) and transmitter (TX) boards. Pin assignments here match `firmware/<target>/main/config.h` exactly; if the two ever disagree, the `config.h` file is the source of truth and this doc must be updated. |
| 4 | + |
| 5 | +## Topology overview |
| 6 | + |
| 7 | +| Device | Power source | Where it lives | Duty cycle | |
| 8 | +|---|---|---|---| |
| 9 | +| **RX** (receiver / display unit) | 5V USB-C wall brick (≥1.5A) → on-board 3.3V LDO | Indoors, near a power outlet, within WiFi range | Always-on; WiFi STA + LoRa RX listening continuously | |
| 10 | +| **TX** (transmitter / sensor unit) | 6V solar panel → CN3791 MPPT charger → 18650 Li-ion → MT3608 boost → 5V rail | Outdoors, mounted on or near the water tank, often far from the house | Deep-sleep cycle: wakes every 5 min, samples ultrasonic + LoRa burst (~3s awake), sleeps | |
| 11 | + |
| 12 | +The TX is the energy-constrained device. The RX is intentionally indoor-and-plugged so its power budget (display + WiFi + LoRa-listen + LEDs) doesn't have to fit a small solar harvest. |
| 13 | + |
| 14 | +--- |
| 15 | + |
| 16 | +## Receiver (RX) wiring |
| 17 | + |
| 18 | +There are **two RX hardware variants** depending on the ESP32 chip on hand: |
| 19 | +- **DevKit ESP32 v1** (CP2102, 30-pin header) — full-size, more GPIOs, easier to breadboard |
| 20 | +- **ESP32-C3 SuperMini** — pocket-sized, fewer GPIOs |
| 21 | + |
| 22 | +Both variants use the **same external modules** (RYLR998 LoRa, SH1106 OLED, WS2812B LEDs). Only the GPIO assignments differ. Power comes from a 5V USB-C wall brick into the dev-board's USB connector; the on-board AMS1117-3.3 LDO derives the 3.3V rail used by RYLR998. |
| 23 | + |
| 24 | +### RX block diagram |
| 25 | + |
| 26 | +``` |
| 27 | + [5V USB-C wall brick, ≥1.5A] |
| 28 | + │ |
| 29 | + ▼ |
| 30 | + ┌──────────────────────┐ |
| 31 | + │ ESP32 (DevKit / C3) │ |
| 32 | + │ USB connector │ |
| 33 | + └─┬──────────────────┬─┘ |
| 34 | + │ 5V rail │ 3.3V rail (from on-board LDO) |
| 35 | + ▼ ▼ |
| 36 | + ┌────────────┐ ┌────────────┐ ┌──────────────────┐ |
| 37 | + │ WS2812B ×2 │ │ RYLR998 │ │ SH1106 OLED │ |
| 38 | + │ (status + │ │ LoRa │ │ 128×64, I²C 0x3C │ |
| 39 | + │ water lvl)│ │ UART @ │ │ 3.3V or 5V │ |
| 40 | + │ VDD = 5V │ │ 115200 │ │ tolerant module │ |
| 41 | + │ DIN = GPIO │ │ VCC = 3.3V │ │ SDA, SCL pins │ |
| 42 | + └────────────┘ └────────────┘ └──────────────────┘ |
| 43 | +``` |
| 44 | + |
| 45 | +### Pin map — DevKit ESP32 RX |
| 46 | + |
| 47 | +Source: `firmware/receiver/main/config.h` |
| 48 | + |
| 49 | +| GPIO | Function | Connects to | Notes | |
| 50 | +|---|---|---|---| |
| 51 | +| 13 | WS2812B data | DIN of first LED in 2-LED chain | 3.3V data may need 74AHCT125 buffer if flicker observed | |
| 52 | +| 16 | UART2 RX | RYLR998 TXD | DevKit has UART2 free; C3 doesn't | |
| 53 | +| 17 | UART2 TX | RYLR998 RXD | | |
| 54 | +| 21 | I²C SDA | SH1106 SDA | Standard I²C bus | |
| 55 | +| 22 | I²C SCL | SH1106 SCL | | |
| 56 | +| 5V | Power | WS2812B VDD, OLED VCC (5V variant) | From USB | |
| 57 | +| 3.3V | Power | RYLR998 VCC, OLED VCC (3.3V variant) | From on-board LDO | |
| 58 | +| GND | Ground | All modules | Single common ground | |
| 59 | + |
| 60 | +### Pin map — ESP32-C3 SuperMini RX |
| 61 | + |
| 62 | +Source: `firmware/receiver-c3/main/config.h` |
| 63 | + |
| 64 | +| GPIO | Function | Connects to | Notes | |
| 65 | +|---|---|---|---| |
| 66 | +| 2 | WS2812B data | DIN of first LED in chain | | |
| 67 | +| 9 | I²C SDA | SH1106 SDA | C3 GPIO matrix routes I²C anywhere | |
| 68 | +| 10 | I²C SCL | SH1106 SCL | | |
| 69 | +| 20 | UART1 RX | RYLR998 TXD | C3 only has UART0 (USB) and UART1 | |
| 70 | +| 21 | UART1 TX | RYLR998 RXD | | |
| 71 | +| 5V | Power | WS2812B VDD, OLED VCC (5V variant) | From USB | |
| 72 | +| 3.3V | Power | RYLR998 VCC, OLED VCC (3.3V variant) | From on-board LDO | |
| 73 | +| GND | Ground | All modules | Single common ground | |
| 74 | + |
| 75 | +--- |
| 76 | + |
| 77 | +## Transmitter (TX) wiring — solar-powered, two power-monitoring variants |
| 78 | + |
| 79 | +The TX is `firmware/transmitter/` (ESP32-C3 SuperMini). The board ships in two variants depending on the level of power-monitoring telemetry desired: |
| 80 | + |
| 81 | +- **Variant A — Voltage divider only** (basic): battery percentage from ADC reading. Cheapest BOM. Used in the entry-level product SKU. |
| 82 | +- **Variant B — INA219 over I²C** (full): bidirectional current, accurate bus voltage, and computed power. Used in the premium SKU and required for any cloud-side energy analytics. |
| 83 | + |
| 84 | +The firmware **auto-detects** which variant is installed by I²C-scanning at INA219's default address (`0x40`) at boot. Users can also force a mode via the web UI dropdown (Auto / Force INA219 / Force voltage divider / Disabled). The mode is saved to NVS. |
| 85 | + |
| 86 | +### TX power chain |
| 87 | + |
| 88 | +``` |
| 89 | + ┌──────────────────────┐ |
| 90 | + │ Solar panel │ 6V open-circuit, 180mA short-circuit |
| 91 | + │ (6V / 180mA / ~1W) │ (Robu / generic crystalline) |
| 92 | + └──────────┬───────────┘ |
| 93 | + │ Vin |
| 94 | + ▼ |
| 95 | + ┌──────────────────────┐ |
| 96 | + │ CN3791 │ MPPT, tracks Vmpp ~4.5V on this panel |
| 97 | + │ MPPT Solar Charger │ ~85% efficiency under MPPT |
| 98 | + │ (Robu listing) │ R_PROG sets charge current (default ~500mA) |
| 99 | + └──────────┬───────────┘ |
| 100 | + │ BAT± to cell |
| 101 | + ▼ |
| 102 | + ┌──────────────────────┐ |
| 103 | + │ 18650 Li-ion │ 3000 mAh, with PCB protection circuit |
| 104 | + │ (3.0–4.2V) │ (mandatory — CN3791 has no over-discharge protection) |
| 105 | + └──────────┬───────────┘ |
| 106 | + │ |
| 107 | + ├─► [Variant A: voltage divider 100k/100k → GPIO0 (ADC1_CH0)] |
| 108 | + │ |
| 109 | + ├─► [Variant B: INA219 in series with battery+ → I²C to ESP32] |
| 110 | + │ |
| 111 | + ▼ |
| 112 | + ┌──────────────────────┐ |
| 113 | + │ MT3608 Boost │ 3.7V → 5V regulated output |
| 114 | + │ 3.7V → 5V │ ~85% efficiency |
| 115 | + │ EN tied high (or to GPIO10 for low-power deep-sleep gating) |
| 116 | + └──────────┬───────────┘ |
| 117 | + │ +5V rail |
| 118 | + ▼ |
| 119 | + ┌──────────┴────────────────────────────────────────────┐ |
| 120 | + ▼ ▼ ▼ ▼ |
| 121 | + ┌────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ |
| 122 | + │ ESP32-C3 │ │ WS2812B │ │ AJ-SR04M │ │ Common │ |
| 123 | + │ SuperMini │ │ ×2 │ │ Sonar │ │ GND │ |
| 124 | + │ VBUS = 5V │ │ VDD = 5V │ │ VCC = 5V │ │ │ |
| 125 | + └─────┬──────┘ │ DIN = G7 │ │ TRIG = G4│ └──────────┘ |
| 126 | + │ └──────────┘ │ ECHO = G5│ |
| 127 | + │ └──────────┘ |
| 128 | + │ on-board AMS1117-3.3 LDO |
| 129 | + ▼ |
| 130 | + 3.3V rail → RYLR998 LoRa (VCC, RXD = GPIO21, TXD = GPIO20) |
| 131 | +``` |
| 132 | + |
| 133 | +### TX pin map — Variant A (voltage divider only) |
| 134 | + |
| 135 | +Source: `firmware/transmitter/main/config.h` (BAT_ADC_CHANNEL = 0) |
| 136 | + |
| 137 | +| GPIO | Function | Connects to | |
| 138 | +|---|---|---| |
| 139 | +| 0 | ADC1_CH0 — battery voltage | Junction of 100kΩ / 100kΩ divider on Vbat (battery+ → 100k → GPIO0 → 100k → GND) | |
| 140 | +| 4 | Ultrasonic TRIG | AJ-SR04M TRIG | |
| 141 | +| 5 | Ultrasonic ECHO | AJ-SR04M ECHO | |
| 142 | +| 7 | WS2812B data | DIN of first WS2812B (2 LEDs in series) | |
| 143 | +| 8 | On-board LED | Built-in (no external) | |
| 144 | +| 9 | Button | Tactile switch to GND, internal pull-up | |
| 145 | +| 20 | UART1 RX | RYLR998 TXD | |
| 146 | +| 21 | UART1 TX | RYLR998 RXD | |
| 147 | +| 1, 2, 3, 6, 10 | **FREE** | Reserved for future expansion | |
| 148 | + |
| 149 | +### TX pin map — Variant B (INA219 over I²C) |
| 150 | + |
| 151 | +Adds I²C bus on GPIO1/2; voltage divider is **not installed** (INA219's bus-voltage register replaces it). |
| 152 | + |
| 153 | +| GPIO | Function | Connects to | |
| 154 | +|---|---|---| |
| 155 | +| 0 | (unused — divider not installed in this variant) | — | |
| 156 | +| 1 | I²C SDA | INA219 SDA (with 4.7kΩ pull-up to 3.3V) | |
| 157 | +| 2 | I²C SCL | INA219 SCL (with 4.7kΩ pull-up to 3.3V) | |
| 158 | +| 4 | Ultrasonic TRIG | AJ-SR04M TRIG | |
| 159 | +| 5 | Ultrasonic ECHO | AJ-SR04M ECHO | |
| 160 | +| 7 | WS2812B data | WS2812B DIN | |
| 161 | +| 8 | On-board LED | Built-in | |
| 162 | +| 9 | Button | Tactile switch | |
| 163 | +| 20 | UART1 RX | RYLR998 TXD | |
| 164 | +| 21 | UART1 TX | RYLR998 RXD | |
| 165 | +| 3, 6, 10 | **FREE** | Reserved | |
| 166 | + |
| 167 | +### INA219 wiring detail (Variant B) |
| 168 | + |
| 169 | +The INA219 is wired with its shunt resistor **in series with the battery's positive terminal**, so a single shunt sees both charge and discharge currents. INA219's signed shunt-voltage register distinguishes the direction automatically. |
| 170 | + |
| 171 | +``` |
| 172 | + [Battery+] ──[INA219 SHUNT (R_SH)]── [Common+ node] ─┬─ CN3791 BAT pin |
| 173 | + │ │ └─ MT3608 boost input |
| 174 | + V+ V- |
| 175 | + ▲ ▲ |
| 176 | + │ │ |
| 177 | + INA219 V+ pin connects to battery side of shunt |
| 178 | + INA219 V- pin connects to common-node side of shunt |
| 179 | +
|
| 180 | + INA219 SDA → ESP32-C3 GPIO1 |
| 181 | + INA219 SCL → ESP32-C3 GPIO2 |
| 182 | + INA219 VS → ESP32-C3 3.3V output pin (powers the IC; ≠ load voltage) |
| 183 | + INA219 GND → common ground |
| 184 | + INA219 ADDR → 0x40 (default; A0/A1 jumpers left open) |
| 185 | +``` |
| 186 | + |
| 187 | +**Sign convention** (firmware-level interpretation): |
| 188 | +- Positive shunt current → battery is **discharging** (current leaving battery toward boost converter) |
| 189 | +- Negative shunt current → battery is **charging** (current flowing from CN3791 into battery) |
| 190 | + |
| 191 | +Bus voltage register reads the V- side relative to GND, which is the battery voltage (post-shunt; the shunt drop is millivolts and negligible for SoC display). |
| 192 | + |
| 193 | +### Sensor auto-detect & manual override |
| 194 | + |
| 195 | +Firmware behavior at boot: |
| 196 | + |
| 197 | +1. Initialize I²C bus on GPIO1 (SDA) / GPIO2 (SCL) at 100 kHz |
| 198 | +2. Probe address `0x40` with a single read |
| 199 | +3. If ACK → `power_mode = "ina219"`, save to NVS (overwrites manual setting only if `manual_override = "auto"`) |
| 200 | +4. If NACK → `power_mode = "voltage"`, fall back to ADC on GPIO0 with the existing `battery_monitor` divider logic |
| 201 | +5. NVS-stored `power_mode_override` field can force any mode: `"auto"`, `"ina219"`, `"voltage"`, `"disabled"` |
| 202 | + |
| 203 | +Web UI exposes the override under TX Settings → Power Monitoring with a `<select>` dropdown showing the auto-detected value. PWA reads `/api/system → power_mode` and conditionally renders basic vs rich power telemetry. |
| 204 | + |
| 205 | +--- |
| 206 | + |
| 207 | +## Bill of Materials (BOM additions) |
| 208 | + |
| 209 | +For the canonical full BOM see `BOM.csv` in this directory. New entries added 2026-04-27 to support the solar-TX power chain: |
| 210 | + |
| 211 | +| Component | Variant | Approx ₹ (Robu / generic) | Notes | |
| 212 | +|---|---|---|---| |
| 213 | +| Solar panel 6V / 180mA crystalline | A & B | ~150 | Polycrystalline, ~250×80 mm | |
| 214 | +| CN3791 MPPT solar charger module | A & B | ~120 | Buck-MPPT with R_PROG charge-current setting | |
| 215 | +| 18650 Li-ion 3000 mAh + protected holder | A & B | ~200 | Protection circuit mandatory (CN3791 lacks over-discharge cutoff) | |
| 216 | +| MT3608 DC-DC boost module 3.7V→5V | A & B | ~50 | EN pin recommended to GPIO10 for deep-sleep gating | |
| 217 | +| 100 kΩ resistor ×2 (voltage divider) | A only | ~5 | 1% tolerance recommended for accurate SoC | |
| 218 | +| INA219 module (Adafruit-style, 0.1Ω shunt) | B only | ~120 | Default I²C address 0x40 | |
| 219 | +| 4.7 kΩ resistor ×2 (I²C pull-ups) | B only | ~5 | Most INA219 modules already include these onboard | |
| 220 | + |
| 221 | +**Variant A total adder:** ~₹525 |
| 222 | +**Variant B total adder:** ~₹640 |
| 223 | + |
| 224 | +--- |
| 225 | + |
| 226 | +## Future expansion (reserved free GPIOs on TX) |
| 227 | + |
| 228 | +| GPIO | Reserved for | Why | |
| 229 | +|---|---|---| |
| 230 | +| GPIO3 | CN3791 STAT pin (charging-LED output) | Direct read of charger state without inferring from current sign | |
| 231 | +| GPIO6 | Future PIR / motion sensor | Wake-on-motion to extend battery on long deep-sleep | |
| 232 | +| GPIO10 | MT3608 boost EN pin | Disable boost in deep-sleep to recover ~120 mAh/day quiescent loss | |
| 233 | + |
| 234 | +--- |
| 235 | + |
| 236 | +## Firmware compatibility |
| 237 | + |
| 238 | +The shared `battery_monitor` component (in both `public/firmware/transmitter/components/battery_monitor/` and `cloud/firmware/Transmitter-IDF/components/battery_monitor/`) currently provides voltage-divider support. INA219 + auto-detect support is added in **both trees** as a hardware-platform feature (not a cloud-roadmap feature) — see `project_firmware_strategy.md` rule #2 in project memory. |
| 239 | + |
| 240 | +After the firmware update lands, the API surface becomes: |
| 241 | + |
| 242 | +```c |
| 243 | +// power_monitor.h (was battery_monitor.h) |
| 244 | + |
| 245 | +typedef enum { |
| 246 | + POWER_MODE_NONE, |
| 247 | + POWER_MODE_VOLTAGE, |
| 248 | + POWER_MODE_INA219, |
| 249 | +} power_mode_t; |
| 250 | + |
| 251 | +typedef struct { |
| 252 | + power_mode_t mode; |
| 253 | + int pct; // 0–100 estimated SoC |
| 254 | + uint32_t vbat_mv; // battery voltage |
| 255 | + int32_t current_ma; // signed; +ve = discharging, -ve = charging (INA219 only; 0 in voltage mode) |
| 256 | + int32_t power_mw; // V × I (INA219 only; 0 in voltage mode) |
| 257 | + bool charging; // explicit flag (from current sign or voltage trend) |
| 258 | +} power_reading_t; |
| 259 | + |
| 260 | +esp_err_t power_init(void); // auto-detect + NVS-stored override |
| 261 | +esp_err_t power_read(power_reading_t *out); |
| 262 | +esp_err_t power_set_override(const char *mode); // "auto" | "ina219" | "voltage" | "disabled" |
| 263 | +``` |
| 264 | +
|
| 265 | +--- |
| 266 | +
|
| 267 | +## License |
| 268 | +
|
| 269 | +Hardware design is open: BOM under CC BY-SA 4.0, schematics & wiring docs (this file) under MIT. Firmware in `public/firmware/` is MIT; firmware in `cloud/firmware/` is AGPL-3.0 (proprietary cloud variant). |
0 commit comments