Motivation
I wanted to know the actual temperature and humidity on my balcony. Weather apps use data from the nearest official station, which can be a few kilometers away and at a different altitude. So I built my own sensor and put it outside.
The Problem Statement
Requirements:
- Real-time temperature & humidity monitoring (outdoor balcony)
- Remote access via multiple platforms (web, mobile, desktop)
- No cloud vendor lock-in (local network only)
- 24/7 continuous operation
- Minimal maintenance
- Production-grade reliability
Constraints:
- Available components (ESP8266, DHT-22, BMP180)
- Keep it simple (KISS principle)
- No over-engineering
Hardware Design
Component Selection
| Component | Reason |
|-----------|--------|
| **ESP8266 NodeMCU** | Low-cost Wi-Fi + processing in one module |
| **DHT-22** | Stable, ±0.5°C accuracy, widely available |
| **BMP180** | I2C pressure sensor, reliable, simple |
| **5V DC Power** | USB-powered, simple external adapter, zero complexity |
| **Protective Enclosure** | Weatherproof case for exposed balcony deployment |
Schematic
┌─────────────────────────┐
│ 5V DC Power Supply │
│ (USB or external) │
└────────────┬────────────┘
│
├─→ [ESP8266 NodeMCU]
│ ├─ GPIO14 (D5) ──→ DHT22 data
│ ├─ GPIO4 (D2) ──→ BMP180 SDA (I2C)
│ ├─ GPIO5 (D1) ──→ BMP180 SCL (I2C)
│ └─ A0 ─────────→ Battery voltage (optional)
│
└─→ [Protective Weatherproof Case]
Why Mains Power?
Initially considered battery-powered. Decision: **mains-powered is better for this use case:**
- 24/7 continuous operation (no downtime waiting for charge)
- Zero maintenance (no battery replacement, no degradation curve)
- Simpler architecture (optimize for heat, not power)
- Cost-effective (5V adapter costs $5)
Firmware Development
Architecture Decision: Light Sleep Mode
Initial approach: Run at full power constantly.
**Problem:** Unnecessary heat generation, CPU always active.
**Solution:** Implement WiFi Light Sleep mode:
void setup() {
WiFi.setSleepMode(WIFI_LIGHT_SLEEP); // Called ONCE at startup
// ... rest of setup
}
void loop() {
if (currentMillis - previousMillis >= 10000) { // Every 10 seconds
readSensors();
delay(9000); // Light sleep for 9 seconds
}
delay(50); // Small delay to prevent CPU thrashing
}
Results:
- Active time: ~1 second per 10-second cycle
- Light sleep: 0.9 mA (WiFi stays connected)
- Average consumption: ~9 mA
- CPU temperature: Significantly reduced
**Key insight:** Light Sleep ≠ Deep Sleep. WiFi connection persists, crucial for real-time monitoring.
Multi-Environment Build Configuration
Used PlatformIO for clean separation:
[env:production] # Real sensors, optimized for reliability [env:debug] build_flags = -DDEBUG_MODE # Mock data for testing without hardware
Why this matters:
- Test Android/Windows apps without physical ESP
- Develop firmware features in isolation
- Simulate edge cases (low battery, sensor failures)
- CI/CD can run both configurations
---
Code Highlights
Graceful Sensor Handling
#ifdef DEBUG_MODE
dhtAvailable = true;
bmpAvailable = true;
#else
// Real sensor initialization with error handling
dht.begin();
float testTemp = dht.readTemperature();
dhtAvailable = !isnan(testTemp);
// Similar for BMP180...
#endif
Why: If sensors fail, the...
Read more »
Osman Mazinov
Wassim
ACROBOTIC Industries
JP Gleyzes