USB GPIO EXTENDER – another option for managing loads from a Mikrotik router

Previously, I wrote several articles about the possibility of controlling relays directly from the Mikrotik OS Router of devices such as

Laurent

And

Rodos

and also when

using the capabilities of the built-in GPIO RB Mikrotik 33G

. Many people consider this unnecessary or do not underestimate it, but in vain, since this is a very useful functionality, for example, for rebooting frozen network equipment, turning on additional equipment or hardware protection of a PC, NAS, etc. directly from the router.

Recently, I found on the Internet the domestic company “Open Development” and its product USB GPIO EXTENDER – a small and nice device for a USB port that has 10 digital lines “on board”: 5 input lines and 5 output lines.

image
Figure 1. USB GPIO EXTENDER

As a non-professional, I can’t judge the hardware and reliability of the module (professionals can comment on this), the board looks neat, all elements are “solid-state”, no “can” capacitors.

The device supports the following commands:

image

And when they are executed, it returns responses from the device in the same format:

image

As we can see, there is no response to the reboot command, and this is incorrect. First, it would be possible to give a response to the command, and then execute it (that is, from the programmer’s point of view, you just need to introduce a delay for the reboot so that the module has time to send a response to “~B”).
Unfortunately, the exchange of commands/responses is not carried out in AT format, which does not allow the full functionality of the device from Mikrotik Router OS to be used. From the Router OS you can send a command to the device via the standard ppp-out interface in the “dial-on-demand” field:

/interface ppp-client add name=«USB-GPIO-EXTENDER» dial-on-demand=no port=$UGEport modem-init=«~S1» null-modem=yes disabled=no

which I have already used before, for example, when creating “

Voice router

“, but it is impossible to get an answer. Thus, firstly, we really don’t know whether the command has reached the module and cannot use commands to read the state of IN lines, which is a shame – it would be possible to connect various digital sensors. I really hope that the developers of Open Development will hear me and in future versions will come to support AT commands in the USB GPIO EXTENDER since they have managed simple text commands (thank you, at least not with HEX byte sequences – that’s already progress).

To the credit of the developers, it should be noted that the module is perfectly defined by Linux and, accordingly, Router OS, which in /system resourse usb adequately shows the name of the module by which it can be easily identified in the system:

image

Rice. 2 Output to Terminal /system resource usb print when the module is connected

Due to the above restrictions, I only implemented support for control commands for output (OUT) lines and a reset command (an example of connecting a 4-channel 220V relay module to a USB GPIO EXTENDER is shown in Fig. 3 below).

image
Fig 3. Connection diagram of USB GPIO EXTENDER input/output lines to a 4-channel relay block (for example, lines 3 and 4 are connected)

The corresponding script function for controlling output lines for the Mikrotik router is presented under the spoiler

OpenDevExt

#----------------------------------------------------------------------------
# Functon support OPEN Dev USB GPIO EXTENDER by Sertik 24/09/2024 version 1.0
#----------------------------------------------------------------------------
# check in ROS 6.49.10

# usage: 

# $OpenDevExt list
# $OpenDevExt logic [true/false]
# $OpenDevExt reset
# $OpenDevExt SetOut XXYXY [0-1]
# $OpenDevExt OutOn X [1-5]
# $OpenDevExt OutOff Y [1-5]

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

# Поддерживаемые функцией в данной версии команды:
# ------------------------------------------------
# ~Pxxxxх Запись значений всех выходов в виде «xxxxx». Например «~P11001».
# ~Sx Установка выхода GPIO в x в 1.
# ~Rx Установка выхода GPIO в x в 0.
# ~B Перезагрузка модуля. При перезагрузке все out-выходы устанавливаются в 0 ! 
# (то есть если подключена нагрузка с прямой логикой - все реле выключаются, а если с обратной - все включаются !)
# logic - чтение/установка логики out-выходов: true - прямая; false - обратная (для реле с прямой и обратной логикой включения/выключения выходов)
# если logic не был задан функция устанавливает прямую логику работы, если был задан - запоминается в global $OpenDevReleLogic 
# и команды SetOut OutOn и OutOff работают по ней.


:global  OpenDevExt do={

:local version "1.0 by Sertik 24/09/2024"

# установка переменных  
:local ModuleType "OpenDevUsbGPIOExtender"
:local portTypeUSB "usb"
:global OpenDevModuleType $ModuleType
:local USBresLinuxName "USB GPIO Extender"
: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"
   "reset"="~B"
 }

# если имя команды не задано вернуть ошибку
: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]"
: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")}
}

# проверка определения модуля в USB прерываниях Linux ядра Router OS
: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

# если возможный порт так и не найден или изначально в $ODUsbGPIOExtPort установлен ошибочный порт, то выдать ошибку
    :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"}

# если функцией был найден (определён) новый порт, а $ODUsbGPIOExtPort, который использовался функцией ранее,  инактивировался, то выбрать в качестве рабочего новый найденный порт
   :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}
  
# одна консоль может быть настроена только на один порт или консоли вообще может не быть в /system console !
  :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
}

# удалить интерфейс ppp-клиента на всякий случай, если был оставлен ошибочным вызовом
      :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")

# передача команды устройству serial-MP3-player через ppp-client
     /interface ppp-client add name=$PppclientName dial-on-demand=no port=$ODUsbGPIOExtPort modem-init=("$cmd"."$2") null-modem=yes disabled=no
     :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={}
     }
# конец работы вернуть "OK"
   :return OK
}

.

Unfortunately, the manufacturer only supplies the device without a housing. At the same time, the pins of the connection block are directed upwards from the board, and not back, which makes it inconvenient to independently select the case for the device, increasing its expected height, rather than length, which would be more compact and logical. If the manufacturer planned that his device would be installed in the case of any customer product, then he should have slightly increased the size of the module board and made mounting holes in it, which are also missing, and instead of the “female” USB connector, install a “male” connector ”, which the customer could display on the panel of his case.

Upon a power reset or reset command, the module resets all output lines to “0”. If a load is connected to them, then depending on the type of relay logic it will either turn on or off. The module does not have an option to save the state of lines during a cold/hot restart, although flash memory (given the presence of a programmable version with TOIC which I don’t have time to deal with), the latter probably does.

Considering the convenient external connection to Mikrotik via a USB port and taking into account patriotism (I honestly admit that it’s not that there are no imported analogues, but I’ve definitely never seen them), we give the manufacturer a “four” for the device for now and hope to make these necessary improvements in the plan for a future version of the module firmware.

All materials are also presented in corresponding section of the author on GitHub.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *