Pull to refresh

Arduino & OpenHAB

Reading time 8 min
Views 52K
В предыдущей статье мы познакомились с промышленным стандартом Modbus и встроили его поддержку в Arduino, осталось состыковать устройство с платформой OpenHAB.

В этот раз мы узнаем, как настроить плагин и интерфейс OpenHAB для работы с устройством, основы адресации и отладки протокола Modbus. В работе представлен эксперимент с исходным кодом плагина, а на страничке vk.com/myremoter можно обсудить открытый контроллер умного дома, который мы будем использовать в следующих экспериментах.

Но, давайте ещё раз посмотрим, какие преимущества даст нам применение стандарта Modbus?
Современный дом — сложное инженерное сооружение, где управление освещением не самая главная функция. Кроме датчиков в доме устанавливают системы кондиционирования и вентиляции, управления отоплением, дренажными насосами и скважинами. Такие задачи можно поручить специальному или промышленному контроллеру, в этом случае протокол Modbus поможет объединить все инженерные системы в единую сеть, а простой и недорогой контроллер, работающий на одной с ними шине, добавит дополнительный контроль и функционал, сэкономив немало средств. В пользу этого низкие требования к оборудованию, открытость стандарта, массовость его применения, хотя, быть может, основной секрет популярности Modbus его гибкость при стыковке программ и оборудования.

Ну что же, пора приступать к работе.

Установим OpenHAB, конфигуратор и эмулятор null модемного соединения
Посмотрим документацию, откроем страницу загрузки и скачаем Runtime core и Demo setup, распакуем их в C:\openhab, затем скачаем openHAB Designer и распакуем его в C:\openhab\designer. Проверим наличие Java, выполнив java –version, если Java отсутствует, выполним её установку по инструкции.
Запустим OpenHAB, выполнив C:\openhab\start.bat и откроем веб-интерфейс по ссылке localhost:8080/openhab.app?sitemap=demo

Теперь качаем и устанавливаем com0com, запускаем Setup из меню программы, смотрим, для каких последовательных портов создана виртуальная пара (у меня это COM7 + COM8). Используем их для связи OpenHAB и симулятора.

Затем установим плагин Modbus Tcp Binding который обеспечит взаимодействие OpenHAB с modbus устройствами. Откроем страницу загрузки, скачаем Addons, распакуем org.openhab.binding.modbus-1.6.2.jar в папку C:\openhab\addons.

После этого настроим связь плагина с устройством, для этого запустим конфигуратор OpenHAB и откроем файл openhab_default.cfg. Найдём раздел Modbus Binding и добавим в конец этого раздела следующую информацию, затем сохраним файл.

modbus:serial.slave1.connection=COM7
modbus:serial.slave1.id=1
modbus:serial.slave1.start=0
modbus:serial.slave1.length=4
modbus:serial.slave1.type=discrete

modbus:serial.slave2.connection=COM7
modbus:serial.slave2.id=1
modbus:serial.slave2.start=16
modbus:serial.slave2.length=4
modbus:serial.slave2.type=coil

modbus:serial.slave3.connection=COM7
modbus:serial.slave3.id=1
modbus:serial.slave3.start=2
modbus:serial.slave3.length=3
modbus:serial.slave3.type=input

modbus:serial.slave4.connection=COM7
modbus:serial.slave4.id=1
modbus:serial.slave4.start=5
modbus:serial.slave4.length=3
modbus:serial.slave4.type=holding

Настройки содержат 4 группы адресов сопоставленных с регистрами устройства. В каждой группе указан номер последовательного порта, modbus адрес контроллера, modbus адрес первого в группе регистра, количество регистров в группе и тип этих регистров. Группы адресов станут связующим звеном между элементами OpenHAB и устройством.

Теперь настроим связь OpenHAB с регистрами устройства. Откроем в конфигураторе файл demo.items и добавим в конец этого файла следующий код:

Group	FF_Modbus 	"Modbus" 	(All)
Contact MB_DT0		"DT0 [MAP(en.map):%s]"	(FF_Modbus){modbus="slave1:0"}
Contact MB_DT1		"DT1 [MAP(en.map):%s]"	(FF_Modbus){modbus="slave1:1"}
Contact MB_DT2		"DT2 [MAP(en.map):%s]"	(FF_Modbus){modbus="slave1:2"}
Contact MB_BTN		"BTN [MAP(en.map):%s]"	(FF_Modbus){modbus="slave1:3"}
Switch  MB_CL16		"CL16" 			(FF_Modbus){modbus="slave2:0"}
Switch  MB_CL17		"CL17" 			(FF_Modbus){modbus="slave2:1"}
Switch  MB_CL18		"CL18" 			(FF_Modbus){modbus="slave2:2"}
Switch  MB_LED 	 	"LED" 			(FF_Modbus){modbus="slave2:3"}
Number  MB_INPT2	"INPT2[%d]" 		(FF_Modbus){modbus="slave3:0"}
Number  MB_INPT3	"INPT3[%d]" 		(FF_Modbus){modbus="slave3:1"}
Number  MB_INPT4	"INPT4[%d]" 		(FF_Modbus){modbus="slave3:2"}
Number  MB_HOLD5	"HOLD5[%d]" 		(FF_Modbus){modbus="slave4:0"}
Number  MB_HOLD6	"HOLD6[%d]" 		(FF_Modbus){modbus="slave4:1"}
Number  MB_HOLD7	"HOLD7[%d]" 		(FF_Modbus){modbus="slave4:2"}

