В предыдущей статье подробно изложено подключение Modbus RTU устройства к конвертеру Modbus RTU <-> TCP. Подключим конвертер Modbus TCP и, соответственно, устройство Modbus RTU к облаку IoT Hub.
Для начала воспользуемся статьей на сайте Microsoft по подключению modbus устройств. Поскольку объяснение в статье очень подробное, нет смысла дублировать. Для моего оборудования настройки будут такими:
- IP: 192.168.26.110
- Address: 30001 + 1 (смещение) = 30002. Адресация Input Register начинается с 30001 адреса + смещение.
- Port: 8899 — в документации Microsoft и на сайте github с исходниками модуля modbus нет упоминания о том, как указать порт и не сказано какой порт является дефолтным. Посмотрим как отреагирует модуль, если опустить этот параметр.
- HwId — поправил название устройства, MAC адрес оставил из примера, не критично.
- DisplayName — поправил на «Temp».
Результирующий JSON для вставки в properties.
"properties.desired": {
"PublishInterval": "2000",
"SlaveConfigs": {
"TermoSensor": {
"SlaveConnection": "192.168.26.110",
"HwId": "TermoSensor-0a:01:01:01:01:01",
"Operations": {
"Op01": {
"PollingInterval": "1000",
"UnitId": "1",
"StartAddress": "30002",
"Count": "1",
"DisplayName": "Temp"
}
}
}
}
}
}
После развертывания модуля получаем следующиий набор ошибок:
PS C:\WINDOWS\system32> iotedge logs modbus -f
IoT Hub module client initialized.
Desired property change:
{"PublishInterval":"2000","SlaveConfigs":{"TermoSensor":{"SlaveConnection":"192.168.26.110","HwId":"PowerMeter-0a:01:01:01:01:01","Operations":{"Op01":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30002","Count":"1","DisplayName":"Temp"}}}},"$version":1}
Attempt to load configuration: {"PublishInterval":"2000","SlaveConfigs":{"TermoSensor":{"SlaveConnection":"192.168.26.110","HwId":"PowerMeter-0a:01:01:01:01:01","Operations":{"Op01":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30002","Count":"1","DisplayName":"Temp"}}}},"$version":1}
Invalid TcpPort: , set to DefaultTcpPort: 502
Invalid RetryCount: , set to DefaultRetryCount: 10
Invalid RetryInterval: , set to DefaultRetryInterval: 50
Empty CorrelationId: , set to DefaultCorrelationId: DefaultCorrelationId
Connect Slave failed
Connection refused 192.168.26.110:502
Connection lost, reconnecting...
Итак:
- Порт по умолчанию — 502.
- Свойство для указания номера порта: TcpPort. О существовании этого параметра почему-то ничего не говорится в документации.
- Параметры RetryCount и RetryInterval можно не задавать, они есть по умолчанию.
Откорректируем Module Identity Twin. Возможность редактировать Module Identity Twin появляется только после того как модуль устройство развернуто на IoT Edge устройстве.
Добавляем в Module Identity Twin в desired строчку «TcpPort»: «8899», сохраняем. В логах видим:
Desired property change:
{"PublishInterval":"2000","SlaveConfigs":{"TermoSensor":{"SlaveConnection":"192.168.26.110","TcpPort":"8899","HwId":"PowerMeter-0a:01:01:01:01:01","Operations":{"Op01":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30002","Count":"1","DisplayName":"Temp"}}}},"$version":2}
Attempt to load configuration: {"PublishInterval":"2000","SlaveConfigs":{"TermoSensor":{"SlaveConnection":"192.168.26.110","TcpPort":"8899","HwId":"PowerMeter-0a:01:01:01:01:01","Operations":{"Op01":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30002","Count":"1","DisplayName":"Temp"}}}},"$version":2}
Invalid RetryCount: , set to DefaultRetryCount: 10
Invalid RetryInterval: , set to DefaultRetryInterval: 50
Empty CorrelationId: , set to DefaultCorrelationId: DefaultCorrelationId
Saving reported properties: {"PublishInterval":2000,"SlaveConfigs":{"TermoSensor":{"Operations":{"Op01":{"PollingInterval":1000,"UnitId":1,"StartAddress":"30002","Count":1,"DisplayName":"Temp","CorrelationId":"DefaultCorrelationId"}},"SlaveConnection":"192.168.26.110","RetryCount":10,"RetryInterval":50,"TcpPort":8899,"HwId":"PowerMeter-0a:01:01:01:01:01","BaudRate":null,"StopBits":null,"DataBits":null,"Parity":null}}}
30002: 261
30002: 261
30002: 261
Соответственно, видим, что модуль корректно отрабатывает смену настроек в desired. Появился введенный параметр «TcpPort»:»8899″ и он корректно отработал.
Добавим в desired операцию по считыванию поля влажности получаемого с адреса 300001 + 2.
"Op02": {
"PollingInterval": "1000",
"UnitId": "1",
"StartAddress": "30003",
"Count": "1",
"DisplayName": "Humidity"
}
Финально properties.desired должен выглядеть так:
"properties.desired": {
"PublishInterval": "2000",
"SlaveConfigs": {
"TermoSensor": {
"SlaveConnection": "192.168.26.110",
"HwId": "TermoSensor-0a:01:01:01:01:01",
"TcpPort": "8899",
"Operations": {
"Op01": {
"PollingInterval": "5000",
"UnitId": "1",
"StartAddress": "30002",
"Count": "1",
"DisplayName": "Temp"
},
"Op02": {
"PollingInterval": "5000",
"UnitId": "1",
"StartAddress": "30003",
"Count": "1",
"DisplayName": "Humidity"
}
}
}
}
}
Модуль получает информацию о смене desired property и выводит новые данные:
Desired property change:
{"PublishInterval":"2000","SlaveConfigs":{"TermoSensor":{"SlaveConnection":"192.168.26.110","HwId":"PowerMeter-0a:01:01:01:01:01","Operations":{"Op01":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30002","Count":"1","DisplayName":"Temp"},"Op02":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30003","Count":"1","DisplayName":"Humidity"}},"TcpPort":"8899"}},"$version":3}
Attempt to load configuration: {"PublishInterval":"2000","SlaveConfigs":{"TermoSensor":{"SlaveConnection":"192.168.26.110","HwId":"PowerMeter-0a:01:01:01:01:01","Operations":{"Op01":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30002","Count":"1","DisplayName":"Temp"},"Op02":{"PollingInterval":"1000","UnitId":"1","StartAddress":"30003","Count":"1","DisplayName":"Humidity"}},"TcpPort":"8899"}},"$version":3}
Invalid RetryCount: , set to DefaultRetryCount: 10
Invalid RetryInterval: , set to DefaultRetryInterval: 50
Empty CorrelationId: , set to DefaultCorrelationId: DefaultCorrelationId
Empty CorrelationId: , set to DefaultCorrelationId: DefaultCorrelationId
Saving reported properties: {"PublishInterval":2000,"SlaveConfigs":{"TermoSensor":{"Operations":{"Op01":{"PollingInterval":1000,"UnitId":1,"StartAddress":"30002","Count":1,"DisplayName":"Temp","CorrelationId":"DefaultCorrelationId"},"Op02":{"PollingInterval":1000,"UnitId":1,"StartAddress":"30003","Count":1,"DisplayName":"Humidity","CorrelationId":"DefaultCorrelationId"}},"SlaveConnection":"192.168.26.110","RetryCount":10,"RetryInterval":50,"TcpPort":8899,"HwId":"PowerMeter-0a:01:01:01:01:01","BaudRate":null,"StopBits":null,"DataBits":null,"Parity":null}}}
30002: 262
30003: 303
30002: 262
30003: 303
После проверки стоит сразу изменить интервал опроса сенсора хотя-бы до 5000 мс, в противном случае за час в облако уйдет 3600 х 2= 7200 сообщений и 8000 сообщений бесплатного аккаунта быстро закончатся.
Не забывайте останавливать работу модуля,
когда отправка данных в IoT Hub не нужна!
После того как IoT Hub получит 8000 сообщений, Microsoft заблокирует доступ к нему. Нельзя даже зайти на IoT Edge устройства, чтобы исправить настройки. Доступ появится на следующий день.
В исходниках модуля нашел описание параметров:
{
"PublishInterval": "push interval in millisecond",
"SlaveConfigs": {
"Slave01": {
"SlaveConnection": "ipv4 address or the serial port name to Modbus device",
"TcpPort": "tcp port of Modbus connection, default is 502",
"RetryCount": "the max retry attempt when socket receive buffer is empty, default is 10",
"RetryInterval": "the wait interval in millisecond between each retry, default is 50",
"HwId": "unique HW Id defined by user",
"BaudRate": "baud rate of serial communication (Modbus RTU only)",
"DataBits": "data bits of serial communication (Modbus RTU only)",
"StopBits": "stop bits of serial communication (Modbus RTU only)",
"Parity": "parity of serial communication (Modbus RTU only)",
"FlowControl": "flow control of serial communication (Modbus RTU only)",
"Operations": {
"Op01": {
"PollingInterval": "polling interval in millisecond",
"UnitId": "unit id of Modbus device",
"StartAddress": "starting address of read request",
"Count": "unit count of read request",
"DisplayName": "alternative name defined by user",
"CorrelationId": "id used to group output message"
}
}
}
}
}
Ссылки
Для полноты информации можно почитать.