Адаптация прошивки USB GPIO EXTENDER T под нужды Микротик

от автора

Недавно я писал статью про модуль USB GPIO EXTENDER компании «Открытые разработки», где было отмечено, что прошивка этой версии модуля не поддерживает формат AT-команд для /ppp-client at-chat Роутер ОС Микротик, поэтому отправить команды модулю можно, а получить ответ нельзя. Это даёт возможность управления выходными линиями GPIO, но не позволяет использовать входные линии. Хочу немного дополнить предыдущую статью описанием другой, программируемой версии модуля USB GPIO EXTENDER T (TOIC), выполненной на темно-синей плате в отличии от непрограммируемой версии (а может такая попалась мне), которая содержит встроенный язык программирования TOIC, а компания поставляет среду разработки и прошивки скрипта в модуль для этого языка. Версия с TOIC поставляется с демонстрационными скриптами Demo1 и Demo2 доступными на сайте разработчика. Demo 1 практически аналогичен прошивке непрограммируемого USB GPIO EXTENDER (5 линий OUT, 4 линии IN). Demo 2 (под спойлером) поддерживает не только GPIO линии ввода/вывода, но и ADC, PWM и SPI.

Скрипт прошивки USB GPIO EXTENDER T Demo 2

#define FW_STR "2.1 02 Aug 2021"    /* IO1 - PA4 - ADC IO2 - PA2 - OUTPUT IO3 - PA0 - SPI CS IO4 - PA6 - SPI MISO IO5 - PA7 - SPI MOSI IO6 - PA3 - INPUT IO7 - PA1 - PWM OUTPUT IO8 - NC IO9 - PB1 - PWM INPUT IOx - PA5 - SPI SCK LED - PF0  */  var _msg() {       var c = stoi(&MSG.RX, 'c');       memcpy(SYS.RAM, &MSG.RX, MSG.SIZE);       switch (c) {           case 'S':               PA2.VALUE = 1;               break;           case 'R':               PA2.VALUE = 0;               break;           case 'A':               sprintf(&UART0.TX, "A%d\n", PA4.VALUE);               break;          case 'K':               PA0.VALUE = 0;               memcpy(&SPI.DR, SYS.RAM+1, MSG.SIZE-1);               sprintf(&UART0.TX, "K%d\n", SYS.RAM+1);               PA0.VALUE = 1;               break;           case 'G':               sprintf(&UART0.TX, "G%d\n", PA3.VALUE);               break;           case 'F':               sprintf(&UART0.TX, "F%d\n", PB1.VALUE);               break;           case 'I':               sprintf(&UART0.TX, "I%s\n", FW_STR);               break;                  default:               break;       } }   var io_setup() {     // IO1 - PA4 - ADC     PA4.MODE = GPIO_MODE_ADC;      // IO2 - PA2 - OUTPUT     PA2.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;     // IO6 - PA3 - INPUT     PA3.MODE = GPIO_MODE_INPUT | GPIO_PULL_DOWN;     // IO7 - PA1 - PWM OUTPUT     PA1.MODE = GPIO_MODE_OPWM|GPIO_INIT_LOW|GPIO_OTYPE_PP;     // IO9 - PB1 - PWM INPUT     PB1.MODE = GPIO_MEASURE_FREQ|GPIO_MODE_IPWM|GPIO_INIT_LOW;      // LED - PF0     PF0.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_HIGH|GPIO_OTYPE_PP;          // IO3 - PA0 - SPI CS     // IO4 - PA6 - SPI MISO     // IO5 - PA7 - SPI MOSI     // IOx - PA5 - SPI SCK     PA0.MODE = GPIO_INIT_LOW|GPIO_OTYPE_PP|GPIO_TYPE_SOFTWARE|GPIO_MODE_OUTPUT;      SPI.SETUP = SPI_SETUP_POLARITY_LOW|SPI_SETUP_EDGE_LEADING|1000/*kBod*/;     SPI.EN = 1;          PA1.PWM = 128;      TIM2.FREQ = TIM3.FREQ = 2;                                     TIM2.EN = TIM3.EN = 1;     UART0.CFG = UART_MODE_PLAIN | UART_CONFIG_START;     __enable_irq(); }   var main() {      io_setup();     while (1) {};     return 0; } 