В первой строке определена группа FF_Modbus которая объединит добавляемые элементы. Каждый элемент задан его типом, названием, форматом текстовой надписи, списком групп в которых он состоит и настройками связи с устройством. Мы использовали элементы трёх типов – Contact, Switch, Number, а в настройках связи указали тип (modbus), имя одной из групп заданных в настройках плагина (например, slave1) и порядковый номер регистра в этой группе.

Наглядно представить взаимосвязь между регистрами контроллера, обычными и логическими адресами Modbus, группами регистров в настройках плагина и элементами OpenHAB нам поможет следующая таблица.



Настало время отобразить наши элементы в интерфейсе. Откроем в конфигураторе файл demo.sitemap и добавим в него описание двух фреймов:

	Frame {
		Group item=FF_Modbus icon="attic"
	}

	Frame {
		Text item= MB_DT0
		Text item= MB_DT1
		Text item= MB_DT2
		Text item= MB_BTN
		Switch item= MB_CL16
		Switch item= MB_CL17
		Switch item= MB_CL18
		Switch item= MB_LED
		Text item= MB_INPT2 
		Text item= MB_INPT3
		Text item= MB_INPT4
		Setpoint item= MB_HOLD5 minValue=0 maxValue=50 step=1
		Setpoint item= MB_HOLD6 minValue=0 maxValue=500 step=10
		Setpoint item= MB_HOLD7 minValue=0 maxValue=500 step=100
	}

Первый фрейм содержит группу FF_Modbus, он отобразит все элементы, входящие в неё.
Во втором фрейме каждый элемент имеет индивидуальные настройки интерфейса. В настройках указан тип виджета, связь с элементом и в некоторых случаях дополнительные параметры. Ознакомиться с принципами построения интерфейса OpenHAB можно в этом описании.



Попробуем протестировать созданную конфигурацию на симуляторе.

Скачаем симулятор RSsim 8.19 со страницы загрузки и распакуем его в папку C:\arduino. У программы есть интересная особенность, она бесплатна, и доступны её исходные коды, но при этом она требует регистрации. В инструкции сказано, что для регистрации нужно открыть окно About MOD_RSsim, нажать кнопку «Register», в поле registration name ввести “Completely Free”, а в поле registration key “66840713”.

Запускаем симулятор, выполняем регистрацию, в поле Port выбираем MODBUS RS-232, нажимаем кнопку , в диалоге настроек последовательного порта указываем второй порт виртуальной пары (например, COM8), скорость 9600, 1 стоповый бит, нет контроля чётности.





Запускаем OpenHAB и открываем веб-интерфейс, щёлкаем по переключателям и смотрим, как изменяются значения в таблице Coil симулятора, затем открываем в симуляторе таблицу Digital Inputs, изменяем значения в регистрах с адресами 10001-10004 и контролируем изменение состояния контактов в веб-интерфейсе. После этого открываем таблицу Analog Inputs, вводим значения в регистры 30003-30005 и контролируем изменение значений в полях INPT2- INPT4. И в завершении задаём значения в полях HOLD5-HOLD7 и проверяем их соответствие в регистрах 40006-40008 симулятора. Вы уже обратили внимание, что в симуляторе использована логическая адресация. Для того, что бы окончательно не запутаться в регистрах, адресах и элементах используйте ранее приведённую таблицу.

На первый взгляд всё получилось, но если внимательно посмотреть на консоль OpenHAB заметно, что при каждом опросе контроллера, плагин отправляет в шину событие, даже если ничего не изменилось. Похоже, что для решения этой проблемы придётся изучить и доработать плагин.

Описание решения
Краткую информацию по архитектуре плагина OpenHAB можно посмотреть здесь.
Вопросы настройки среды разработки для OpenHAB отражены в этом руководстве.

В процессе работы изменения коснулись двух файлов ModbusGenericBindingProvider.java и ModbusBinding.java.

ModbusGenericBindingProvider содержит вложенный класс ModbusBindingConfig который хранит конфигурацию элемента, создадим в нём механизм сохранения текущего состояния.

