Моя борьба с автоматикой шлагбаума SPbarrier

Стояла задача научиться управлять шлагбаумом через RS-485. Шлагбаум питерской конторы АПС-СПБ с китайской автоматикой управления. Можно управлять сухими контактами и через gsm модуль, который поддерживает управление через приложение (по факту замыкает тот же сухой контакт). Но как известно,  это не наш метод!

a9a2fde21cfc391db94e874c6e75aa4c.png

Для начала была запрошена информация у производителя, на что был получен файл для диагностики автоматики, который работает под Windows. Поигравшись с открытие-закрытием шлагбаума перешел к изучению трафика, проходящего через порт. В результате чего нашел, что для открытия и закрытия программа отправляет команду 1 в coil регистры 0 и 1. Что ж, уже хорошо, уже можно отправлять команду на открытие или закрытие. Но нам же нужны статусы! С помощью снифера так же удалось найти, что программа «общается» с автоматикой по следующим адресам:

  • Holding 0 примерно 30 регистров, что соответствует настройке параметров работы шлагбаума, которые так же дублируются на самой автоматике физически, т.е. их можно выставив «понажимав» кнопочки на самой автоматике.

  • Holding 53248 примерно 25 регистров. Тут передавались разные состояния и параметры, какой за что отвечает можно было только догадываться, либо сверять эти значения с теми, что выдавало приложение.

Сначала хотел сделать через найденые параметры Hall и Trans. Понятия не имею, что они значат, но в приложении отображались, и как сказал выше, нашел их адреса снифиром. Данные параметры приходили на адреса 53252 (Hall) и 53253 (Trans) и при открытом состоянии были 2/13 Hall/Trans, а при закрытом 6/1184 соответственно.

Дальше настала очередь «засунуть» все это добро в Wiren Board. Подключив автоматику ко 2 му порту начал изучать запросы ответы с помощью утилиты modbus client. Далее,  заметки на полях:

modbus_client --debug -mrtu -pnone -s2 /dev/ttyRS485-2 -a1 -t0x03 -r53248 -c 25 // чтение параметров начинается отсюда  25 регистров
modbus_client --debug -mrtu -pnone -s2 /dev/ttyRS485-2 -a1 -t0x03 -r53252 // параметр Hall в приложении. если ответ 2 ОТКРЫТ. Если 6 - ЗАКРЫТ  

modbus_client --debug -mrtu -pnone -s2 /dev/ttyRS485-2 -a1 -t0x03 -r53253 // параметр Trans в приложении. если ответ 13 ОТКРЫТ. Если 1184 - ЗАКРЫТ  

modbus_client --debug -mrtu -pnone -s2 /dev/ttyRS485-2 -a1 -t0x05 -r0x00 0x01 // открытие шлагбаума!

modbus_client --debug -mrtu -pnone -s2 /dev/ttyRS485-2 -a1 -t0x05 -r0x01 0x01 // закрытие шлагбаума!

Но тут пришла хорошая новость, производитель шлагбаумов, по моей просьбе запросил описание протокола у производителя автоматики, и те наконец ответили. Таким образом у меня появилось еще два файла — RS485 interface protocol.pdf и 操作说明 RS485.docx. Второй был прогнан через яндекс переводчик, и получился — 操作说明 RS485 (1).docx. Попытавшись проникнуться дзеном понять что, же имели ввиду китайцы, пришел к выводу, что все необходимые состояния передаются в 1 адресе, и адрес этот должен быть 14-м. Не спрашивайте как, но я нашел ЭТОТ 14 адрес (там целая детективная история). В общем, 53268 — это именно тот адрес. который содержит в себе состояния шлагбаума. Раскладывается достаточно просто, если понимать процесс. Полученное значение, это бинарное состояние значений зашифрованное в HEX. Сам я не сварщик, но с подсказки старших товарищей накидал себе такую картинку, по ней уже смог понять, что состояния получены верные.

f767fab709cc2ebedb573cca8b9ba0d0.png

Дальше я просто создал примитивное устройство в wiren board по шаблону, в которое вывел интересующие меня параметры и состояния. Шаблон — config-SPbarrier-03L.json

{
    "device_type": "SPbarrier",
    "device": {
        "name": "SPbarrier-03L",
        "id": "spb-03l",
        "max_read_registers": 60,
        "response_timeout_ms": 200,
        "frame_timeout_ms": 36,
        "channels": [
            {
                "name": "K1",
                "reg_type": "coil",
                "address": 0,        // Команда на открытие
                "type": "switch"
            },
            {
                "name": "K2",
                "reg_type": "coil",
                "address": 1,        // Команда на закрытие
                "type": "switch"
            },
            {
                "name": "K3",
                "reg_type": "coil",
                "address": 2,        // Команда на остановку
                "type": "switch"
            },
            {
                "name": "K4",
                "reg_type": "coil",
                "address": 3,        // Команда на самотестирование
                "type": "switch"
            },
            {
                "name": "Param 0",
                "reg_type": "holding",
                "address": 0,        // Скорость открытия 25-95, шаг 1
                "type": "value"
            },
            {
                "name": "Param 1",
                "reg_type": "holding",
                "address": 1,        // Скорость закрытия 25-95, шаг 1
                "type": "value"
            },
            {
                "name": "Param 2",
                "reg_type": "holding",
                "address": 2,        //
                "type": "value"
            },
            {
                "name": "Param 3",
                "reg_type": "holding",
                "address": 3,        //
                "type": "value"
            },
            {
                "name": "Param 4",
                "reg_type": "holding",
                "address": 4,        //
                "type": "value"
            },
            {
                "name": "Param 5",
                "reg_type": "holding",
                "address": 5,        //
                "type": "value"
            },
            {
                "name": "Param 6",
                "reg_type": "holding",
                "address": 6,        //
                "type": "value"
            },
            {
                "name": "Param 7",
                "reg_type": "holding",
                "address": 7,        //
                "type": "value"
            },
            {
                "name": "Param 8",
                "reg_type": "holding",
                "address": 8,        //
                "type": "value"
            },
            {
                "name": "Param 9",
                "reg_type": "holding",
                "address": 9,        // Задержка перед автоматическим закрытием, 0-90 секунд, шаг 1. 0 - не будет закрываться автоматически
                "type": "value"
            },
            {
                "name": "Param 10",
                "reg_type": "holding",
                "address": 10,        //
                "type": "value"
            },
            {
                "name": "Param 11",
                "reg_type": "holding",
                "address": 11,        //
                "type": "value"
            },
            {
                "name": "Param 12",
                "reg_type": "holding",
                "address": 12,        //
                "type": "value"
            },
            {
                "name": "Param 13",
                "reg_type": "holding",
                "address": 13,        //
                "type": "value"
            },
            {
                "name": "Param 14",
                "reg_type": "holding",
                "address": 14,        //
                "type": "value"
            },
            {
                "name": "Param 15",
                "reg_type": "holding",
                "address": 15,        //
                "type": "value"
            },
            {
                "name": "Param 16",
                "reg_type": "holding",
                "address": 16,        // RS-485 адрес, от 1 до 32
                "type": "value"
            },
            {
                "name": "Param 17",
                "reg_type": "holding",
                "address": 17,        // Скорость RS-485 порта. 0 - 9600, 1 - 19200, 2 - 38400. Изменения вступают в силу после перезагрузке по питанию
                "type": "value"
            },
            {
                "name": "Param 18",
                "reg_type": "holding",
                "address": 18,        //
                "type": "value"
            },
            {
                "name": "Param 19",
                "reg_type": "holding",
                "address": 19,        //
                "type": "value"
            },
            {
                "name": "Param 20",
                "reg_type": "holding",
                "address": 20,        //
                "type": "value"
            },
            {
                "name": "Param 21",
                "reg_type": "holding",
                "address": 21,        //
                "type": "value"
            },
            {
                "name": "Param 22",
                "reg_type": "holding",
                "address": 22,        //
                "type": "value"
            },
            {
                "name": "Param 23",
                "reg_type": "holding",
                "address": 23,        //
                "type": "value"
            },
            {
                "name": "Param 24",
                "reg_type": "holding",
                "address": 24,        //
                "type": "value"
            },
            {
                "name": "Param 25",
                "reg_type": "holding",
                "address": 25,        //
                "type": "value"
            },
            {
                "name": "Param 26",
                "reg_type": "holding",
                "address": 26,        //
                "type": "value"
            },
            {
                "name": "Param 27",
                "reg_type": "holding",
                "address": 27,        //
                "type": "value"
            },
            {
                "name": "Param 28",
                "reg_type": "holding",
                "address": 28,        //
                "type": "value"
            },
            {
                "name": "Param 29",
                "reg_type": "holding",
                "address": 29,        //
                "type": "value"
            },
            {
                "name": "Param 30",
                "reg_type": "holding",
                "address": 30,        //
                "type": "value"
            },
            {
                "name": "Param 31",
                "reg_type": "holding",
                "address": 31,        //
                "type": "value"
            },
            {
                "name": "Param 32",
                "reg_type": "holding",
                "address": 32,        //
                "type": "value"
            },
            {
                "name": "Param 33",
                "reg_type": "holding",
                "address": 33,        //
                "type": "value"
            },
            {
                "name": "Param 34",
                "reg_type": "holding",
                "address": 34,        //
                "type": "value"
            },
            {
                "name": "Param 35",
                "reg_type": "holding",
                "address": 35,        //
                "type": "value"
            },
            {
                "name": "Param 36",
                "reg_type": "holding",
                "address": 36,        // № версии
                "type": "value"
            },
            {
                "name": "Input 1",
                "reg_type": "holding",
                "address": 53252,        //
                "type": "value"
            },
            {
                "name": "Input 2",
                "reg_type": "holding",
                "address": 53253,        //
                "type": "value"
            },
            {
                "name": "Input 3",
                "reg_type": "holding",
                "address": 53262,        //
                "type": "value"
            },
            {
                "name": "Status 0",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:0:1",        // нулевой бит (первый справа, с младшего бита) маски в регистре 53268 (регистр работы, рабочее состояние, в движении сейчас или нет. 1 - в движении, 0 - в покое)
                "format": "u16"
            }, {
                "name": "Status 1",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:1:1",      // первый бит (второй справа, с младшего бита) маски в регистре 53268  (направление движения. 1 - вниз, закрывается. 0 - вверх, открывается)
                "format": "u16"
            },
            {
                "name": "Status 2",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:2:1",        // питание
                "format": "u16"
            }, {
                "name": "Status 3",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:3:1",      // пусто
                "format": "u16"
            },
            {
                "name": "Status 4",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:4:1",        // нормальное питание - перевод с китайского
                "format": "u16"
            }, {
                "name": "Status 5",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:5:1",      // псамотестирование
                "format": "u16"
            },
            {
                "name": "Status 6",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:6:1",        // самотестирование ошибка
                "format": "u16"
            }, {
                "name": "Status 7",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:7:1",      // пусто
                "format": "u16"
            },
            {
                "name": "Status 8",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:8:1",        // пусто
                "format": "u16"
            }, {
                "name": "Status 9",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:9:1",      // шлагбаум в нижнем положении
                "format": "u16"
            },
            {
                "name": "Status 10",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:10:1",        // шлагбаум в верхнем положении
                "format": "u16"
            }, {
                "name": "Status 11",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:11:1",      // зафиксирован, не двигается
                "format": "u16"
            },
            {
                "name": "Status 12",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:12:1",        // зеленый свет горит
                "format": "u16"
            }, {
                "name": "Status 13",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:13:1",      // красный свет горит
                "format": "u16"
            },
            {
                "name": "Status 14",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:14:1",        // пусто
                "format": "u16"
            }, {
                "name": "Status 15",
                "type": "switch",
                "reg_type": "holding",
                "address": "53268:15:1",      // пусто
                "format": "u16"
            }
        ]
    }
}

В идеале хотелось добавить это устройство в Sprut.Hub, и потом уже привязаться к необходимым статусам и командам виртуальным устройством «Гаражные ворота», но так и не смог написать шаблон :(. Поэтому пришлось привлекать тяжелую артиллерию — IOBROKER.

В IOBROKER создал несколько объектов под эту задачу, и набросал blockly в котором заложена логика работы «Гаражных ворот». Если коротко описать логику, то получается так:

  • Подписываемся на изменения TargetDoorState, поменялась и равна 0 — дергаем команду открыть, равна 1 — команду закрыть

  • подписываемся на Status 11 (шлагбаум в покое) если этот статус навен 0, то шлагбаум двигается и нужно понять куда? Status 1 равен 1 — закрывается, ставим CurrentDoorState на 3. Равен 0 — открывается, ставим CurrentDoorState на 2

  • Если Status 11 равен 1, значит шлагбаум в покое, значит нужно выяснить в каком именно «покое»? Status 9 = 1 — закрыт. Status 10 = 1 — открыт.  CurrentDoorState на 1 и 0 соответственно.


  
    0_userdata.0.office.Шлагбаум.TargetDoorState
    ne
    
    
      
        
        
          
            EQ
            
              
                state.val
              
            
            
              
                0
              
            
          
        
        
          
            
            mqtt.5.devices.spb-03l_1.controls.K1.on
            FALSE
            
              
                1
              
            
          
        
        
          
            EQ
            
              
                state.val
              
            
            
              
                1
              
            
          
        
        
          
            
            mqtt.5.devices.spb-03l_1.controls.K2.on
            FALSE
            
              
                1
              
            
          
        
      
    
  
  
    mqtt.5.devices.spb-03l_1.controls.Status_11
    ne
    
    
      
        
        
          
            EQ
            
              
                state.val
              
            
            
              
                0
              
            
          
        
        
          
            
            
              
                EQ
                
                  
                    val
                    mqtt.5.devices.spb-03l_1.controls.Status_1
                  
                
                
                  
                    1
                  
                
              
            
            
              
                
                0_userdata.0.office.Шлагбаум.CurrentDoorState
                FALSE
                
                  
                    3
                  
                
              
            
            
              
                EQ
                
                  
                    val
                    mqtt.5.devices.spb-03l_1.controls.Status_1
                  
                
                
                  
                    0
                  
                
              
            
            
              
                
                0_userdata.0.office.Шлагбаум.CurrentDoorState
                FALSE
                
                  
                    2
                  
                
              
            
          
        
        
          
            EQ
            
              
                state.val
              
            
            
              
                1
              
            
          
        
        
          
            
            
              
                EQ
                
                  
                    val
                    mqtt.5.devices.spb-03l_1.controls.Status_9
                  
                
                
                  
                    1
                  
                
              
            
            
              
                
                0_userdata.0.office.Шлагбаум.CurrentDoorState
                FALSE
                
                  
                    1
                  
                
              
            
            
              
                EQ
                
                  
                    val
                    mqtt.5.devices.spb-03l_1.controls.Status_10
                  
                
                
                  
                    1
                  
                
              
            
            
              
                
                0_userdata.0.office.Шлагбаум.CurrentDoorState
                FALSE
                
                  
                    0
                  
                
              
            
          
        
      
    
  

На этом собственно все! Шлагбаум отлично управляется с Homekit, можно голосом сказать «Сири, открой шлагбаум» и не искать иконку приложения. Так же видно в каком сейчас положении шлагбаум и если необходимо — закрыть его.

Habrahabr.ru прочитано 5750 раз