Данный скрипт следующим образом настраивает пины устройства:

IO1 — PA4 — АЦП (0-3.3В)
IO2 — PA2 — Дискретный выход
IO3 — PA0 — SPI CS
IO4 — PA6 — SPI MISO
IO5 — PA7 — SPI MOSI
IO6 — PA3 — Дискретный вход (до 3.3В)
IO7 — PA1 — PWM выход с фиксированной частотой
IO8 — не используется
IO9 — PB1 — PWM вход (до 3.3В)
IOx — PA5 — SPI SCK

и поддерживает следующие команды:

S — Установить выход (2) в 1,
R — Установить выход (2) в 0,
G — Считать данные со входа (6),
A — Считать данные с АЦП (1),
K — Отправить в SPI данные и прочитать ответ,
F — Считать данные с определителя частоты PWM (9),
I — Считать информацию о версии скрипта.

На сайте TOIC, также разработанным Open Development, есть весьма урезанное описание среды разработки и языка программирования и данный пример скрипта, а также скрипты для других устройств компании. Но без описаний переменных скрипта и значений регистров модуля до конца понять всё не представляется возможным.
Я немного модифицировал скрипт для возможности полноценного использования устройства в Микротик Роутер ОС, добавив в возвраты команд \r\nOK\r\n, что позволяет интерфейсу /ppp-client при at-chat «увидеть» OK от устройства и вернуть буфер в Роутер ОС.

Скрипт прошивки USB GPIO EXTENDER T Demo 2 (modify Sertik 08/10/2024)

#define FW_STR "2.1 for Mikrotik modify Sertik"    /* IO1 - PA4 - ADC IO2 - PA2 - OUTPUT IO3 - PA0 - SPI CS IO4 - PA6 - SPI MISO IO5 - PA7 - SPI MOSI IO6 - PA3 - INPUT IO7 - PA1 - PWM OUTPUT IO8 - NC IO9 - PB1 - PWM INPUT IOx - PA5 - SPI SCK LED - PF0  */  var _msg() {       var c = stoi(&MSG.RX, 'c');       memcpy(SYS.RAM, &MSG.RX, MSG.SIZE);       switch (c) {           case 'S':               PA2.VALUE = 1;               sprintf(&UART0.TX, "%d\r\nOK\r\n", PA2.VALUE);               break;           case 'R':               PA2.VALUE = 0;               sprintf(&UART0.TX, "%d\r\nOK\r\n", PA2.VALUE);               break;           case 'A':               sprintf(&UART0.TX, "%d\r\nOK\r\n", PA4.VALUE);               break;          case 'K':               PA0.VALUE = 0;               memcpy(&SPI.DR, SYS.RAM+1, MSG.SIZE-1);               sprintf(&UART0.TX, "%d\r\nOK\r\n", SYS.RAM+1);               PA0.VALUE = 1;               break;           case 'G':               sprintf(&UART0.TX, "%d\r\nOK\r\n", PA3.VALUE);               break;           case 'F':               sprintf(&UART0.TX, "%d\r\nOK\r\n", PB1.VALUE);               break;           case 'L':               PF0.VALUE = 1;               sprintf(&UART0.TX, "OK\r\n");               delay (1000)               PF0.VALUE = 0;               break;           case 'I':               sprintf(&UART0.TX, "%s\r\nOK\r\n", FW_STR);               break;                  default:               break;       } }   var io_setup() {     // IO1 - PA4 - ADC     PA4.MODE = GPIO_MODE_ADC;      // IO2 - PA2 - OUTPUT     PA2.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;     // IO6 - PA3 - INPUT     PA3.MODE = GPIO_MODE_INPUT | GPIO_PULL_DOWN;     // IO7 - PA1 - PWM OUTPUT     PA1.MODE = GPIO_MODE_OPWM|GPIO_INIT_LOW|GPIO_OTYPE_PP;     // IO9 - PB1 - PWM INPUT     PB1.MODE = GPIO_MEASURE_FREQ|GPIO_MODE_IPWM|GPIO_INIT_LOW;      // LED - PF0     PF0.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;          // IO3 - PA0 - SPI CS     // IO4 - PA6 - SPI MISO     // IO5 - PA7 - SPI MOSI     // IOx - PA5 - SPI SCK     PA0.MODE = GPIO_INIT_LOW|GPIO_OTYPE_PP|GPIO_TYPE_SOFTWARE|GPIO_MODE_OUTPUT;      SPI.SETUP = SPI_SETUP_POLARITY_LOW|SPI_SETUP_EDGE_LEADING|1000/*kBod*/;     SPI.EN = 1;          PA1.PWM = 128;      TIM2.FREQ = TIM3.FREQ = 2;                                     TIM2.EN = TIM3.EN = 1;     UART0.CFG = UART_MODE_PLAIN | UART_CONFIG_START;     __enable_irq(); }   var main() {      io_setup();     while (1) {};     return 0; 

