diff --git a/README.md b/README.md
index 61e170d..2487da9 100644
--- a/README.md
+++ b/README.md
@@ -20,30 +20,12 @@ Note: Multiple attempts to contact the manufacturer for documentation have recei
With that out of the way, on to the fun stuff!
-## Features
-
-- Control the AOOSTAR WTR MAX and GEM12+ PRO second screen from Linux.
-- Switch the display on or off.
-- Display images (with automatic scaling and partial update support).
-- Proof-of-concept demo for drawing shapes and text.
-- USB device/serial port selection.
-
-
-## Display
-
-Known information:
-
-- **Screen size:** 2.86" ≈ 68 × 27 mm
-- **Resolution:** 960 × 376
-- **Manufacturer:** Synwit
-- **Connected over USB UART** with a proprietary serial communication protocol:
- - **USB device ID:** `416:90A1` (as shown by `lsusb`)
- - **Linux device (example on Debian):** `/dev/ttyACM0`
- - **1,500,000 baud**, 8N1 (likely ignored; actual USB transfer speed is much higher)
-
+**See [Linux shell commands](doc/shell_commands.md) on how to switch off the display with standard Linux commands!**
## Reverse Engineering
+Reverse engineered LCD commands: [doc/lcd_protocol.md](doc/lcd_protocol.md)
+
### Motivation
Developing open client software to use the embedded second screen on various Linux distributions.
@@ -62,9 +44,9 @@ The display remains on continuously (24×7) if the official software is not runn
### Goals
-- [ ] Reverse engineer the LCD serial protocol to provide open screen software.
+- [x] Reverse engineer the LCD serial protocol to provide open screen software.
- Utilize the official AOOSTAR-X display software by sniffing USB communication, using `strace`, and decompiling the Python app.
-- [ ] Document known commands so clients in other programming languages can be written.
+- [x] Document known commands so clients in other programming languages can be written.
- [ ] Eventually, create a Rust crate for easy integration into other Rust applications.
**Out of scope:**
@@ -73,33 +55,13 @@ The display remains on continuously (24×7) if the official software is not runn
That would be an interesting task — potentially uncovering additional display commands — but is outside the project's current scope.
- Reimplementing the full AOOSTAR-X display software, which is overly complex for most use cases.
-## Linux Shell Control Commands
+### Features
-Turning the display on or off is possible directly in a Linux shell!
-
-Add your user to the `dialout` group for access to `/dev/ttyACM0`:
-
-```shell
-sudo usermod -a -G dialout $USER
-```
-
-> You may have to log out and back in for group changes to take effect.
-> If not using a Debian based Linux, the tty device might have a different name, or not using the `dialout` group.
-
-
-### Turn display on
-
-```shell
-stty -F /dev/ttyACM0 raw
-printf "\252U\252U\v\0\0\0" > /dev/ttyACM0
-```
-
-### Turn display off
-
-```shell
-stty -F /dev/ttyACM0 raw
-printf "\252U\252U\12\0\0\0" > /dev/ttyACM0
-```
+- Control the AOOSTAR WTR MAX and GEM12+ PRO second screen from Linux.
+- Switch the display on or off.
+- Display images (with automatic scaling and partial update support).
+- Proof-of-concept demo for drawing shapes and text.
+- USB device/serial port selection.
## Setup
@@ -180,27 +142,6 @@ asterctl --image img/aybabtu.png
This expects a 960 × 376 image (other sizes are automatically scaled and the aspect ratio is ignored).
See Rust image crate for [supported image formats](https://github.com/image-rs/image?tab=readme-ov-file#supported-image-formats).
-## Development
-
-- When sending an image to the screen, the image must be in **RGB565** format (16 bits per pixel).
- - All graphic operations are performed on the loaded RGB888 image buffer.
- - The image is automatically converted to RGB565 when sending it to the display.
-- The 1.5 Mbps baud rate set in the client is ignored, as actual USB bulk transfer achieves much higher throughput.
-For reference, at the nominal serial rate (~1,500,000 baud), it would take approximately 6 seconds to transfer a full image of 721,920 bytes (960 × 376 × 2):
- - Display protocol: payload per chunk = 47 bytes; header per chunk = 12 bytes
- - Number of chunks: 721,920 / 47 ≈ 15,360 chunks
- - Total transmitted data: 15,360 chunks × 59 bytes/chunk = 906,240 bytes
- - Serial frame format: 1 start bit + 8 data bits + 1 stop bit = 10 bits/byte
- - Effective byte rate: 1,500,000 bits/sec / 10 bits/byte = 150,000 bytes/sec
- - Transfer time: 906,240 bytes / 150,000 bytes/sec ≈ 6 seconds
-- **Performance:**
- - Displaying the first fullscreen image takes around 1.3 seconds.
- - When switching the display on, the old image is immediately shown.
- - Once the new image is fully transferred and the end-header command is sent, the display firmware switches to the new image.
-- **Partial Updates:**
- - A frame cache is used to send only changed chunks after the initial image is displayed, greatly speeding up partial screen updates.
- - The chunk size is 47 bytes, determined from the original app. It is unknown if other chunk sizes are supported.
-
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
diff --git a/doc/img/lcd_off.png b/doc/img/lcd_off.png
new file mode 100644
index 0000000..a5ce2cf
Binary files /dev/null and b/doc/img/lcd_off.png differ
diff --git a/doc/img/lcd_on.png b/doc/img/lcd_on.png
new file mode 100644
index 0000000..3099565
Binary files /dev/null and b/doc/img/lcd_on.png differ
diff --git a/doc/img/send_image.png b/doc/img/send_image.png
new file mode 100644
index 0000000..aada995
Binary files /dev/null and b/doc/img/send_image.png differ
diff --git a/doc/lcd_protocol.md b/doc/lcd_protocol.md
new file mode 100644
index 0000000..28dd8a0
--- /dev/null
+++ b/doc/lcd_protocol.md
@@ -0,0 +1,99 @@
+# LCD Protocol
+
+This page contains the current state of the reverse engineered AOOSTAR display protocol.
+
+See [Linux shell commands](shell_commands.md) how you can switch the display on and off with standard Linux commands.
+
+- **Resolution:** 960 × 376
+- **Manufacturer:** Synwit
+- **Connected over USB UART** with a proprietary serial communication protocol:
+ - **USB device ID:** `416:90A1` (as shown by `lsusb`)
+ - **Linux device (example on Debian):** `/dev/ttyACM0`
+ - **1,500,000 baud**, 8N1 (likely ignored; actual USB transfer speed is much higher)
+
+## Display Off
+
+**Request:**
+
+
+
+
+```
+@startebnf lcd_off
+lcd_off = 0xAA, 0x55, 0xAA, 0x55, 0x0A, 0x00, 0x00, 0x00 ;
+@endebnf
+```
+
+
+
+**Response:**
+- Success: character `A`
+- Error: _unknown_
+
+## Display On
+
+**Request:**
+
+
+
+
+```
+@startebnf lcd_on
+lcd_on = 0xAA, 0x55, 0xAA, 0x55, 0x0B, 0x00, 0x00, 0x00 ;
+@endebnf
+```
+
+
+
+**Response:**
+- Success: character `A`
+- Error: _unknown_
+
+Note:
+- When switching the display on, the last displayed image is immediately shown.
+
+## Display Image
+
+**Request:**
+
+
+
+
+```
+@startebnf send_image
+send_image = img_cmd_start, { data_chunk }, img_cmd_end ;
+
+img_cmd_start = 0xAA, 0x55, 0xAA, 0x55, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x2F, 0x00, 0x04, 0x0B, 0x00 ;
+data_chunk = chunk_header, chunk_offset, rgb565_chunk ;
+chunk_header = 0xAA, 0x55, 0xAA, 0x55, 0x08, 0x00, 0x00, 0x00 ;
+chunk_offset = ? 32 bit offset in little-endian format ? ;
+rgb565_chunk = 47 * ? byte image data in RGB565 format from given index ?;
+img_cmd_end = 0xAA, 0x55, 0xAA, 0x55, 0x06, 0x00, 0x00, 0x00 ;
+@endebnf
+```
+
+
+
+**Response:**
+- Success: character `A`
+- Error: _unknown_
+
+Notes:
+- When sending an image to the screen, the image must be in **RGB565** format (16 bits per pixel).
+ - `asterctl` performs all graphic operations on an RGB888 image buffer.
+ - `asterctl` automatically converts the image to RGB565 when sending it to the display.
+- The 1.5 Mbps baud rate set in the client is ignored, as actual USB bulk transfer achieves much higher throughput.
+ For reference, at the nominal serial rate (~1,500,000 baud), it would take approximately 6 seconds to transfer a full image of 721,920 bytes (960 × 376 × 2):
+ - Display protocol: payload per chunk = 47 bytes; header per chunk = 12 bytes
+ - Number of chunks: 721,920 / 47 ≈ 15,360 chunks
+ - Total transmitted data: 15,360 chunks × 59 bytes/chunk = 906,240 bytes
+ - Serial frame format: 1 start bit + 8 data bits + 1 stop bit = 10 bits/byte
+ - Effective byte rate: 1,500,000 bits/sec / 10 bits/byte = 150,000 bytes/sec
+ - Transfer time: 906,240 bytes / 150,000 bytes/sec ≈ 6 seconds
+- **Performance:**
+ - Displaying the first fullscreen image takes around 1.3 seconds.
+ - Once the new image is fully transferred and the end-header command is sent, the display firmware switches to the new image.
+- **Partial Updates:**
+ - `asterctl` uses a frame cache to send only changed chunks after the initial image is displayed, greatly speeding up partial screen updates.
+ - The chunk size is 47 bytes, determined from the original app. It is unknown if other chunk sizes are supported.
+ - There are no fractional chunks: 960x376 x 2 bytes/pixel / 47 bytes/chunk = 15360 chunks
diff --git a/doc/shell_commands.md b/doc/shell_commands.md
new file mode 100644
index 0000000..af46a8f
--- /dev/null
+++ b/doc/shell_commands.md
@@ -0,0 +1,27 @@
+# Linux Shell Control Commands
+
+Turning the display on or off is possible directly in a Linux shell!
+
+Add your user to the `dialout` group for access to `/dev/ttyACM0`:
+
+```shell
+sudo usermod -a -G dialout $USER
+```
+
+> You may have to log out and back in for group changes to take effect.
+> If not using a Debian based Linux, the tty device might have a different name, or not using the `dialout` group.
+
+
+## Turn display on
+
+```shell
+stty -F /dev/ttyACM0 raw
+printf "\252U\252U\v\0\0\0" > /dev/ttyACM0
+```
+
+## Turn display off
+
+```shell
+stty -F /dev/ttyACM0 raw
+printf "\252U\252U\12\0\0\0" > /dev/ttyACM0
+```