#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 от устройства и вернуть буфер в Роутер ОС.
#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» для проверки связи с модулем.
#---------------------------------------------------------------- # 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 скрипта, то её я тоже модифицировал аналогичным образом:
/*
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.
И под него соответствующая функция для Микротик
#---------------------------------------------------------------- # 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/
Добавить комментарий