Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Zizm ZM194-D9Y power meter #241

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions power_meters/zizm_ZM194-D9Y/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Zizm ZM194-D9Y

This [Enapter Device Blueprint](https://go.enapter.com/marketplace-readme) integrates **Zizm ZM194-D9Y** - 3-Phase Multi-function Power Meter with [ModBus RTU](https://go.enapter.com/developers-enapter-modbus) over [RS-485 communication interface](https://go.enapter.com/developers-enapter-rs485).

## Connect to Enapter

- Sign up to the Enapter Cloud using the [Web](https://cloud.enapter.com/) or mobile app ([iOS](https://apps.apple.com/app/id1388329910), [Android](https://play.google.com/store/apps/details?id=com.enapter&hl=en)).
- Use the [Enapter ENP-RS485](https://go.enapter.com/handbook-enp-rs485) module for physical connection. See [connection instructions](https://go.enapter.com/handbook-enp-rs485-conn) in the module manual.
- [Add ENP-RS485 to your site](https://go.enapter.com/handbook-mobile-app) using the mobile app.
- [Upload](https://go.enapter.com/developers-upload-blueprint) this blueprint to ENP-RS485.
- Use the `Set Up Connection` command in the Enapter mobile or Web app to set up Modbus RTU communication parameters:
- Baudrate;
- Modbus address;
- Parity.
194 changes: 194 additions & 0 deletions power_meters/zizm_ZM194-D9Y/firmware.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
local config = require('enapter.ucm.config')

BAUDRATE = 'baudrate'
ADDRESS = 'address'
PARITY = 'parity'

-- Default RS485 communication interface parameters
DATA_BITS = 8
STOP_BITS = 1

function main()
config.init({
[BAUDRATE] = { type = 'number', required = true, default = 9600 },
[ADDRESS] = { type = 'number', required = true, default = 1 },
[PARITY] = { type = 'string', required = true, default = 'N' },
})

local values, err = config.read_all()
if err then
enapter.log('cannot read config: ' .. tostring(err), 'error')
return nil, 'cannot_read_config'
else
local baudrate, parity = values[BAUDRATE], values[PARITY]

local result = rs485.init(baudrate, DATA_BITS, parity, STOP_BITS)
if result ~= 0 then
enapter.log('RS485 init error: ' .. rs485.err_to_str(result), error, true)
end
end

scheduler.add(30000, send_properties)
scheduler.add(1000, send_telemetry)
end

function send_properties()
enapter.send_properties({
vendor = 'Zizm',
model = 'ZM194-D9Y',
})
end

function send_telemetry()
local telemetry = {}
local status = 'ok'

local all_metrics = {
voltage_a = {
register_address = 0x0000,
register_count = 2,
fn = touint32,
},
voltage_b = {
register_address = 0x0002,
register_count = 2,
fn = touint32,
},
voltage_c = {
register_address = 0x0004,
register_count = 2,
fn = touint32,
},
line_voltage_ab = {
register_address = 0x0006,
register_count = 2,
fn = touint32,
},
line_voltage_bc = {
register_address = 0x0008,
register_count = 2,
fn = touint32,
},
line_voltage_ca = {
register_address = 0x000A,
register_count = 2,
fn = touint32,
},
current_a = {
register_address = 0x000C,
register_count = 2,
fn = touint32,
},
current_b = {
register_address = 0x000E,
register_count = 2,
fn = touint32,
},
current_c = {
register_address = 0x0010,
register_count = 2,
fn = touint32,
},
active_power_a = {
register_address = 0x0012,
register_count = 2,
fn = touint32,
},
active_power_b = {
register_address = 0x0014,
register_count = 2,
fn = touint32,
},
active_power_c = {
register_address = 0x0016,
register_count = 2,
fn = touint32,
},
total_active_power = {
register_address = 0x0018,
register_count = 2,
fn = touint32,
},
reactive_power_a = {
register_address = 0x001A,
register_count = 2,
fn = touint32,
},
reactive_power_b = {
register_address = 0x001C,
register_count = 2,
fn = touint32,
},
reactive_power_c = {
register_address = 0x001E,
register_count = 2,
fn = touint32,
},
total_reactive_power = {
register_address = 0x0020,
register_count = 2,
fn = touint32,
},
frequency = {
register_address = 0x0032,
register_count = 2,
fn = touint32,
},
total_active_electric_energy = {
register_address = 0x0038,
register_count = 2,
fn = touint32,
},
forward_active_electric_energy = {
register_address = 0x003A,
register_count = 2,
fn = touint32,
},
reverse_active_electric_energy = {
register_address = 0x003C,
register_count = 2,
fn = touint32,
},
}

for name, metric in pairs(all_metrics) do
if
not add_to_telemetry(
name,
telemetry,
metric.register_address,
metric.register_count,
metric.fn
)
then
status = 'read_error'
end
end

telemetry['status'] = status
enapter.send_telemetry(telemetry)
end

function add_to_telemetry(metric_name, tbl, register_address, registers_count, fn)
local address, err = config.read(ADDRESS)
if err == nil then
local data, result = modbus.read_holdings(address, register_address, registers_count, 1000)
if data then
tbl[metric_name] = fn(data) / 1000.0 -- coefficient 0.001 for all metrics
return true
else
enapter.log(
'Register ' .. register_address .. ' reading failed: ' .. modbus.err_to_str(result)
)
end
end
return false
end

function touint32(register)
local raw_str =
string.pack('BBBB', register[1] >> 8, register[1] & 0xff, register[2] >> 8, register[2] & 0xff)
return string.unpack('>I2', raw_str)
end

main()
189 changes: 189 additions & 0 deletions power_meters/zizm_ZM194-D9Y/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
blueprint_spec: device/1.0
display_name: Zizm ZM194-D9Y
description: 3-Phase Multi-function Power Meter.
icon: enapter-power-meter
vendor: zizm
author: enapter
support:
url: https://go.enapter.com/enapter-blueprint-support
email: [email protected]
license: MIT
verification_level: verified

communication_module:
product: ENP-RS485
lua:
file: firmware.lua
dependencies:
- enapter-ucm

properties:
vendor:
type: string
display_name: Vendor
model:
type: string
display_name: Model

telemetry:
status:
display_name: Modbus status
type: string
enum:
- ok
- read_error
total_active_power:
type: float
unit: kwatt
display_name: Total Active Power
total_active_electric_energy:
type: float
unit: kwatth
display_name: Total Active Electric Energy
active_power_a:
type: float
unit: kwatt
display_name: Active Power A
active_power_b:
type: float
unit: watt
display_name: Active Power B
active_power_c:
type: float
unit: kwatt
display_name: Active Power C
voltage_a:
type: float
unit: volt
display_name: Phase A Voltage
voltage_b:
type: float
unit: volt
display_name: Phase B Voltage
voltage_c:
type: float
unit: volt
display_name: Phase C Voltage
line_voltage_ab:
type: float
unit: volt
display_name: Line Voltage AB
line_voltage_bc:
type: float
unit: volt
display_name: Line Voltage BC
line_voltage_ca:
type: float
unit: volt
display_name: Line Voltage CA
current_a:
type: float
unit: amp
display_name: Phase A Current
current_b:
type: float
unit: amp
display_name: Phase B Current
current_c:
type: float
unit: amp
display_name: Phase C Current
frequency:
type: float
unit: hertz
display_name: Frequency
total_reactive_power:
type: float
unit: watt
display_name: Total Reactive Power
reactive_power_a:
type: float
unit: watt
display_name: Reactive Power A
reactive_power_b:
type: float
unit: watt
display_name: Reactive Power B
reactive_power_c:
type: float
unit: watt
display_name: Reactive Power C
forward_active_electric_energy:
type: float
unit: kwatth
display_name: Forward Active Electric Energy
reverse_active_electric_energy:
type: float
unit: kwatth
display_name: Reverse Active Electric Energy

command_groups:
connection:
display_name: Connection

commands:
# TODO: mark commands containing secrets
write_configuration:
display_name: Set Up Connection
description: Set Modbus RTU connection
group: connection
populate_values_command: read_configuration
ui:
icon: file-document-edit-outline
arguments:
baudrate:
display_name: Baudrate
type: integer
required: true
enum:
- 2400
- 4800
- 9600
address:
display_name: Modbus address
type: integer
required: true
min: 1
max: 247
parity:
display_name: Parity
type: string
required: true
enum:
- N
- E
- O
read_configuration:
display_name: Read Connection Parameters
group: connection
ui:
icon: file-check-outline
.cloud:
category: power_meters
mobile_main_chart: total_active_power
mobile_telemetry:
- total_active_electric_energy
- total_active_power
- active_power_a
- active_power_b
- active_power_c
- voltage_a
- voltage_b
- voltage_c
- current_a
- current_b
- current_c
- frequency
mobile_charts:
- total_active_electric_energy
- total_active_power
- active_power_a
- active_power_b
- active_power_c
- voltage_a
- voltage_b
- voltage_c
- current_a
- current_b
- current_c
- frequency