Skip to content

Sensors Module

Guard: ESP3D_SENSOR_FEATURE (CMake option SENSOR_SERVICE)

Status: dormant / not shipping. SENSOR_SERVICE is fully wired through cmake/features.cmake and cmake/sanity_check.cmake, and the runtime code (main/modules/sensors/, [ESP210], [ESP400]/[ESP401] settings, the mDNS sensor TXT record) is complete and buildable — but no board’s CMakeLists.txt or variants.py currently sets SENSOR_SERVICE to ON. This is reserved groundwork for a future ESP3D-X port with sensor hardware, not a bug or oversight. Nothing here applies to any currently shipping SKU.


The Sensors module (main/modules/sensors/) provides a unified interface for reading environmental data from one of several hardware sensor drivers compiled into the firmware at build time. A single global esp3d_sensor object manages the selected sensor, handles periodic polling, formats output for the [ESP210] command, and notifies registered callbacks on every successful reading.

The module is a compile-time-selected, single-driver abstraction: exactly one sensor driver is compiled per build, chosen via SENSOR_SERVICE + SENSOR_DHT / SENSOR_BMP280 / SENSOR_ANALOG CMake options (enforced in cmake/sanity_check.cmake:186-201). It sits within the Storage & Configuration group alongside the filesystem, update service, and config-file modules because the polling interval is a persistent setting backed by NVS via ESP3DSettings.


SENSOR_DHT

SENSOR_BMP280

SENSOR_ANALOG

yes

ESP3DX::begin

main/core/esp3d_x.cpp

esp3d_sensor.begin

ESP3DSettings - NVS

esp3d_sensor_interval

default 30 000 ms

SENSOR_TYPE

CMake option

DHT One-wire Driver

hardware/common/drivers/sensor_dht

BMP280 / BME280 I²C Driver

hardware/common/drivers/sensor_bmp280

sensor_analog Driver

hardware/common/drivers/sensor_analog

ESP-IDF ADC Oneshot Unit

sensor_analog_converter_fn_t

customizations/sensor_analog

Network service tick

esp3d_x_network.cpp

esp3d_sensor.handle

Interval elapsed?

doRead

compile-time branched

no runtime dispatch

ESP3DSensorData

esp3d_sensor_cb_t callback

fired synchronously

getDataString

e.g. 24.5C 65.2%

Caller-defined action

e.g. push to esp3d_values

or WebSocket

ESP210 Command Handler

type + interval + reading


owns (value member)

pre-configured by BSP before begin

produced by driver on each read

ESP3DSensorData

+bool valid

+float temperature

+bool temperature_valid

+float humidity

+bool humidity_valid

+float pressure

+bool pressure_valid

+float analog_value

+bool analog_valid

+int analog_raw

ESP3DSensor

-bool _started

-uint32_t _interval

-int64_t _lastReadTime

-ESP3DSensorData _data

-esp3d_sensor_cb_t _callback

-void* _callbackCtx

-char _dataString[128]

+begin() : bool

+end() : void

+handle() : void

+started() : bool

+interval() : uint32_t

+setInterval(uint32_t) : void

+readNow() : bool

+getData() : const ESP3DSensorData&

+getDataString() : const char

+setCallback(cb, ctx) : void

+getModelString() : const char*

-doRead() : bool

sensor_analog_config_t

+adc_unit_t adc_unit

+adc_channel_t adc_channel

+adc_atten_t adc_atten

+adc_bitwidth_t adc_bitwidth

+sensor_analog_converter_fn_t converter

+const char* unit

sensor_analog_data_t

+bool valid

+int raw

+float value


ESP3DSensorData — Unified Reading Struct

Section titled “ESP3DSensorData — Unified Reading Struct”

File: main/modules/sensors/esp3d_sensor.h

A flat struct representing one complete sensor reading. All fields are always present regardless of which driver is compiled. Fields not produced by the active driver remain 0.0f / 0 with their _valid flag set to false.

FieldTypeDescription
validbooltrue if the most recent read succeeded overall
temperaturefloatTemperature in °C
temperature_validbooltrue for DHT11, DHT22, BMP280, BME280
humidityfloatRelative humidity in %
humidity_validbooltrue for DHT11, DHT22, BME280
pressurefloatAtmospheric pressure (driver-defined unit)
pressure_validbooltrue for BMP280, BME280
analog_valuefloatConverted analog reading (e.g. voltage in V)
analog_validbooltrue for ANALOG driver
analog_rawintRaw 12-bit ADC value (ANALOG driver only)

File: main/modules/sensors/esp3d_sensor.h / esp3d_sensor.cpp

The central management class. A single global instance esp3d_sensor is declared at the bottom of the header. The class is final and non-copyable. It normalizes results from any compiled driver into the common ESP3DSensorData struct — the consumer never needs to know which driver was compiled in.

begin()\nread interval from NVS\ndriver pre-configured by board_init

readNow()\nimmediate read bypassing interval timer

end()

Uninitialized

Started

MethodDescription
begin()Reads polling interval from ESP3DSettings (NVS key esp3d_sensor_interval, default 30 000 ms). The low-level driver must already be configured by board_init. Returns true on success.
end()Stops polling, resets internal state.
handle()Non-blocking, interval-gated poll. Called from the network service tick. Calls doRead() if the interval has elapsed, then fires the callback synchronously.
readNow()Triggers an immediate read outside the normal interval schedule. Returns true on success.
getData()Returns a const reference to the last cached ESP3DSensorData.
getDataString()Formats data as an ESP210-compatible string (e.g. "24.5[C] 65.2[%]"). Returns pointer to an internal 128-byte static buffer — valid until the next call.
setCallback(cb, ctx)Register a callback invoked after every successful read. Pass nullptr to unregister.
setInterval(ms)Change polling interval at runtime. 0 disables automatic polling (use readNow() manually).
getModelString()Static. Compile-time branched. Returns "DHT11", "DHT22", "BMP280", "BME280", or "ANALOG".
started()Returns true if active.
interval()Returns the current polling interval in milliseconds.

typedef void (*esp3d_sensor_cb_t)(const ESP3DSensorData &data, void *user_ctx);

Invoked synchronously from the same execution context as handle() or readNow() — no observer/subscription indirection. The sensor module itself provides no ESP3DValuesIndex integration; that wiring must be added by the caller if UI display is needed (see ESP3DValues Integration below).

⚠️ LVGL Constraint: If the callback modifies LVGL objects, it must be called from Core 1 (the LVGL task). Use change detection before updating widgets to avoid unnecessary redraws.


SENSOR_TYPE

CMake option

SENSOR_DHT

DHT11 / DHT22

one-wire

SENSOR_BMP280

BMP280 / BME280

I²C

SENSOR_ANALOG

ADC oneshot

+ user converter

sensor_analog_configure

hardware/common/drivers/sensor_analog

sensor_analog_deinit

sensor_analog_converter_fn_t

customizations/sensor_analog

Exactly one driver is compiled per build. SENSOR_SERVICE requires selecting exactly one of SENSOR_DHT, SENSOR_BMP280, or SENSOR_ANALOG — enforced in cmake/sanity_check.cmake:186-201.

CMake flagFeature defineDriver pathData populated
SENSOR_DHTESP3D_SENSOR_DHT_FEATUREhardware/common/drivers/sensor_dht/temperature, humidity
SENSOR_BMP280ESP3D_SENSOR_BMP280_FEATUREhardware/common/drivers/sensor_bmp280/temperature, pressure, humidity (BME280 only)
SENSOR_ANALOGESP3D_SENSOR_ANALOG_FEATUREhardware/common/drivers/sensor_analog/analog_value, analog_raw

Bit-banged one-wire protocol on a single GPIO (sensor_dht_config_t::data_pin). Requires an external 4.7 kΩ–10 kΩ pull-up resistor. sensor_dht_read() is blocking (~4–5 ms) and uses a FreeRTOS critical section (portMUX) for the timing-sensitive bit read.

Minimum re-read interval: 1 s for DHT11, 2 s for DHT22. Faster polling returns stale data or read errors.

I²C sensor. sensor_bmp280_configure() probes the bus, reads the chip ID to auto-detect BMP280 vs BME280, and loads calibration data. Requires the I²C bus to be initialized (e.g. via bus_i2c_init()) before calling. Humidity is only populated for BME280 — ESP3DSensorData::humidity_valid remains false on plain BMP280.

ADC oneshot read via ESP-IDF esp_adc/adc_oneshot.h. Configurable unit, channel, attenuation, and bit width. An optional sensor_analog_converter_fn_t function pointer converts the raw ADC integer to a physical unit. The unit field in sensor_analog_config_t is a display string used in getDataString().

For the full analog driver API, see analog_sensor.md.

FieldTypeDescription
adc_unitadc_unit_tADC_UNIT_1 or ADC_UNIT_2
adc_channeladc_channel_tADC channel connected to the sensor pin
adc_attenadc_atten_tInput attenuation (affects measurable voltage range)
adc_bitwidthadc_bitwidth_tADC resolution (typically 12-bit on ESP32-S3)
convertersensor_analog_converter_fn_traw int → float. Pass NULL for raw value passthrough.
unitconst char*Display unit label, e.g. "V", "C", "%"

Default Converter (customizations/sensor_analog/customizations.h)

Section titled “Default Converter (customizations/sensor_analog/customizations.h)”
static inline float sensor_analog_converter(int raw_value) {
return (float)raw_value * 3.9f / 4095.0f;
}

Maps the 12-bit ADC range (0–4095) to 0–3.9 V. Replace with a board-specific function to convert to temperature, percentage, or any other unit.


Hardware Pin

ADC / One-wire / I²C

Low-level Driver

sensor_analog / sensor_dht / sensor_bmp280

Driver data struct

e.g. sensor_analog_data_t

ESP3DSensor::doRead

compile-time branched

no runtime dispatch

maps to ESP3DSensorData

ESP3DSensorData

cached in _data member

getData

const reference to cached struct

getDataString

128-byte static buffer

e.g. 24.5C 65.2% or 1.23V

esp3d_sensor_cb_t callback

fired synchronously on each read

ESP210 Command Handler

type + interval + reading

plain text or JSON

Active Transport

Serial / BT / USB / WiFi

Caller-defined action

e.g. esp3d_values.set_value

or WebSocket push


Network Tick\nesp3d_x_network.cppESP3DSettings (NVS)ESP3DSensorLow-level Driverboard_init (BSP)ESP3DX::beginNetwork Tick\nesp3d_x_network.cppESP3DSettings (NVS)ESP3DSensorLow-level Driverboard_init (BSP)ESP3DX::beginADC / I²C / GPIO initialized\ndriver ready for readsalt[yes][no]loop[Every network service tick]sensor_*_configure(&config)esp3d_sensor.begin()readUint32(esp3d_sensor_interval)_interval ms (default 30 000)true (_started = true)esp3d_sensor.handle()elapsed >= _interval?doRead() - driver read calldriver data structpopulate ESP3DSensorDatainvoke _callback(_data, _callbackCtx)no-op

ESP3DValuesIndex / esp3d_values Integration

Section titled “ESP3DValuesIndex / esp3d_values Integration”

None by default. Unlike most other subsystems (WiFi status, connection status, etc.), the sensor module does not register an ESP3DValuesIndex observable. There is no sensor_* entry in the values system, and no UI screen or LVGL widget currently subscribes to sensor data.

Sensor data is currently exposed only through ESP commands and mDNS:

