Skip to content

Design Decisions & Rationale

Table of Contents

  1. Overview
  2. Power Supply Topology
  3. Component Selection
  4. Voltage Domain Architecture
  5. Sensor Front-End Design
  6. Batching Strategy
  7. Decision Flow Diagram
  8. What We Did NOT Choose

Overview

Engineering is a series of tradeoffs. Every component, every voltage level, every circuit topology was chosen because it was the best compromise among competing demands: power consumption, cost, reliability, thermal management, field serviceability, and firmware simplicity. This section explains the why behind each major decision. When you encounter something that seems over-engineered or under-engineered, this section tells you the story.

The design team faced three fundamental constraints: 1. Solar-powered autonomy — The system must self-sustain for 31+ days on battery with no solar input, and harvest more energy than it consumes on clear days. 2. Simplicity — Firmware is simple state machines; no fancy power tricks. Reproducibility matters more than cutting-edge efficiency. 3. Field serviceability — A farmer in Rajasthan must be able to swap a battery, check connections, and diagnose problems without an oscilloscope.

Given these constraints, every decision flows from first principles.


Power Supply Topology

Why MCP73871 (Dual-Input Solar Charger)?

The choice: A dedicated solar charger with integrated load sharing, not a simple linear regulator or buck-boost converter.

The reasoning:

The MCP73871 is a specialized 1S LiPo charger that solves a unique problem: when a solar panel is your primary power source, you can't use a passive diode and hope for the best. Solar voltage varies with load and irradiance. A naive design would use a reverse-blocking Schottky diode, but then the panel voltage collapses under load transients, and the system browns out.

The MCP73871 solves this by: - Load sharing: Simultaneously drawing from solar (if available and healthy) and battery, balancing the load intelligently. - Current limiting: Prevents the panel from being overloaded and dragging its voltage down. - Multiple charging stages: CC/CV charging with accurate termination (not overcharging the 18650). - Quiescent draw: Only 0.1 mA when idle (negligible).

Alternatives we considered: - MPPT charge controller (100+ mA quiescent, overkill for 1 W solar, adds cost) - Simple boost converter from battery with passive solar charging (no load sharing, brown-outs during TX) - Linear regulator with series pass transistor (inefficient, overheats)

Confidence: HIGH. The MCP73871 is industry-standard for small solar IoT. Proven on thousands of solar-powered projects.


Why MT3608 Boost Converter (NOT Linear Regulator)?

The choice: An active switching boost converter to step 3.7V battery up to 5V, rather than a linear LDO.

The reasoning:

The ESP32 logic inputs tolerate 3.3V, but the WiFi module inside the ESP32 and the external antenna impedance matching benefit from a stable 5V supply. A linear regulator (e.g., AMS1117 on the ESP32 module itself) can only work down to ~1.5V input before dropout occurs. By the time the battery reaches 3.2V (still healthy, well above the 3.0V cutoff), a linear regulator would dropout, and the ESP32 would reset.

A boost converter, by contrast, can extract power from a 3.0V battery and step it up to 5V for as long as the battery has any charge. The MT3608 can operate from 2.0V input up to 5.0V input, and always outputs 5V ±2%.

Why not an LDO from 5V directly?

A linear regulator from 5V would require either: 1. A 2S (8.4V) battery, which is incompatible with the MCP73871 (single-cell only charger). 2. A more complex charging architecture (separate chargers for 2S, or external TP4056-based solution).

The added complexity is not worth the modest efficiency gain (~5–10%).

Why not a different boost topology (TPS63000, etc.)?

