# UART finger service `serial/example_uart.py` processes 1–4 finger cameras or recorded videos and publishes the SDK's normal and shear values over a separate data UART. Python 3.10+, the board's installed `orisys` SDK and `pyserial` are required. Run inside the board environment prepared by `setup/sdk_setup.sh`; install `pyserial` there with `python -m pip install pyserial` if needed. ## Launch from the login console The TTL login console and data UART must use **different device ports**. Start the script in the login shell; connect the data receiver to the port selected by `--port`. A shell/getty or another serial program must not own that data port. The default is `/dev/ttyHS1`, 115200 baud, 8N1, with no flow control. This service does not configure pin multiplexing or enable the board's UART hardware. From the board's `dragon-q6a` checkout: ```bash # Host commands control connection and streaming (default: one camera). python serial/example_uart.py --port /dev/ttyHS1 --config finger # Four configured sources; use 1, 2, 3 or 4 entries as required. python serial/example_uart.py --port /dev/ttyHS1 --sources 0 2 4 6 # A passive receiver needs only to read: connection and streaming start automatically. python serial/example_uart.py --port /dev/ttyHS1 --sources 0 2 --read-only # Start streaming immediately, while still accepting UART commands. python serial/example_uart.py --port /dev/ttyHS1 --sources 0 2 --autostart ``` Use actual camera indices/device paths discovered on the board; `0 2 4 6` is only an example. Slot IDs are fixed by `--sources` order, not camera index. Repeat `--config` once per source for different sensor configurations, or supply one shared configuration. Existing `--camera-id` and `--video` remain available for a single source. `--sources` replaces those options. `--show` requires a local GUI and is off by default. Logs go to the login console, replies to the data UART. In `--read-only` mode, all incoming UART commands are ignored. End the process with Ctrl-C in the login shell. Startup still emits `OK`/`ERR` records; the receiver should select `force,` lines. Without `--autostart` or `--read-only`, a passive reader receives only `OK ready` until a host sends `connect` and `run`. ## Command protocol Commands are UTF-8, case-insensitive, terminated by LF, CR or CRLF. Incomplete lines remain buffered across serial timeouts. Lines exceeding 1024 bytes are discarded through their terminator and produce `ERR command ...` in control mode. Quote file paths containing spaces. Linux device paths are recommended on Q6A. | Command | Behavior | |---|---| | `search` | List cameras, then `OK search N`; use before connecting. Rejected while streaming. | | `connect` | Connect slot 1 using its configured source; in single-source mode, prefer the latest search result for a camera. | | `connect SOURCE` | Legacy form: connect slot 1 to this camera index, device path or file. | | `connect ID SOURCE` | Connect/replace one slot; stop streaming that slot. Other slots keep their connections. | | `connect all` | Connect all configured slots; report each success/failure independently. | | `run [ID\|all]` | Stream the selected slot, or all connected slots when omitted. Requires a connection. | | `read [ID\|all]` | Publish one newly processed measurement per selected slot; defaults to all configured slots. Does not start streaming. | | `stop [ID\|all]` | Stop selected streaming, or all when omitted; keep cameras connected. | | `quit` / `exit` | Exit and release all camera and UART resources. | `connect 2` means **camera index 2 on slot 1**, preserving the old protocol. To connect slot 2, send `connect 2 /dev/video2` or use `connect all`. There are no motor-control commands; these commands control acquisition only. Single-source measurements retain the original format: ```text force,1.250000,-2.500000,3.750000 ``` When configured with multiple sources, measurements include the slot ID even if only one source connected successfully: ```text OK connect 2 /dev/video2 (camera) OK run 1 2 force,1,1.250000,-2.500000,3.750000 force,2,0.500000,0.100000,-0.200000 ERR run 2 processing failed OK eof 1 ``` Force fields are `FNORMAL`, `FSHEARX`, `FSHEARY` in SDK axes. They may be raw or calibrated; **do not assume Newtons**. The Python API exposes SDK units per reading, using `unknown` if an older SDK does not expose them. CSV preserves the original fields and carries no units, timestamps or cross-sensor synchronization. Each processing failure or EOF stops only the affected slot. Processing exceptions produce `ERR`, never fabricated zero force. `--no-loop-video` stops files at EOF; the default tries one rewind, resets the tracker and retries one frame. Failed preparation releases the newly opened runtime; replacing a connection releases the old one before opening the replacement. A failed replacement requires retrying `connect`. Startup continues with any successfully connected slots and fails if none connect. Reconnect and run explicitly after a camera failure. ## Callable acquisition API `serial/finger_api.py` owns acquisition and runtime cleanup; importing it opens no UART or camera. Place that directory on the caller's module search path: ```python from finger_api import FingerSensorBank bank = FingerSensorBank([0, 2], configs=["finger"]) try: bank.connect(1) bank.connect(2) reading = bank.read(1) if reading is not None: print(reading.normal, reading.shear_x, reading.shear_y, reading.units) finally: bank.close() ``` The caller owns one thread and must close the bank. `read()` returns `None` on capture failure/EOF and raises on processing failure. It returns an image and three scalar forces from the same processed frame. One file or camera is read at a time; the API introduces no workers beyond those owned by the SDK. ## Verification and limits Hardware-free regression tests cover command fragmentation, CR/LF, legacy output, 1–4 slot identities, selective control, failure isolation, read-only startup and cleanup. They use in-memory serial and runtime fakes, not physical devices: ```bash python -m unittest discover -s tests -v ``` Q6A UART wiring, installed ARM SDK compatibility, camera bandwidth, sustained frame rates and latency remain unverified. The shipped ARM wheel is compiled `orisys 0.5.0`; local tests do not execute it. SDK capture and processing remain synchronous from this service's perspective. Commands are checked before each round; a slow grab, processing call, connection or camera scan can delay them. Serial writes have a finite timeout (default 1 second), and transport failure terminates the service with cleanup. At 115200 baud/8N1, the data budget is 11,520 bytes/s, shared by all measurements and replies. Measure actual frame rates and stop latency on the board before choosing a higher baud rate.