element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
    About the element14 Community
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      •  Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Smart Home & Healthcare Challenge
  • Challenges & Projects
  • Design Challenges
  • Smart Home & Healthcare Challenge
  • More
  • Cancel
Smart Home & Healthcare Challenge
Forum VitaRF - Part 4 - Sending Sensor Data to Home Assistant via BLE on NRF52
  • News
  • Projects
  • Forum
  • Details
  • Leaderboard
  • Files
  • Members
  • DC
  • More
  • Cancel
  • New
Join Smart Home & Healthcare Challenge to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 2 replies
  • Subscribers 18 subscribers
  • Views 65 views
  • Users 0 members are here
  • design challenge
  • home assistant
  • nordic
  • nrf52
  • uno q
  • arduino
  • smart home & healthcare
  • linux
Related

VitaRF - Part 4 - Sending Sensor Data to Home Assistant via BLE on NRF52

arvindsa
arvindsa 22 hours ago

Recap: I am building a wireless vitals sensor node (heart rate, SpO₂, temperature) that reports over BLE into Home Assistant as your smart way to keep watch over our loved ones, built up in three phases — DK bring-up, a custom miniaturized nRF52832 PCB, then a Matter over Thread proof of concept.

Past Posts

  1. VitaRF - Part 1 - The Plan
  2. VitaRF - Part 2 - Interface MAX30102 on NRF52
  3. VitaRF - Part 3 - Interfacing MAX30208 on NRF52

In this post, I will be exploring how I paired Home assistant with the BLE beacon data. Post 3 closed with both sensors reading independently on the nRF52832 DK. I wanted to start with PCB Design, But I have to a little more research before it. The obvious next step is BLE into Home Assistant. 

UNO Q is all the rage now

The UNO Q is a Linux-capable board (aarch64, Debian 13 trixie under the hood) with Docker and Docker Compose already on it, so spinning up an Home Assistant Container should be easy, and With Qualcomm investing quite a lot on marketing for Uno Q, I should ride the wave.

There's also a practical reason to lean on the UNO Q here rather than my Radxa: I've got a handful of UNO Qs but only one Radxa Q6A, and I'd rather keep that one free for something that actually needs its extra power punch. tech_nickk's Hackster writeup on running Home Assistant on the UNO Q via Docker plus MQTT was good confirmation that UNO Q is up for the job. But instead of a command, I went with a compose file

Commands, in order:

mkdir -p ~/homeassistant/config
cd ~/homeassistant
nano docker-compose.yml # docker-compose.yml content below
docker compose pull
docker compose up -d

services:
  homeassistant:
    container_name: homeassistant
    image: ghcr.io/home-assistant/home-assistant:stable
    volumes:
      - ./config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
    restart: unless-stopped
    privileged: true
    network_mode: host

I relied on AI to generate the compose file. Also I saw the first 10minutes of  Ultimate Home Assistant Beginner’s Guide!  at https://www.youtube.com/watch?v=Z4gvkmJ8q48. Just enough to get an understanding of the Home assistant. It seemed pretty straightforward to me.

network_mode: host and privileged: true apparently is needed in the compose file so that the container gets direct access to the board's onboard Bluetooth adapter. It's importance came to me in hindsight.

Once the pull finally finished, docker compose up -d came back in about a second and onboarding started right away - welcome screen, then account setup:

image

Then this, still inside onboarding, before I'd touched any integration settings myself:

image

HA had already found the Bluetooth integration as a compatible device on its own, off the UNO Q's onboard adapter. Perfecto

image

That's Home Assistant installed. Now to give it something real to talk to. One problem was that the disk went from 4.2GB free to 1.6GB free, though memory was only around 800gb. I still have to see the memory usage after some time of usage. I also am pretty sure the linux will soon eat up the remaining 1.6Gb.

BTHome instead of a connectable GATT service

Home Assistant's Bluetooth integration has built-in, zero-config support for BTHome-formatted advertisements (bthome.io) - it should auto-discovers the device from the broadcast alone, no pairing, no custom integration.

I am quite familiar with the nRF5 SDK which has ble_app_hrs example for the standard Bluetooth Heart Rate Service (GATT, UUID 0x180D), which I can simply paste into my existing sensor integration code and flashed it, but Home Assistant core has no built-in integration - that would mean writing a custom HA component just to get the data out of the BLE beacon.

Getting the wire format right

With no previous experience, I went through the bthome.io/format page. Temperature property existed but not SPO2 or Heart rate, I thought it would be based on GATT services.

The Service Data UUID should be 0xFCD2 to be recognized as BTHome Compliant beacon. The temperature object id is 0x02, signed int16, factor 0.01, degrees C. I googled the nativity of the developer of BTHome - Ernst Klamer, He is from Netherlands and hence the SI units of Temperature.

HR value will have to go out as BTHome's generic "count" object (0x09, uint8, factor 1, range 0-255) - bpm fits the range exactly

