Work more on Bluetti

This commit is contained in:
2026-06-06 18:23:23 +10:00
parent d532c7da89
commit 8c7b28a82b
5 changed files with 235 additions and 21 deletions
+41 -13
View File
@@ -35,7 +35,7 @@ three; which one you use depends on your model's generation.
| # | Channel | Where | Status | Use for |
|---|---|---|---|---|
| 1 | **`BluettiBLE`** — BLE GATT connection | `src/BluettiBLE.*` | ✅ **Working** (builds, on-hardware-pending per model) | Older/plaintext models — full read **+ control** |
| 2 | **`BluettiADV`** — BLE advertisement broadcast | `src/BluettiADV.*` | **Ready, blocked on key** | Newer encrypted models — read-only, local |
| 2 | **`BluettiADV`** — BLE advertisement broadcast | `src/BluettiADV.*` | 🔑 **Key obtained, on-hardware validation pending** | Newer encrypted models — read-only, local |
| 3 | **Cloud Open API** — HTTPS | `cloud/bluetti.mjs` | ⏳ **Ready, pending account approval** | Any model incl. EL300 — read **+ control**, via cloud |
### 1. `BluettiBLE` — local GATT connection ✅ Working
@@ -45,13 +45,15 @@ plaintext generation** (AC300, AC200M, EB3A, EP500P, …). The library compiles
links; per-model field maps are ported from the reference project and want a
final on-hardware sanity check. See [Quick start](#quick-start).
### 2. `BluettiADV` — local advertisement broadcast ⏳ Ready, blocked on key
### 2. `BluettiADV` — local advertisement broadcast 🔑 Key obtained
Passively decodes the encrypted monitoring broadcast that **newer** units emit
(Elite / V2 / EP600 / AC180 / AC200L …). Read-only, no connection. The decoder is
implemented (AES-128-CTR + the official BLE-ADV field layout) and builds, but it
needs the device's **16-byte AES key**, which Bluetti has not yet exposed in the
app for these models. Ready to run the moment a key is available. See
[Advertisement mode](#advertisement-mode-bluettiadv).
implemented (AES-128-CTR + the official BLE-ADV field layout) and builds. The
device's **16-byte AES key** is now available (exported from the app — see
[Advertisement mode](#advertisement-mode-bluettiadv)); the AdvMonitor example is
wired with it and matches the first broadcaster automatically. Remaining step is an
on-hardware capture to confirm the key/nonce/offsets — validate offline with
`tools/decode.mjs` against a captured packet.
### 3. Cloud Open API — HTTPS ⏳ Ready, pending account approval
For encrypted models (incl. the **EL300**) whose local channels are locked, the
@@ -228,12 +230,38 @@ connection but **broadcast** an encrypted monitoring snapshot in their BLE
advertisements — no connection, no pairing, multiple listeners, low power. This is
the same scheme Victron uses. `BluettiADV` reads it.
You need two things from the owner's side:
You need the **16-byte AES key** (32 hex chars) — from the BLUETTI app or the
device's Webserver (Bluetooth-data / developer section). The export is a small CSV
whose **3rd line is the AES key**; e.g.:
1. The **16-byte AES key** (32 hex chars) — copy it from the BLUETTI app or the
device's Webserver (Bluetooth-data / developer section).
2. The device's **BLE MAC address** — a scanner app shows it; confirm the unit
broadcasts manufacturer data starting `06 0F` (company ID `0x0F06`).
```
bluetti
1780650490894 # timestamp / PIN (unused)
53afcd9aef4ff020f472686c00283ce5 # <-- the 16-byte AES key
6bc305f0… # token (unused for advertisements)
```
The **BLE MAC is optional**: pass `""` and the first Bluetti broadcaster the ESP32
hears is adopted automatically (set a real MAC only if several Bluetti units are
in range). Confirm the unit broadcasts manufacturer data starting `06 0F` (company
ID `0x0F06`) with a scanner, or just run the example with debug on.
> **Validate the key offline first (no hardware):** capture a `raw:060f…` line
> from the AdvMonitor debug output and run
> `node tools/decode.mjs '<paste it>'`. It reads the key from the CSV, AES-CTR
> decrypts the packet, and prints the decoded fields with sanity checks — so you
> know the key works before relying on it.
> **First check the unit actually broadcasts telemetry.** Run AdvMonitor with
> debug on and look at your Bluetti's line:
> - `… mfg:060f… <- BLUETTI TELEMETRY (0x0F06)` → telemetry is being broadcast;
> `BluettiADV` decodes it.
> - `… mfg:424c… <- Bluetti nameplate only, NO telemetry` → the unit advertises a
> static "BLUETT" nameplate (company id `0x4C42`) but **no monitoring payload**.
> Observed on EL300 / EL100 V2: the BLE-ADV broadcast feature isn't enabled on
> that firmware. Look for a "Bluetooth broadcast / open data" toggle in the app;
> if there's none, use the **Cloud Open API** instead. (On these units the
> exported AES key is the GATT secure-channel key, not an advertisement key.)
```cpp
#include <Arduino.h>
@@ -254,8 +282,8 @@ void setup() {
bluetti.begin(5);
bluetti.setDebug(true);
bluetti.setCallback(onAdv);
bluetti.addDevice("My Elite", "AA:BB:CC:DD:EE:FF",
"112233445566778899aabbccddeeff00");
bluetti.addDevice("My Elite", "" /* empty MAC = match first broadcaster */,
"53afcd9aef4ff020f472686c00283ce5");
}
void loop() { bluetti.loop(); }