diff --git a/analog_io_modules/infrasensing_env-thum/README.md b/analog_io_modules/infrasensing_env-thum/README.md index 2c3c7914..ca8ac79a 100644 --- a/analog_io_modules/infrasensing_env-thum/README.md +++ b/analog_io_modules/infrasensing_env-thum/README.md @@ -1,6 +1,6 @@ # InfraSensing Temperature & Humidity sensor ENV-THUM -This [Enapter Device Blueprint](https://go.enapter.com/marketplace-readme) integrates **InfraSensing ENV-THUM** - RH sensor created for monitoring temperature and humidity levels indoor - via [Modbus TCP](https://go.enapter.com/developers-modbustcp) implemented on [Enapter Virtual UCM](https://go.enapter.com/handbook-vucm). +This [Enapter Device Blueprint](https://go.enapter.com/marketplace-readme) integrates two **InfraSensing ENV-THUM** RH sensors connected to [InfraSensing SensorGateway](https://go.enapter.com/infrasensing-sensorgateway) using LAN port. They communicate with [Enapter Virtual UCM](https://go.enapter.com/handbook-vucm) via [Modbus TCP](https://go.enapter.com/developers-modbustcp). ## Connect to Enapter @@ -9,10 +9,11 @@ This [Enapter Device Blueprint](https://go.enapter.com/marketplace-readme) integ - Create [Enapter Virtual UCM](https://go.enapter.com/handbook-vucm). - [Upload](https://go.enapter.com/developers-upload-blueprint) this blueprint to Enapter Virtual UCM. - Use the `Configure` command in the Enapter mobile or Web app to set up the device communication parameters: - - IP address (use either static IP or DHCP reservation); - - Modbus Unit ID + - IP address (default - `192.168.11.160`) + - Modbus Unit ID (default - `1`) ## References - [Temperature & Humidity Sensor product page](https://go.enapter.com/infrasensing-env-thum) +- [Infrasensing SensorGateway: How it works video](https://go.enapter.com/infrasensing-sensorgateway-video) - [Modbus TCP manual](https://go.enapter.com/infrasensing-modbus-manual) diff --git a/analog_io_modules/infrasensing_env-thum/firmware.lua b/analog_io_modules/infrasensing_env-thum/firmware.lua index fa6d5b6a..f74e061f 100644 --- a/analog_io_modules/infrasensing_env-thum/firmware.lua +++ b/analog_io_modules/infrasensing_env-thum/firmware.lua @@ -6,28 +6,21 @@ MODEL = 'ENV-THUM' -- Configuration variables must be also defined -- in `write_configuration` command arguments in manifest.yml IP_ADDRESS_CONFIG = 'ip_address' +MODBUS_PORT_CONFIG = 'modbus_port' UNIT_ID_CONFIG = 'modbus_unit_id' +NODE_CONFIG = 'node' -- Initiate device firmware. Called at the end of the file. function main() - scheduler.add(30000, send_properties) - scheduler.add(1000, send_telemetry) - config.init({ - [IP_ADDRESS_CONFIG] = { type = 'string', required = true }, - [UNIT_ID_CONFIG] = { type = 'number', required = true }, + [IP_ADDRESS_CONFIG] = { type = 'string', required = true, default = '192.168.11.160' }, + [UNIT_ID_CONFIG] = { type = 'number', required = true, default = 1 }, + [MODBUS_PORT_CONFIG] = { type = 'number', required = true, default = 502 }, + [NODE_CONFIG] = { type = 'number', required = true, default = 7 }, }) - enapter.register_command_handler('set_threshold', command_set_threshold) -end - -function send_properties() - local properties = {} - - properties.vendor = VENDOR - properties.model = MODEL - - enapter.send_properties(properties) + scheduler.add(1000, send_telemetry) + scheduler.add(30000, send_properties) end function send_telemetry() @@ -41,54 +34,92 @@ function send_telemetry() return end - local telemetry = {} - local alerts = {} - local status = 'ok' - - for i = 0, 2 do - local data = device:read_inputs(30200 + i * 32, 2) - if data then - telemetry['value' .. i] = toFloat32(data) - end - - local data = device:read_input_status(10201 + i * 32, 1) - if data then - telemetry['alarm_down' .. i] = data[1] - table.insert(alerts, 'alarm_down' .. i) - status = 'down' - end - - local data = device:read_input_status(10202 + i * 32, 1) - if data then - telemetry['alarm_warn' .. i] = data[1] - table.insert(alerts, 'alarm_warn' .. i) - status = 'warning' + local node, err = config.read(NODE_CONFIG) + if err then + enapter.log('cannot read config: ' .. tostring(err), 'error') + enapter.send_telemetry({ status = 'error', alerts = { 'cannot_read_config' } }) + else + local telemetry = {} + local alerts = {} + local status = 'ok' + + for i = 0, node do + if i == 1 then + goto continue + end + + local data = device:read_inputs(200 + i * 32, 2) + if data then + telemetry['value' .. i] = toFloat32(data) + else + status = 'no_data' + end + + local data = device:read_inputs(201 + i * 32, 1) + if data then + telemetry['type' .. i] = get_type(data[1]) + else + status = 'no_data' + end + ::continue:: end - local data = device:read_holdings(40200 + i * 32, 2) - if data then - telemetry['thr_high_down' .. i] = toFloat32(data) - end + telemetry.alerts = alerts + telemetry.status = status + enapter.send_telemetry(telemetry) + end +end - local data = device:read_holdings(40202 + i * 32, 2) - if data then - telemetry['thr_high_warn' .. i] = toFloat32(data) - end +function send_properties() + local properties = {} - local data = device:read_holdings(40204 + i * 32, 2) - if data then - telemetry['thr_low_high' .. i] = toFloat32(data) - end + properties.vendor = VENDOR + properties.model = MODEL - local data = device:read_holdings(40206 + i * 32, 2) - if data then - telemetry['thr_low_warn' .. i] = toFloat32(data) - end - end + enapter.send_properties(properties) +end - telemetry.alerts = alerts - telemetry.status = status - enapter.send_telemetry(telemetry) +function get_type(val) + local types = { + [1] = 'Temperature', + [2] = 'Humidity', + [3] = 'Airflow', + [4] = 'Shock', + [5] = 'Dust', + [7] = 'Sound Pressure', + [8] = 'Power Failure Sensor', + [9] = 'Leak', + [10] = 'CO', + [11] = 'Air Pressure', + [12] = 'Security', + [13] = 'Dewpoint', + [14] = 'Fuel Level (Fuel Level Sensor)', + [15] = 'Flow Rate (Fuel Level Sensor)', + [16] = 'Resistance', + [17] = 'TVOC', + [18] = 'CO2', + [19] = 'Motion (EXP4HUB)', + [20] = 'O2', + [21] = 'Light', + [22] = 'CO', + [23] = 'HF', + [24] = 'Volt (AC/DC Power Sensor)', + [25] = 'Amp (AC/DC Power Sensor)', + [26] = 'Watt (AC/DC Power Sensor)', + [27] = 'Watthour (AC/DC Power Sensor)', + [28] = 'Ping', + [29] = 'H2', + [30] = 'Voltage Status', + [31] = 'THD', + [32] = 'Frequency', + [33] = 'Location and Distance', + [34] = 'Tilt (Inclination)', + [35] = 'Particle(PM)', + [36] = 'Radon', + [37] = 'kW (AC Meter3 Power)', + [38] = 'kWh (AC Meter 3 energy)', + } + return types[val] end -- Holds global device connection @@ -104,46 +135,27 @@ function connect_device() enapter.log('cannot read config: ' .. tostring(err), 'error') return nil, 'cannot_read_config' else - local address, unit_id = values[IP_ADDRESS_CONFIG], values[UNIT_ID_CONFIG] - if not address or not unit_id then + local address, unit_id, port = values[IP_ADDRESS_CONFIG], values[UNIT_ID_CONFIG], values[MODBUS_PORT_CONFIG] + if not address or not unit_id or not port then return nil, 'not_configured' else -- Declare global variable to reuse connection between function calls - device = SensorGwModbusTcp.new(address, tonumber(unit_id)) + device = SensorGwModbusTcp.new(address .. ':' .. math.floor(port), tonumber(unit_id)) + enapter.log(address .. ':' .. math.floor(port)) device:connect() return device, nil end end end -function command_set_threshold(ctx, args) - local thresholds = { - ['High Down'] = 0, - ['High Warn'] = 2, - ['Low Down'] = 4, - ['Low Warn'] = 6, - } - - if device then - local err = device:write_holdings(args.node * 32 + 40200 + thresholds[args.threshold], args.value) - if err == 0 then - return 'Reset command is sent' - else - ctx.error('Command failed, Modbus TCP error: ' .. tostring(err)) - end - else - ctx.error('Device connection is not configured') - end -end - function toFloat32(data) local raw_str = string.pack('BBBB', data[1] >> 8, data[1] & 0xff, data[2] >> 8, data[2] & 0xff) return string.unpack('>f', raw_str) end -------------------------------------------- --- InfraSensing SensorGateway ModbusTCP API -------------------------------------------- +----------------------------------------------- +-- InfraSensing SensorGateway Modbus TCP API -- +----------------------------------------------- SensorGwModbusTcp = {} @@ -167,7 +179,7 @@ function SensorGwModbusTcp:read_inputs(address, number) local registers, err = self.modbus:read_inputs(self.unit_id, address, number, 1000) if err and err ~= 0 then - enapter.log('Register ' .. tostring(address) .. ' read error: ' .. err, 'error') + enapter.log('Read input register ' .. tostring(address) .. ' read error: ' .. err, 'error') if err == 1 then -- Sometimes timeout happens and it may break underlying Modbus client, -- this is a temporary workaround which manually reconnects. @@ -185,7 +197,7 @@ function SensorGwModbusTcp:read_input_status(address, number) local registers, err = self.modbus:read_discrete_inputs(self.unit_id, address, number, 1000) if err and err ~= 0 then - enapter.log('Register ' .. tostring(address) .. ' read error: ' .. err, 'error') + enapter.log('Read input status register ' .. tostring(address) .. ' read error: ' .. err, 'error') if err == 1 then -- Sometimes timeout happens and it may break underlying Modbus client, -- this is a temporary workaround which manually reconnects. @@ -203,7 +215,7 @@ function SensorGwModbusTcp:read_holdings(address, number) local registers, err = self.modbus:read_inputs(self.unit_id, address, number, 1000) if err and err ~= 0 then - enapter.log('Register ' .. tostring(address) .. ' read error: ' .. err, 'error') + enapter.log('Read holding register ' .. tostring(address) .. ' read error: ' .. err, 'error') if err == 1 then -- Sometimes timeout happens and it may break underlying Modbus client, -- this is a temporary workaround which manually reconnects. @@ -221,7 +233,7 @@ function SensorGwModbusTcp:write_holdings(address, number) local err = self.modbus:write_holding(self.unit_id, address, number, 1000) if err ~= 0 then - enapter.log('Register ' .. tostring(address) .. ' write error: ' .. err, 'error') + enapter.log('Writ holding register ' .. tostring(address) .. ' write error: ' .. err, 'error') if err == 1 then -- Sometimes timeout happens and it may break underlying Modbus client, -- this is a temporary workaround which manually reconnects. diff --git a/analog_io_modules/infrasensing_env-thum/manifest.yml b/analog_io_modules/infrasensing_env-thum/manifest.yml index f4b68fc1..f63b9390 100644 --- a/analog_io_modules/infrasensing_env-thum/manifest.yml +++ b/analog_io_modules/infrasensing_env-thum/manifest.yml @@ -33,76 +33,91 @@ telemetry: type: string enum: - ok - - warning - - down + - no_data + type0: + display_name: Type Node 0 + type: string + enum: + - Temperature + - Humidity + - Dewpoint + type1: + display_name: Type Node 1 + type: string + enum: + - Temperature + - Humidity + - Dewpoint + type2: + display_name: Type Node 2 + type: string + enum: + - Temperature + - Humidity + - Dewpoint + type3: + display_name: Type Node 3 + type: string + enum: + - Temperature + - Humidity + - Dewpoint + type4: + display_name: Type Node 4 + type: string + enum: + - Temperature + - Humidity + - Dewpoint + type5: + display_name: Type Node 5 + type: string + enum: + - Temperature + - Humidity + - Dewpoint + type6: + display_name: Type Node 6 + type: string + enum: + - Temperature + - Humidity + - Dewpoint + type7: + display_name: Type Node 7 + type: string + enum: + - Temperature + - Humidity + - Dewpoint value0: display_name: SensorGateway Temperature type: float unit: celsius - value1: - display_name: Temperature + value2: + display_name: Temperature 1 type: float unit: celsius - value2: - display_name: Humidity + value3: + display_name: Humidity 1 type: float unit: percent - alarm_down0: - display_name: SensorGateway Alarm Down - type: integer - alarm_warn0: - display_name: SensorGateway Alarm Warning - type: integer - thr_high_down0: - display_name: SensorGateway Threshold High Down - type: float - thr_high_warn0: - display_name: SensorGateway Threshold High Warn - type: float - thr_low_down0: - display_name: SensorGateway Threshold Low Down - type: float - thr_low_warn0: - display_name: SensorGateway Threshold Low Down - type: float - - alarm_down1: - display_name: Temprerature Sensor Alarm Down - type: integer - alarm_warn1: - display_name: Temperature Sensor Alarm Warning - type: integer - thr_high_down1: - display_name: Temperature Sensor Threshold High Down - type: float - thr_high_warn1: - display_name: Temperature Sensor Threshold High Warn - type: float - thr_low_down1: - display_name: Temperature Sensor Threshold Low Down + value4: + display_name: Dew Point 1 type: float - thr_low_warn1: - display_name: Temperature Sensor Threshold Low Down - type: float - - alarm_down2: - display_name: Humidity Sensor Alarm Down - type: integer - alarm_warn2: - display_name: Humidity Sensor Alarm Warning - type: integer - thr_high_down2: - display_name: Humidity Sensor Threshold High Down - type: float - thr_high_warn2: - display_name: Humidity Sensor Threshold High Warn + unit: celsius + value5: + display_name: Temperature 2 type: float - thr_low_down2: - display_name: Humidity Sensor Threshold Low Down + unit: celsius + value6: + display_name: Humidity 2 type: float - thr_low_warn2: - display_name: Humidity Sensor Threshold Low Down + unit: percent + value7: + display_name: Dew Point 2 type: float + unit: celsius alerts: cannot_read_config: @@ -113,34 +128,8 @@ alerts: severity: info display_name: Modbus TCP Connection Not Configured description: Use "Configure" command to setup Modbus TCP connection. - alarm_warn0: - severity: warning - display_name: SensorGateway Alarm Warning - description: SensorGateway Warning Threshold has been passed. - alarm_down0: - severity: error - display_name: SensorGateway Alarm Down - description: SensorGateway Down Threshold has been passed. - alarm_warn1: - severity: warning - display_name: Temperature Sensor Alarm Warning - description: Temperature Sensor Warning Threshold has been passed. - alarm_down1: - severity: error - display_name: Temperature Sensor Alarm Down - description: Temperature Sensor Down Threshold has been passed. - alarm_warn2: - severity: warning - display_name: Humidity Sensor Alarm Warning - description: Humidity Sensor Warning Threshold has been passed. - alarm_down2: - severity: error - display_name: Humidity Sensor Alarm Down - description: Humidity Sensor Down Threshold has been passed. command_groups: - thresholds: - display_name: Alarm Thresholds connection: display_name: Connection commands: @@ -162,47 +151,40 @@ commands: description: Modbus Unit ID of SensorGateway type: integer required: true + modbus_port: + display_name: Modbus Port + description: Modbus port of SensorGateway + type: integer + required: true + node: + display_name: Node + description: Node number can be found in SensorGateway web interface. + type: integer + min: 2 + max: 40 + required: true read_configuration: display_name: Read Configuration group: connection ui: icon: wrench-outline mobile_quick_access: true - set_threshold: - display_name: Set Alarm Threshold - group: thresholds - ui: - icon: counter - arguments: - node: - display_name: Sensor - description: Number of node. - type: integer - min: 0 - max: 40 - threshold: - display_name: Threshold type - type: string - required: true - enum: - - High Down - - High Warn - - Low Down - - Low Warn - value: - display_name: Threshold value - description: Numeric value of corresponding threshold. - type: float - required: true .cloud: category: analog_io_modules - mobile_main_chart: value1 mobile_telemetry: - value0 - - value1 - value2 + - value3 + - value4 + - value5 + - value6 + - value7 mobile_charts: - value0 - - value1 - value2 + - value3 + - value4 + - value5 + - value6 + - value7