diff --git a/charger/abl.go b/charger/abl.go index 02b77b402..f5ab357a4 100644 --- a/charger/abl.go +++ b/charger/abl.go @@ -3,6 +3,7 @@ package charger // LICENSE // Copyright (c) 2019-2022 andig +// Copyright (c) 2022 premultiply // This module is NOT covered by the MIT license. All rights reserved. @@ -20,6 +21,7 @@ package charger import ( "encoding/binary" "fmt" + "time" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/util" @@ -34,14 +36,16 @@ type ABLeMH struct { } const ( - ablRegFirmware = 0x01 - ablRegStatus = 0x04 - ablRegEnabled = 0x0F - ablRegAmpsConfig = 0x14 - ablRegStatusLong = 0x2E + ablRegFirmware = 0x01 + ablRegStatus = 0x04 + ablRegModifyState = 0x05 + ablRegEnabled = 0x0F + ablRegAmpsConfig = 0x14 + ablRegStatusLong = 0x2E - ablAmpsDisabled uint16 = 0x03E8 - ablSensorPresent = 1 << 5 + ablAmpsDisabled uint16 = 0x03E8 + + ablSensorPresent = 1 << 5 ) var ablStatus = map[byte]string{ @@ -108,8 +112,7 @@ func NewABLeMH(uri, device, comset string, baudrate int, slaveID uint8) (api.Cha curr: uint16(6 / 0.06), } - _, _ = wb.conn.ReadHoldingRegisters(ablRegFirmware, 2) - b, err := wb.conn.ReadHoldingRegisters(ablRegFirmware, 2) + b, err := wb.get(ablRegFirmware, 2) // check presence of current sensor if err == nil && (b[3]&ablSensorPresent != 0) { @@ -119,10 +122,28 @@ func NewABLeMH(uri, device, comset string, baudrate int, slaveID uint8) (api.Cha return wb, err } +func (wb *ABLeMH) set(reg, val uint16) error { + b := make([]byte, 2) + binary.BigEndian.PutUint16(b, val) + + //write two times + _, _ = wb.conn.WriteMultipleRegisters(reg, 1, b) + _, err := wb.conn.WriteMultipleRegisters(reg, 1, b) + + return err +} + +func (wb *ABLeMH) get(reg, count uint16) ([]byte, error) { + //read two times + _, _ = wb.conn.ReadHoldingRegisters(reg, count) + b, err := wb.conn.ReadHoldingRegisters(reg, count) + + return b, err +} + // Status implements the api.Charger interface func (wb *ABLeMH) Status() (api.ChargeStatus, error) { - _, _ = wb.conn.ReadHoldingRegisters(ablRegStatus, 1) - b, err := wb.conn.ReadHoldingRegisters(ablRegStatus, 1) + b, err := wb.get(ablRegStatus, 1) if err != nil { return api.StatusNone, err } @@ -144,8 +165,7 @@ func (wb *ABLeMH) Status() (api.ChargeStatus, error) { // Enabled implements the api.Charger interface func (wb *ABLeMH) Enabled() (bool, error) { - _, _ = wb.conn.ReadHoldingRegisters(ablRegEnabled, 5) - b, err := wb.conn.ReadHoldingRegisters(ablRegEnabled, 5) + b, err := wb.get(ablRegEnabled, 5) if err != nil { return false, err } @@ -161,17 +181,17 @@ func (wb *ABLeMH) Enable(enable bool) error { u = wb.curr } - b := make([]byte, 2) - binary.BigEndian.PutUint16(b, u) - - _, _ = wb.conn.WriteMultipleRegisters(ablRegAmpsConfig, 1, b) - _, err := wb.conn.WriteMultipleRegisters(ablRegAmpsConfig, 1, b) + err := wb.set(ablRegAmpsConfig, u) return err } // MaxCurrent implements the api.Charger interface func (wb *ABLeMH) MaxCurrent(current int64) error { + if current < 6 { + return fmt.Errorf("invalid current %d", current) + } + return wb.MaxCurrentMillis(float64(current)) } @@ -179,14 +199,17 @@ var _ api.ChargerEx = (*ABLeMH)(nil) // MaxCurrent implements the api.ChargerEx interface func (wb *ABLeMH) MaxCurrentMillis(current float64) error { + if current < 6 { + return fmt.Errorf("invalid current %.1f", current) + } + // calculate duty cycle according to https://www.goingelectric.de/forum/viewtopic.php?p=1575287#p1575287 - wb.curr = uint16(current / 0.06) + cur := uint16(current / 0.06) - b := make([]byte, 2) - binary.BigEndian.PutUint16(b, wb.curr) - - _, _ = wb.conn.WriteMultipleRegisters(ablRegAmpsConfig, 1, b) - _, err := wb.conn.WriteMultipleRegisters(ablRegAmpsConfig, 1, b) + err := wb.set(ablRegAmpsConfig, cur) + if err == nil { + wb.curr = cur + } return err } @@ -199,8 +222,7 @@ func (wb *ABLeMH) currentPower() (float64, error) { // Currents implements the api.MeterCurrent interface func (wb *ABLeMH) currents() (float64, float64, float64, error) { - _, _ = wb.conn.ReadHoldingRegisters(ablRegStatusLong, 5) - b, err := wb.conn.ReadHoldingRegisters(ablRegStatusLong, 5) + b, err := wb.get(ablRegStatusLong, 5) if err != nil { return 0, 0, 0, err } @@ -222,9 +244,23 @@ var _ api.Diagnosis = (*ABLeMH)(nil) // Diagnose implements the api.Diagnosis interface func (wb *ABLeMH) Diagnose() { - _, _ = wb.conn.ReadHoldingRegisters(ablRegFirmware, 2) - b, err := wb.conn.ReadHoldingRegisters(ablRegFirmware, 2) + b, err := wb.get(ablRegFirmware, 2) if err == nil { fmt.Printf("Firmware: %0 x\n", b) } } + +var _ api.AlarmClock = (*ABLeMH)(nil) + +// WakeUp implements the api.AlarmClock interface +func (wb *ABLeMH) WakeUp() error { + // temporary jump to status E0 (Outlet disabled) + err := wb.set(ablRegModifyState, 0xE0E0) + if err == nil { + time.Sleep(3 * time.Second) + // jump back to state A1 (Waiting for EV) + err = wb.set(ablRegModifyState, 0xA1A1) + } + + return err +}