Добавим в этот класс переменную
private State mb_itemState = UnDefType.NULL;

Исправим код функции
State getItemState() {
	return mb_itemState;
}

И добавим функцию
void setItemState(State state) {
	mb_itemState = state;
}

Класс ModbusBinding содержит два метода:
protected void internalUpdateItem(String slaveName, InputRegister[] registers, String itemName)
Который отправляет событие обновления в шину OpenHAB для данных типа holding.

protected void internalUpdateItem(String slaveName, BitVector coils, String itemName)
Который отправляет событие обновления в шину OpenHAB для данных типа coil.

Эти методы вызываются каждый раз после опроса ведомого устройства. Поправим их код таким образом, чтобы отправлять событие только в том случае, если данные изменились.

protected void internalUpdateItem(String slaveName, InputRegister[] registers,String itemName) {
	for (ModbusBindingProvider provider : providers) {
		if ( !provider.providesBindingFor(itemName) ) {
			continue;
		}
		ModbusBindingConfig config = provider.getConfig(itemName);
		if ( !config.slaveName.equals(slaveName)) {
			continue;
		}
		String slaveValueType = modbusSlaves.get(slaveName).getValueType();
		double rawDataMultiplier = modbusSlaves.get(slaveName).getRawDataMultiplier();
		State newState = extractStateFromRegisters(registers, config.readRegister, slaveValueType);
		/* receive data manipulation */
		if (config.getItem() instanceof SwitchItem) {
			newState = newState.equals(DecimalType.ZERO) ? OnOffType.OFF : OnOffType.ON;
		}
		if (( rawDataMultiplier != 1 ) && (config.getItem() instanceof NumberItem)) {
			double tmpValue = (double)((DecimalType)newState).doubleValue() * rawDataMultiplier;
			newState =  new DecimalType( String.valueOf(tmpValue) );
		}
		State currentState = config.getItemState();
		if (! newState.equals(currentState)) {
			eventPublisher.postUpdate(itemName, newState);
			config.setItemState(newState); //!!!
		}
	}
}
	
protected void internalUpdateItem(String slaveName, BitVector coils, String itemName) {
	for (ModbusBindingProvider provider : providers) {
		if (provider.providesBindingFor(itemName)) {
			ModbusBindingConfig config = provider.getConfig(itemName);
			if (config.slaveName.equals(slaveName)) {
				boolean state = coils.getBit(config.readRegister);
				State currentState = provider.getConfig(itemName).getItemState();
				State newState = provider.getConfig(itemName).translateBoolean2State(state);
				if (!newState.equals(currentState)) {
					eventPublisher.postUpdate(itemName, newState);
					config.setItemState(newState); //!!!
				}
			}
		}
	}
}


Исправленный плагин вместе с исходными кодами размещён в этом репозитории.
Останавливаем OpenHAB, скачиваем и копируем org.openhab.binding.modbus-1.6.2.jar в папку C:\openhab\addons. Запускаем C:\openhab\start.bat, открываем веб-интерфейс и проверяем работу еще раз. Теперь всё хорошо, события появляются только тогда, когда изменяется значение соответствующего регистра.

Осталось самое интересное — проверить взаимодействие OpenHAB непосредственно с контроллером из предыдущей статьи.
Подключаем USB кабель контроллера к компьютеру, смотрим, на какой порт встал переходник (например, COM6), останавливаем OpenHAB, открываем в конфигураторе файл openhab_default.cfg, в разделе Modbus Binding в параметрах связи для каждой группы регистров исправляем номер порта (например, modbus:serial.slave1.connection=COM6). Сохраняем файл, запускаем OpenHAB и открываем веб-интерфейс. Попробуем поменять значение в полях HOLD5…HOLD7 и СL16…СL18, при этом должно поменяться значение в соответствующем поле INPT2..INPT4 и DT0..DT2, затем нажмём на кнопку макета, при этом должно поменяться значение в поле BTN, щёлкнем по полю LED, при этом должен загореться или потухнуть светодиод.

Что мы получили в результате нашей работы:
1. состыковали modbus устройство с платформой OpenHAB;
2. познакомились с принципами построения интерфейса в OpenHAB;
3. познакомились с внутренней структурой плагина, это позволило исправить неточность в его работе.

Выводы:
На основе контроллера Arduino и платформы OpenHAB не трудно создать программно-аппаратное решение для автоматизации, например в системе Умный дом. Для дальнейших практических экспериментов попробуем определить основной функционал и требования к контроллеру и системе в целом, для обсуждения этого вопроса создана страничка открытого проекта vk.com/myremoter.
Tags:
Hubs:
+16
Comments 4
Comments Comments 4

Articles