OpenSCQ30 icon indicating copy to clipboard operation
OpenSCQ30 copied to clipboard

Add support for P40i

Open FokiSensei opened this issue 10 months ago • 4 comments

Arch Llinux, Gnom, Wayland.

I tried to connect my P40i earbuds, but I got an error. There is the log from the terminal.

  2025-03-06T08:28:27.369926Z  WARN  A3936: unknown button action 0xFF, falling back to default value
    at lib/src/devices/a3936/structures.rs:117

  2025-03-06T08:28:27.369941Z  WARN  A3936: unknown button action 0xFF, falling back to default value
    at lib/src/devices/a3936/structures.rs:117

  2025-03-06T08:28:27.369944Z  WARN  A3936: unknown button action 0xA, falling back to default value
    at lib/src/devices/a3936/structures.rs:117

  2025-03-06T08:28:27.369948Z  WARN  A3936: unknown button action 0xFF, falling back to default value
    at lib/src/devices/a3936/structures.rs:117

  2025-03-06T08:28:27.369963Z  WARN  A3936: unknown button action 0xFF, falling back to default value
    at lib/src/devices/a3936/structures.rs:117

  2025-03-06T08:28:27.369966Z  WARN  A3936: unknown button action 0xFF, falling back to default value
    at lib/src/devices/a3936/structures.rs:117

  2025-03-06T08:28:27.369970Z  WARN  A3936: unknown button action 0xFF, falling back to default value
    at lib/src/devices/a3936/structures.rs:117

  2025-03-06T08:28:27.370232Z ERROR  select device

Caused by:
    0: /usr/src/debug/openscq30/OpenSCQ30-1.19.3/lib/src/soundcore_device/device/soundcore_device.rs:96:41: TryIntoInboundPacketError { message: "Error(VerboseError { errors: [([255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Nom(MapOpt)), ([255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Context(\"left double click\")), ([255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Context(\"custom button model\")), ([0, 1, 5, 5, 0, 0, 48, 49, 46, 54, 55, 48, 49, 46, 54, 55, 51, 57, 53, 53, 57, 56, 52, 55, 52, 52, 54, 54, 101, 102, 101, 51, 48, 46, 49, 46, 56, 4, 254, 254, 180, 152, 140, 140, 120, 133, 136, 142, 120, 120, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Context(\"a3945 state update packet\")), ([0, 1, 5, 5, 0, 0, 48, 49, 46, 54, 55, 48, 49, 46, 54, 55, 51, 57, 53, 53, 57, 56, 52, 55, 52, 52, 54, 54, 101, 102, 101, 51, 48, 46, 49, 46, 56, 4, 254, 254, 180, 152, 140, 140, 120, 133, 136, 142, 120, 120, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Nom(Alt))] })" }
    1: Error(VerboseError { errors: [([255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Nom(MapOpt)), ([255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Context("left double click")), ([255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Context("custom button model")), ([0, 1, 5, 5, 0, 0, 48, 49, 46, 54, 55, 48, 49, 46, 54, 55, 51, 57, 53, 53, 57, 56, 52, 55, 52, 52, 54, 54, 101, 102, 101, 51, 48, 46, 49, 46, 56, 4, 254, 254, 180, 152, 140, 140, 120, 133, 136, 142, 120, 120, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Context("a3945 state update packet")), ([0, 1, 5, 5, 0, 0, 48, 49, 46, 54, 55, 48, 49, 46, 54, 55, 51, 57, 53, 53, 57, 56, 52, 55, 52, 52, 54, 54, 101, 102, 101, 51, 48, 46, 49, 46, 56, 4, 254, 254, 180, 152, 140, 140, 120, 133, 136, 142, 120, 120, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 10, 255, 255, 99, 102, 255, 255, 68, 68, 51, 1, 81, 0, 2, 0, 255, 1, 0, 0, 0, 0, 255, 255, 1, 49, 1, 1, 0, 90, 0, 1, 2, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Nom(Alt))] })
    at gui/src/main.rs:288 

Let me know if you need more info.

FokiSensei avatar Mar 06 '25 08:03 FokiSensei

Hey I am interested in working on this but could do with help finding the right bluetooth packets. I've got Wireshark working as per these instructions but cannot identify any relevant settings information.

Image

JessFairbairn avatar Nov 02 '25 17:11 JessFairbairn

Does this filter help? btrfcomm contains 08:ee:00:00:00 || btrfcomm contains 09:ff:00:00:01

Everything will be prefixed with one of those two sets of 5 bytes. Numbers are from here, with the former being from app to earbuds (outbound) and the latter being from earbuds to app (inbound).

Here's an example from some Q20i logs someone sent me a while back. Image

Oppzippy avatar Nov 02 '25 17:11 Oppzippy

Ah I think I've got somewhere, it doesn't automatically send the settings on connection but does when I use .\openscq30_cli-windows-x86_64.exe get transparency-mode (or several of the 'get' commands) in the command line. Your filter works in that case.

Here is an example packet for the p40i:

0010   00 00 01 01 01 a3 00 00 01 05 05 00 01 30 31 2e   .............01.
0020   36 31 30 31 2e 36 31 33 39 35 35 39 38 34 37 34   6101.61395598474
0030   34 36 36 66 35 37 33 30 2e 31 2e 38 03 08 00 96   466f5730.1.8....
0040   8c 64 8c 82 8c 96 96 78 00 ff ff ff ff ff ff ff   .d.....x........
0050   ff ff ff ff 00 ff ff ff ff ff ff ff ff ff 00 ff   ................
0060   ff ff ff ff ff ff ff ff 00 00 00 00 00 00 ff ff   ................
0070   ff ff ff ff ff ff ff 00 ff ff ff ff ff ff ff ff   ................
0080   ff 00 00 00 0a ff ff 63 66 f4 ff 44 44 35 00 51   .......cf..DD5.Q
0090   01 02 01 ff 01 00 00 00 00 ff ff 01 6f 00 01 01   ............o...
00a0   5f 00 01 02 02 01 ff ff ff ff ff ff ff ff ff ff   _...............
00b0   67 89                                             g.

Now hopefully I can begin the slow process of deciphering which byte is which!

JessFairbairn avatar Nov 02 '25 18:11 JessFairbairn

I forgot that I actually started deciphering it already a few months ago. This is what I had done, so you can use it for reference if you want: https://github.com/Oppzippy/OpenSCQ30/blob/master/tools/soundcore-device-faker/devices/a3955.toml

At a glance, the main thing of note is that it uses an unusual button configuration format that is, out of the currently implemented devices, only shared with the a3959: https://github.com/Oppzippy/OpenSCQ30/blob/4b5f9c2811d967da1492f5ccdd4bfde5e0593c67/lib/src/devices/soundcore/a3959.rs#L58-L114

Oppzippy avatar Nov 02 '25 20:11 Oppzippy