Improved attempts, still need encyption
This commit is contained in:
@@ -27,6 +27,23 @@ The protocol is ported from the excellent
|
||||
time in round-robin.
|
||||
- Zero external dependencies — uses the stock ESP32 Arduino BLE stack.
|
||||
|
||||
## Two modes: connection vs. advertisement
|
||||
|
||||
Bluetti exposes two different BLE channels, and this library has a class for each:
|
||||
|
||||
| | `BluettiBLE` (connection) | `BluettiADV` (advertisement) |
|
||||
|---|---|---|
|
||||
| Transport | GATT connect + Modbus poll | passive advertisement scan |
|
||||
| Data | full register set + **control** | monitoring snapshot, **read-only** |
|
||||
| Encryption | none (older models only) | **AES-128-CTR**, key from the app |
|
||||
| Works on | AC300, AC200M, EB3A, EP500P, … (older/plaintext generation) | newer **encrypted** generation: Elite / V2 / EP600, etc. |
|
||||
|
||||
If `BluettiBLE` connects but never returns data and the unit disconnects after a
|
||||
few seconds, your device is the newer encrypted generation — use **`BluettiADV`**
|
||||
instead (see [Advertisement mode](#advertisement-mode-bluettiadv) below). The
|
||||
connection/control channel on those models is locked behind a proprietary
|
||||
handshake and is not supported; the advertisement channel is open and documented.
|
||||
|
||||
## Supported devices
|
||||
|
||||
| Model | Enum | Status (from reference project) |
|
||||
@@ -182,11 +199,68 @@ struct BluettiData {
|
||||
Control writes use the same frame with `cmd=0x06` and the value placed
|
||||
big-endian in the length field.
|
||||
|
||||
## Advertisement mode (`BluettiADV`)
|
||||
|
||||
Newer Bluetti units (Elite, *V2, EP600, AC180, AC200L, …) refuse the plaintext
|
||||
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:
|
||||
|
||||
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`).
|
||||
|
||||
```cpp
|
||||
#include <Arduino.h>
|
||||
#include "BluettiADV.h"
|
||||
|
||||
BluettiADV bluetti;
|
||||
|
||||
void onAdv(const BluettiAdvDevice* dev) {
|
||||
if (dev->lastRecordType == BLUETTI_ADV_MONITORING) {
|
||||
const auto& m = dev->monitoring;
|
||||
Serial.printf("SoC %u%% in %u W out %u W\n",
|
||||
m.soc, m.inputPower, m.outputPower);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
bluetti.begin(5);
|
||||
bluetti.setDebug(true);
|
||||
bluetti.setCallback(onAdv);
|
||||
bluetti.addDevice("My Elite", "AA:BB:CC:DD:EE:FF",
|
||||
"112233445566778899aabbccddeeff00");
|
||||
}
|
||||
|
||||
void loop() { bluetti.loop(); }
|
||||
```
|
||||
|
||||
The device cycles through several **record types**; each callback sets
|
||||
`dev->lastRecordType` and refreshes one of the sub-structs:
|
||||
|
||||
- `BLUETTI_ADV_MONITORING` (`0x80`) — SoC, in/out power, charge state, event flags
|
||||
- `BLUETTI_ADV_BATTERY` (`0x02`) — pack voltage, current, temperature, SoC
|
||||
- `BLUETTI_ADV_INVERTER` (`0x0B`) — battery V/A, AC output, PV power, yield
|
||||
- `BLUETTI_ADV_CONFIG` (`0x81`) — modes (inverter, ECO, charging), settings
|
||||
|
||||
This mode is **read-only** — there is no advertisement-based control.
|
||||
|
||||
> Note: the advertisement bit-layout is implemented from Bluetti's official BLE
|
||||
> ADV spec (V1.0, 2025-07-10). The exact AES nonce/counter construction and a few
|
||||
> field offsets are best confirmed against a real packet — run with
|
||||
> `setDebug(true)` (it dumps the decrypted payload) and sanity-check a value or
|
||||
> two. Please report corrections.
|
||||
|
||||
## Examples
|
||||
|
||||
- **BasicRead** — connect to one device and print all values.
|
||||
- **BasicRead** — connect to one (older/plaintext) device and print all values.
|
||||
- **Control** — read, then toggle AC/DC output on a schedule.
|
||||
- **Logger** — print only when values change (snapshot/change-detection pattern).
|
||||
- **AdvMonitor** — read a newer (encrypted) device via `BluettiADV` advertisements.
|
||||
|
||||
## Adding a new model
|
||||
|
||||
|
||||
Reference in New Issue
Block a user