Embedded Systems
StillCold
BLE-Based Embedded Temperature Monitoring
Screenshots
Overview
StillCold is a battery-friendly embedded temperature monitoring system designed to broadcast real-time sensor readings over Bluetooth Low Energy (BLE). A nearby device — phone, tablet, or laptop — can connect and read current temperature and humidity without any Wi-Fi dependency, cloud account, or internet connection required.
The system uses an HTU21D sensor for temperature and humidity acquisition over I²C and an ESP32-C6 for BLE broadcasting, with a modular architecture that cleanly separates sensing logic from wireless communication.
Problem
Most consumer temperature monitoring solutions require a Wi-Fi connection, a cloud account, and a proprietary mobile app. This creates several failure modes: cloud service outages, privacy concerns, and dependence on ongoing vendor support. For applications where simple, reliable local access to temperature data is what matters, this complexity is unnecessary overhead.
The design goal for StillCold was a monitoring system that works deterministically, requires no network infrastructure, and exposes data in a standard, open format (BLE GATT) that any compatible device can read.
Approach
The architecture separates two concerns that are often conflated in embedded sensor projects: data acquisition and data exposure. The sensing module handles HTU21D initialization, periodic I²C reads, and error detection. The BLE module handles service advertisement, characteristic updates, and connection management. Neither module has direct knowledge of the other's implementation — they communicate through a shared data structure.
BLE GATT was chosen as the communication protocol because it is a published, open standard supported natively on iOS, Android, macOS, and Windows. This means StillCold data can be read by any BLE scanning app without a custom client application. For validation during development, I used serial diagnostics and nRF Connect (a BLE scanning tool) to verify characteristic values before building any client-side tooling.
The sensor acquisition loop runs on a fixed interval with watchdog-compatible timing to ensure the system recovers from I²C bus hangs without requiring a manual reset — a real-world reliability requirement that standard tutorial implementations ignore.
Tech Stack
| Technology | Rationale |
|---|---|
| ESP32-C6 | RISC-V based microcontroller with native BLE 5.0 support, low power modes, and strong Arduino IDE compatibility. |
| HTU21D sensor | I²C digital temperature/humidity sensor with good accuracy, well-documented register map, and reliable Arduino library support. |
| BLE GATT | Open standard for exposing sensor data; readable by any BLE-capable device without a custom client app. |
| Embedded C/C++ | Arduino framework for hardware abstraction; C/C++ for performance-sensitive timing and bus management code. |
Key Decisions & Tradeoffs
BLE vs. Wi-Fi. Wi-Fi would have enabled remote access and cloud integration with minimal additional code. I chose BLE specifically because the design goal was local, infrastructure-free access. A Wi-Fi implementation would have introduced network dependency, power consumption overhead, and the need for network credentials management — all of which conflict with the core requirement of deterministic, standalone operation.
Module separation vs. simpler monolithic design. Separating sensing and BLE modules adds a small amount of structural overhead compared to a single loop that reads the sensor and updates BLE characteristics directly. The benefit is that either module can be modified, replaced, or tested independently. If I swap the HTU21D for a different sensor, the BLE module requires no changes. That maintainability benefit justified the added structure even for a project of this scale.
Standard GATT vs. custom BLE service. I implemented a custom BLE service rather than using the standard Environmental Sensing Profile. This was a tradeoff: custom services require clients to know the characteristic UUIDs, while standard profiles work with generic BLE tools automatically. For this project, the custom service allowed more flexibility in data representation, but a production implementation would be better served by the standard profile.
What I Learned
- Embedded development surfaces a class of bugs — bus hangs, timing-dependent failures, hardware-specific register behavior — that are invisible in software-only development. Defensive programming at the hardware interface is not optional; it is the baseline.
- Separating concerns in embedded firmware is as valuable as in application software, but the coupling is physical as well as logical. Understanding the I²C bus timing constraints and BLE advertising intervals required reasoning about hardware behavior, not just software structure.
- Validating end-to-end data flow with serial diagnostics and a BLE scanner before writing any client code was the right approach. It confirmed the hardware was behaving correctly before adding application-layer complexity.