The DJI went silent for four hours
Contents
Part 4 ended on a rule for the observer, the Go service on my router that turns two power stations' readings into NUT (Network UPS Tools) status for the machines they keep running: a DJI that stops answering for minutes is an alert in its own right, never a sign that the grid is fine. On 20 October the DJI Power 2000 stopped answering at about 13:10. It answered again a little before 18:00, after I power-cycled it. While the grid was up, nothing alerted me to the silence.
The rule had been built. It was waiting for a different kind of silence.
The bridge kept talking, the station didn't#
Both stations are read by one ESP32-S3 bridge over Bluetooth LE, which publishes a reading for each to MQTT every 20 seconds. From about 13:10 every DJI poll ended with station_not_found. The AC180, the second station, a BLUETTI, found in the same scan by the same board, kept reporting normally, so the board, its Wi-Fi and the broker were fine.
The first suspect was the limit from part 4: the DJI accepts one Bluetooth central at a time, and DJI Home on a phone can take the slot. The app was closed. A passive scan from a PC, bluetoothctl --timeout 15 scan on, didn't see the station either. Nothing I knew of held a connection to it, and it wasn't advertising. As far as Bluetooth was concerned, the station wasn't there.
The station itself had nothing to say about it. Its display has no Bluetooth icon even when BLE works, and DJI Home has no Bluetooth or Wi-Fi toggle, so neither one is a check.
Fresh errors are not silence#
The alert from part 7 covers a bridge that goes quiet. This bridge never went quiet. Every 20 seconds it published a fresh reading that said ok:false and gave the error. The observer drops any reading that isn't OK before it touches its state:
if retained || !r.OK {
return ""
}
So NUT kept serving the DJI's charge and runtime from 13:10 as if they were current, and the only sign was observer.data_age, which stood at 15938 seconds, about 4.4 hours, in the reading I have. On mains, a stale charge figure does no harm. It just waits for an outage.
The grid went while the DJI was blind#
The outage began a little after 17:20. With no DJI data, the observer fell back on the grid canaries from part 5: a smart plug that measures mains voltage and a lamp, and the grid counts as down only when both are. The DJI's first tier went to low battery on the canaries alone, and the gaming PC, the machine on that tier, shut down cleanly at 17:43. No fresh DJI reading went into that decision, which is what the canaries were for.
About 17:55 I power-cycled the DJI, and moved it closer to the bridge while I was at it. Bluetooth came back at once, and the bridge now sees it at −35 to −45 dBm, against −61 to −70 in part 4. I changed two things at once, so I can't say which one fixed it.
The ha-dji-power-ble issue tracker has the same symptom. In issue #32, a Power 2000 with its display and Wi-Fi icon on vanished from every Bluetooth scanner. The maintainer suggested a full power cycle: AC unplugged, power button held until the screen is completely dark, then on again. It worked, and the station advertised again right away. For the reporter's second unit, reloading the adapters, the ESPHome proxy and the integration hadn't helped; a power cycle had. The same reporter had seen the station reject its login with status 03 before it disappeared, and the power cycle cleared that too. In issue #33, about a status 03 rejection, another owner whose Power 2000 had dropped off with "no recent advertisement" got it back with a full power cycle after "months" of uptime. The maintainer suspects a firmware bug that hits after about 49.7 days of uptime, which is 2³² milliseconds, and says it isn't confirmed. My station was two weeks old. Whatever this was, it wasn't that counter.
Four thousand logins a day#
My suspect is connect churn. The bridge in part 4 held no link: every 20 seconds it scanned, connected, logged in, read one report and disconnected. That is 4,320 connections a day. It also never acknowledged anything. The station's 0x62 config pushes can ask for an acknowledgement, and the protocol notes say the integration answers those with payload 01, the same sequence and command, and the response flag 0x80. The integration also keeps one connection open instead of reconnecting. Nobody has shown that churn kills the station's advertising. It was the biggest difference between my bridge and the integration.
The firmware changed the same day to work like the integration. The bridge connects, subscribes and logs in once, then keeps the link. Notifications go into a queue. 0x61 reports update a live reading. 0x62 pushes that ask for it get the acknowledgement:
if ((k.flags & 0x60) && djiWr) {
uint8_t ok = 0x01;
auto f = duml_frame(k.seq, 0x80, DJI_CONFIG, &ok, 1);
djiWr->writeValue(f.data(), f.size(), true);
}
Each poll now sends one GET (command 0x60, key 0x00) for the record that holds the grid flag, and takes the newest report. A link that delivers nothing for three polls in a row gets dropped and rebuilt:
if (rep || grid) { djiStale = 0; return; }
out.error = "no_data";
if (++djiStale >= 3) dji_drop("no data for 3 polls");
One side effect undoes my own diagnosis. While the bridge holds the link, the station doesn't advertise, so a PC scan that can't see it is now normal, and the check I used on 20 October no longer tells me anything. DJI Home can only reach the station over Wi-Fi or the cloud now.
A four-hour-old "on line" came back mid-outage#
The outage wasn't over when the station came back, and the bridge's return exposed a bug in the observer. Its first fresh reading arrived at 17:56:21. The observer then logged "grid back for 5m0s, tier LB cleared", served the tier as on line for 15 seconds, then logged a fresh "grid lost" and a second "LOW BATTERY".
The cause was in how the observer handled being blind. While the bridge had no data, the hosts were served the canaries' view, on battery, but the station's own stored state stayed what the bridge last said at 13:10: on line. The first fresh reading made the observer trust its stored state again before the debounce had seen the new readings. That state said on line since 13:10, which more than satisfied the five-minute grid-back rule, so the low-battery latch cleared on the spot. Fifteen seconds later the debounce caught up with reality.
A host in the middle of a shutdown countdown would have cancelled it. The gaming PC was already off, so this one cost nothing.
The fix runs on the first reading after a blind spell. If the hosts were being served a canary override, that override becomes the station's state, keeping the time the canary went down, and the new readings continue from there:
p, since, ok := s.canaryOverride()
if !ok {
return
}
if p == PowerOB {
s.toOB(since)
} else {
s.power, s.powerSince = PowerOL, since
s.haveRtC = false
}
Two tests replay the sequence, and both failed before the fix. It went live at midnight, at the start of 21 October.
Twelve days without a DJI dropout#
From 21 October to 2 November, twelve days, the DJI didn't drop out once on its own. There was one stale window, from 02:01 to 03:50 on 1 November, but it hit both stations at once, so it came from the bridge or the network, not the DJI. I haven't found its cause. Twelve quiet days fit the churn theory. They don't prove it.
That stale window also showed which half of the alerting works. Five minutes in, the observer sent ALERT station dji2000: no fresh data for 5m1s while on grid (canary up). The case from 20 October, a bridge that publishes fresh ok:false readings for hours, still raises nothing, and NUT would still serve a charge from four hours ago as current. Both are on the list. Until they're done, a bridge that keeps reporting ok:false while the grid is up tells me nothing.
Filed under: esp32, bluetooth, ups, power, mqtt, go
Read next#
- The UPS said 2 minutes at 95 % — the next false low-battery alarm, from a station field that changes meaning while it recharges.
- The DJI's grid flag is labeled charge type — the connect-per-poll bridge this replaces, and the one-central limit.
- A null battery reading cleared the low-battery alarm — the observer, its staleness rules and the two canaries that carried this outage.
Sources#
- ha-dji-power-ble issue #32 — a Power 2000 that rejected its login with status
03, then stopped advertising; a full power cycle cleared both, adapter and integration reloads didn't help a second unit; the 49.7-day suspicion. - ha-dji-power-ble issue #33 — status
03login rejections; in the thread, a station with no recent advertisement brought back by a power cycle after months of uptime. - ha-dji-power-ble protocol notes — the
0x60GET,0x62pushes, and the acknowledgement with payload01and flag0x80. - zuyan9/ha-dji-power-ble — the Home Assistant integration whose one-connection, acknowledge-the-pushes behaviour the bridge now copies.