Turning a cheap Bluetooth battery monitor into a simple long-term 12V battery monitoring system.
The Problem
I have two cars that don’t get driven nearly as often as they probably should. After dealing with dead batteries more than once, I wanted a simple car battery monitoring system that could warn me before one died.
That’s normally not much of a problem—until one of them sits long enough that its 12V battery dies.
After dealing with that more than once, I wanted something a little better than occasionally walking into the garage with a multimeter and wondering whether I should drive a particular car that day.
The actual goal was pretty simple:
If a car’s battery is getting low, tell me before it becomes a dead battery.
I already use Home Assistant and MQTT for other monitoring, so ideally the cars would just become another set of sensors I could graph and eventually alert on.
Car Battery Monitoring Hardware
I started with an ANCEL BM200 Bluetooth battery monitor.
It’s a small module that permanently attaches to the battery terminals and communicates over Bluetooth Low Energy. The stock ANCEL app can display current voltage, estimate state of charge, maintain historical voltage data, and even capture events such as starter cranking voltage.
For the always-on collector, I dug out a Raspberry Pi Zero W.
The architecture ended up being pleasantly simple:
ANCEL BM200
│
│ Bluetooth LE
▼
Raspberry Pi Zero W
│
│ MQTT
▼
Mosquitto
│
▼
Home Assistant
The Pi doesn’t need to understand whether the battery is healthy or decide when I should drive the car.
Its job is simply:
Connect → read → publish → disconnect.
Home Assistant can handle the intelligence later.
Talking to the BM200 Without the App
This was where the project got more interesting.
The BM200 advertises itself over BLE as a BM6, and fortunately its protocol has already received some attention from other people reverse engineering these devices.
I used the open-source bm6-battery-monitor Python project as the starting point.
Once Bluetooth was working, scanning from the Pi found the monitor:
Address RSSI
3C:AB:72:EB:D0:A2 -56Code language: CSS (css)
And querying it produced exactly what I needed:
Voltage: 12.19v
Temperature: 39C
SoC: 13%Code language: HTTP (http)
That was the important milestone.
I now had battery telemetry without requiring the manufacturer’s app.
A Couple Raspberry Pi Gotchas
Of course, it wasn’t quite that easy.
Bluetooth on my Pi initially refused to scan even though the Bluetooth service itself was running. rfkill revealed that the adapter was simply soft blocked:
Bluetooth
Soft blocked: yes
Hard blocked: no
A quick:
sudo rfkill unblock bluetooth
brought it back to life.
The other annoyance was Python crypto support. The BM6 script expected:
from Crypto.Cipher import AESCode language: JavaScript (javascript)
while the current Raspberry Pi OS package exposed it as:
from Cryptodome.Cipher import AESCode language: JavaScript (javascript)
After that small change, the existing BM6 code worked.
I also eventually abandoned trying to build all of the Python dependencies through pip on the tiny ARMv6 Pi Zero. Debian’s packaged versions of Bleak and PyCryptodome worked perfectly well and saved a lot of unnecessary compilation pain.
Sometimes the best optimization is just not compiling Python dependencies on a Pi Zero.
Publishing the Data to MQTT
Once I could reliably retrieve a reading, I wrapped the BM6 script with a small Python collector.
Every five minutes it connects to the monitor, takes one reading, disconnects, and publishes the results to MQTT:
cars/fusion/battery/voltage
cars/fusion/battery/temperature
cars/fusion/battery/soc
cars/fusion/battery/last_seen
cars/fusion/battery/json
A typical payload looks something like:
{
"vehicle": "fusion",
"voltage": 12.19,
"temperature": 39.0,
"soc": 13,
"last_seen": "2026-09-03T20:46:55+00:00"
}Code language: JSON / JSON with Comments (json)
I intentionally chose a five-minute polling interval.
There isn’t much value in hammering a car battery monitor every few seconds when I’m primarily interested in what happens over hours or days. It also leaves the BM200 disconnected almost all of the time, so I can still open the manufacturer’s iPhone app when I want its more detailed diagnostics.
The collector runs as a systemd service, so the Pi has effectively become an appliance:
Boot
↓
Connect to Wi-Fi
↓
Start BM6 collector
↓
Read every five minutes
↓
Publish to MQTT
If the Pi or collector disappears, its MQTT Last Will also marks the collector offline.
Home Assistant MQTT Discovery
I initially expected the Home Assistant side to involve manually creating several MQTT sensors.
Instead, I added MQTT Discovery messages to the collector.
When it starts, it publishes discovery configurations for:
- Battery voltage
- State of charge
- Temperature
- Last seen
Home Assistant then automatically creates a device called Ford Fusion Battery, with all four entities attached to it.
No Home Assistant YAML required.
The collector is also designed around a small device configuration list, so adding the second car later should amount to adding its Bluetooth address and restarting the service. MQTT Discovery will create another Home Assistant device automatically.
That’s exactly the sort of infrastructure I like: adding another thing shouldn’t mean rebuilding everything around it.
What About Battery State of Charge?
The BM200 also reports a battery state-of-charge percentage.
It’s useful for a dashboard, but I’m not planning to use it as the primary source for alerts.
The monitor isn’t connected to a true battery management system that can directly measure remaining amp-hours. The percentage is an estimate derived by the monitor from the information it has available.
Voltage is the measurement I actually care about.
For example, the Fusion was sitting at:
12.19 V
13% estimated SOCCode language: CSS (css)
which was sufficiently low that the project immediately accomplished its original purpose:
I took the Fusion to an appointment instead of letting it continue sitting in the driveway.
The BM200 Is Smarter Than My MQTT Collector
One unexpected discovery was that the ANCEL hardware itself stores considerably more information than I’m retrieving.
I started the Fusion while my phone wasn’t connected to the monitor. When I later opened the ANCEL app, it downloaded the event from the BM200 and showed:
Minimum cranking voltage: 10.68 V
Cranking duration: 900 msCode language: CSS (css)
It also recovered historical voltage information.
So the BM200 is apparently doing its own continuous monitoring and storing at least some history locally.
My MQTT collector doesn’t attempt to retrieve any of that.
And I’ve decided that’s fine.
The division of responsibility is actually pretty nice:
Home Assistant
├── Long-term five-minute voltage history
├── Temperature
├── SOC estimate
├── Availability / last seen
└── Future alerts
ANCEL App
├── Detailed onboard history
├── Cranking tests
├── Charging tests
└── Diagnostic information
I could spend considerably more time reverse engineering the history-transfer portion of the BLE protocol.
But that would solve a problem I don’t actually have.
The One Problem: Bluetooth Range
There is one major remaining issue.
The Bluetooth range sucks.
The Pi Zero could see the BM200 reasonably well from inside the Fusion, but simply moving the Pi into another car parked directly behind it was enough for the monitor to disappear entirely.
Even an iPhone struggles to communicate with the BM200 unless it’s relatively close to the car.
That’s not particularly surprising when you consider what’s happening: a tiny BLE transmitter is installed underneath a giant metal hood, surrounded by an engine, battery, wiring, and the rest of a car.
The Pi Zero W’s tiny onboard Bluetooth antenna certainly isn’t helping.
My next experiment is therefore a USB Bluetooth adapter with an external antenna. If that can reliably receive both cars from somewhere inside the garage, the project is basically complete.
If not, I’ll have to reconsider collector placement rather than trying to brute-force increasingly terrible RF conditions.
The Actual End Goal
I’m deliberately not trying to make the Raspberry Pi decide whether a battery is healthy.
The Pi gathers data.
Home Assistant decides what to do with it.
Once I have enough real-world history to establish what normal looks like for these cars, I’ll build an automation based on sustained low voltage—probably something along the lines of:
Battery voltage below threshold
│
├── only while car is home
│
├── sustained long enough
│ to ignore temporary dips
│
▼
Send alertCode language: JavaScript (javascript)
Or, translated into the considerably more useful version:
Fusion battery is getting low. Drive the fucking thing.
That’s ultimately the entire reason this project exists.
Final Thoughts
This ended up being exactly the sort of project I enjoy.
The individual components aren’t particularly complicated: a $20-ish battery monitor, an old Raspberry Pi, a little Python, MQTT, and Home Assistant.
The useful part is connecting them.
Instead of periodically remembering to check two cars that don’t get driven very often, I can let a cheap monitor collect the measurement, let the Pi transport it, and let Home Assistant remember for me.
There are still things I could improve. I could reverse engineer the BM200’s historical data, capture cranking events directly, increase the sampling rate, or build battery-health calculations into the collector.
But none of those are necessary to solve the original problem.
I just want my house to tell me which fucking car I need to drive.
And now it can.