На базе модифицированной прошивки я написал также новую функцию OpnDevExtTOIC version 1, полностью поддерживающую в Микротик эту прошивку и позволяющую получать все ответы от устройства, в том числе состояние линии IN, значения PWM и ADC и ответы SPI-интерфейса, добавив ещё инструкцию (case ‘L ‘) для управления LED-индикатором, которую также использую как тестовую команду «AT» для проверки связи с модулем.

fOpnDevExtTOIC version 1

#---------------------------------------------------------------- # Functon support OPEN Dev USB GPIO EXTENDER TOIC # by Sertik version 1.0 08/10/2024 #---------------------------------------------------------------- # check in ROS 6.49.10  # usage:   # $fOpenDevExtTOIC list - output a list of function commands for the module to the terminal and log # :put [$fOpenDevExtTOIC logic] - return the set value of the operating logic # $fOpenDevExtTOIC logic [true/false] - set forward/reverse operating logic # $fOpenDevExtTOIC OutOn - turn on OUT (2) # $fOpenDevExtTOIC OutOff - turn off OUT (2) # $fOpenDevExtTOIC In - read IN (6) # $fOpenDevExtTOIC SPI xxxxx - send xxxxx to SPI and return module`s answer # $fOpenDevExtTOIC PWM - read PWM-OUT # $fOpenDevExtTOIC AT - check connect and LED # $fOpenDevExtTOIC ATI - request information about the firmware version  # module commands: # ----------------------------------------------------------------------- # S sets the output (2) to 1. # R sets the output (2) to 0. # G read the current value of input (6) # A - read ADC # K - send data to SPI and take answer # F - read data in PWM # L - test connect # I request information about the firmware version.   :global  fOpenDevExtTOIC do={   :local version "1.0 08/10/2024"  :local ModuleType "USB GPIO EXTENDER TOIC" :local portTypeUSB "usb" :global OpenDevModuleType $ModuleType :local USBresLinuxName "TOIC-F0-GE" :global OpenDevReleLogic :local UsbGpioExtFlag false :local portUSB; :local BaudRate 9600 :local DataBits 8 :local Parity none :local StopBits 1 :local FlowControl none :local PppclientName $ModuleType  :local ArrayCom {    "logic"="X"    "OutOn"="S"    "OutOff"="R"    "In"="G"    "ADC"="A"    "PWM"="F"    "SPI"="K"    "ATI"="I"    "AT"="L"  }  :if ([:len $1]=0) do={:return "Еrror: no set name command"}  # help    :if ($1="help") do={       :put ""; :put "---- Function support for $OpenDevModuleType ----"                    :put "      version $version"                    :put  " usage:"  :terminal style "syntax-meta" :put "$0 help"  :put "$0 list" :put "$0 logic [true/false]" :put "$0 reset" :put "$0 SetOut XXYXY [0-1]" :put "$0 ReadIn" :put "$0 In [6-9]" :put "$0 OutOn X [1-5]" :put "$0 OutOff Y [1-5]" :put "$0 AT" :put "$0 ATI" :terminal style none   :return []}  # list     :if ($1="list") do={       :put ""; :put "<---- Supported $OpenDevModuleType commands  ---->"           :foreach k,v in $ArrayCom do={:put ("  "."$k")}   :return []}  # logic    :if ($1="logic") do={        :if (($2="true") or ($2="false")) do={            :if ($2="true") do={:set OpenDevReleLogic true}            :if ($2="false") do={:set OpenDevReleLogic false}            :return OK      } else={:if ([:len $2]=0) do={          :if (($OpenDevReleLogic=true) or ($OpenDevReleLogic=false)) do={          :return $OpenDevReleLogic} else={:return "logic is not specified or incorrect"}                  }     :return ("Error"." $0"." $1")} }  local UsbGpioExtName :do { :set UsbGpioExtName [/system resource usb get [/system resource usb find name~$USBresLinuxName] name]     } on-error={} :if ($UsbGpioExtName=$USBresLinuxName) do={:set UsbGpioExtFlag true} :if ($UsbGpioExtFlag=false) do={:return "Error: Not find $OpenDevModuleType module in ROS system. Please, check device in USB port"}   :global ODUsbGPIOExtPort :local NewPort :local NowPort $ODUsbGPIOExtPort; # сохранить текущий порт  :do {     :foreach portId in=[/port find name~$portTypeUSB !inactive] do={:set portUSB ([/port get $portId]->"name")}     } on-error={}        :set NewPort $portUSB      :if (([:len $NewPort]=0) or ([:len [/port find name=$NewPort]]=0)) do={:return "Error: Not find port for $OpenDevModuleType module, port inactive or busy. Please, check /port"}     :if (($NowPort!=$NewPort) and ([/port find name=$NowPort and inactive=yes])) do={:set ODUsbGPIOExtPort $NewPort} else={:set ODUsbGPIOExtPort $NowPort}    :if ([:len $ODUsbGPIOExtPort]=0) do={:set ODUsbGPIOExtPort $NewPort}      :local consoleFlagOff false         if ([:len [/system console find port=$ODUsbGPIOExtPort and !disabled]]>0) do={                 :set consoleFlagOff true                 /system console set [/system console find port=$ODUsbGPIOExtPort] disable=yes         }      do {              /port set [/port find name=$ODUsbGPIOExtPort] baud-rate=$BaudRate data-bits=$DataBits parity=$Parity stop-bits=$StopBits flow-control=$FlowControl           } on-error={:return "Error set port $ODUsbGPIOExtPort. Function $0 d`not work"}   # main function`s code    :local cmd ($ArrayCom->$1)     :if ([:len $cmd]=0) do={:return "Error: bad command"}                :put "Execute command $OpenDevModuleType: $1 $2"                :log warning "Execute command $OpenDevModuleType: $1 $2"  :if ([:len $OpenDevReleLogic]=0) do={:set OpenDevReleLogic true} :if (($1="OutOn") && ($OpenDevReleLogic=false)) do={:set cmd ($ArrayCom->"OutOff")} :if (($1="OutOff") && ($OpenDevReleLogic=false)) do={:set cmd ($ArrayCom->"OutOn")}  :if ([/interface ppp-client find name=$PppclientName]) do={/interface ppp-client remove [/interface ppp-client find name=$PppclientName]}  # :put ("Send module "."$USBresLinuxName "."command: "."$cmd"."$2")       /interface ppp-client add name=$PppclientName dial-on-demand=no port=$ODUsbGPIOExtPort null-modem=yes disabled=yes      :delay 1s      :local GPIOanswer [/interface ppp-client at-chat $PppclientName input=("$cmd"."$2") as-value]      /interface ppp-client remove [/interface ppp-client find name=$PppclientName]     :if ($consoleFlagOff) do={    :do {/system console set [/system console find port=$ODUsbGPIOExtPort] disable=no} on-error={}      } # end     :return $GPIOanswer }

Функция имеет более удобные названия команд ($1 параметр) для модуля, они приведены в коде функции.

Надо отметить, что в

/system resource usb print

TOIC-версия модуля отображается как «TOIC-F0-GE», а не как «USB GPIO EXTENDER», что актуально для непрограммируемой версии. Не знаю одинаково ли это для всех экземпляров TOIC-версии, или имя в firmware может меняться взависимости от партии или прошивки TOIC. Если у Вас отображается другое имя, то его нужно заменить в коде моей функции (переменная USBresLinuxName), иначе функция не найдет модуль.

Пригодится кому-либо или нет — вопрос, у меня работает. Дальше ковырять скрипт я не стал, так как не имею навыков программирования на TOIC, представляющим собой какую-то урезанную смесь Питона и Си++ (возможно тем, кто свободно программирует на этих языках и посмотрит исходники скриптов выше, это не составит никакого труда).

Что касается версии Demo 1 скрипта, то её я тоже модифицировал аналогичным образом:

Скрипт прошивки USB GPIO EXTENDER T Demo 1 (modify Sertik 08/10/2024)

#define FW_STR «1.1 for Mikrotik modify Sertik»

/*
IO1 — PA4 — OUTPUT
IO2 — PA2 — OUTPUT
IO3 — PA0 — OUTPUT
IO4 — PA7 — OUTPUT
IO5 — PA6 — OUTPUT

IO6 — PA3 — INPUT
IO7 — PA1 — INPUT
IO8 — PA13 — INPUT
IO9 — PB1 — INPUT
IO10 — BOOT

IOx — PA5 — INPUT PULLED DONW R10K
LED — PF0

*/

