IoT-CO2Tracker
The IoT-CO2Tracker is a compact device that measures CO₂, temperature, and humidity. Initially developed on an ESP8266, it was later moved to an ESP32 using the IoT-PostBox board. The project also serves to validate the board’s capabilities, though that is not its primary purpose.
An external GPS module can be connected via the serial port, allowing the device to publish both sensor readings and location data. This data is sent via LoRaWAN to The Things Network (TTN) for coverage and connectivity analysis, and over Wi‑Fi using MQTT to your own broker. When needed, GPS tracks are logged to the onboard flash storage and can be downloaded for visualization through a built‑in FTP server. The latest hardware revision includes a 2" TFT screen that displays live sensor values, connectivity, and battery status, with a user interface built on LVGL v9 and designed with SquareLine.
Communication flow
flowchart LR
Device[IoT-CO2Tracker]
Broker[(MQTT Broker)]
GW[LoRaWAN Gateway]
TTN[The Things Network]
NR[Node-RED]
HA[Home Assistant]
DB[(InfluxDB)]
G[Grafana]
Flash[(LittleFS)]
PC[PC]
Viz[GPSVisualizer]
Device -- MQTT (JSON) --> Broker
Broker --> NR
Device -- LoRaWAN (binary) --> GW --> TTN --> NR
NR --> DB --> G
NR -.->|update device*| HA
Device -- GPS tracks --> Flash -- FTP download --> PC --> Viz
* planned (to be implemented)
MQTT
- Topic:
/iot-co2Tracker/<deviceId>/data
Example payload (without GPS):
{
"CO2": 222,
"temp": 33.02967834,
"humidity": 30.82733154,
"rssi_STA": -38,
"vBat": 4.21023798,
"vBus": 5.021749973,
"PowerStatus": 2,
"ChargingStatus": 2
}Example payload (with GPS):
{
"CO2": 229,
"temp": 31.5396347,
"humidity": 29.40368652,
"lat": 51.27265917,
"lng": 12.37718133,
"date": 120925,
"time": 15070704,
"speed": 0.11112,
"satellites": 4,
"altitude": 197.6,
"hdop": 8.33,
"course": 120925,
"rssi_STA": -54,
"vBat": 4.116175175,
"vBus": 0.01325,
"PowerStatus": 3,
"ChargingStatus": 1
}LoRaWAN uplink
| Field | C type | Bytes | Units | Notes |
|---|---|---|---|---|
| latitude | uint32_t | 4 | degrees | 24‑bit value scaled to 4 bytes ((lat+90)/180*16777215) |
| longitude | uint32_t | 4 | degrees | 24‑bit value scaled to 4 bytes ((lng+180)/360*16777215) |
| altitude | uint16_t | 2 | m | |
| hdop | uint16_t | 2 | HDOP | |
| speed | uint16_t | 2 | km/h | value = km/h × 100 |
| satellites | uint8_t | 1 | count | |
| course | uint16_t | 2 | degrees | |
| time | uint32_t | 4 | HHMMSScc | local offset applied |
| vBat | uint16_t | 2 | mV | |
| vBus | uint16_t | 2 | mV | |
| CO2 | uint16_t | 2 | ppm | |
| temp | uint16_t | 2 | °C | value = °C × 100 |
| hum | uint16_t | 2 | % | value = % × 100 |
| powerStatus | uint8_t | 1 | enum | |
| chargingStatus | uint8_t | 1 | enum |
Notes
- Multi‑byte fields are big‑endian (MSB first).
- Latitude/Longitude use 24‑bit effective values packed into 4 bytes (high byte may be 0).
- Total payload length: 33 bytes.
Decoders: see formaters/ for TTN uplink decoders and field mapping.
TTN setup (OTAA)
- Create an application
co2Trackerand register a device. - Set the device DEVEUI, APPEUI, and APPKEY to your
config.json. - Add the custom uplink decoder from formaters/custom to your TTN application.
- Join the device and verify uplinks in the TTN console under the Application live data tab.
FTP GPS logs
- If the
local_logsconfiguration is enabled in the config.json file, GPS log files will be created on the LittleFS partition. The file names will follow a specific naming convention, such asGPS_2025_9_10.csv. - Connect via FTP to the device server using the username and password configured in config.json to download the GPS log file.
- CSV columns follow:
time,latitude,longitude,altitude,speed,hdop,satellites,course,vBat,vBus,PowerStatus,ChargingStatus,co2,temp,hum
18182040,51.27235567,12.37730017,115.1,31.503,1.06,12,162.6,3.98449,0.00795,1,2,239,22.34,57.10
18182553,51.27197583,12.37750567,115.6,31.540,1.06,12,162.8,3.98449,0.00795,1,2,232,22.34,57.15
18183040,51.27155317,12.37771250,116.4,29.595,1.06,12,167.4,3.98073,0.00795,1,2,221,22.36,57.19
18183541,51.27119583,12.37782367,117.2,29.058,1.15,12,167.5,3.98073,0.00795,1,2,207,22.38,57.31- You can visualize the GPS tracks using tools like GPSVisualizer by uploading the CSV file. A good filter in this case can the IoT-CO2Tracker GPSVisualizer configuration option.
![]()
Software
Firmware
- PlatformIO project (ESP32). FreeRTOS tasks for sensors, UI, and communications.
- Main Libraries used:
- WebConfigServer: to configure the device settings over a web interface and manage multiple common services
- MQTTClient: MQTT client library
- LoRaWANClient: LoRaWAN C++ client based on Arduino-LMIC
- PowerManagement: power management library to read battery and power states
- SparkFun SCD30: read CO₂, temperature, and humidity from the SCD30 sensor
- TinyGPSPlus: parse GPS data
Display & User Interface (UI-LCD)
- Designed for a 2.0" GMT020-02 TFT SPI LCD (ST7789V driver, non-touch).
- Uses LVGL v9.
- Interface designed with SquareLine. The project can be found under
SquareLineProject/.
Backend services
- Node-RED subscribes to MQTT and TTN, processes messages, and stores data values in InfluxDB (requires node-red-contrib-influxdb node installation).

