AdvancedController — удалённое управление по RS485. Описание протокола и интеграция ===================================================================================== Версия прошивки: r7.2.6. Английская версия: REMOTE_API_EN.txt. 1. ФИЗИЧЕСКИЙ УРОВЕНЬ --------------------- Порт контроллера: INPUT (RS485, разъём у разъёма питания). Внутри: USART2, PD5 TX, PD6 RX, PD7 — управление направлением приёмопередатчика. Параметры линии: 9600 бод, 8 бит данных, без чётности, 1 стоп-бит (9600 8N1). Полудуплекс, двухпроводная линия A/B. На стороне ПК — адаптер USB-RS485 с автоматическим управлением направлением (виртуальный COM-порт). Согласующий резистор 120 Ом на концах длинной линии по правилам RS485. На этой шине должен быть только ПК: контроллер сам ничего не передаёт, кроме ответов на команды. 2. КАДР И ПРАВИЛА ОБМЕНА ------------------------ Запрос от ПК: CMD [ARG1 [ARG2]] - текст ASCII, регистр не важен (br 12 = BR 12); - разделитель аргументов — пробел; терминатор — CR, LF или CR+LF; - длина строки без терминатора не более 64 символов. Ответ контроллера на КАЖДЫЙ запрос: OK команда выполнена OK <данные> команда выполнена, есть данные (VER, STATUS) ERR <код> <текст> команда не выполнена (см. п. 5) Стоп-и-жди: ПК отправляет следующую команду только после получения ответа (или истечения таймаута). Контроллер обрабатывает одну строку за проход главного цикла; строки, пришедшие раньше, ждут в буфере (256 байт). Разворот линии: после ответа выдержите не меньше 5 мс перед следующим запросом. Контроллер переключает приёмопередатчик RS485 обратно на приём примерно через 2 мс после конца своего ответа; байты, отправленные раньше, теряются, и на испорченную строку контроллер отвечает ERR 1 UNKNOWN или ERR 5 CHECKSUM. RemoteClient (lib/remoteclient.cpp) выдерживает эту паузу сам. Таймауты на стороне ПК (рекомендация): обычные команды 0,5 с PRESET LOAD/SAVE, KEY LOAD*/SAVE* 3 с KEY XH1/XH2, KEY UP/DOWN/LEFT/RIGHT в режиме редактирования перекрестья 2–3 с RESET, FOCUS, KEY CAMRESET, KEY FOCUS 10 с Ответ на долгие команды приходит ПОСЛЕ их выполнения (RESET ≈ 6 с, FOCUS ≈ 5 с). Во время выполнения долгой команды контроллер не обрабатывает новые строки; они не теряются и будут обработаны после (ответы придут с задержкой). Поздний ответ: если ПК не дождался ответа (таймаут), ответ всё равно придёт позже и может быть принят за ответ на следующую команду. Перед следующей командой такие строки нужно отбросить (RemoteClient делает это сам: после таймаута выдерживает 0,3 с тишины и очищает буфер приёма). Повторы: при отсутствии ответа повторяйте только идемпотентные команды (PING, VER, STATUS, CAM, BR, AGC, ZOOM, FLIP, MIR, TEMP, LED). Команды KEY, RESET, FOCUS, PRESET повторять автоматически нельзя — повторная отправка даст повторное действие. Ресинхронизация: если между байтами строки пауза больше 100 мс, недостроенная строка отбрасывается без ответа. Строка длиннее 64 символов — ответ ERR 4 после её терминатора. Контрольная сумма (необязательно): к запросу можно добавить «*hh», где hh — XOR всех символов до «*» в шестнадцатеричном виде (стиль NMEA): PING*10 → OK PONG ADVCTRL*48 Если запрос содержит сумму, контроллер её проверяет (несовпадение → ERR 5) и добавляет сумму к ответу. Без «*» суммы нет ни в запросе, ни в ответе. 3. КОМАНДЫ ---------- Все команды со значениями действуют на ТЕКУЩУЮ камеру (см. CAM и STATUS). Команда Аргументы Действие / ответ ------------------- ----------------- ------------------------------------------- PING — OK PONG ADVCTRL. Проверка связи и опознание контроллера (сигнатура «PONG ADVCTRL»). VER — OK r7.2.6. Версия прошивки. ID — OK ADVCTRL FW=r7.2.6 CAMS=4 UID=<24 hex>. Опознание: прошивка, число камер, уникальный номер микроконтроллера. STATUS — OK CAM=.. BR=.. ... (см. п. 4). LEDS — OK <6 hex>. Состояние 24 светодиодов клавиатуры, как их показывает устройство (см. п. 4а). CAM n 1..4 Выбрать камеру n (как кнопка CAM SELECT, светодиоды выбора обновляются). BR n 0..20 Яркость камеры. AGC n 0..20 AGC камеры. ZOOM n 0..16 Цифровой зум камеры. FLIP 0|1 — Переворот изображения выкл/вкл. MIR 0|1 — Зеркало выкл/вкл. TEMP 0|1 — Показ температуры на OSD камеры выкл/вкл. LED n 0..3 Уровень подсветки: 0 выкл, 1 = 25 %, 2 = 75 %, 3 = 100 %. Выключает режим AUTO. LED AUTO 0|1 — Автоматическая подсветка по входу 0–10 В выкл/вкл (для всех каналов). PRESET LOAD 1|2 — Загрузить пользовательский пресет камеры. PRESET SAVE 1|2 — Сохранить текущие настройки камеры в пресет. RESET — Сброс всех камер к настройкам по умолчанию (кнопка CAM RESET, ≈ 6 с). FOCUS — Инициализация перекрестий текущей камеры (кнопка FOCUS, ≈ 5 с). KEY <имя> см. п. 6 Виртуальное нажатие кнопки клавиатуры. IDENTIFY — Светодиод шестерёнки быстро мигает 3 с — чтобы найти глазами, с каким из контроллеров работает ПК. Пример сеанса (терминал, 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 -------------- CAM номер текущей камеры 1..4 BR яркость 0..20 AGC 0..20 ZOOM 0..16 FLIP 0/1 MIR 0/1 TOSD температура на OSD 0/1 LED уровень подсветки 0..3 (ручной) AUTO автоподсветка 0/1 CT температура камеры 1, °C (приходит от камеры; до первого сообщения 25) CT2 температура камеры 2, °C (поле в конце строки STATUS) PT температура процессора контроллера, °C (внутренний датчик, ориентировочно) LT состояние световой колонны: G — норма (зелёный), R — перегрев камеры (красный) XH1, XH2 перекрестья 1 и 2 показаны 0/1 Значения BR/AGC/ZOOM/FLIP/MIR/TOSD/LED хранятся в контроллере отдельно для каждой камеры и отражают как команды с ПК, так и нажатия кнопок. Значения самой камеры после PRESET LOAD контроллеру неизвестны (ограничение камеры): после загрузки пресета задайте нужные величины командами BR/AGC/ZOOM явно. 4а. LEDS — БИТЫ СВЕТОДИОДОВ --------------------------- Ответ — 24-битная маска в виде 6 шестнадцатеричных цифр (бит = 1: горит). 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 Пример: OK 00A000 -> биты 13 (UP), 15 (RIGHT), 17 (ENTER)... считать по маске. Виртуальная панель (bin/AdvancedControllerPanel) опрашивает LEDS 5 раз в секунду и повторяет индикацию устройства. CAM1SEL/CAM2SEL — два светодиода кнопки выбора камеры: камера 1 = 1/0, 2 = 0/1, 3 = 1/1, 4 = 0/0. 5. КОДЫ ОШИБОК -------------- ERR 1 UNKNOWN неизвестная команда ERR 2 ARG неверное число или формат аргументов ERR 3 RANGE аргумент вне диапазона ERR 4 TOOLONG строка длиннее 64 символов ERR 5 CHECKSUM контрольная сумма не совпала 6. ВИРТУАЛЬНЫЕ КНОПКИ (KEY) --------------------------- KEY воспроизводит нажатие кнопки мембранной клавиатуры и проходит через тот же обработчик, что и реальная кнопка: те же режимы, светодиоды, блокировки. Ответ OK приходит после выполнения нажатия. ENTER LONGENTER (долгое — открыть OSD-меню камеры) UP DOWN LEFT RIGHT стрелки (действуют в активном режиме) FLIP MIRROR переключить переворот / зеркало ZOOM BRIGHT AGC включить/выключить режим регулировки стрелками XH1 XH2 показать/скрыть перекрестье 1 / 2 XH1LONG XH2LONG режим редактирования перекрестья (стрелки двигают линии) CAMRESET сброс всех камер (= RESET); кнопка «три ползунка», её светодиод мигает во время сброса TEMP температура на OSD камеры вкл/выкл LEDON ручной режим подсветки вкл/выкл (стрелки меняют уровень) LEDAUTO автоподсветка вкл/выкл LOAD1 LOAD2 SAVE1 SAVE2 пресеты камеры SYSRESET выключить все активные режимы регулировки; кнопка-шестерёнка (её светодиод = признак удалённого управления, п. 7) CAMSEL следующая камера (1→2→3→4→1) FOCUS инициализация перекрестий (= FOCUS) Модальность: кнопки ZOOM, BRIGHT, AGC, LEDON, XH1LONG, XH2LONG, LONGENTER включают режим, в котором стрелки UP/DOWN/LEFT/RIGHT меняют соответствующий параметр. Одновременно активен один режим регулировки. Для ПО ПК проще использовать прямые команды (BR, AGC, ZOOM, LED, CAM), а KEY — для того, чего нет в прямых командах (перекрестья, OSD-меню). 7. ПОВЕДЕНИЕ И ОГРАНИЧЕНИЯ -------------------------- - Клавиатура контроллера работает параллельно с ПК. Реальное нажатие имеет приоритет: виртуальное нажатие выполнится в следующем проходе цикла. - Изменения, сделанные кнопками, видны ПК через STATUS. - Камера 4 подключена к контроллеру только подсветкой (ШИМ); команды камере при выбранной камере 4 никуда не отправляются (порт INPUT занят ПК). - Во время RESET/FOCUS контроллер занят до 6 с; сторожевой таймер при этом не срабатывает. - Контроллер не шлёт ничего по своей инициативе. Для отображения состояния опрашивайте STATUS, например раз в секунду. - Индикация оператору: пока с ПК приходят команды, светодиод кнопки-шестерёнки на устройстве мигает двумя короткими вспышками с паузой 2 с; гаснет через 5 с после последней команды. Это делает прошивка; ПО ПК ничего для этого не делает (в LEDS это бит 22 SYSRESET — светодиод шестерёнки; бит 7 CAMMENU — светодиод кнопки «три ползунка»). Команда IDENTIFY заставляет тот же светодиод быстро мигать 3 с (100 мс вкл / 100 мс выкл) — так находят, какой из нескольких контроллеров подключён к данному порту; потом обычная индикация возобновляется. 8. РЕКОМЕНДАЦИИ ПО ИНТЕГРАЦИИ В ПО КЛИЕНТА ------------------------------------------ 1. Один поток (или одна очередь) обмена на порт: строго стоп-и-жди, между концом ответа и следующим запросом не меньше 5 мс (см. п. 2). 2. После открытия порта: подождать 100 мс, очистить входной буфер, затем ОБНАРУЖЕНИЕ КОНТРОЛЛЕРА: PING с таймаутом 0,3 с, до 2 попыток. Ответ, начинающийся с «OK PONG ADVCTRL», означает «контроллер обнаружен»; любой другой ответ или тишина — «контроллер не обнаружен» (на порту другое устройство или ничего). После обнаружения — ID (версия прошивки, число камер, UID микроконтроллера — по нему различают несколько контроллеров). Поиск нужного порта среди многих: перебрать все COM-порты той же процедурой (RemoteClient::detect(), RemoteClient::findController(); remote_test --scan / --detect). 3. Каждую команду ждать до таймаута из п. 2; строки, не начинающиеся с OK/ERR, игнорировать (тест связи, мусор при включении). 4. Повторять по таймауту только идемпотентные команды (список в п. 2); перед повтором отбросить поздний ответ (см. п. 2). 5. ERR — не обрыв связи: показать пользователю и продолжать. 6. Состояние интерфейса ПК синхронизировать по STATUS (раз в 1 с или после каждой команды), а не по «предполагаемому» состоянию. 7. При отсутствии ответа на 3 PING подряд — считать связь потерянной, переоткрыть порт. 8. Контрольную сумму включать в промышленных условиях (длинная линия, помехи). C++ / Qt 6 — блокирующий клиент (lib/remoteclient.h), для консольной программы или рабочего потока: #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"); // показать перекрестье 1 if (!rc.brightness(99)) // контроллер ответит ERR 3 RANGE qDebug() << "controller said" << rc.lastReply().code << rc.lastReply().text; C++ / Qt 6 — приложение с окном (lib/controllerlink.h): клиент работает в своём потоке, результаты приходят сигналами в поток интерфейса: #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 бита светодиодов */ }); link->open("/dev/ttyUSB0"); link->send("CAM 2"); link->send("BR 12"); Полный пример использования ControllerLink — виртуальная панель (panel/deviceview.cpp). Сборка: в своём CMake-проекте найти Qt (find_package(Qt6 REQUIRED COMPONENTS Core SerialPort) и qt_standard_project_setup()), затем add_subdirectory(<путь>/lib) и target_link_libraries(<ваша цель> PRIVATE AdvancedControllerRemote) — или добавить четыре файла из lib/ в свою цель. Пример на C# (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); // разворот линии (п. 2) sp.ReadTimeout = timeoutMs; sp.DiscardInBuffer(); sp.WriteLine(line); while (true) { // пропускаем строки, не являющиеся ответом 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)); Псевдокод для C/встроенных систем: wait_ms(5); /* разворот линии после предыдущего ответа */ 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. ФАЙЛЫ В ЭТОЙ ПАПКЕ --------------------- build.sh сборка проекта и установка исполняемых файлов в bin/ run.sh запуск виртуальной передней панели (bin/AdvancedControllerPanel) CMakeLists.txt проект CMake: lib, panel, tools lib/ библиотека-клиент для встраивания в ПО: remoteclient.h/.cpp RemoteClient — блокирующий, один порт controllerlink.h/.cpp ControllerLink — RemoteClient в своём потоке, результаты сигналами Qt (окна) panel/ виртуальная передняя панель (Qt Widgets): кнопки как на устройстве, светодиоды повторяют реальные (LEDS) tools/remote_test.cpp утилита (bin/remote_test): --list, --scan, --detect, --monitor, -c CMD, --selftest, интерактив docs/ этот документ (RU/EN) и справка по кнопкам панели (PANEL_HELP.txt) Панель работает с тремя контроллерами одновременно: вкладки CONTROLLER 1..3, у каждой свой порт, OPEN/CLOSE и индикатор (серый — порт закрыт, красный — порт открыт, контроллер не отвечает, зелёный — связь есть). Требуется: Linux, Qt 6.5 или новее с модулем Qt Serial Port, компилятор C++17 (GCC или Clang), CMake 3.16 или новее. Порты вида /dev/ttyUSB0, пользователь должен состоять в группе dialout (sudo usermod -a -G dialout $USER).