var GPIO_OUT = [PA4.VALUE, PA2.VALUE, PA0.VALUE, PA7.VALUE, PA6.VALUE];
var GPIO_IN = [PA3.VALUE, PA1.VALUE, PA13.VALUE, PB1.VALUE, PB1.VALUE];

var io_setup() {
// OUTPUTS
PA4.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
PA2.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
PA0.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
PA7.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
PA6.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
// INPUTS
PA3.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
PA1.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
PA13.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
PB1.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
PA5.MODE = GPIO_MODE_INPUT | GPIO_PULL_DOWN; // unused
// LED — PF0
PF0.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
// UART
UART0.CFG = UART_MODE_PLAIN | UART_CONFIG_START;
__enable_irq();
}

var _msg() {
if(MSG.PORT == 4) {
memcpy(&RING.BUF, &MSG.RX, MSG.SIZE);
}
}

var main() {
var c;
var pstate;
var p_index;
var p;
io_setup();
if((RING.ALLOC = 16) != 16) {
return 1;
}
SYS.WDG = 1;
while(1){
if(RING.QUEUE > 0) {
c = RING.PULL;
if (c == ‘~’) {
pstate = 1;
p_index = 0;
} else if (pstate == 1) {
pstate = 0;
switch © {
case ‘I’:
sprintf(&UART0.TX, «%s\r\nOK\r\n», FW_STR);
pstate = 0;
break;
case ‘L’:
PF0.VALUE = 1;
sprintf(&UART0.TX, «OK\r\n»);
delay (1000);
PF0.VALUE = 0;
break;
case ‘B’:
sprintf(&UART0.TX, «module reseting\r\nOK\r\n»);
delay (1000);
SYS.RESET = 1;
break;
case ‘A’:
sprintf(&UART0.TX, «%d%d%d%d%d\r\nOK\r\n», *GPIO_IN[0], *GPIO_IN[1], *GPIO_IN[2], *GPIO_IN[3], *GPIO_IN[4]);
pstate = 0;
break;
case ‘S’:
case ‘R’:
case ‘G’:
case ‘P’:
pstate = c;
break;
default:
break;
}
} else if (pstate) {
p = c — ‘0’;
if (pstate == ‘S’ || pstate == ‘R’) { // ~S or ~R
if (p > 0 && p < 6) {
GPIO_OUT[p — 1] = (pstate == ‘S’);
sprintf(&UART0.TX, «%c%d\r\nOK\r\n», pstate, p);
} else {
sprintf(&UART0.TX, «Err\r\n»);
}
pstate = 0;
} else if (pstate == ‘G’) { // ~G
if (p > 0 && p < 6) {
sprintf(&UART0.TX, «%d\r\nOK\r\n», *GPIO_IN[p — 1]);
} else {
sprintf(&UART0.TX, «Err\r\n»);
}
pstate = 0;
} else if (pstate == ‘P’) { // ~P
GPIO_OUT[p_index++] = (c == ‘1’);
if (p_index == 5) {
sprintf(&UART0.TX, «%d%d%d%d%d\r\nOK\r\n», *GPIO_OUT[0], *GPIO_OUT[1], *GPIO_OUT[2], *GPIO_OUT[3], *GPIO_OUT[4]);
pstate = 0;
}
}
}
} else {
delay(10);
}
SYS.WDG = 42;
}

return 0;
}

