Add support for P40i
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.
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.
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.
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!
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