Indoor Air Quality (IAQ) sensor modbus connection not working
-
Hello,
I'm trying to connect an RW-TH version of the Unipi indoor air quality sensor (IAQ) to an Axon S105 via modbus on RS-485 (port 3).
After modifying /etc/evok.conf and adding /etc/hw_definitions/testIAQ.yaml the web interface does not show the system parameters and I can't poll any IAQ parameters via json or rest API./etc/evok.conf:
#!!! Do not use '#' for comments !!! [MAIN] ; !!! ALL MAIN SECTION OPTIONS ARE MANDATORY !!! config_version = 2.5 ; Configuration file version, DO NOT CHANGE! use_schema_verification = False ; Enabling this will deny any requests that do not match the JSON Schema; NOTE THAT THIS RESULTS IN A SIGNIFICANT INCREASE IN LATENCY AND SHOULD NOT BE USED EXCEPT FOR TESTING log_level = DEBUG ; Minimum severity of messages to be logged; one of INFO, DEBUG, WARNING, ERROR, CRITICAL log_file = /var/log/evok.log ; Log file to use; will be cleared on boot port = 8080 ; !!! Internal API port - only change if you are certain you know what you are doing; FOR OUR WEB INTERFACE THE PORT SHOULD BE CHANGED IN "/etc/evok-nginx.conf" INSTEAD !!! webhook_enabled = False ; Enables webhook notification - see e.g. https://sendgrid.com/blog/whats-webhook/ webhook_address = http://127.0.0.1:80 ; Put your server endpoint address here (e.g. http://123.123.123.123:/wh ) webhook_device_mask = ["input","wd"] ; List of device types to notify on (written as a JSON list) - adding AI will generate a large amount of messages! webhook_complex_events = False ; EVOK will send POST requests with the same data as WebSocket, rather than an empty GET request soap_server_enabled = False ; Enables the simple SOAP server; use only if you need the functionality soap_server_port = 8081 ; !!! IF SOAP SERVER IS ENABLED, THIS PORT NEEDS TO BE UNIQUE (i.e. different from the port setting above) !!! force_immediate_state_changes = False ; Outputs will return the value they are set to, rather than the value that the device is currently aware of websocket_all_filtered = False ; 'All' WebSocket requests will be subject to the filtering set by 'filter' [NEURON_1] global_id = 1 ; Mandatory, REQUIRED TO BE UNIQUE allow_register_access = False ; Optional, False default scan_frequency = 10 ; Optional, 10 default, scanning frequency in [Hz] scan_enabled = True ; Optional, True default ; Below you can find examples for connecting devices over UART; first example is a Neuron extension while the second is a custom third-party device ; Devices sharing a port use the port settings of the first device on that port (baud rate, parity, stop bits) ; !!! Note that device_name has to match a filename in the /etc/hw_definitions directory !!! See /etc/hw_definitions/CUSTOM_MODBUS_DEVICE.yaml for an example [EXTENSION_1] global_id = 2 ; Mandatory, REQUIRED TO BE UNIQUE device_name = testIAQ ; Mandatory, must match name of .yaml modbus map file in /etc/hw_definitions modbus_uart_port = /dev/ttyS1 ; Mandatory neuron_uart_circuit = 1_01 ; Optional, allows associating extensions with specific Neuron UART-over-Modbus ports (not possible for non-Modbus UART ports, e.g. /dev/ttyUSB0 or /dev/ttyS0) allow_register_access = True ; Optional, False default, is mandatory with third-party devices address = 1 ; Optional, 1 default scan_frequency = 10 ; Optional, 10 default, scanning frequency in [Hz] scan_enabled = True ; Optional, True default ; Note that the following settings will be inherited by other devices sharing the same port, i.e. /dev/extcomm/0/0 baud_rate = 19200 ; Optional, NEEDS UNIPI IMAGE TO WORK WITH UNIPI SERIAL PORTS! USE API TO CONFIGURE UART MANUALLY IF USING STANDARD RASPBIAN parity = N ; Optional, NEEDS UNIPI IMAGE TO WORK WITH UNIPI SERIAL PORTS! USE API TO CONFIGURE UART MANUALLY IF USING STANDARD RASPBIAN stop_bits = 1 ; Optional, NEEDS UNIPI IMAGE TO WORK WITH UNIPI SERIAL PORTS! USE API TO CONFIGURE UART MANUALLY IF USING STANDARD RASPBIAN ;[EXTENSION_2] ;global_id = 3 ; Mandatory, REQUIRED TO BE UNIQUE ;device_name = CUSTOM MODBUS DEVICE ; Mandatory, must match name of .yaml modbus map file in /etc/hw_definitions ;modbus_uart_port = /dev/extcomm/0/0 ; Mandatory ;neuron_uart_circuit = 1_01 ; Optional, allows associating extensions with specific Neuron UART-over-Modbus ports (not possible for non-Modbus UART ports, e.g. /dev/ttyUSB0 or /dev/ttyS0) ;allow_register_access = True ; Mandatory with third-party devices ;address = 1 ; Optional, 15 default ;scan_frequency = 2 ; Optional, 1 default, scanning frequency in [Hz] ;scan_enabled = True ; Optional, True default [OWBUS_1] owbus = /dev/i2c-0 ; Mandatory, scanned bus (--i2c=/dev/i2c-1:ALL or localhost:2122 or 'u' for USB dongle) interval = 3 ; Mandatory, [s] length of sensor reading scan_interval = 300 ; Mandatory, [s] How often the scanning is done ; See below for 1Wire extension module configuration ; Example for the 1W-4R/4DI extension module, 1W-8R is almost the same, only with inputs instead of relays ; ; - Map a new 1Wire sensor with the appropriate address, type and interval ; - The syntax can be either SENSOR or 1WDEVICE ; - Setting the correct reading interval is crucial to achieve ideal performance; the default interval is 15s ; ;[1WDEVICE_2] ;bus = 1 ;address = 29F39A17000000BC ;type = DS2408 ;interval = 1 ; ;[1WRELAY_10] ;sensor = 2 ;pin = 0 ; ;[1WRELAY_11] ;sensor = 2 ;pin = 1 ; ;[1WRELAY_12] ;sensor = 2 ;pin = 2 ; ;[1WRELAY_13] ;sensor = 2 ;pin = 3 ; ;[1WINPUT_20] ;sensor = 2 ;pin = 4 ; ;[1WINPUT_21] ;sensor = 2 ;pin = 5 ; ;[1WINPUT_22] ;sensor = 2 ;pin = 6 ; ;[1WINPUT_23] ;sensor = 2 ;pin = 7
/etc/hw_definitions/testIAQ.yaml:
# unipi iaq1926 - Indoor air quality sensor # File version: 1.0 # Min. firmware version: 3.00 --- type: testIAQ modbus_register_blocks: - board_index : 1 start_reg : 0 count : 86 frequency : 50 type : input modbus_features: - type : UNIT_REGISTER name : "temperature" count : 1 major_group : 1 value_reg : 0 unit : " C" datatype : float32 - type : UNIT_REGISTER name : "rel_humid" count : 1 major_group : 1 value_reg : 6 unit : "%" datatype : float32 - type : UNIT_REGISTER name : "dew_point" count : 1 major_group : 1 value_reg : 8 unit : " C" datatype : float32 - type : UNIT_REGISTER name : "abs_humid" count : 1 major_group : 1 value_reg : 10 unit : "%" datatype : float32 - type : UNIT_REGISTER name : "CO2" count : 1 major_group : 1 value_reg : 18 unit : "ppm" datatype : float32 - type : UNIT_REGISTER name : "VOC_index" count : 1 major_group : 1 value_reg : 26 unit : "N/A" datatype : float32 - type : UNIT_REGISTER name : "VOC_precision" count : 1 major_group : 1 value_reg : 28 unit : "N/A" datatype : float32 - type : UNIT_REGISTER name : "VOC_CO2_eq" count : 1 major_group : 1 value_reg : 34 unit : "ppm" datatype : float32 - type : UNIT_REGISTER name : "ambient_light" count : 1 major_group : 1 value_reg : 42 unit : "lux" datatype : float32 - type : UNIT_REGISTER name : "atm_pressure" count : 1 major_group : 1 value_reg : 76 unit : "hPa" datatype : float32 - type : UNIT_REGISTER name : "uptime" count : 1 major_group : 1 value_reg : 84 unit : "sec" datatype : float32
Config web page before enabling modbus extension:
Config web page after enabling modbus extension:
The Axon is polling the IAQ sensor periodically as expected, and the sensor responds. The waveforms look good on the oscilloscope.
When polling the sensor manually from command line with mbpoll, the received data looks good:root@S105-sn715:/home/unipi# mbpoll -1 -a 1 -b 19200 -t 3 -r 0 -c 86 -P none /dev/ttyS1 -0 mbpoll 1.4-12 - FieldTalk(tm) Modbus(R) Master Simulator Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; type 'mbpoll -w' for details. Protocol configuration: Modbus RTU Slave configuration...: address = [1] start reference = 0, count = 86 Communication.........: /dev/ttyS1, 19200-8N1 t/o 1.00 s, poll rate 1000 ms Data type.............: 16-bit register, input register table -- Polling slave 1... [0]: 16824 [1]: 59049 (-6487) [2]: 32704 [3]: 0 [4]: 32704 [5]: 0 [6]: 16913 [7]: 53026 (-12510) [8]: 16620 [9]: 53073 (-12463) [10]: 16624 [11]: 48977 (-16559) [12]: 32704 [13]: 0 [14]: 32704 [15]: 0 [16]: 32704 [17]: 0 [18]: 32704 [19]: 0 [20]: 32704 [21]: 0 [22]: 32704 [23]: 0 [24]: 32704 [25]: 0 [26]: 16864 [27]: 30269 [28]: 16256 [29]: 0 [30]: 32704 [31]: 0 [32]: 32704 [33]: 0 [34]: 17404 [35]: 44002 (-21534) [36]: 32704 [37]: 0 [38]: 32704 [39]: 0 [40]: 32704 [41]: 0 [42]: 16757 [43]: 9542 [44]: 32704 [45]: 0 [46]: 32704 [47]: 0 [48]: 32704 [49]: 0 [50]: 32704 [51]: 0 [52]: 32704 [53]: 0 [54]: 32704 [55]: 0 [56]: 32704 [57]: 0 [58]: 32704 [59]: 0 [60]: 32704 [61]: 0 [62]: 32704 [63]: 0 [64]: 32704 [65]: 0 [66]: 32704 [67]: 0 [68]: 32704 [69]: 0 [70]: 32704 [71]: 0 [72]: 32704 [73]: 0 [74]: 32704 [75]: 0 [76]: 17532 [77]: 61276 (-4260) [78]: 32704 [79]: 0 [80]: 32704 [81]: 0 [82]: 32704 [83]: 0 [84]: 17590 [85]: 40960 (-24576)
The /var/log/evok.log does not contain any useful information despite the fact that the log level was set to DEBUG:
2020-09-18 16:09:19,489 - evok - INFO - Starting using config file /etc/evok.conf 2020-09-18 16:09:19,554 - evok - INFO - YAML Definition loaded: xG18.yaml, type: 3, definition count 0 2020-09-18 16:09:19,624 - evok - INFO - YAML Definition loaded: xS10.yaml, type: 3, definition count 1 2020-09-18 16:09:19,708 - evok - INFO - YAML Definition loaded: xS50.yaml, type: 3, definition count 2 2020-09-18 16:09:19,777 - evok - INFO - YAML Definition loaded: xS40.yaml, type: 3, definition count 3 2020-09-18 16:09:19,879 - evok - INFO - YAML Definition loaded: /etc/hw_definitions/BuiltIn/S105.yaml, type: UniPiBuiltIn 2020-09-18 16:09:19,944 - evok - INFO - YAML Definition loaded: xS11.yaml, type: 3, definition count 4 2020-09-18 16:09:19,974 - evok - INFO - YAML Definition loaded: CUSTOM_MODBUS_DEVICE.yaml, type: 3, definition count 5 2020-09-18 16:09:20,041 - evok - INFO - YAML Definition loaded: xS30.yaml, type: 3, definition count 6 2020-09-18 16:09:20,125 - evok - INFO - YAML Definition loaded: xS51.yaml, type: 3, definition count 7 2020-09-18 16:09:20,215 - evok - INFO - YAML Definition loaded: testIAQ.yaml, type: 3, definition count 8 2020-09-18 16:09:20,226 - evok - INFO - YAML Definition loaded: evok-alias.yaml, type: 2, definition count 0 2020-09-18 16:09:20,264 - evok - INFO - HTTP server listening on port: 8080 2020-09-18 16:09:20,311 - evok - INFO - SPI client started 2020-09-18 16:09:20,317 - evok - INFO - Entering OWW loop with PID 739 2020-09-18 16:09:20,318 - evok - INFO - Reading the UART board on Modbus address 1 2020-09-18 16:09:20,325 - evok - INFO - Reading SPI boards 2020-09-18 16:09:20,344 - evok - INFO - No board on SPI 2 2020-09-18 16:09:20,350 - evok - INFO - No board on SPI 3 2020-09-18 16:09:20,351 - evok - INFO - Alias loaded: <neuron.Relay object at 0xffff81ed5210> al_lights_kitchen 2020-09-18 16:09:20,353 - evok - INFO - Alias loaded: <neuron.Relay object at 0xffff81ed5390> al_lights_bedroom 2020-09-18 16:09:20,461 - evok - INFO - Alias loaded: <neuron.Relay object at 0xffff81ed5210> al_lights_kitchen 2020-09-18 16:09:20,464 - evok - INFO - Alias loaded: <neuron.Relay object at 0xffff81ed5390> al_lights_bedroom 2020-09-18 16:09:30,402 - evok - DEBUG - New WebSocket client connected 2020-09-18 16:10:00,860 - evok - DEBUG - New WebSocket client connected
There seems to be a problem with the parsing of the incoming modbus data.
I would really appreciate some ideas on how to get this to work.
Thank you! -
Hello @Balardo,
can you please share reply to the /rest/all GET request?
e.g. type http://<IP OF YOUR UNIT>/rest/all to your browser. -
Hello @martin_triska,
There is the result of the /rest/all request:
[{"counter_modes": ["Enabled", "Disabled"], "glob_dev_id": 1, "modes": ["Simple", "DirectSwitch"], "value": 0, "circuit": "1_01", "debounce": 50, "counter": 0, "counter_mode": "Enabled", "dev": "input", "mode": "Simple"}, {"counter_modes": ["Enabled", "Disabled"], "glob_dev_id": 1, "modes": ["Simple", "DirectSwitch"], "value": 0, "circuit": "1_02", "debounce": 50, "counter": 0, "counter_mode": "Enabled", "dev": "input", "mode": "Simple"}, {"counter_modes": ["Enabled", "Disabled"], "glob_dev_id": 1, "modes": ["Simple", "DirectSwitch"], "value": 0, "circuit": "1_03", "debounce": 50, "counter": 0, "counter_mode": "Enabled", "dev": "input", "mode": "Simple"}, {"counter_modes": ["Enabled", "Disabled"], "glob_dev_id": 1, "modes": ["Simple", "DirectSwitch"], "value": 0, "circuit": "1_04", "debounce": 50, "counter": 0, "counter_mode": "Enabled", "dev": "input", "mode": "Simple"}, {"relay_type": "digital", "glob_dev_id": 1, "pwm_duty": 0, "dev": "relay", "modes": ["Simple", "PWM"], "pwm_freq": 39, "value": 0, "alias": "al_lights_kitchen", "mode": "Simple", "circuit": "1_01", "pending": false}, {"relay_type": "digital", "glob_dev_id": 1, "pwm_duty": 0, "dev": "relay", "modes": ["Simple", "PWM"], "pwm_freq": 39, "value": 0, "alias": "al_lights_bedroom", "mode": "Simple", "circuit": "1_02", "pending": false}, {"pwm_duty": 0, "glob_dev_id": 1, "pwm_freq": 39, "modes": ["Simple", "PWM"], "value": 0, "circuit": "1_03", "pending": false, "relay_type": "digital", "dev": "relay", "mode": "Simple"}, {"pwm_duty": 0, "glob_dev_id": 1, "pwm_freq": 39, "modes": ["Simple", "PWM"], "value": 0, "circuit": "1_04", "pending": false, "relay_type": "digital", "dev": "relay", "mode": "Simple"}, {"glob_dev_id": 1, "unit": "V", "range_modes": ["10.0"], "value": 0.012965346525057231, "circuit": "1_01", "modes": ["Voltage", "Current"], "range": "10.0", "dev": "ai", "mode": "Voltage"}, {"modes": ["Voltage", "Current", "Resistance"], "value": 0.0, "glob_dev_id": 1, "dev": "ao", "circuit": "1_01", "unit": "V", "mode": "Voltage"}, {"value": 0, "circuit": "1_01", "dev": "led", "glob_dev_id": 1}, {"value": 0, "circuit": "1_02", "dev": "led", "glob_dev_id": 1}, {"value": 0, "circuit": "1_03", "dev": "led", "glob_dev_id": 1}, {"value": 0, "circuit": "1_04", "dev": "led", "glob_dev_id": 1}, {"circuit": "1_01", "value": 0, "glob_dev_id": 1, "dev": "wd", "timeout": 5000, "was_wd_reset": 0, "nv_save": 0}, {"glob_dev_id": 1, "last_comm": 0.03806304931640625, "ver2": "0.2", "sn": 715, "circuit": "1", "model": "S105", "dev": "neuron", "board_count": 1}, {"uart_circuit": "1_01", "dev": "extension", "glob_dev_id": 2, "last_comm": 4.979914903640747, "circuit": "UART_1_2", "model": "testIAQ", "uart_port": "/dev/ttyS1"}, {"conf_value": 3262, "glob_dev_id": 1, "sw_address": 0, "parity_modes": ["None", "Odd", "Even"], "stopb_modes": ["One", "Two"], "speed_modes": ["2400bps", "4800bps", "9600bps", "19200bps", "38400bps", "57600bps", "115200bps"], "dev": "uart", "circuit": "1_01", "parity_mode": "None", "stopb_mode": "One", "speed_mode": "19200bps"}, {"bus": "/dev/i2c-0", "interval": 3.0, "dev": "owbus", "scan_interval": 300.0, "circuit": "1", "do_scan": false, "do_reset": false}, {"name": "dew_point", "value": 12.253130912780762, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_8", "unit": "\u00b0C"}, {"name": "CO2", "value": NaN, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_18", "unit": "ppm"}, {"name": "rel_humid", "value": 57.526512145996094, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_6", "unit": "%"}, {"name": "temperature", "value": 20.957672119140625, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_0", "unit": "\u00b0C"}, {"name": "abs_humid", "value": 10.488215446472168, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_10", "unit": "%"}, {"name": "VOC_CO2_eq", "value": 490.800048828125, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_34", "unit": "ppm"}, {"name": "VOC_index", "value": 19.734819412231445, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_26", "unit": "N/A"}, {"name": "uptime", "value": 1274.0, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_84", "unit": "sec"}, {"name": "ambient_light", "value": 22.00320053100586, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_42", "unit": "lux"}, {"name": "atm_pressure", "value": 999.4600219726562, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_76", "unit": "hPa"}, {"name": "VOC_precision", "value": 1.0, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_1_2_28", "unit": "N/A"}]
It looks like the IAQ sensor is being read, the data looks good.
Interestingly /rest/all works, but /json/all returns {"status": "error", "message": "Internal Server Error", "code": 500}
What could be the problem with the json request?
On what address could we read the sensor? (ex: /res/testIAQ/temperature)
Thank you for you help. -
Hello @Balardo,
you are right - there was a bug in schema validation for /json/all endpoint. It should be fixed in the next release.
According to the direct Modbus unit register access - you can use e.g.
http://<your IP>/json/unit_register/UART_15_2_42to get the ambient_light register:
{"status": "success", "data": {"name": "ambient_light", "value": 122.51519775390625, "glob_dev_id": 2, "dev": "unit_register", "circuit": "UART_15_2_42", "unit": "lux"}}
Register values has been also added to the web control panel:
You can test it by install the test version:
sudo su apt update && apt install evok=2.3.3.test.20200923165635*