I also came across bthome-ble on GitHub and there too in theirconst.py no heart rate was mentioned.

The firmware

I simply took the nRF5 SDK's ble_app_beacon example (examples/ble_peripheral/ble_app_beacon, PCA10040) and swapped the manufacturer-specific-data AD structure for a BTHome service-data AD structure, and add an app_timer driven refresh every 5s.

Building the actual BTHome payload is just this - device info byte, then (object id, value) pairs, straight out of the table I checked above:

/* BTHome service data payload: device info byte + (object id, value) pairs. */
#define BTHOME_PAYLOAD_LEN  (1 + (1 + 2) + (1 + 1))
static uint8_t m_bthome_payload[BTHOME_PAYLOAD_LEN];

static void bthome_payload_encode(void)
{
    uint8_t idx = 0;

    m_bthome_payload[idx++] = BTHOME_DEVICE_INFO_UNENCRYPTED_V2;

    m_bthome_payload[idx++] = BTHOME_OBJ_TEMPERATURE;
    m_bthome_payload[idx++] = (uint8_t) (m_dummy_temp_centidegrees & 0xFF);        /* LSB first (little-endian). */
    m_bthome_payload[idx++] = (uint8_t) ((m_dummy_temp_centidegrees >> 8) & 0xFF);

    m_bthome_payload[idx++] = BTHOME_OBJ_COUNT_U8;
    m_bthome_payload[idx++] = m_dummy_hr_bpm;
}

And the timer callback that walks the dummy values every 5s and pushes a fresh advertisement:

static void dummy_timer_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);

    int16_t temp_step = (int16_t) (next_rand() % 21) - 10;   /* +/- 0.10 degrees C. */
    m_dummy_temp_centidegrees += temp_step;
    if (m_dummy_temp_centidegrees < 3600) m_dummy_temp_centidegrees = 3600;  /* clamp 36.00 - 38.50 C */
    if (m_dummy_temp_centidegrees > 3850) m_dummy_temp_centidegrees = 3850;

    int8_t hr_step = (int8_t) (next_rand() % 5) - 2;          /* +/- 2 bpm. */
    int16_t new_hr = (int16_t) m_dummy_hr_bpm + hr_step;
    if (new_hr < 55) new_hr = 55;
    if (new_hr > 110) new_hr = 110;
    m_dummy_hr_bpm = (uint8_t) new_hr;

    bthome_payload_encode();
    advertising_data_set(false);
}

next_rand() is a tiny linear congruential generator which is quite fast.

I checked the beacon data using nRF Connect on my phone - the live scan on the left, the raw AD structure breakdown on the right, Sweet - Working on the first try. Well, Actually my first try was to send a static value, Only in the next step I added the random generator.

image

VitaRF-Dummy, MAC E2:3E:96:D7:7E:B6, service data UUID 0xFCD2, 1000ms advertising interval - matches the firmware source. Three AD structures, exactly as built: Flags (0x01, BR/EDR not supported), Service Data 16-bit UUID (0x16, UUID 0xFCD2 + payload 40 02 56 0E 09 48), and Complete Local Name (0x09, "VitaRF-Dummy"). Decoding that payload by hand: 0x40 is the device-info byte, 0x02 56 0E is the temperature object (little-endian 0x0E56 = 3670 = 36.70C), 0x09 48 is the count object (0x48 = 72). Matches the firmware's startup constants exactly.

It shows up in Home Assistant

Settings -> Devices and Services -> Integrations, under Discovered: VitaRF-Dummy 7EB6 under BTHome, sitting right next to the Bluetooth integration that was already configured. Hit Add, and it's a device with real sensor readings:

image

Temperature 36.7C, Count 72. Straight out of the box picked up by the UNO Q's onboard hci0

Left it running for a bit and checked the history graphs afterward, Count on the left and Temperature on the right:

image

 The Graph above shows Count (heart Rate) walking 72 down to 56, Temperature walking 36.7C up to 37.4C and then down. Now for a longer 2-hour window:

image

The Values kept changing regularly for the full two hours, no flatlines, no gaps

Code

The Full code for all the posts till now will be at https://gitlab.com/arvindsa/vitarf-e14

Final notes

Home Assistant is running on the Arduino UNO Q with Docker, and a dummy BLE broadcaster on the nRF52832 DK proved the whole path end to end: nRF52832 -> BTHome advertisement -> UNO Q's onboard Bluetooth adapter -> Home Assistant. This was an easy post. With major time went solely to understand the BTHome format.

  • Sign in to reply
  • Cancel
Parents
  • embeddedguy
    embeddedguy 16 hours ago

    Good project, nice post.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Reply
  • embeddedguy
    embeddedguy 16 hours ago

    Good project, nice post.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Children
  • arvindsa
    arvindsa 16 hours ago in reply to embeddedguy

    Thanks. Finally able to put the extra UNOs to work.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2026 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube