Add MX-T40 BLE desktop controller and protocol documentation
Checks / tests (ubuntu-latest, 3.11) (push) Canceled after 0s
Checks / tests (ubuntu-latest, 3.14) (push) Canceled after 0s
Checks / tests (windows-latest, 3.11) (push) Canceled after 0s
Checks / tests (windows-latest, 3.14) (push) Canceled after 0s
Checks / package (push) Canceled after 0s

This commit is contained in:
lda committed 2026-10-25 04:43:47 +07:00 Verified
commit 038e152bbc
38 files changed
+2344

No files matched your search

+58
View File
@@ -0,0 +1,58 @@
# Architecture
The protocol module has no Bluetooth or GUI dependencies. Tk exists only in
`controller/gui.py`, which `cli.py` imports after parsing arguments. Importing
the package or running `--help` does not load Tk or Bleak.
| Module | Responsibility |
| --- | --- |
| `protocol.py` | Packet encoding, stream framing, observed checksum variants |
| `commands.py` | Semantic command builders; no packet layout in Tk callbacks |
| `session.py` | Handshake, outgoing sequence numbers, connection readiness |
| `messages.py` | English command/reply descriptions and bounded activity history |
| `worker.py` | Thread lifecycle and event/command queues |
| `ble_link.py` | Bleak connection, notifications, characteristic writes |
| `link.py`, `transport.py` | Experimental RFCOMM and serial adapters |
| `gui.py` | Tk controls and event rendering |
| `cli.py`, `__main__.py` | Installed command and backwards-compatible module entry |
| `ble_probe.py`, `probe.py` | Endpoint diagnostics |
| `windows_bluetooth.py` | Experimental RFCOMM initialization |
## Session interface
Create one `Session` per connection. Write `session.start()` to begin. Pass
received chunks to `session.receive(chunk)` and write every returned `outgoing`
packet. `Update.frames` carries decoded packets; `became_ready` is emitted once
when the speaker acknowledges the link-complete request. Use
`session.command(payload)` for subsequent outgoing packets.
Both transports cross this same seam. The session owns framing and the state
machine, so fixes do not need to be repeated in separate Bluetooth loops.
A transport connection, a GATT write response, a Samsung packet acknowledgment,
and a speaker state report are distinct events. Only the link-complete ACK
makes the session ready. Speaker notifications have an independent sequence
counter and cannot substitute for that ACK.
## Threads and ownership
Tk runs on the main thread. Each connection worker owns its transport. BLE's
async event loop lives entirely within the worker thread. The GUI submits
payloads through a bounded command queue and consumes events through another
queue; workers never call Tk methods.
BLE notifications enter a bounded async queue and can contain partial or
combined packets. The decoder retains framing state between notifications.
Queue overflow terminates the connection rather than silently losing framing.
Writes are spaced and never automatically retried. Retrying a volume or mute
key could perform the action twice. The GUI enables controls only after the
protocol handshake and disables them on connection closure.
## Testing
Protocol tests use captured byte fixtures independently of the outgoing
encoder. Session tests cover split replies, duplicate replies, and ACK matching.
Transport tests verify subscription order, write mode, disconnects, resource
cleanup, and readable error phases. Presentation tests verify the difference
between acknowledgment and a reported state. No test opens physical Bluetooth.
+32
View File
@@ -0,0 +1,32 @@
# Implemented and potential features
“Verified” below means the owner exercised the control on a physical MX-T40,
with packet reports or direct observation. It does not establish compatibility
with other Samsung models.
| Feature | Status |
| --- | --- |
| BLE connection/status queries | Verified |
| EQ presets | Verified, including Rock/Hip-hop/Standard transitions |
| Bass boost, volume, mute | Verified |
| Lighting presets and RGB color | Verified; RGB activates COLOR mode |
| Play/pause and source selection | Owner reports working; playback echo is an event, not absolute state |
| DJ effects: off, delay, filter, flanger, chorus, wah-wah | Found in Android app; not implemented or tested here |
| Tempo controls | Found in Android app; not implemented or tested here |
| USB playback, repeat/shuffle, browsing and track/time information | Found in Android app; not implemented or tested here |
| Speaker grouping / wireless party controls | Found in Android app; not implemented or tested here |
| Bluetooth multi-connect setting/status | Found in Android app; model compatibility unverified |
| Arbitrary per-band EQ | No Samsung app command established |
## Next work
Keep the working transport stable. Add read-only status decoding before new
controls, then validate one feature at a time on hardware. DJ effects are a
useful next feature because the app contains both effect selection and level
commands. Grouping is more involved because it changes relationships between
speakers and needs more than one device for testing.
The firmware XML is a Qualcomm configuration schema, not a dump of configured
values or a Samsung command dictionary. It documents audio routing/gain curves,
button/LED patterns, codec-related definitions, power policies, and GAIA fields.
Listed schema options do not prove those features are enabled on the MX-T40.
+107
View File
@@ -0,0 +1,107 @@
# Samsung control protocol notes
These original interoperability notes describe observations from an MX-T40 and
analysis of the Android Sound Tower app version 0.0.80. Vendor application code
and firmware are not included. The working desktop transport is BLE; the iOS
app's internal transport/framework has not been established.
## BLE endpoints
| Role | UUID |
| --- | --- |
| Samsung service | `0000ffa0-0000-1000-8000-00805f9b34fb` |
| Write, with response | `0000ffa1-0000-1000-8000-00805f9b34fb` |
| Notify | `0000ffa2-0000-1000-8000-00805f9b34fb` |
Subscribe before sending the connection request. Commands currently exposed by
the GUI fit within 20 bytes; no larger-command fragmentation scheme is claimed.
The speaker also exposes a separate Qualcomm service with UUID
`00001100-d102-11e1-9b23-00025b00a5a5`, which this controller does not use.
MX-T40 model advertisements include FE40/FF40; the Android discovery code also
maps FE41/FF41 to T40 names. An advertisement UUID identifies discovery, not
necessarily a writable control service.
## Framing
```text
00 BB TYPE SEQUENCE LENGTH_HI LENGTH_LO PAYLOAD... CHECKSUM
```
Type 0 is an acknowledgment; type 1 carries data. Length counts payload bytes
only, in big-endian order. Outgoing sequence numbers cycle 1–127, matching the
Android app's signed-byte rollover. Incoming notifications use a separate
sequence counter, observed above 127 as well.
Outgoing checksums sum type, sequence, both length bytes, and payload, modulo
256. Incoming packets have several observed checksum variants:
- Connection/source reports: sum excluding type.
- Sound reports: sum including type.
- Lighting reports `51 03 MODE`: captured packets match a checksum calculated
as though MODE were 1, across Ambient/Dance/Star/Lover/Off observations.
- Play/pause echoes `70 11`: the two captured replies similarly match a key
slot of 1. This compatibility rule is scoped to that specific payload.
Why the firmware emits these variants is unknown. Strict decoding recognizes
these observed rules. BLE uses advisory mode, retaining structurally framed
packets with unfamiliar checksums and flagging them in the activity log. This
is compatibility behavior, not a cryptographic integrity guarantee.
## Handshake
1. Send payload `01`.
2. Receive ACK and connection information beginning `02`.
3. Send payload `04` (link complete).
4. Receive the ACK matching that outgoing sequence; only then enable controls.
5. Query source (`32`) and EQ (`42 03`).
The captured connection info contains model byte `34` and source IDs
`01 02 04 05`: Bluetooth, USB 1, AUX 1, AUX 2. The two bytes following the initial
`02` are interpreted by the Android app as protocol version.
## Payloads
Numbers below are hexadecimal unless explicitly called decimal.
| Action | Payload |
| --- | --- |
| Remote key | `70 KEY` |
| Select source | `30 SOURCE` |
| Query source | `32` |
| Set sound option | `40 TYPE OPTION VALUE EXTRA` |
| Query sound option | `42 TYPE` |
| Set system option | `50 TYPE VALUE` |
| Query system option | `52 TYPE` |
| Set RGB color | `60 02 RED GREEN BLUE` |
Sound TYPE: volume=1, subwoofer=2, EQ=3, bass=4, DJ=5, tempo=6, mute=7.
For the implemented EQ/bass controls OPTION and EXTRA are zero.
Sound replies use `41 TYPE VALUE VALUE2`, a different layout from writes.
EQ values (decimal): Party=1, Standard=2, Hip-hop=3, EDM=4, Rock=5, Latin=6,
House=7, Reggae=8. Bass values: on=0, off=1. Mute reports: on=1, off=2.
System TYPE=3 controls lighting. Mode values (decimal): Off=0, Ambient=1,
Party=2, Dance=3, Thunder=4, Star=5, Lover=6, Color=7. Reply: `51 03 MODE`.
The speaker displays COLOR when the owner chooses an RGB color. The desktop
queries lighting mode after RGB writes rather than treating ACK as mode proof.
Remote keys: volume up=`0F`, volume down=`10`, play/pause=`11`, mute=`14`.
A reply `70 11` is a button-event echo, not an absolute playing/paused state.
## Additional app commands, untested here
The app defines grouping `05 TYPE CONTROL`, USB playback/browsing/status and
repeat controls, DJ selection/levels, tempo, and Bluetooth multi-connect state.
See FEATURES.md for the distinction between discovered and verified behavior.
Do not infer arbitrary EQ-band access from the firmware configuration schema.
## Evidence and limits
Tests contain small, independently captured packet fixtures: connection info,
ACKs, source/EQ replies, lighting checksum variants, and play/pause echoes. Real
Bluetooth addresses are replaced with synthetic ones. Physical verification
covers one MX-T40 on Windows; other models, firmware versions, and platforms
remain unverified. RFCOMM channel 1 worked initially but later failed repeatedly;
no general explanation or guaranteed workaround has been established.