Firmware for a BLE-enabled smart coaster based on the Arduino Nano 33 BLE.
It measures cup weight via a load cell + HX711, detects drinks and refills, and sends compact BLE events to the Android companion app. The NeoPixel ring provides immediate visual feedback so patients know the system is working without looking at a screen.
Evaluated in a clinical pilot with 8 patients.
Developed as part of my Bachelor's thesis in Information Systems at TUM.
![]() Intake detected |
![]() Refill detected |
![]() Drink reminder |
![]() Idle state |
| Component | Part |
|---|---|
| MCU | Arduino Nano 33 BLE |
| Sensor | HX711 load cell amplifier + load cell (mounted under coaster) |
| LEDs | Adafruit NeoPixel ring (23 pixels) |
| Connectivity | Bluetooth Low Energy (GATT server) |
| Power | USB or 3.3V supply |
The firmware tares an empty cup at startup, then continuously monitors weight changes to classify events:
- Intake — weight drops by more than 15 g.
- Refill — weight increases by more than 30 g.
- Cup removed — weight falls below
tare - 30 g.
All readings pass through waitForStableReadingRaw() which takes multiple HX711 samples and checks for small deltas between them, filtering out sensor noise and transient vibrations.
The NeoPixel ring gives patients and staff immediate visual confirmation without needing to check the app:
| State | Animation |
|---|---|
| Boot | Blue ring while system initializes |
| Tare successful | Small white segment (idle) |
| Drink detected | Green wave animation |
| Refill detected | Blue wave animation |
| Cup removed | Full red ring |
| Reminder from app | Multi-color flash sequence, returns to idle |
All LED animations call BLE.poll() inside their loops to keep the Bluetooth link responsive while pixels are updating.
The firmware exposes a custom BLE service with two characteristics — one for sending events to the Android app (notify), one for receiving commands (write). The Android app maintains up to three parallel connections, one per coaster.
Since clinical staff may move out of Bluetooth range of the Android monitoring device, the firmware implements a circular ring buffer with a 30-event capacity to ensure no data loss.
- Store: intakes and refills are queued in the nRF52840 SRAM while the connection is lost.
- Sync: when the Android monitoring device reconnects and subscribes to notifications, the buffer flushes all pending events.
- Throttle: during sync, buffered messages are sent every 1000 ms to avoid flooding the Android BLE stack, ensuring every packet is received and processed.
| UUID | Role |
|---|---|
6d12c00c-d907-4af8-b4d5-42680cdbbe04 |
Service |
c663891c-6163-43cc-9ad6-0771785fde9d |
TX Characteristic (notify to Android) |
ab36ebe1-b1a5-4c46-b4e6-d54f3fb53247 |
RX Characteristic (write from Android) |
Coaster to Android (TX characteristic, ASCII):
I 45.23 a # Intake of 45.23 g from cup "a"
R 32.10 a # Refill of 32.10 g for cup "a"
I= intake (patient drank),R= refill (staff topped up)amount= grams of watercup= cup identifier (single character)
Android to Coaster (RX characteristic, 1 byte):
0x01— triggersflashMultiColor5Times()reminder animation.
Calibration values from lab measurements:
float storedOffset = 71306.00;
float storedScaleFactor = -1095.25;| Threshold | Value | Purpose |
|---|---|---|
MIN_CUP_WEIGHT |
200 g | Rejects unrealistically light cups during tare |
DRINK_SENSITIVITY |
15 g | Weight drop larger than this triggers intake event |
REFILL_SENSITIVITY |
30 g | Weight increase larger than this triggers refill event |
CUP_REMOVED_MARGIN |
30 g | Weight below (tare - margin) triggers cup-removed state |
- Open
smart-coaster-firmware.inoin Arduino IDE. - Select board: Tools > Board > Arduino Mbed OS > Arduino Nano 33 BLE.
- Select the correct port.
- Click Verify then Upload.
arduino-cli core update-index
arduino-cli core install arduino:mbed_nano
arduino-cli compile \
--fqbn arduino:mbed_nano:nano33ble \
smart-coaster-firmware.ino
arduino-cli upload \
-p /dev/ttyACM0 \
--fqbn arduino:mbed_nano:nano33ble \
smart-coaster-firmware.inoAdjust the port as needed for your system.
| Library | Purpose |
|---|---|
| HX711 | ADC driver for the load cell amplifier |
| Adafruit NeoPixel | RGB LED ring control for status animations |
| ArduinoBLE | BLE GATT server with custom service and characteristics |
Weight stabilization logic, event classification, circular data buffering, BLE payload formatting, reconnection strategy, and LED animation patterns are implemented manually.
This firmware is designed to work with the Android companion app: github.com/vladyslavm-dev/smart-coaster-android
The Android app maintains three parallel BLE connections, parses the event messages, aggregates intake per patient with time-windowed summaries, and exports data as CSV.
- Add BLE-accessible calibration mode to eliminate hardcoded offset factor.
- Support multiple cup profiles with different tare values.
- Add battery level reporting via BLE for wireless deployments.
MIT License — Copyright (c) 2025 Vladyslav Marchenko
See LICENSE for details.
Vladyslav Marchenko
- GitHub: @vladyslavm-dev
- Website: vladyslavm.dev



