2026-10-20 15:36:24 +06:00
2026-10-20 15:36:24 +06:00
2026-10-20 15:36:24 +06:00
2026-10-20 15:36:24 +06:00
2026-10-20 15:36:24 +06:00

SHT41 BTHome Beacon

An ultra-low-power temperature and humidity beacon for the nRF52840, built to run for a long time on a single CR2032 coin cell. It reads an SHT41 sensor once a minute and broadcasts the values as a BTHome v2 Bluetooth LE advertisement, so Home Assistant (and any other BTHome-compatible receiver) picks it up automatically with no pairing or connection.

Features

  • Temperature (0.1 °C resolution), relative humidity (1 %) and battery voltage (1 mV) in a standard BTHome v2 packet
  • Sensor is power-gated from a GPIO and powered only for ~15 ms per measurement
  • Non-connectable advertising every 10 s, measurement every 60 s
  • DC/DC converters enabled on both regulator stages (REG0 and REG1)
  • CPU sleeps between events via FreeRTOS tickless idle
  • Bare-metal TWIM (I²C) and SAADC drivers that are enabled only while in use
  • Battery measured internally through VDDH/5, so no external voltage divider is needed
  • SHT41 readings are CRC-checked; on a failed read the last good values keep being advertised

Hardware

Part Notes
Pro Micro nRF52840 (nice!nano v2 compatible) Uses the Adafruit UF2 bootloader + S140 SoftDevice
Adafruit SHT41 breakout (or bare SHT41) I²C address 0x44
CR2032 coin cell Connected to the board's battery input

Wiring

SHT41 nRF52840 GPIO Pro Micro label
VIN P0.17 D2 / 017
SDA P0.20 D3 / 020
SCL P0.22 D4 / 022
GND GND GND

The sensor's VIN comes from a GPIO, not from the 3V3 rail, which is what allows it to be switched off completely between measurements. Pins are configured as raw nRF GPIO numbers (port * 32 + pin) and driven directly through registers, so the Arduino/Feather pin mapping is irrelevant. Internal pull-ups are enabled on SDA/SCL as a backup, but the breakout's own pull-ups are what the bus normally relies on.

Building and flashing

The project uses PlatformIO with the Adafruit nRF52 Arduino core. The Feather nRF52840 board definition is used because it shares the same bootloader and SoftDevice as the nice!nano.

Expected layout:

.
├── platformio.ini
└── src/
    └── main.ino

Build and upload:

pio run                 # build
pio run -t upload       # flash over USB (double-tap reset to enter the bootloader if needed)

Flash over USB without the coin cell inserted, or make sure your board's power path can handle both sources at once.

If you prefer the Arduino IDE, install the Adafruit nRF52 core and select Tools → Board → Adafruit Feather nRF52840 Express.

Configuration

All user settings are #defines at the top of main.ino:

Setting Default Description
SENS_PWR_PIN 17 GPIO powering the sensor
SENS_SDA_PIN / SENS_SCL_PIN 20 / 22 I²C pins
MEAS_INTERVAL_MS 60000 Time between measurements (ms)
ADV_INTERVAL 16000 Advertising interval in 0.625 ms units (16000 = 10 s)
TX_POWER_DBM 0 TX power: -40, -20, -16, -12, -8, -4, 0, 2…8 dBm. Lower saves power but reduces range
DEVICE_NAME "SHT41" Advertised name

The device name is placed in the same 31-byte advertising packet as the BTHome data, so keep it to 13 characters or fewer.

BTHome payload

Service data under UUID 0xFCD2, device info byte 0x40 (BTHome v2, unencrypted, regular interval), followed by objects in ascending ID order:

Object ID Measurement Type Factor
0x0C Voltage uint16 0.001 V
0x2E Humidity uint8 1 %
0x45 Temperature sint16 0.1 °C

Home Assistant

With the BTHome integration and a Bluetooth adapter or ESPHome Bluetooth proxy in range, the beacon is discovered automatically within a few seconds of powering up. Accept the discovery to get temperature, humidity and voltage entities. Values update once a minute.

How it works

On boot the firmware enables the DC/DC converters, sets the sensor pins to high-impedance, preconfigures the TWIM peripheral, starts the SoftDevice and begins advertising. Every MEAS_INTERVAL_MS it then:

  1. Powers the SHT41 and switches SDA/SCL to I²C mode
  2. Sends the high-precision measurement command (0xFD), sleeps ~10 ms, and reads 6 bytes
  3. Disables TWIM, sets the bus pins to high-Z and then cuts sensor power (in that order, to avoid back-feeding the sensor through the I/O lines)
  4. Verifies both CRCs and converts the raw values
  5. Samples VDDH/5 with the SAADC right after the sensor load, then turns the ADC off
  6. Rebuilds the advertisement with the new values

Between measurements the CPU stays in System ON sleep while the SoftDevice handles advertising.

Notes

  • Serial is intentionally never started; USB is unpowered on battery anyway.
  • The status LED blink during advertising is disabled.
  • The first measurement after boot is published even if it fails (as zeros), so the device is visible right away; later failures keep the previous values.
S
Description
No description provided
Readme
34 KiB
0 Stars 1 Watchers 0 Forks
Languages
C++ 100%