Настроенные пины:

IO1 — PA4 — выход 1
IO2 — PA2 — выход 2
IO3 — PA0 — выход 3
IO4 — PA7 — выход 4
IO5 — PA6 — выход 5
IO6 — PA3 — Вход1
IO7 — PA1 — Вход2
IO8 — PA13 — Вход3
IO9 — PB1 — Вход4
IO10 — Контакт входа в загрузчик
IOx — PA5 — Доп. вход с подтяжкой резистром 10КОм к земле
PF0 — управляемый светодиод

Демонстрационный код 1 поддерживает следующие команды:

~Sx Установка выхода в x в 1
~Rx Установка выхода в x в 0
~Gx Чтение текущего значения входа x
~A Чтение значений всех входов в виде «xxxxx». Пример ответа «~A11001»
~Pxxxxх Запись значений всех выходов в виде «xxxxx». Например «~P11001»
~B Перезагрузка модуля.
~I Запросить информация о прошивке.
Примечание: т.к. IO10 используется для перехода в загрузчик, то он не используется в программе и вместо него при ~A отображается еще раз сигнал IO9.

И под него соответствующая функция для Микротик

fOpenDevExtTOIC version 2

#---------------------------------------------------------------- # Functon support OPEN Dev USB GPIO EXTENDER T (TOIC) # by Sertik version 2.0 08/10/2024 #---------------------------------------------------------------- # check in ROS 6.49.10  # usage:   # $fOpenDevExt list - output a list of function commands for the module to the terminal and log # :put [f$OpenDevExt logic] - return the set value of the operating logic # $fOpenDevExt logic [true/false] - set forward/reverse operating logic # $fOpenDevExt reset - module reset # $fOpenDevExt SetOut XXYXY [0-1] - setting all Out outputs, for example 00111 # $fOpenDevExt OutOn X [1-5] - “turn on” output X  # $fOpenDevExt In X [1-5] - read IN X # $fOpenDevExt ReadIn XXYXY - read all In input # $fOpenDevExt OutOff Y - “turn off” output Y [1-5] # $fOpenDevExt AT - test connect # $fOpenDevExt ATI - firmware information  # module commands: # ----------------------------------------------------------------------- # ~Sx sets the output in x to 1. # ~Rx sets the output at x to 0. # ~Gx read the current value of input x. # ~A read the values of all inputs as "xxxxx". Example response "~A11001". # ~Pxxxxx writes the values of all outputs as “xxxxx”. For example "~P11001". # ~B reboot the module. # ~I request information about the firmware version. # ~L test AT connect   :global  fOpenDevExtTOIC do={   :local version "2.0 08/10/2024"  :local ModuleType "USB GPIO EXTENDER TOIC" :local portTypeUSB "usb" :global OpenDevModuleType $ModuleType :local USBresLinuxName "TOIC-F0-GE" :global OpenDevReleLogic :local UsbGpioExtFlag false :local portUSB; :local BaudRate 9600 :local DataBits 8 :local Parity none :local StopBits 1 :local FlowControl none :local PppclientName $ModuleType  :local ArrayCom {    "logic"="X"    "OutOn"="~S"    "OutOff"="~R"    "SetOut"="~P"    "ReadIn"="~A"    "In"="~G"    "reset"="~B"    "AT"="~L"   "ATI"="~I"  }  :if ([:len $1]=0) do={:return "Еrror: no set name command"}  # help    :if ($1="help") do={       :put ""; :put "---- Function support for $OpenDevModuleType ----"                    :put "      version $version"                    :put  " usage:"  :terminal style "syntax-meta" :put "$0 help"  :put "$0 list" :put "$0 logic [true/false]" :put "$0 reset" :put "$0 SetOut XXYXY [0-1]" :put "$0 OutOn X [1-5]" :put "$0 OutOff Y [1-5]" :put "$0 ReadIn" :put "$0 In [6-9]" :put "$0 AT" :put "$0 ATI" :terminal style none   :return []}  # list     :if ($1="list") do={       :put ""; :put "<---- Supported $OpenDevModuleType commands  ---->"           :foreach k,v in $ArrayCom do={:put ("  "."$k")}   :return []}  # logic    :if ($1="logic") do={        :if (($2="true") or ($2="false")) do={            :if ($2="true") do={:set OpenDevReleLogic true}            :if ($2="false") do={:set OpenDevReleLogic false}            :return OK      } else={:if ([:len $2]=0) do={          :if (($OpenDevReleLogic=true) or ($OpenDevReleLogic=false)) do={          :return $OpenDevReleLogic} else={:return "logic is not specified or incorrect"}                  }     :return ("Error"." $0"." $1")} }  local UsbGpioExtName :do { :set UsbGpioExtName [/system resource usb get [/system resource usb find name~$USBresLinuxName] name]     } on-error={} :if ($UsbGpioExtName=$USBresLinuxName) do={:set UsbGpioExtFlag true} :if ($UsbGpioExtFlag=false) do={:return "Error: Not find $OpenDevModuleType module in ROS system. Please, check device in USB port"}   :global ODUsbGPIOExtPort :local NewPort :local NowPort $ODUsbGPIOExtPort; # сохранить текущий порт  :do {     :foreach portId in=[/port find name~$portTypeUSB !inactive] do={:set portUSB ([/port get $portId]->"name")}     } on-error={}        :set NewPort $portUSB      :if (([:len $NewPort]=0) or ([:len [/port find name=$NewPort]]=0)) do={:return "Error: Not find port for $OpenDevModuleType module, port inactive or busy. Please, check /port"}     :if (($NowPort!=$NewPort) and ([/port find name=$NowPort and inactive=yes])) do={:set ODUsbGPIOExtPort $NewPort} else={:set ODUsbGPIOExtPort $NowPort}    :if ([:len $ODUsbGPIOExtPort]=0) do={:set ODUsbGPIOExtPort $NewPort}      :local consoleFlagOff false         if ([:len [/system console find port=$ODUsbGPIOExtPort and !disabled]]>0) do={                 :set consoleFlagOff true                 /system console set [/system console find port=$ODUsbGPIOExtPort] disable=yes         }      do {              /port set [/port find name=$ODUsbGPIOExtPort] baud-rate=$BaudRate data-bits=$DataBits parity=$Parity stop-bits=$StopBits flow-control=$FlowControl           } on-error={:return "Error set port $ODUsbGPIOExtPort. Function $0 d`not work"}   # main function`s code    :local cmd ($ArrayCom->$1)     :if ([:len $cmd]=0) do={:return "Error: bad command"}                :put "Execute command $OpenDevModuleType: $1 $2"                :log warning "Execute command $OpenDevModuleType: $1 $2"  :if ((($1="OutOn") or ($1="OutOff")) and ([:len $2]!=1)) do={:return ("Error function: "."$0 "."$2 - there is incorrect data, set number in range [1-5]")}  :if (($1="SetOut") and ([:len $2]!=5)) do={:return ("Error function: "."$0 "."$2 - there is incorrect data, set the status of 5 outputs")}   :if ([:len $OpenDevReleLogic]=0) do={:set OpenDevReleLogic true} :if (($1="OutOn") && ($OpenDevReleLogic=false)) do={:set cmd ($ArrayCom->"OutOff")} :if (($1="OutOff") && ($OpenDevReleLogic=false)) do={:set cmd ($ArrayCom->"OutOn")}  :if (($1="SetOut") && ($OpenDevReleLogic=false)) do={ :local r ""     :for i from=0 to=([:len $2] -1) do={       :if ([:pick $2 $i (1 + $i)] = "1") do={:set r ($r . "0")} else={:set r ($r . "1")}     } :set $2 $r }  :if ([/interface ppp-client find name=$PppclientName]) do={/interface ppp-client remove [/interface ppp-client find name=$PppclientName]}  # :put ("Send module "."$USBresLinuxName "."command: "."$cmd"."$2")       /interface ppp-client add name=$PppclientName dial-on-demand=no port=$ODUsbGPIOExtPort null-modem=yes disabled=yes      :local GPIOanswer [/interface ppp-client at-chat $PppclientName input=("$cmd"."$2") as-value]      :delay 1s      /interface ppp-client remove [/interface ppp-client find name=$PppclientName]     :if ($consoleFlagOff) do={    :do {/system console set [/system console find port=$ODUsbGPIOExtPort] disable=no} on-error={}      } # end     :return $GPIOanswer }

