Ну а пока про то, как подружить его с Beckhoff. Структура ПЛК, похоже, препятствует прямому обращению по Modbus к битовым регистрам (coils) - по крайней мере, обратиться к ним у меня не получилось ни из OpenHAB, ни из внешних программ типа Modbus Poll - при обращении "read coil", "write coil" - сообщение об ошибке "неправильный адрес" в ответ. Если кто-то знает, как это победить - буду признателен за совет. Возможно, поможет настройка в KS2000. Ну а пока что я научился только читать-писать пословно в область памяти с адреса 16384 (0х4000) - это позволяет мне обмениваться данными с OpenHAB, хотя и не дает возможности напрямую взаимодействовать с аппаратными входами-выходами устройства. Приходится прибегать к программным костылям, но это проблема не самая страшная.
Итак, конфиг OpenHAB (не забудьте скопировать в папку addons соответствующий байндинг для Modbus - org.openhab.binding.modbus-1.7.1):
Код: Выделить всё
modbus:tcp.slave1.connection=192.168.0.108:502
modbus:tcp.slave1.id=1
modbus:tcp.slave1.start=0
modbus:tcp.slave1.length=16
modbus:tcp.slave1.type=holding
modbus:tcp.slave2.connection=192.168.0.108:502
modbus:tcp.slave2.id=1
modbus:tcp.slave2.start=16384
modbus:tcp.slave2.length=16
modbus:tcp.slave2.type=holding
описание тестовых айтемов (для отслеживания и управления выходами Beckhoff):
Код: Выделить всё
Switch Beckhoff_Input10 (gModbus) { modbus="slave1:0"}
Switch Beckhoff_Input11 (gModbus) { modbus="slave1:1"}
Switch Beckhoff_Input20 (gModbus) { modbus="slave2:0"}
Switch Beckhoff_Input21 (gModbus) { modbus="slave2:1"}
Код: Выделить всё
Frame {
Group item=gModbus label="Modbus"
}
Код: Выделить всё
VAR_GLOBAL
OH_I1 AT %QB128: INT; // "slave1:0"
OH_I2 AT %QB130: INT; // "slave1:1"
OH_O1 AT %MB0: INT; // "slave2:0"
OH_O2 AT %MB2: INT; // "slave2:1"
IN_E_K1 AT %IX0.0: BOOL; // кнопка аппаратного входа
IN_E_K2 AT %IX0.1: BOOL; // кнопка аппаратного входа
OUT_E_L1 AT %QX0.0: BOOL; // выход на реле (лампочка)
OUT_E_L2 AT %QX0.1: BOOL; // выход на реле (вентилятор)
END_VAR
Без участия OpenHAB все было достаточно просто - два таймера, каждый со своей логикой в ПЛК, и все работало как часы. Но подключив все это хозяйство к OpenHAB, сразу возникло желание не только видеть в OpenHAB состояние лампочки и вентилятора, но и иметь возможность независимо включать и выключать оба прибора из программной оболочки, параллельно с аппаратной кнопкой. Что естественно привело к конфликту приоритетов - что считать более главным - нажатия кнопки или команды OpenHAB.
В конечном итоге после двух вечеров экспериментов решение приняло такой вид:
Функциональный блок кнопки без таймера (нужно для принудительного включения-выключения):
Код: Выделить всё
FUNCTION_BLOCK SM_BUTT
VAR_INPUT
IN: BOOL;
END_VAR
VAR_OUTPUT
Q:BOOL := FALSE;
END_VAR
VAR
MEM:BOOL := FALSE;
END_VAR
IF IN AND NOT MEM THEN
Q:= NOT Q;
END_IF
MEM := IN;
Код: Выделить всё
FUNCTION_BLOCK SM_BUTT_TIMER
VAR_INPUT
IN: BOOL; (* вход за которым следим *)
TC: TIME; (* время длинного нажатия *)
TC2: TIME; (* время работы вентилятора *)
END_VAR
VAR_OUTPUT
Q1: BOOL := FALSE; (* состояние лампочки *)
Q2: BOOL := FALSE; (* состояние вентилятора *)
END_VAR
VAR
TX: TON;
TX2: TON;
MEM1, MEM2, MEM3: BOOL;
END_VAR
TX (IN := IN, PT := TC); (* отправляем данные в таймеры *)
TX2(IN := NOT Q1, PT:=TC2);
IF IN AND NOT MEM1 THEN (* если нажали аппаратную кнопку *)
Q1 := NOT Q1; (* сразу меняем состояние лампочки *)
END_IF
IF TX.Q AND NOT MEM2 THEN (* сигнал таймера1 когда лампочка горит *)
Q2 := Q1; (* состояние вентилятора = состоянию лампочки *)
END_IF
IF TX2.Q AND NOT MEM3 THEN (* сигнал от таймера 2 *)
IF NOT Q1 THEN (* если лампочка выключена *)
Q2 := FALSE; (* выключить и вентилятор *)
END_IF
END_IF
MEM1 := IN;
MEM2 := TX.Q;
MEM3 := TX2.Q;
Код: Выделить всё
PROGRAM MAIN
VAR
BUTT_E_B1:SM_BUTT;
BUTT_E_B2:SM_BUTT_TIMER;
LampState , FanState: BOOL := FALSE;
END_VAR
IF NOT (INT_TO_BOOL(OH_O1) = LampState) THEN (* Если поменялось состояние лампочки в OpenHAB *)
BUTT_E_B2 (IN:=TRUE, TC:=t#1s, TC2:=t#10s); (* "нажать" на аппаратную кнопку программно *)
ELSE
BUTT_E_B2 (IN:=IN_E_K1, TC:=t#1s, TC2:=t#10s); (* иначе запустить обычную проверку нажатия *)
END_IF
OUT_E_L1 := BUTT_E_B2.Q1; (* переключить выход лампочки *)
OH_O1 := BOOL_TO_INT(OUT_E_L1); (* и сохранить значение в OpenHAB *)
LampState := OUT_E_L1;
IF NOT (INT_TO_BOOL(OH_O2) = FanState) THEN (* если поменялось состояние вентилятора в OpenHAB *)
FanState := INT_TO_BOOL(OH_O2); (* переключить вентилятор *)
ELSE
FanState := BUTT_E_B2.Q2; (* иначе присвоить вентилятору состояние выхода по значению кнопки *)
END_IF
OUT_E_L2 := FanState;
OH_O2 := BOOL_TO_INT(OUT_E_L2);
Но в таком виде все работает отлично - аппаратная кнопка полностью выполняет свою функцию, все включения-выключения лампочки и вентилятора видны в интерфейсе OpenHAB, и принудительное переключение элементов интерфейса мышкой вызывает соответствующую реакцию выходов Beckhoff.
Как-то так. Если будут вопросы - задавайте. Ну и буду рад вашим собственным описаниям интеграции устройств с OpenHAB и прикольных сценариев для него, так как только начинаю его осваивать. Но подчеркну еще раз - система полностью удовлетворяет мои немудреные потребности. Я не представляю задачи по домашней автоматизации, которую с ее помощью невозможно было бы решить. Недостаток - отсутствие нормальной документации, но активное сообщество частично это компенсирует. Любую задачу или проблему кто-то уже решал до тебя, нужно только найти это обсуждение в гугле.