Exposure pointDetail
[ESP210]Get/set polling interval (admin-only to set); returns type + interval + current formatted value as plain text or JSON (main/core/commands/esp210.cpp:32)
[ESP400]Lists esp3d_sensor_interval among general settings (esp400.cpp:549)
[ESP401]Generic settings-write; on writing esp3d_sensor_interval also calls esp3d_sensor.setInterval(value32) live (esp401.cpp:229-230)
[ESP420]Includes current sensor reading (or "OFF" if not started) in the device info dump (esp420.cpp:868)
mDNS sensor TXT recordAdvertises the compiled model name — see Network & Web Services

Any future UI integration (e.g. a status screen tile) would require adding a new ESP3DValuesIndex entry and pushing callback data into esp3d_values — that plumbing does not exist today. See values.md for the observable system API.


enforce exactly one type

board CMakeLists.txt

SENSOR_SERVICE=ON

SENSOR_ANALOG=ON

cmake/features.cmake

define mapping

ESP3D_SENSOR_FEATURE

ESP3D_SENSOR_ANALOG_FEATURE

sensor_analog.c compiled

hardware/common/drivers/sensor_analog

customizations/sensor_analog/customizations.h

included for converter fn

main/modules/sensors/ included

main/CMakeLists.txt

ESP3DSensor::doRead

uses sensor_analog_read branch

cmake/sanity_check.cmake

line 186-201

CMake flagMust be ON alongside SENSOR_SERVICECompiled driver
SENSOR_DHTYesDHT11/DHT22 one-wire
SENSOR_BMP280YesBMP280/BME280 I²C
SENSOR_ANALOGYesADC oneshot + user converter
(none set)—Rejected by cmake/sanity_check.cmake

The sensor module follows the project’s memory constraint rules:

  • No dynamic allocation in handle() or doRead() — all buffers are fixed.
  • _dataString[128] is a statically-sized member buffer for getDataString().
  • ESP3DSensorData is a value member inside ESP3DSensor (lives in .bss, no heap).
  • The callback is a single function pointer — no heap allocation for subscriber lists.
  • sensor_analog_config_t is copied into driver-internal static storage during sensor_analog_configure() — no runtime allocation after init.

FileRole
main/modules/sensors/esp3d_sensor.h / .cppESP3DSensor facade — begin()/handle()/readNow()/getDataString()
hardware/common/drivers/sensor_dht/sensor_dht.{h,c,config.h}DHT11/DHT22 one-wire driver
hardware/common/drivers/sensor_bmp280/sensor_bmp280.{h,c,config.h}BMP280/BME280 I²C driver
hardware/common/drivers/sensor_analog/sensor_analog.{h,c,config.h}ADC oneshot driver
customizations/sensor_analog/customizations.hDefault ADC → voltage converter (raw * 3.9 / 4095)
main/core/commands/esp210.cpp[ESP210] — get/set interval, get current reading
main/core/commands/esp400.cpp / esp401.cppGeneric settings list/write integration
main/core/commands/esp420.cpp[ESP420] device info dump
main/core/esp3d_x.cpp:149-155esp3d_sensor.begin() at boot
main/modules/network/esp3d_x_network.cpp:107-109esp3d_sensor.handle() polling tick
main/core/includes/esp3d_settings_defs.inc:711-724esp3d_sensor_interval NVS setting (default 30 000 ms)
cmake/features.cmakeFeature flag → define mapping
cmake/sanity_check.cmake:186-201”Exactly one sensor type” enforcement

DocumentRelevance
analog_sensor.mdFull sensor_analog driver API (ADC config, deinit, data struct)
Hardware_Peripheral_Drivers.mdAll hardware drivers: display, touch, IO expanders, physical inputs
Core_Platform_&_Infrastructure.mdESP3DSettings (NVS), ESP3DValues observable system
values.mdESP3DValues API — needed if adding future UI integration
filesystem.mdSibling module in Storage & Configuration group
update_service.mdSibling module in Storage & Configuration group
Network_&_Web_Services.mdmDNS sensor TXT record, communication transports
bsp_board_initialization.mdBSP board_init() — sensor driver pre-configuration per board