Article·· 13 min read· AI-assisted

Reverse-engineering FlyBuds 3 to diagnose a 105-second battery collapse

How a 96-byte firmware extension exposed raw battery voltage over Bluetooth and separated a real power failure from a misleading percentage display.

One FlyBuds 3 earbud appeared to charge unusually fast, then died within minutes. The other earbud lasted for hours. The stock interface exposed only a percentage, so the original question was deceptively simple: was the left battery genuinely collapsing, or was the percentage display wrong?

The final test answered the important part. After its charging indication stopped, the isolated left earbud measured 3.98 V at rest. With audio playing at 25% host volume, its local battery reading fell to 3.53 V by 98.963 seconds. The Bluetooth endpoint was gone by 105.069 seconds, and the earbud could not reconnect without being charged again.

That is a real power failure, not a cosmetic battery-percentage bug.

[!WARNING] This is an engineering report, not a flashing guide. The experiment involved an explicitly authorized firmware downgrade and could have permanently disabled both earbuds. The modified proprietary image is not distributed with this article.

What was being tested

Several faults can produce the same user-visible symptom:

The investigation used three confidence levels:

Level Meaning
Confirmed Directly observed in a live capture or exact binary path
Strongly supported Best explanation of the observations, but not uniquely measured
Unresolved The available hardware did not expose the measurement needed to distinguish it

The constraints mattered as much as the goal. The earbuds were not opened, the Tribit Android application was never installed or executed, no manufacturer Windows executable was run, and no firmware bytes were transmitted before explicit destructive authorization. APKs, firmware packages and public SDK sources were analysed offline.

Stage one: establish that the failure was side-specific

The charging case was not a USB diagnostic device

Connecting the retail case to a PC produced power but no useful USB data interface. Windows exposed the earbuds’ normal Bluetooth audio/control services and a private classic-Bluetooth SPP service named JL_SPP. Opening that channel passively returned no stream because it is a request/response protocol, not a debug console.

The usable stock request was command 0x4E. Its response carried:

One subtlety was critical: the two version triplets on the wire were both copied from the responding local image. They were not independent left/right version reports. Separate left-only and right-only requests were therefore required. Those captures confirmed that both physical earbuds ran V1.3.3 before the modification.

Sparse stock discharge tests

With the right earbud in the case and audio on the left, stock 0x4E queries every ten seconds produced this sequence:

Elapsed time Left percentage
2.0 s 80%
10.1–20.1 s 70%
30.0–40.1 s 60%
50.1–60.1 s 50%
70.1 s 40%
80.1–90.1 s 30%
100.0 s 20%
113.1 s no response

When the endpoint disappeared, audio moved back to the laptop speakers. The failure was not limited to the diagnostic channel.

After only a few minutes in the case, the left earbud reported 90% again. Under audio load it fell to 50% in two seconds, 30% in ten seconds, 20% in twenty seconds and became unreachable at roughly 33 seconds.

A qualitative right-ear control used the same laptop and audio source. The exact volume was not recorded, so it was not treated as a laboratory-grade matched load. Even so, the right remained responsive for five minutes and changed only from 100% to 90%. The order-of-magnitude difference localized the fault to the left earbud.

What the stock percentage really represented

Across every inspected firmware image from V1.2.1 through V1.3.2, battery percentage was derived from AD_CH_VBAT through a fixed voltage-threshold ladder. It was not based on a fuel gauge, measured milliamp-hours or a Coulomb counter.

That changed the interpretation of the stock tests:

Recovering and understanding the firmware lineage

The public Tribit update route supplied V1.3.2 while the earbuds reported V1.3.3. An authorized query using a synthetic guest identity with no personal identifiers returned 14 retained FlyBuds 3 update records and 13 unique packages. Embedded versions covered V1.2.1 through V1.3.2; none contained V1.3.3.

Three server records were labelled one patch level above the version embedded in their image. From that point onward, embedded bytes and validated container metadata—not API titles—were treated as the source of truth.

The useful toolchain was layered:

The firmware and public SDK evidence identify a JieLi BR30/AC69-compatible design. The exact commercial part number remains probable rather than proven: package markings are indirect, and AC897N also appears as an SDK/configuration-family label. JieLi’s public AC63 SDK provided strong semantic matches, not proof that Tribit’s private source was byte-identical.

FCC photographs show cells labelled 3.7 V, 0.13 Wh, which is approximately 35 mAh nominal. That is label-derived capacity, not a measurement of present health. The same evidence did not reveal a dedicated fuel-gauge or current-sense device.

The telemetry boundary

Static analysis found useful local measurements and charger state, but no calibrated discharge-current path:

Available Meaning
VBAT Local cell voltage, reported in 10 mV units
LDO5V Charger/case input ADC channel
DTEMP Internal thermal ADC voltage; no validated conversion to °C
P3_ANA_READ Input and charge-termination comparator state
P3_CHG_CON0 Charger and charger-bandgap enable bits
P3_CHG_CON1 Programmed charge-current stage and full-voltage selector

The following were not available:

The 20 mA and 30 mA values found in charger code were programmed charging stages, not measured battery current. Treating them as telemetry would have produced a false diagnosis.

Designing a minimally invasive diagnostic extension

The exact V1.3.2 Bluetooth command router contained unused entries. Request 0x4F was a genuine no-op in the stock application, making it preferable to replacing a working user command.

The final extension used three small hooks and a 96-byte appended routine. It preserved existing executable addresses and kept the original and modified images inside the same sector-aligned 0x6E000 flash footprint. Instead of creating a second packet implementation, it reused the existing 0x54 response builder and transport path. The original 0x54 EQ query continued to follow its normal path; only a preceding 0x4F request selected the diagnostic payload.

The ten-byte schema was:

Offset Field
0 Schema version (2)
1–2 Local VBAT, little-endian, in 10 mV units
3–4 DTEMP ADC voltage
5–6 LDO5V ADC voltage
7 Raw P3_ANA_READ
8 Raw P3_CHG_CON0
9 Raw P3_CHG_CON1

This could expose every battery-relevant state already available to the application. It could not invent a current sensor or Coulomb counter that the hardware did not provide.

Why a left-only Bluetooth update was impossible

The host sent one OTA stream over the active SPP connection:

Command Function
AA40 Query updater state and resume offset
AA41 Start and announce image length
AA42 Transfer chunks of up to 500 bytes
AA44 Verify/apply the completed image

There was no left/right selector in those frames. Exact TWS callback reconstruction explained the live behaviour:

  1. The SPP-connected earbud forwarded the OTA command under a private TWS function ID.
  2. With the sibling connected, the controller dispatched the same callback locally and remotely.
  3. The registered OTA handler ignored the local/remote flag, so both earbuds executed their own write, verify and apply stages.
  4. With the sibling absent, every isolated-left AA41 attempt remained silent and no AA42 payload followed.

The right earbud therefore could not act as a non-writing relay. Stock Bluetooth OTA offered only two topologies: no update with the peer absent, or a bilateral update with the peer present.

The user eventually accepted that either or both earbuds could become unusable and explicitly authorized a bilateral experiment. That changed the permitted target set; it did not change the protocol topology.

Safety gates and the completed OTA

The writer used native Windows RFCOMM rather than the unstable virtual COM layer. Its live mode required:

The accepted package was a V1.3.2-based downgrade. No V1.3.3 restoration image and no sealed-device automatic rollback had been demonstrated. The risk was real and explicitly accepted.

The completed transfer reported:

Result Value
Chunks acknowledged 896 / 896
Firmware bytes acknowledged 447,942 / 447,942
Final AA42 status 3
Final AA44 status 4
Elapsed time 211.979 s

After reboot, the responding endpoint reported V1.3.2 and returned a 19-byte schema-2 response to 0x4F. Later, the physical left was connected alone and independently confirmed the same version and diagnostic schema. The coordinated protocol result strongly supports bilateral installation, but only the left was physically isolated for a post-flash identity check.

The decisive isolated-left experiment

The left earbud was charged until its charging indication stopped. This wording is deliberate: it does not prove that the cell reached full chemical charge. The right remained in the case, and the stock response identified the connected endpoint as role 0, left 90%, right 0%.