- Example flow: backend/nodered/flows.json
- Grafana dashboards read from InfluxDB.

- Example dashboard: backend/grafana/IoT-Co2Tracker_dashboard.json
- TTN formatters: see decoders in formaters/ for field mapping.
Hardware
Main Components
The essential hardware components for this project include:
- Board: IoT-PostBox v1 (ESP32/ESP32-S2 with onboard RFM95W LoRa module)
- Sensor: SCD30 (CO₂, temperature, humidity) over I²C
- GPS (optional): u-blox M8N or compatible GPS module using serial UART
- Display (optional): 2.0" GMT020-02 TFT OLED SPI LCD with ST7789V driver
- Power: battery support with charging capabilities using IoT-PostBox
3D Design & Enclosure
Note
Explore the 3D model interactively. Use mouse to rotate, zoom and pan. View Fullscreen 🔍
The enclosure is designed specifically for the IoT-PostBox v1, SCD30 sensor, 2" TFT display, GPS module, and a 1100mAh LiPo battery. It features precise cutouts for connectors, display, and sensor airflow, as well as rear openings for the LoRa antenna, battery switch, and GPS cable. The parametric FreeCAD model allows quick customization for alternative hardware by modifying spreadsheet parameters. Assembly uses four M3x5mm screws.
Resources:
- FreeCAD sources in 3Ddesigns/
- External STL references documented in 3Ddesigns/lib/readme.md.
Pinout
Pinout highlights (see boards/ and boards/variants/iotpostbox_v1/ for full mapping)
Build & flash
- Open the project using PlatformIO with VS Code.
- Configure services and device following the WebConfigServer style, editing the
data/config/config.jsonand uploading the Filesystem Image to the device. - Build and upload the firmware to an IoT-PostBox device.
- Use the device WebConfigServer UI web portal for advanced options.