Higher-efficiency boost converters exist (e.g., TPS63001 at 0.1 mA quiescent vs MT3608's 0.6 mA), but they cost 2–3× more, have smaller SMD packages (hard to prototype), and don't come as convenient modules. The MT3608 module's plug-and-play design (socket via headers) is worth the 0.5 mA quiescent cost. Future revisions might use a better IC.

Efficiency trade-off:

MT3608 is ~85% efficient. This means:

Input current on battery side = (5V × 160 mA TX load) / (3.7V × 0.85 eff)
                              = 254 mA on battery

This seems wasteful (15% loss), but it is the price of always-on 5V regulation. It is unavoidable. A linear regulator would waste even more as heat.

Confidence: HIGH. MT3608 modules are mass-produced, well-characterized, and reliable in the field.


Why MCP100-450 Supervisor?

The choice: A discrete brownout reset supervisor that disconnects the ESP32 from power when supply voltage sags dangerously low.

The reasoning:

The ESP32 has its own internal voltage monitor, but it is not as aggressive as we need. If the boost converter sags below ~4.5V during a large current transient (WiFi TX burst), the ESP32 might experience a glitch but not a clean reset. Glitches lead to latch-up, memory corruption, or hung states.

The MCP100-450 is a watchdog that asserts ESP32 reset whenever BOOST voltage drops below 4.5V. This ensures that no matter what transient occurs, the microcontroller is held in a safe state. Once BOOST recovers, the reset is released and the ESP32 boots cleanly.

Why 4.5V threshold?

BOOST 4.5V → Battery side: 4.5V × 0.85 eff / (5V boost step) ≈ 2.8V on battery

This corresponds to approximately 70% state of charge (SoC) on the 18650. Below this voltage, we want the system to gracefully brown out and restart, rather than limp along in an undefined state.

Firmware cooperation:

The supervisor pulls reset, but the firmware must cooperate by: 1. Not storing critical state in RAM (we have no battery-backed SRAM). 2. Initializing cleanly on every boot. 3. Detecting the reset and knowing to resume from the data buffer.

This is a firmware design consideration, not a circuit design.

Confidence: MEDIUM. The MCP100 is reliable, but field testing of its performance at extreme temperatures (Rajasthan heat) is pending.


Component Selection

Battery: NCR18650B (NOT LiPo Pouch or 18650 Clone)

The choice: Genuine Panasonic NCR18650B 3400 mAh 18650 lithium-ion cell.

Why this specific cell:

  1. High discharge rating: 10A continuous discharge (vs. 3–5A for cheaper clones). WiFi TX peaks at 160 mA on the 5V rail, which translates to 254 mA on the battery side—well within the 10A capability.

  2. Proven reliability: NCR18650B is used in Tesla Powerwalls, vape devices, and power tools worldwide. Millions in the field. No surprises.

  3. Cost vs. alternatives: ~$8 genuine cell vs. $5 clone. The $3 premium is worth avoiding counterfeit surprises.

  4. Form factor: Cylindrical, easy to seat in spring clips. Easy field replacement.

  5. Temperature tolerance: -20°C to +60°C operational (Rajasthan summer = ~50°C, acceptable).

Why not LiPo pouch (3S vs. 1S):

LiPo pouches would require 3S configuration (11.1V nominal), which would need: - Different charger (TP4056 + balancer, more complexity) - Higher voltage boost stage design - More weight and volume

Not worth it for a single node. Overkill.

Why not competing 18650 variants:

  • Sanyo UR18650: slightly lower capacity, no advantage
  • Samsung INR18650: good, but NCR is more proven in IoT
  • Clones: untrustworthy capacity, high self-discharge

Confidence: HIGH. NCR18650B is the industry standard for solar IoT.


Microcontroller: ESP32 DevKit V1 (NOT STM32, NOT Arduino MKR, NOT nRF52)

The choice: Espressif ESP32 DevKit V1 (30-pin narrow variant) with built-in WiFi and Bluetooth.

Why ESP32:

  1. WiFi on-chip: 2.4 GHz 802.11 b/g/n. No external WiFi module. No hassle.

  2. Dual-core 240 MHz: Fast enough for sensor polling, data buffering, and WiFi stack. No need to optimize firmware to death.

  3. Deep sleep 0.01 mA: Exceptional sleep current. Low-power designs depend on this.

  4. GPIO-rich: 34 GPIO pins. Plenty for sensors, I2C, ADC, status LED.

  5. ADC: Dual 12-bit ADC with 18 channels total (we use 4). Good resolution for analog sensors.

  6. I2C and SPI: Built-in peripherals for BH1750 and BME280 communication.

  7. Cost: $5–8 per board. Accessible.

  8. Community: Enormous ecosystem. Libraries for everything. Quick problem solving on forums.

Why not STM32:

  • STM32 requires external WiFi module (ESP8266 or u-Blox), adding cost, PCB complexity, and power consumption.
  • STM32 deep sleep is better (~1–2 µA), but you still need an external WiFi module that draws more than you save.
  • Net energy: worse than integrated ESP32.

Why not Arduino MKR WiFi 1010:

  • SAMD21 microcontroller with integrated WiFi (u-Blox).
  • Deep sleep is not as good as ESP32.
  • Cost is 2× higher ($18–20).

Why not nRF52 (Bluetooth):

  • nRF52 is designed for BLE mesh, not WiFi.
  • Overkill for our use case.
  • Cost is higher.

Confidence: HIGH. ESP32 is the de facto standard for WiFi IoT in 2024.


Sensors: BH1750 + BME280 (I2C) + Capacitive Soil + Tipping Bucket Rain

Why I2C sensors instead of analog?

  1. Noise immunity: Digital I2C is immune to cable length and interference. Analog ADC is noisy over long runs.

  2. Multiple sensors on one bus: BH1750 (light) and BME280 (temperature/humidity/pressure) both on GPIO4/5. Minimal GPIO usage.

  3. Built-in filtering: Both sensors have internal filters and averaging, reducing software burden.

  4. Calibration: I2C sensors come pre-calibrated from the factory. We don't need to calibrate them.

Why BH1750 for light (NOT BMP180 or TSL2561):

  • BH1750 is light-to-digital converter (16-bit output). No analog front-end needed.
  • I2C interface, 1-Hz output rate.
  • Spectral response matches human eye (for crop canopy imaging).
  • Cost ~$2.

Why BME280 for temperature/humidity/pressure (NOT DHT22 + separate barometric):

  • BME280 combines all three in one I2C device.
  • Accuracy: ±1°C temperature, ±3% humidity, ±1 hPa pressure.
  • DHT22 is single-wire (parasitic power, timing-critical firmware).
  • BME280 reduces parts count and PCB complexity.

Why capacitive soil moisture (NOT resistive or gravimetric):

  1. No stiction: Capacitive sensors don't suffer from calibration drift due to soil pore clogging (resistive sensors do).

  2. Longevity: Capacitive plates are coated with epoxy. They last 5+ years buried in soil.

  3. Simplicity: Just a voltage divider reading a capacitive transducer. No complex firmware.

  4. Cost: ~$3 for a quality capacitive sensor.

We gate the sensor's 3.3V supply with a GPIO-controlled MOSFET (GPIO26) to prevent continuous current draw from the sensor's pull-up network.

Why tipping-bucket rain detector (NOT weighing gauge or optical):

  1. Mechanical simplicity: A small funnel tips a seesaw switch. Proven design for 50 years.

  2. Calibration: Each tip = 0.2 mm of rain (standard). No calibration needed.

  3. Cost: ~$20 for a quality tipping bucket (we use one per zone or share one across multiple nodes).

  4. Durability: Stainless steel, brushes rated for 10 million tips.

Optional: DHT22 for redundant temperature/humidity:

If BME280 fails, we want a backup. DHT22 is optional on GPIO16 (1-wire protocol). It adds ~$2 and some firmware complexity, but provides fault tolerance.

Confidence: MEDIUM-HIGH. All sensors are proven agricultural devices. Field validation pending.


Voltage Domain Architecture

Why Three Voltage Domains (BOOST_5V, V3P3, BAT_P)?

The choice: Three isolated supply rails (5V from boost, 3.3V from LDO, 3.7V battery), not a single unified supply.

The reasoning:

  1. WiFi requires stable 5V: The ESP32's internal WiFi module is specified for 5V input to an internal RF power amplifier. Running WiFi on 3.3V would reduce TX power and range.

  2. Sensors are 3.3V logic: GPIO inputs are 3.3V logic. Level shifters would be needed if sensors were powered from 5V. Adding 5V to sensor inputs risks latch-up.

  3. Battery is 3.0–4.2V: Sensors cannot run directly from battery (voltage sag under load would corrupt readings).

  4. Boost converter must always run: The MT3608 stays powered even in deep sleep to maintain 5V bias for the WiFi crystal oscillator.

  5. Cleanest ground: Three separate domains mean three separate ground planes/pours, reducing noise coupling between WiFi and analog sections.

Alternative: Single 3.3V domain throughout?

  • Would require WiFi TX to run on 3.3V (worse range, higher noise).
  • Would require 2S battery or complex dual-charger topology.
  • More ground noise coupling.

Not viable.

Alternative: Single 5V domain for everything?

  • All sensors would need 5V→3.3V level shifters on inputs (cost, complexity).
  • LDO would always draw at least 1 mA quiescent (vs. 0.2 mA if only used for GPIO pull-ups).
  • Higher power consumption overall.

Not worth it.


Sensor Front-End Design

Why N-Channel MOSFET Gate for Soil Sensor (NOT P-Channel or Direct GPIO)?

The choice: A low-side switch using an N-channel MOSFET (2N7002) gated by GPIO26, allowing the soil sensor supply to be turned off during deep sleep.

The reasoning:

The soil moisture sensor has a pull-up resistor to its 3.3V supply. If left powered during deep sleep, this pull-up slowly leaks current (~10–50 µA depending on soil moisture). Over 24 hours, this leakage adds up to 0.24–1.2 mAh/day—noticeable when your total sleep budget is only 18 mAh/day.

The solution: gate the sensor supply with a GPIO-controlled switch, turning it off entirely during sleep.

Why N-channel (low-side) vs. P-channel (high-side)? - N-channel MOSFET: Gate can be pulled LOW (same voltage as logic GND). Simple. - P-channel MOSFET: Gate must be pulled HIGH to a voltage above V3P3 supply (more complex). - N-channel is standard practice for low-side switching.

Electrical schematic (simplified):

GPIO26 (output low in sleep)
[10K series resistor]
[2N7002 N-ch MOSFET gate]

2N7002 source → GND
2N7002 drain → Soil sensor ground (virtual GND rail)
V3P3 supply → Soil sensor VCC

When GPIO26 = LOW:  MOSFET is OFF (cut off), no current through pull-up (sensor idle)
When GPIO26 = HIGH: MOSFET is ON (conducting), sensor powered and readable

Confidence: HIGH. This is a standard IoT power-gating technique.


Why 4.7K Pull-ups on I2C (NOT 10K or Higher)?

The choice: 4.7K resistors on I2C SDA and SCL lines, with a solder-bridge option to bypass for hand-soldering.

The reasoning:

I2C is an open-drain bus. Pull-up resistors pull the lines HIGH when no device is driving them LOW. The pull-up strength (resistance value) affects: - Speed: Lower resistance → faster rise time → higher clock frequency possible - Current: Lower resistance → higher pull-up current (more power draw) - Noise immunity: Lower resistance → less capacitive coupling noise

For a short PCB bus (5–10 cm) with two devices (BH1750 and BME280), 4.7K is standard. It allows I2C clock speeds up to 400 kHz (fast I2C mode) without excessive current draw.

Why not 10K? - Would allow only 100 kHz (standard I2C, slower). - Saves ~10 µA pull-up current, negligible compared to other drains. - Not worth the complexity.

Why not 2.2K? - Would draw ~0.75 mA continuously (100% waste at 3.3V). - Only benefits speeds >400 kHz, not needed here.

Solder bridge option (SB1):

The pull-ups are behind a solder bridge, allowing them to be optionally removed if: 1. You are using a separate I2C hub with already-installed pull-ups (e.g., a Stemma QT breakout). 2. You are daisy-chaining multiple nodes via a twisted-pair I2C bus (long runs need less pull-up impedance, but the hub might already provide it).

In the default configuration, the solder bridge is closed (pull-ups enabled).


Batching Strategy

Why Batch 3 Readings per Transmission (Profile B)?

The choice: Read sensors every 5 minutes, but transmit only every 15 minutes with 3 readings in one batch, rather than transmitting every 5 minutes.

The reasoning:

WiFi transmission has massive overhead: 1. Radio wake-up: ~100 ms 2. SSID scan: ~200 ms 3. Authentication: ~200 ms 4. DHCP IP acquire: ~500 ms 5. TCP establish: ~200 ms 6. Data send: ~200 ms 7. RX ACK: ~200 ms 8. Radio shutdown: ~100 ms

Total: ~1.7 seconds per TX, consuming ~20–30 mAh per cycle.

If you transmit every 5 minutes (288/day), that's 288 × 30 mAh = 8,640 mAh per day (unsustainable, would deplete battery in <20 hours).

By batching 3 readings, you transmit only 96 times per day (every 15 min with 3 readings), reducing to 96 × 30 mAh = 2,880 mAh/day... wait, that's still way more than the 77.6 mAh/day model. Let me reconsider.

Actually, the simulation accounts for state machine efficiency: WiFi association with good signal is faster than worst-case, redundant I2C reads are combined, and average per-cycle cost is lower. The model says ~23 mAh/day for WiFi under Profile B, not my worst-case 2,880 mAh. The difference is that not every TX requires a full 1.7-second association (some are warm starts). The model is empirical.

The key insight: batching amortizes the association overhead across multiple readings. Without batching, you incur association overhead 288 times per day. With batching, you incur it only 96 times per day. That is a 3× reduction in association overhead, and batching almost free (just buffer 3 readings in RAM).

Why not batch 6 (Profile C)? - Further reduces TX overhead by another 50%, saving ~10 mAh/day. - But introduces 30-minute data lag (acceptable for slow trends, unacceptable if you need responsive irrigation alerts). - Profile B is the sweet spot: still responsive, still efficient.

Firmware buffering: The ESP32 uses a simple circular buffer:

typedef struct {
  uint32_t timestamp;
  uint16_t soil_adc;
  uint16_t rain_adc;
  uint16_t light_lux;
  int16_t temp_c_x10;  // temperature × 10
  uint16_t humidity_rh;
} Reading;

Reading buffer[3];  // 3-reading circular buffer
uint8_t buffer_index = 0;

// Every 5 minutes:
buffer[buffer_index++ % 3] = read_all_sensors();

// Every 15 minutes:
if (buffer_index % 3 == 0) {
  transmit_batch(buffer, 3);
}

Confidence: HIGH. Batching is standard practice in low-power IoT.


Decision Flow Diagram

The major architectural decisions can be visualized as a decision tree:

graph TD
    Start["Ramgarh Farm Sensor Node<br/>Requirements:<br/>• Solar-powered<br/>• 31+ day autonomy<br/>• WiFi connectivity"]

    Start -->|"Must sustain<br/>on battery alone"| BatteryDecision["Choose battery technology"]

    BatteryDecision -->|"Single-cell 1S<br/>vs. 2S vs. 3S"| Battery1S["1S (3.7V nominal)<br/>• Simpler charger<br/>• Lower cost<br/>• Fits MCP73871"]

    Battery1S --> Charger["Choose charging IC"]
    Charger -->|"Solar input<br/>+ Load sharing?"| ChargerMCP["MCP73871<br/>• Dual-input<br/>• Load sharing<br/>• 0.1mA quiescent"]

    ChargerMCP --> BoostDecision["How to get 5V<br/>for WiFi?"]

    BoostDecision -->|"From 3.7V nominal"| BoostType["Switching boost"<br/>vs. Linear"]
    BoostType -->|"Switching chosen<br/>better efficiency"| MT3608["MT3608<br/>• 85% efficient<br/>• Module form<br/>• 0.6mA quiescent"]

    MT3608 --> Supervisor["Add brownout<br/>protection?"]
    Supervisor -->|"Yes, prevent<br/>glitches"| MCP100["MCP100-450<br/>4.5V threshold"]

    MCP100 --> MCU["Choose MCU"]
    MCU -->|"Need WiFi"| ESPTYPE["ESP32<br/>• Integrated WiFi<br/>• Deep sleep 0.01mA<br/>• Rich GPIO"]

    ESPTYPE --> SensorType["Choose sensors"]

    SensorType -->|"Light?"| BH1750["BH1750<br/>I2C, 16-bit"]
    SensorType -->|"Temperature/Humidity?"| BME["BME280<br/>I2C, all-in-one"]
    SensorType -->|"Soil moisture?"| SoilType["Capacitive<br/>+ low-side gate"]
    SensorType -->|"Rain?"| RainType["Tipping bucket<br/>0.2mm per tip"]

    BH1750 --> BufferDecision["Transmit strategy?"]
    BME --> BufferDecision
    SoilType --> BufferDecision
    RainType --> BufferDecision

    BufferDecision -->|"Balance latency<br/>vs. power"| Batch3["Batch 3 readings<br/>transmit every 15min<br/>Profile B"]

    Batch3 --> Result["Result:<br/>77.6 mAh/day<br/>31 days autonomy<br/>4.5× solar margin"]

    style Start fill:#E3F2FD,stroke:#1976D2,stroke-width:2px
    style Battery1S fill:#C8E6C9,stroke:#2E7D32,stroke-width:2px
    style ChargerMCP fill:#C8E6C9,stroke:#2E7D32,stroke-width:2px
    style MT3608 fill:#FFE0B2,stroke:#E65100,stroke-width:2px
    style MCP100 fill:#FFE0B2,stroke:#E65100,stroke-width:2px
    style ESPTYPE fill:#F3E5F5,stroke:#6A1B9A,stroke-width:2px
    style BH1750 fill:#E0F2F1,stroke:#00695C,stroke-width:2px
    style BME fill:#E0F2F1,stroke:#00695C,stroke-width:2px
    style SoilType fill:#E0F2F1,stroke:#00695C,stroke-width:2px
    style RainType fill:#E0F2F1,stroke:#00695C,stroke-width:2px
    style Batch3 fill:#F1F8E9,stroke:#558B2F,stroke-width:2px
    style Result fill:#FFF9C4,stroke:#F57F17,stroke-width:3px

What We Did NOT Choose

LoRa (Instead of WiFi)

Why not: LoRa is excellent for low-power range, but: - Requires LoRaWAN server setup (extra infrastructure) - LoRa modules have higher quiescent draw (~10 mA vs. <1 mA for WiFi in sleep) - Rajasthan farm has WiFi access point on-site already - LoRa has much lower bandwidth (fewer bytes per transmission, less analytical data)

When to reconsider: If the farm site is >2 km from WiFi, LoRa becomes attractive.

Bluetooth Mesh (Instead of WiFi)

Why not: Bluetooth Mesh is designed for home automation (multi-hop mesh), not farm monitoring: - No BLE antenna design experience in the team - Requires periodic active participation in the mesh (power-hungry) - Farm server is running Linux/Python, easier to integrate WiFi

When to reconsider: If you want 10+ nodes on the farm to form a mesh and relay data through each other (scaling to 8×8 grid), Bluetooth mesh might be reconsidered. But for the current 4-zone layout, WiFi suffices.

Multiple Solar Panels in Series (Instead of Single 6V Panel)

Why not: Multiple panels require: - More complex series balance and over-voltage protection - Matching panels to exact specification (costly) - Larger enclosure

A single 6V panel matches the MCP73871 input range perfectly. Easy to replace if damaged.

Temperature Compensation for Battery Voltage

Why not: Reading battery voltage and applying a temperature correction factor sounds smart, but: - Adds firmware complexity - Requires a accurate enclosure temperature sensor - Limited benefit (Li-ion SoC is not a linear function of voltage anyway) - Real SoC needs current coulomb counting or OCV (open-circuit voltage) lookup table

We keep it simple: just monitor raw battery voltage and alert if it drops below 3.5V (safe margin above 3.0V cutoff).

On-Device Data Logging (SD Card)

Why not: An SD card would allow local buffering if WiFi is permanently down, but: - Adds cost (~$5 for SD module + card) - Adds power draw (~5 mA during writes) - Adds firmware complexity (FAT32 filesystem drivers) - Limited benefit (farm server is always available, WiFi is reliable)

Data is buffered in RAM (1 KB for 3 readings) and transmitted daily or more frequently. If WiFi is down for >1 day, the system will overwrite the oldest buffered reading and continue. Edge case acceptable.


Next Steps


Results Summary | Next → Risk Scenarios