Outside the case, before audio, the custom response reported:

Audio then played through the left at 25% host volume. Seventy consecutive samples succeeded. The mean interval was 1.412 seconds and the median was 1.358 seconds.

Elapsed time Local VBAT Stock percentage
Before audio 3.98 V 90%
1.522 s 3.91 V 80%
16.385 s 3.86 V 70%
36.880 s 3.81 V 60%
42.415 s 3.76 V 50%
61.213 s 3.71 V 40%
75.959 s 3.65 V 30%
91.429 s 3.56 V 20%
98.963 s 3.53 V 20%
105.069 s no connection

The complete 70-sample dataset accompanies this article.

Across every valid sample, LDO5V and DTEMP remained zero and the three raw register bytes stayed 02 00 15. The curve was local VBAT outside the case, not a changing charger-input state.

The voltage fell 70 mV between the unloaded baseline and the first loaded sample, 380 mV across the valid loaded series and 450 mV from baseline to the final sample. After the terminal timeout, the earbud could not be powered or reconnected without charging.

What the result proves—and what it does not

Confirmed

Strongly supported

The strongest explanation is extremely low usable cell capacity and/or high resistance in the cell or its internal power path. The quick return to a high reported percentage after a short charge, immediate loaded sag and roughly 105-second shutdown all fit that model.

Still unresolved

Abnormally high left-board current remains possible. Voltage alone follows approximately:

loaded voltage = open-circuit voltage - current × effective resistance

Without measured current, the experiment cannot solve separately for capacity, current or resistance. The 70 mV early drop suggests a load-sensitive path, but it was measured after 1.522 seconds—not as an instantaneous pulse with known current—so it is not a valid direct resistance calculation.

The outside-case test also did not measure LDO5V or charger state while the earbud was physically seated. Incomplete charging or false termination may contribute, although they do not explain why the left delivers so little energy after reporting 90% while the right behaves normally.

Most importantly, the firmware extension diagnosed the failure; it did not repair it. If the root cause is the cell or its connections, replacement is the remedy.

Experimental limitations

Recovery and the charging case

The retail case is not just a passive battery: it has control logic, LEDs and a command relationship with the earbuds. Its documented reset is nevertheless a pairing/TWS state reset, not firmware rollback. The official FlyBuds 3 manual tells the user to delete the old Bluetooth pairing after a reset, which matches address-state deletion rather than application restoration.

A separate factory route exists in principle. JieLi documents a production test box that multiplexes UART over each earbud’s charging-contact pair, supports left/right selection and can retry an interrupted offline side. The wired update procedure and fixture wiring describe that topology. It was not used here: the electrical interface, loader handshake and accepted .ufw/.bfu representation for this exact product were not validated.

Reproducibility record

The principal internal identities were:

Artifact SHA-256
Public V1.3.2 ZIP 8556c676200b7797334d1dd24e5feb726eb7ba3db1d2fe80a51aa9d5a9ab0297
Modified diagnostic ZIP 18b019a1489938a2081a96d784b82a7101b2371ca9bda03629f985d94ee3dd10
Modified inner image 25030b0fd5198557bed41107168ee074edf282593705e51d1f1922bb467d1b3e
Modified decoded image e7da3000c8224ecd4274390596b410973675388296638cc8e4b4a728933ea89e

These hashes identify the experiment; they are not download links. The modified proprietary firmware is intentionally not published.

Primary external references:

Final conclusion

The investigation achieved its non-invasive objective. A custom Bluetooth command exposed every battery-relevant value the known hardware could provide, and the isolated-left run directly measured a collapse from 3.98 V to 3.53 V before shutdown in roughly 105 seconds.

The percentage display was not the fault. The left earbud has almost no usable energy under load. Cell degradation or high effective resistance is the leading diagnosis; abnormal PCB consumption remains the only substantial electrical alternative that cannot be separated without opening the device and measuring current.

Sources

  1. Tribit FlyBuds 3 product page
  2. Official FlyBuds 3 manual
  3. FCC filing and exhibits for BTH92SC
  4. JieLi AC63 public SDK
  5. JieLi wired production-testbox update procedure
  6. ghidra-jieli processor module