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:
- a cell with very little remaining capacity;
- high internal resistance in the cell or its power path;
- incomplete charging or false charge termination;
- abnormal current consumption on the left PCB;
- a voltage-to-percentage calculation error.
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:
- the responding firmware version;
- left and right battery percentages;
- the case percentage;
- a TWS role byte.
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:
- a rapid percentage fall was already indirect evidence of voltage collapse;
- percentage alone could not reveal current, usable capacity or internal resistance;
- exact V1.3.3 code was still unavailable, so raw voltage from the running hardware was needed for a direct result.
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:
- Androguard for the Android DEX call graph and packet builders;
- custom JLFS/SFC parsing, CRC, MD5 and cipher round-trip checks;
- JieLi’s own
pi32v2disassembler for authoritative instruction decoding; - the open-source
ghidra-jieliprocessor module as a secondary cross-check, with its documented incompletepi32v2coverage kept in mind; - custom read-only analyzers and regression tests for tables, branches and OTA state transitions.
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:
- measured charge or discharge current;
- remaining or delivered mAh;
- cycle count;
- state of health;
- direct internal resistance.
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:
- The SPP-connected earbud forwarded the OTA command under a private TWS function ID.
- With the sibling connected, the controller dispatched the same callback locally and remotely.
- The registered OTA handler ignored the local/remote flag, so both earbuds executed their own write, verify and apply stages.
- With the sibling absent, every isolated-left
AA41attempt remained silent and noAA42payload 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:
- exact hashes for the ZIP and its only inner member;
- an exact destructive-operation confirmation;
- a fresh V1.3.3 response;
- both TWS battery fields at or above 80%;
- idle
AA40status 0 and offset 0; AA41status 1 and offset 0;- every ordered
AA42acknowledgement and offset; - final
AA42status 3 beforeAA44was reachable; - final
AA44status 4.
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:
- VBAT: 3.98 V;
- LDO5V: 0;
- DTEMP: 0;
P3_ANA_READ:0x02;P3_CHG_CON0:0x00;P3_CHG_CON1:0x15;- charger disabled, as expected outside the case.
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
- The left earbud has a real, rapid local-voltage collapse under ordinary audio load.
- The stock percentage follows the same VBAT threshold ladder; the display is reporting the collapse rather than causing it.
- The failure is left-specific under the tested host and audio conditions.
- The terminal event is a loss of usable power or power-path operation, not merely a temporary parser or Bluetooth-channel timeout.
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
- The custom run queried Bluetooth approximately every 1.4 seconds, adding radio and CPU activity. Its exact 105-second runtime is therefore not a normal-listening benchmark. The earlier stock run used ten-second queries and still failed at 113 seconds, so query load did not create the underlying fault.
- The right-ear control was qualitative rather than a calibrated A/B test because its exact volume was not recorded.
- Static reverse engineering used the available V1.3.2 image, while the devices initially ran unavailable V1.3.3. Live protocol captures and the successful downgrade closed the relevant OTA and telemetry assumptions, but they did not recover the missing V1.3.3 binary.
- The ADC reports 10 mV units and is not a laboratory battery analyser.
- No current measurement means no defensible mAh, state-of-health or resistance value can be calculated.
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:
- Tribit FlyBuds 3 product page
- Official FlyBuds 3 manual
- FCC filing and exhibits for BTH92SC
- JieLi AC63 public SDK
- JieLi production test-box wired upgrade documentation
ghidra-jieliprocessor module
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.