Тут следует отметить, что как и в непрограммируемой версии модуля, так и данная прошивка TOIC-версии позволяет считывать состояние IN-линий, но не позволяет считывать состояние линий OUT. Функциональность линий назначается скриптом один раз при запуске скрипта и я не знаю может ли меняться, но возможно считывать состояние линий можно только, когда они назначены, как входные. Тогда, чтобы считать состояние OUT-линии, её нужно временно переназначить, а это может изменить её состояние. Это уже вопрос к разработчику — можно ли вообще считывать состояние OUT-линий в его устройстве. Если аппаратно нет — это существенный минус.

Мои версии модифицированных прошивок USB GPIO EXTENDER T (TOIC) от 08.10.2024 г. под Микротик и коды моих функций OpnDevExtTOIC для Роутер ОС можно скачать по ссылке на GitHub.

С появлением модификации прошивок можно адекватно использовать устройство с Микротик, как отправляя команды на выполнение, так и получая ответы. В том числе можно считывать состояние IN-линий и др. в зависимости от версии прошивки.

Например, для версии прошивки 1 с использванием функции версии 2 на

:put ([$fOpenDevExtTOIC In 4]->"output")

получим в Терминале:

1
OK

Значит можно смело использовать команды и возвраты ответов от модуля в своих скриптах Микротик. Например, присоединив к USB GPIO EXTENDER T модуль радиоприемника 433 Мгц можно получить USB-радиопульт с управлением Микротик Роутер ОС для любых нужд, подобно как я это делал для Serial1-порта RBM33G. Теперь это стало возможным для любой RB Микротик с USB-портом.

Надо отметить, что без навыков программирования в Си++ или Питоне прошивки всё равно сложно модифицировать переопределяя пины под конкретные задачи. Разработчику следовало бы написать универсальную прошивку, в которой пользователь просто бы мог назначить пины устройства в пользовательских переменных без каких-либо правок исполняемого кода.

Так что пока за устройство поставим «5-», а за софт к нему «4».


ссылка на оригинал статьи https://habr.com/ru/articles/849246/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *