# frozen_string_literal: true ## # This module is a container for a few classes that wrap a bunch # of ModBus related stuff # module Pzem016 require 'json' require 'awesome_print' ## # Some abstract behavior for the register parser classes # class ModbusRegisters attr_reader :raw, :slave ## # This takes two params: name and slave. Name is a descriptive string # for the slave. Slave is an instance of a ModBus::RTUClient slave # that you've previously set up. # def initialize(name, slave) @slave = slave @name = name @raw = nil end ## # Stub for read logic # def read @raw = [] end ## # Simple basic behavior for turning the data read into a hash # def to_hash { raw: @raw } end ## # Returns the sample data as a json string # def to_json(*_args) to_hash.to_json end ## # Outputs a pretty dump of the readings to STDOUT # def report ap to_hash end end ## # Parsing logic for the "holding registers", also # known as read/write. PZEM-016 is configured via these # class HoldingRegisters < ModbusRegisters ## # Specific holding register read logic for PZEM-016 # def read @raw = @slave.holding_registers[0..6] end ## # Sets the address of the slave to the new address addr # def set_address(addr) @slave.write_single_register(2, addr) end ## # Sets the alarm threshold to the given number of watts def set_alarm(watts) @slave.write_single_register(1, watts) end ## # Clears / resets the KWH (energy) counter on the PZEM-016 # def reset_energy @slave.query("\x42") rescue ModBus::Errors::IllegalFunction # the units respond with an error but in fact do reset the counter true end ## # Specific logic for generating a hash of the holding registers in a PZEM-016 # def to_hash { "DeviceName": @name, "Address": @raw[2], "AlarmWatts": @raw[1] } end end ## # Parsing logic for the "input registers", also # known as readonly. This is where the sample data # shows up # class InputRegisters < ModbusRegisters ## # Specific input register read logic for PZEM-016 # def read @raw = @slave.input_registers[0..9] end ## # Extracts measured volts from the sample data # def volts (@raw[0] * 0.1).round(3) end ## # Extracts measured amps from the sample data # def amps (((@raw[2] << 16) | @raw[1]) * 0.001).round(3) end ## # Extracts measured watts from the sample data # def watts (((@raw[4] << 16) | @raw[3]) * 0.1).round(3) end ## # Extracts cumulative watt-hours from the sample data # def wattHours (((@raw[6] << 16) | @raw[5]) * 0.1).round(3) end ## # Extracts measured frequency from the sample data # def hz (@raw[7] * 0.1).round(3) end ## # Extracts measured power factor from the sample data # def powerFactor (@raw[8] * 0.01).round(3) end ## # Returns true of power consumption has reached the alarm number of watts # def alarm? @raw[9] == 0xFFFF end ## # Specific logic for generating a hash of the input registers in a PZEM-016 # def to_hash { "DeviceName": @name, "Volts": volts, "Amps": amps, "Watts": watts, "WH": wattHours, "HZ": hz, "PF": powerFactor, "Alarm": alarm? } end end end