package main import ( "encoding/binary" "errors" "fmt" ) type register struct { name string conv func([]byte) (float64, error) mult float64 unit string } func (r *register) read(data []byte) (float64, error) { n, err := r.conv(data) return n * r.mult, err } var sungrowInputRegs = map[uint16]*register{ // 4990 - 4999: serial number in ASCII 5001: {"nominal_power", u16req, 0.1, "kW"}, 5003: {"daily_power_yield", u16req, 0.1, "kWh"}, 5004: {"total_power_yield", u32req, 1, "kWh"}, 5006: {"total_running_time", u32req, 1, "h"}, 5008: {"internal_temperature", u16req, 0.1, "°C"}, 5011: {"pv1_potential", u16req, 0.1, "V"}, 5012: {"pv1_current", u16req, 0.1, "A"}, 5013: {"pv2_potential", u16req, 0.1, "V"}, 5014: {"pv2_current", u16req, 0.1, "A"}, 5015: {"pv3_potential", u16opt, 0.1, "V"}, 5016: {"pv3_current", u16opt, 0.1, "A"}, 5017: {"total_dc_power", u32req, 1, "W"}, 5019: {"phase_a_potential", u16req, 0.1, "V"}, 5020: {"phase_b_potential", u16opt, 0.1, "V"}, 5021: {"phase_c_potential", u16opt, 0.1, "V"}, 5022: {"phase_a_current", u16req, 0.1, "A"}, 5023: {"phase_b_current", u16opt, 0.1, "A"}, 5024: {"phase_c_current", u16opt, 0.1, "A"}, 5031: {"output_real_power", u32req, 1, "VA"}, 5033: {"output_reactive_power", s32opt, 1, "VAr"}, 5035: {"power_factor", s16opt, 0.001, ""}, 5036: {"frequency", u16req, 0.1, "Hz"}, 5049: {"nominal_reactive_power", s16opt, 0.1, "kVA"}, 5083: {"meter_power", s32req, 1, "W"}, 5085: {"meter_a_phase_power", s32opt, 1, "W"}, 5087: {"meter_b_phase_power", s32opt, 1, "W"}, 5089: {"meter_c_phase_power", s32opt, 1, "W"}, 5091: {"usage_power", s32req, 1, "W"}, 5093: {"daily_export_energy", u32req, 0.1, "kWh"}, 5095: {"total_export_energy", u32req, 0.1, "kWh"}, 5097: {"daily_import_energy", u32req, 0.1, "kWh"}, 5099: {"total_import_energy", u32req, 0.1, "kWh"}, 5113: {"daily_running_time", u16req, 1, "m"}, 5144: {"total_power_yield_2", u32req, 0.1, "kWh"}, 5148: {"frequency_2", frequency2, 0.01, "Hz"}, } var errSkippableRead = errors.New("skip read") func frequency2(data []byte) (float64, error) { n := binary.BigEndian.Uint16(data) if n == 0xFFFF { // probably a misread return 0, fmt.Errorf("likely misread: frequency_2 read %v", n) } if n == 100 { // this translates as 1 Hz, which is unlikely return 0, fmt.Errorf("%w: frequency_2 is 1.00Hz", errSkippableRead) } return float64(n), nil } func u16opt(data []byte) (float64, error) { return float64(binary.BigEndian.Uint16(data)), nil } func u16req(data []byte) (float64, error) { n := binary.BigEndian.Uint16(data) if n == 0xFFFF { // probably a misread return 0, fmt.Errorf("likely misread: u16req read %v", n) } return float64(n), nil } func s16opt(data []byte) (float64, error) { return float64(int16(binary.BigEndian.Uint16(data))), nil } func s16req(data []byte) (float64, error) { n := binary.BigEndian.Uint16(data) if n == 0x7FFF { // probably a misread return 0, fmt.Errorf("likely misread: s16req read %v", n) } return float64(int16(n)), nil } func u32opt(data []byte) (float64, error) { // Little-endian big-endian :# return float64(uint32(binary.BigEndian.Uint16(data)) + uint32(binary.BigEndian.Uint16(data[2:]))<<16), nil } func u32req(data []byte) (float64, error) { // Little-endian big-endian :# n := uint32(binary.BigEndian.Uint16(data)) + uint32(binary.BigEndian.Uint16(data[2:]))<<16 if n == 0xFFFFFFFF { // probably a misread return 0, fmt.Errorf("likely misread: u32req read %v", n) } return float64(n), nil } func s32opt(data []byte) (float64, error) { // Little-endian big-endian :# return float64(int32(binary.BigEndian.Uint16(data)) + int32(binary.BigEndian.Uint16(data[2:]))<<16), nil } func s32req(data []byte) (float64, error) { // Little-endian big-endian :# n := uint32(binary.BigEndian.Uint16(data)) + uint32(binary.BigEndian.Uint16(data[2:]))<<16 if n == 0x7FFFFFFF { // probably a misread return 0, fmt.Errorf("likely misread: s32req read %v", n) } return float64(int32(n)), nil }