Arduino library for the Seeed MR60BHA2 mmWave sensor kit.
- Author: Lennart Hennigs (https://www.lennarthennigs.de)
- Copyright (C) 2026 Lennart Hennigs.
- Released under the MIT license.
This library wraps the MR60BHA2 breathing and heart rate sensor, a BH1750 ambient light sensor, and a WS2812 RGB LED with a Button2-style callback API.
- Breathing rate, heart rate, presence, distance, and ambient light readings
- Edge-triggered callbacks for all alert conditions — no polling needed
- Three age-specific vital profiles: ADULT, CHILD, TODDLER
- Debounced alert edges (20 s zero-signal, 15 s threshold violation)
- Irregular breathing detection (60-sample rolling standard deviation)
- Light-gated tracking: pause vital monitoring when a room is lit/dark
- LED control via
setLedColor()/setLedOff() - Sensor firmware version query
It has been tested with the Seeed XIAO ESP32-C6.
If you are looking for a lower-level driver for the Seeed mmWave sensors, take a look at the mmWave library.
To see the latest changes to the library please take a look at the Changelog.
If you find this library helpful please consider giving it a ⭐️ at GitHub and/or buy me a ☕️.
Thank you!
Designed for the Seeed XIAO ESP32-C6 with the MR60BHA2 kit:
| Component | Connection |
|---|---|
| MR60BHA2 sensor | Serial0 (D6/D7) |
| BH1750 light sensor | I2C (D4/D5) |
| WS2812 RGB LED | D1 (GPIO 1) |
- Include the library and create an
mmWaveKitinstance.
#include <mmWaveKit.h>
mmWaveKit kit;- Configure and start the sensor in
setup()using aVitalConfigstruct.
void setup() {
Serial.begin(115200);
mmWaveKit::VitalConfig vc;
vc.profile = mmWaveKit::ADULT;
kit.begin(vc, {});
}- Call
update()in yourloop()to process sensor data.
void loop() {
kit.update();
static unsigned long last = 0;
if (millis() - last >= 1000) {
last = millis();
Serial.printf("BR: %d HR: %d present: %s\n",
kit.getBreathingRate(), kit.getHeartRate(),
kit.isPresent() ? "yes" : "no");
}
}- Instead of polling readings in
loop(), you can assign callback functions that fire when alert conditions are detected. - All callbacks share the same signature:
void handler(mmWaveKit& kit, mmWaveKit::Event e, int value)valuecarries the relevant reading at fire time (BR in rpm, HR in bpm, lux;0for presence events).
| Registration | Event | value |
|---|---|---|
onEvent(cb) |
all events (fires before the specific handler) | varies |
onPresenceOn(cb) |
EVT_PRESENCE_ON |
0 |
onPresenceOff(cb) |
EVT_PRESENCE_OFF |
0 |
onNoBreathing(cb) |
EVT_NO_BREATHING |
0 |
onBreathingLow(cb) |
EVT_LOW_BREATHING |
BR |
onBreathingHigh(cb) |
EVT_HIGH_BREATHING |
BR |
onBreathingIrregular(cb) |
EVT_IRREGULAR_BREATHING |
BR |
onNoHeartRate(cb) |
EVT_NO_HEART_RATE |
0 |
onHeartRateLow(cb) |
EVT_LOW_HEART_RATE |
HR |
onHeartRateHigh(cb) |
EVT_HIGH_HEART_RATE |
HR |
onBecameLight(cb) |
EVT_BECAME_LIGHT |
lux |
onBecameDark(cb) |
EVT_BECAME_DARK |
lux |
onLightSituationChanged(cb) |
both light events | lux |
- Please take a look at the included examples (see below) to get an overview of the different callback handlers and their usage.
VitalConfigcontrols the vital sign monitoring behaviour:
struct VitalConfig {
VitalProfile profile = mmWaveKit::ADULT;
uint32_t zeroDebounceMs = 20000; // ms before "no signal" alert fires
uint32_t threshDebounceMs = 15000; // ms before threshold alert fires
};LightConfigcontrols the ambient light sensor and LED:
struct LightConfig {
int threshold = 10; // lux boundary
uint8_t trackMode = LIGHT_TRACK_ALWAYS;
uint8_t ledPin = 1; // NeoPixel data pin
};- The library ships with three age-specific profiles that set normal ranges for breathing rate and heart rate.
| Profile | Breathing | Heart Rate | Sensitivity |
|---|---|---|---|
ADULT |
10–20 rpm | 40–100 bpm | most sensitive |
CHILD |
16–30 rpm | 60–120 bpm | moderate |
TODDLER |
16–45 rpm | 60–160 bpm | least sensitive |
- Narrower normal ranges fire alerts at smaller deviations —
ADULTis most sensitive.
- You can gate vital alert evaluation based on ambient light levels using the
trackModefield inLightConfig.
| Constant | Behaviour |
|---|---|
LIGHT_TRACK_ALWAYS |
always evaluate vital alerts (default) |
LIGHT_TRACK_DARK |
only evaluate when lux < threshold |
LIGHT_TRACK_LIGHT |
only evaluate when lux >= threshold |
- Only one
mmWaveKitinstance per program is supported (file-scope statics). update()evaluates alerts in 1-second buckets; call it everyloop().- Irregular breathing detection requires 60 consecutive 1-second samples.
- BasicReadings.ino – polls sensor readings every second and prints them to Serial
- Callbacks.ino – shows how to assign event handlers for vital sign alerts
- LightTracking.ino – demonstrates light-gated vital monitoring with
LightConfig
See below the constructors and member functions the library provides:
mmWaveKit();
bool begin(const VitalConfig& vc, const LightConfig& lc);
bool begin(HardwareSerial& serial, const VitalConfig& vc, const LightConfig& lc);
void update();
int getBreathingRate();
int getHeartRate();
float getDistance();
int getLux();
bool isPresent();
bool isLight();
bool isDark();
bool isTrackingActive();
int getThreshold();
const AlertState& getAlerts();
bool getFirmwareVersion(uint8_t& major, uint8_t& sub, uint8_t& modified);
bool getFirmwareVersion(char* buf, size_t len);
void setLedColor(uint8_t r, uint8_t g, uint8_t b);
void setLedOff();
void onEvent(CallbackFunction cb);
void onPresenceOn(CallbackFunction cb);
void onPresenceOff(CallbackFunction cb);
void onNoBreathing(CallbackFunction cb);
void onBreathingLow(CallbackFunction cb);
void onBreathingHigh(CallbackFunction cb);
void onBreathingIrregular(CallbackFunction cb);
void onNoHeartRate(CallbackFunction cb);
void onHeartRateLow(CallbackFunction cb);
void onHeartRateHigh(CallbackFunction cb);
void onBecameLight(CallbackFunction cb);
void onBecameDark(CallbackFunction cb);
void onLightSituationChanged(CallbackFunction cb);Open the Arduino IDE, choose "Sketch > Include Library" and search for "mmWaveKit". Or download the ZIP archive (https://github.com/LennartHennigs/mmWaveKit/zipball/master), and choose "Sketch > Include Library > Add .ZIP Library..." and select the downloaded file.
For PlatformIO, add the following to your platformio.ini:
lib_deps =
LennartHennigs/mmWaveKit @ ^1.0.0Dependencies are installed automatically:
- BH1750
^1.3.0 - Adafruit NeoPixel
^1.12.3
Note: The Seeed Arduino mmWave library is not available in the Arduino Library Manager. Install it manually via "Sketch > Include Library > Add .ZIP Library..." or for PlatformIO add https://github.com/Love4yzp/Seeed-mmWave-library to your lib_deps.
MIT License
Copyright (c) 2026 Lennart Hennigs
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.