313 lines
16 KiB
Text
313 lines
16 KiB
Text
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]]<CR><LF>
|
|
- 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<CR><LF> command executed
|
|
OK <data><CR><LF> command executed, data follows (VER, STATUS)
|
|
ERR <code> <text><CR><LF> 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<CR><LF> -> OK PONG ADVCTRL*48<CR><LF>
|
|
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 <name> 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(<path>/lib)
|
|
and target_link_libraries(<your target> 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).
|