AdvancedController - remote control over RS485. Protocol description and integration ===================================================================================== Firmware version: r7.2.6. Russian version: REMOTE_API_RU.txt. 1. PHYSICAL LAYER ----------------- Controller port: INPUT (RS485, the connector next to the power connector). Inside: USART2, PD5 TX, PD6 RX, PD7 = transceiver direction. Line settings: 9600 baud, 8 data bits, no parity, 1 stop bit (9600 8N1). Half duplex, two-wire A/B line. On the PC side: a USB-RS485 adapter with automatic direction control (virtual COM port). 120 ohm termination at the ends of a long line as usual for RS485. Only the PC must be on this bus: the controller never transmits on its own, it only answers commands. 2. FRAME AND EXCHANGE RULES --------------------------- Request from the PC: CMD [ARG1 [ARG2]] - ASCII text, case-insensitive (br 12 = BR 12); - arguments separated by a space; terminator CR, LF or CR+LF; - at most 64 characters without the terminator. Reply of the controller to EVERY request: OK command executed OK command executed, data follows (VER, STATUS) ERR command not executed (section 5) Stop-and-wait: the PC sends the next command only after the reply arrived (or the timeout expired). The controller handles one line per pass of its main loop; lines that arrive earlier wait in a 256-byte buffer. Line turnaround: after a reply wait at least 5 ms before the next request. The controller switches its RS485 transceiver back to receiving about 2 ms after the end of its reply; bytes sent earlier are lost and the controller answers ERR 1 UNKNOWN or ERR 5 CHECKSUM to the damaged line. RemoteClient (lib/remoteclient.cpp) keeps this pause by itself. Timeouts on the PC side (recommendation): ordinary commands 0.5 s PRESET LOAD/SAVE, KEY LOAD*/SAVE* 3 s KEY XH1/XH2, KEY UP/DOWN/LEFT/RIGHT while a crosshair is being edited 2-3 s RESET, FOCUS, KEY CAMRESET, KEY FOCUS 10 s The reply to a long command comes AFTER it has completed (RESET ~6 s, FOCUS ~5 s). While a long command runs the controller does not parse new lines; they are not lost and are handled afterwards (replies arrive late). Late reply: when the PC gave up waiting (timeout) the reply still comes later and would be taken for the reply to the next request. Drop such lines before the next request (RemoteClient does it by itself: after a timeout it waits for 0.3 s of silence and clears the receive buffer). Retries: when no reply arrives, repeat only idempotent commands (PING, VER, STATUS, CAM, BR, AGC, ZOOM, FLIP, MIR, TEMP, LED). Never repeat KEY, RESET, FOCUS or PRESET automatically - a repetition repeats the action. Resynchronisation: a pause of more than 100 ms between the bytes of a line drops the partial line without a reply. A line longer than 64 characters is answered with ERR 4 after its terminator. Checksum (optional): a request may end with "*hh", hh being the XOR of all characters before '*' in hexadecimal (NMEA style): PING*10 -> OK PONG ADVCTRL*48 When the request carries a checksum the controller verifies it (mismatch -> ERR 5) and appends a checksum to the reply. Without '*' there is no checksum in either direction. 3. COMMANDS ----------- All commands with values act on the CURRENT camera (see CAM and STATUS). Command Arguments Action / reply ------------------- ----------------- ------------------------------------------- PING - OK PONG ADVCTRL. Link check and controller detection (signature "PONG ADVCTRL"). VER - OK r7.2.6. Firmware version. ID - OK ADVCTRL FW=r7.2.6 CAMS=4 UID=<24 hex>. Identification: firmware, number of cameras, unique MCU id. STATUS - OK CAM=.. BR=.. ... (section 4). LEDS - OK <6 hex>. State of the 24 keyboard leds as the device shows them (section 4a). CAM n 1..4 Select camera n (like the CAM SELECT button, the selection leds follow). BR n 0..20 Camera brightness. AGC n 0..20 Camera AGC. ZOOM n 0..16 Camera digital zoom. FLIP 0|1 - Image flip off/on. MIR 0|1 - Mirror off/on. TEMP 0|1 - Temperature on the camera OSD off/on. LED n 0..3 Backlight level: 0 off, 1 = 25 %, 2 = 75 %, 3 = 100 %. Ends AUTO mode. LED AUTO 0|1 - Automatic backlight from the 0-10 V input off/on (all channels). PRESET LOAD 1|2 - Load a user preset of the camera. PRESET SAVE 1|2 - Save the camera settings to a preset. RESET - Default settings to all cameras (CAM RESET button, ~6 s). FOCUS - Crosshair initialisation of the current camera (FOCUS button, ~5 s). KEY section 6 Virtual keyboard button. IDENTIFY - The gear led blinks fast for 3 s - to see which controller the PC talks to. Example session (terminal, 9600 8N1, CR+LF): > PING < OK PONG ADVCTRL > VER < OK r7.2.6 > ID < OK ADVCTRL FW=r7.2.6 CAMS=4 UID=3A0032000E51383433353839 > CAM 2 < OK > BR 12 < OK > STATUS < OK CAM=2 BR=12 AGC=4 ZOOM=0 FLIP=0 MIR=0 TOSD=0 LED=3 AUTO=0 CT=41 PT=36 LT=G XH1=0 XH2=0 CT2=40 > BR 99 < ERR 3 RANGE > HELLO < ERR 1 UNKNOWN 4. STATUS FIELDS ---------------- CAM current camera 1..4 BR brightness 0..20 AGC 0..20 ZOOM 0..16 FLIP 0/1 MIR 0/1 TOSD temperature on OSD 0/1 LED backlight level 0..3 (manual) AUTO automatic backlight 0/1 CT temperature of camera 1, C (sent by the camera; 25 until the first message) CT2 temperature of camera 2, C (last field of the STATUS line) PT temperature of the controller CPU, C (internal sensor, approximate) LT light tower: G - normal (green), R - camera overheated (red) XH1, XH2 crosshair 1 / 2 shown 0/1 BR/AGC/ZOOM/FLIP/MIR/TOSD/LED are stored in the controller per camera and reflect both PC commands and keyboard presses. The camera's own values after PRESET LOAD are unknown to the controller (camera limitation): after loading a preset set the wanted values explicitly with BR/AGC/ZOOM. 4a. LEDS - LED BITS ------------------- The reply is a 24-bit mask as 6 hexadecimal digits (bit = 1: led on). 0 TEMP 1 XH2 2 XH1 3 SAVE2 4 SAVE1 5 LOAD2 6 LOAD1 7 CAMMENU 8 FOCUS 9 FLIP 10 MIRROR 11 ZOOM 12 CAM1SEL 13 UP 14 DOWN 15 RIGHT 16 LEFT 17 ENTER 18 BRIGHT 19 AGC 20 LEDAUTO 21 LEDON 22 SYSRESET 23 CAM2SEL The virtual panel (bin/AdvancedControllerPanel) polls LEDS 5 times a second and mirrors the device indication. CAM1SEL/CAM2SEL are the two leds of the camera-select button: camera 1 = 1/0, 2 = 0/1, 3 = 1/1, 4 = 0/0. 5. ERROR CODES -------------- ERR 1 UNKNOWN unknown command ERR 2 ARG wrong number or format of arguments ERR 3 RANGE argument out of range ERR 4 TOOLONG line longer than 64 characters ERR 5 CHECKSUM checksum mismatch 6. VIRTUAL BUTTONS (KEY) ------------------------ KEY reproduces a press of the membrane keyboard and goes through the same handler as the real button: same modes, leds and interlocks. OK is returned after the press has been executed. ENTER LONGENTER (long press - open the camera OSD menu) UP DOWN LEFT RIGHT arrows (act in the active mode) FLIP MIRROR toggle flip / mirror ZOOM BRIGHT AGC enter/leave the arrow-adjust mode XH1 XH2 show/hide crosshair 1 / 2 XH1LONG XH2LONG crosshair edit mode (arrows move the lines) CAMRESET reset of all cameras (= RESET); the "three sliders" button, its led blinks during the reset TEMP temperature on the camera OSD on/off LEDON manual backlight mode on/off (arrows change the level) LEDAUTO automatic backlight on/off LOAD1 LOAD2 SAVE1 SAVE2 camera presets SYSRESET leave all active adjust modes; the gear button (its led = remote-control sign, section 7) CAMSEL next camera (1->2->3->4->1) FOCUS crosshair initialisation (= FOCUS) Modality: ZOOM, BRIGHT, AGC, LEDON, XH1LONG, XH2LONG and LONGENTER enter a mode in which UP/DOWN/LEFT/RIGHT change the corresponding parameter. Only one adjust mode is active at a time. PC software should prefer the direct commands (BR, AGC, ZOOM, LED, CAM) and use KEY for what has no direct command (crosshairs, OSD menu). 7. BEHAVIOUR AND LIMITS ----------------------- - The controller keyboard keeps working next to the PC. A real press wins: a virtual press is executed in the next loop pass. - Changes made with the buttons are visible to the PC through STATUS. - Camera 4 is connected to the controller by its backlight (PWM) only; camera commands with camera 4 selected go nowhere (the INPUT port is the PC's). - During RESET/FOCUS the controller is busy for up to 6 s; the watchdog does not trip. - The controller sends nothing on its own. Poll STATUS, e.g. once per second, to display the state. - Operator indication: while commands keep coming from the PC the gear-button led on the device blinks twice with a 2 s pause; it goes off 5 s after the last command. The firmware does this; the PC software does nothing for it (bit 22 SYSRESET in LEDS = the gear led; bit 7 CAMMENU is the led of the "three sliders" button). The IDENTIFY command makes the same led blink fast for 3 s (100 ms on / 100 ms off) - that is how one finds out which of several controllers is connected to a given port; the normal indication resumes afterwards. 8. INTEGRATION GUIDELINES FOR CLIENT SOFTWARE --------------------------------------------- 1. One thread (or one queue) per port: strictly stop-and-wait, at least 5 ms between the end of a reply and the next request (section 2). 2. After opening the port: wait 100 ms, clear the input buffer, then CONTROLLER DETECTION: PING with a 0.3 s timeout, up to 2 attempts. A reply starting with "OK PONG ADVCTRL" means "controller detected"; any other reply or silence means "controller not detected" (another device or nothing on that port). After the detection: ID (firmware version, number of cameras, MCU UID - it tells several controllers apart). Finding the right port among many: run the same procedure on every COM port (RemoteClient::detect(), RemoteClient::findController(); remote_test --scan / --detect). 3. Wait for each command up to the timeout of section 2; ignore lines that do not start with OK/ERR (link test, garbage at power-on). 4. Repeat only idempotent commands on timeout (list in section 2); drop the late reply before the repeat (section 2). 5. ERR is not a link failure: show it to the user and continue. 6. Synchronise the PC user interface from STATUS (once per second or after each command), not from an assumed state. 7. No reply to 3 consecutive PINGs = link lost: reopen the port. 8. Enable the checksum in industrial environments (long line, noise). C++ / Qt 6 - blocking client (lib/remoteclient.h), for a console program or a worker thread: #include "remoteclient.h" AdvCtrl::RemoteClient rc; if (!rc.open("/dev/ttyUSB0")) { qWarning() << rc.errorString(); return; } if (rc.detect()) { AdvCtrl::Fields id; if (rc.identify(&id)) qDebug() << id.value("FW") << id.value("UID"); } rc.cam(2); rc.brightness(12); rc.led(3); AdvCtrl::Fields st; if (rc.status(&st)) qDebug() << "BR =" << st.value("BR").toInt(); rc.key("XH1"); // show crosshair 1 if (!rc.brightness(99)) // the controller answers ERR 3 RANGE qDebug() << "controller said" << rc.lastReply().code << rc.lastReply().text; C++ / Qt 6 - GUI application (lib/controllerlink.h): the client runs in its own thread, the results arrive as signals in the GUI thread: #include "controllerlink.h" auto *link = new AdvCtrl::ControllerLink(this); connect(link, &AdvCtrl::ControllerLink::stateChanged, this, [](AdvCtrl::ControllerLink::State s) { /* Closed, NoLink, Link */ }); connect(link, &AdvCtrl::ControllerLink::reply, this, [](const QString &command, const QString &result) { /* "OK", "ERR 3 RANGE", "!! ..." */ }); connect(link, &AdvCtrl::ControllerLink::leds, this, [](quint32 mask) { /* 24 led bits */ }); link->open("/dev/ttyUSB0"); link->send("CAM 2"); link->send("BR 12"); The virtual panel (panel/deviceview.cpp) is a complete example of ControllerLink. Build: in your CMake project find Qt (find_package(Qt6 REQUIRED COMPONENTS Core SerialPort) and qt_standard_project_setup()), then add_subdirectory(/lib) and target_link_libraries( PRIVATE AdvancedControllerRemote) - or add the four files of lib/ to your target. C# example (System.IO.Ports.SerialPort): var sp = new SerialPort("/dev/ttyUSB0", 9600, Parity.None, 8, StopBits.One); sp.NewLine = "\r\n"; sp.ReadTimeout = 500; sp.Open(); Thread.Sleep(100); sp.DiscardInBuffer(); string Cmd(string line, int timeoutMs = 500) { Thread.Sleep(5); // line turnaround (section 2) sp.ReadTimeout = timeoutMs; sp.DiscardInBuffer(); sp.WriteLine(line); while (true) { // skip lines that are not replies string r = sp.ReadLine().Trim(); if (r.StartsWith("OK") || r.StartsWith("ERR")) return r; } } Console.WriteLine(Cmd("VER")); Cmd("BR 12"); Console.WriteLine(Cmd("STATUS")); Console.WriteLine(Cmd("RESET", 10000)); Pseudo code for C / embedded hosts: wait_ms(5); /* line turnaround after the previous reply */ send("BR 12\r\n"); line = read_line(timeout); if (starts_with(line, "OK")) ... else if (starts_with(line, "ERR")) code = atoi(line+4); 9. FILES IN THIS FOLDER ----------------------- build.sh builds the project and puts binaries into bin/ run.sh starts the virtual front panel (bin/AdvancedControllerPanel) CMakeLists.txt CMake project: lib, panel, tools lib/ client library to embed into your software: remoteclient.h/.cpp RemoteClient - blocking, one port controllerlink.h/.cpp ControllerLink - RemoteClient in its own thread, results as Qt signals (GUI) panel/ virtual front panel (Qt Widgets): the device buttons, the leds mirror the real ones (LEDS) tools/remote_test.cpp test tool (bin/remote_test): --list, --scan, --detect, --monitor, -c CMD, --selftest, interactive docs/ this document (RU/EN) and the panel button guide (PANEL_HELP.txt) The panel works with three controllers at once: tabs CONTROLLER 1..3, each with its own port, OPEN/CLOSE and indicator (grey - port closed, red - port open but no answer, green - connected). Requirements: Linux, Qt 6.5 or later with Qt Serial Port, GCC or Clang (C++17), CMake 3.16 or later. Ports like /dev/ttyUSB0; the user must be in the dialout group (sudo usermod -a -G dialout $USER).