ABB TACW fw ver 1.6.5 adaptions (#4626)

Charger improvements for ABB Terra AC Wallbox with firmware version 1.6.5 (and newer?) from Nov 2022.
!!! Will break compatibility with older firmware - you have to upgrade the firmware to use the charger !!!

    New: Vehicle connection detection outside of an active session
    New: Getting an enabled state outside of an active session now
    New: Setting max current outside of an active session now
    Charger implementation allows to switch modbus protocol (rtu or tcp) now

Firmware 1.6.5 still has some crazy "non-linear" behavior regarding the sent, applied and read-back max current..
This commit is contained in:
premultiply 2022-11-11 13:08:58 +01:00 • committed by GitHub
parent 9fc017e91c
commit 03cf93f9e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 24 additions and 38 deletions

View file

@ -30,7 +30,6 @@ import (
// ABB charger implementation
type ABB struct {
log *util.Logger
conn *modbus.Connection
curr uint32
}
@ -40,6 +39,7 @@ const (
abbRegFirmware = 0x4004 // Firmware version 2 unsigned RO available
abbRegMaxRated = 0x4006 // Max rated current 2 unsigned RO available
abbRegErrorCode = 0x4008 // Error Code 2 unsigned RO available
abbRegSocketLock = 0x400A // Socket Lock State 2 unsigned RO available
abbRegStatus = 0x400C // Charging state 2 unsigned RO available
abbRegGetCurrent = 0x400E // Current charging current limit 2 0.001 A unsigned RO
abbRegCurrents = 0x4010 // Charging current phases 6 0.001 A unsigned RO available
@ -48,8 +48,6 @@ const (
abbRegSetCurrent = 0x4100 // Set charging current limit 2 0.001 A unsigned WO available
// abbRegSession = 0x4105 // Start/Stop Charging Session 1 unsigned WO available
// abbRegPhases = 0x4102 // Set charging phase 1 unsigned WO Not supported
abbMinCurrent = 0x1720 // 5920 mA
)
func init() {
@ -59,21 +57,19 @@ func init() {
// NewABBFromConfig creates a ABB charger from generic config
func NewABBFromConfig(other map[string]interface{}) (api.Charger, error) {
cc := modbus.Settings{
ID: 1,
Baudrate: 9600,
Comset: "8N1",
ID: 1,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewABB(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
return NewABB(cc.URI, cc.Device, cc.Comset, cc.Baudrate, modbus.ProtocolFromRTU(cc.RTU), cc.ID)
}
// NewABB creates ABB charger
func NewABB(uri, device, comset string, baudrate int, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(uri, device, comset, baudrate, modbus.Rtu, slaveID)
func NewABB(uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(uri, device, comset, baudrate, proto, slaveID)
if err != nil {
return nil, err
}
@ -86,9 +82,8 @@ func NewABB(uri, device, comset string, baudrate int, slaveID uint8) (api.Charge
conn.Logger(log.TRACE)
wb := &ABB{
log: log,
conn: conn,
curr: abbMinCurrent, // assume min current
curr: 6000, // assume min current
}
// keep-alive
@ -110,15 +105,6 @@ func (wb *ABB) status() (byte, error) {
return b[2] & 0x7f, nil
}
func (wb *ABB) session() (bool, error) {
s, err := wb.status()
if err != nil || s == 0 || s == 5 { // ToDo: check if 1 is active/inactive/unknown
return false, err
}
return true, err
}
// Status implements the api.Charger interface
func (wb *ABB) Status() (api.ChargeStatus, error) {
s, err := wb.status()
@ -138,6 +124,13 @@ func (wb *ABB) Status() (api.ChargeStatus, error) {
case 4: // State C2: Charging Contact closed, energy delivering
return api.StatusC, nil
case 5: // Other: Session stopped
b, err := wb.conn.ReadHoldingRegisters(abbRegSocketLock, 2)
if err != nil {
return api.StatusNone, err
}
if binary.BigEndian.Uint32(b) >= 0x0101 {
return api.StatusB, nil
}
return api.StatusA, nil
default: // Other
return api.StatusNone, fmt.Errorf("invalid status: %0x", s)
@ -146,26 +139,16 @@ func (wb *ABB) Status() (api.ChargeStatus, error) {
// Enabled implements the api.Charger interface
func (wb *ABB) Enabled() (bool, error) {
s, err := wb.session()
if err != nil || !s {
return false, err
}
b, err := wb.conn.ReadHoldingRegisters(abbRegGetCurrent, 2)
if err != nil {
return false, err
}
return binary.BigEndian.Uint32(b) >= abbMinCurrent, nil
return binary.BigEndian.Uint32(b) != 0, nil
}
// Enable implements the api.Charger interface
func (wb *ABB) Enable(enable bool) error {
s, err := wb.session()
if err != nil || !s {
return err
}
var current uint32
if enable {
current = wb.curr
@ -196,11 +179,6 @@ func (wb *ABB) MaxCurrentMillis(current float64) error {
return fmt.Errorf("invalid current %.1f", current)
}
s, err := wb.session()
if err != nil || !s {
return err
}
wb.curr = uint32(current * 1e3)
return wb.setCurrent(wb.curr)
@ -271,10 +249,13 @@ func (wb *ABB) Diagnose() {
fmt.Printf("\tFirmware:\t%d.%d.%d\n", b[0], b[1], b[2])
}
if b, err := wb.conn.ReadHoldingRegisters(abbRegMaxRated, 2); err == nil {
fmt.Printf("\tMax rated current:\t%.1fA\n", float32(binary.BigEndian.Uint32(b))/1e3)
fmt.Printf("\tMax rated current:\t%dmA\n", binary.BigEndian.Uint32(b))
}
if b, err := wb.conn.ReadHoldingRegisters(abbRegGetCurrent, 2); err == nil {
fmt.Printf("\tCharging current limit:\t%.1fA\n", float32(binary.BigEndian.Uint32(b))/1e3)
fmt.Printf("\tCharging current limit:\t%dmA\n", binary.BigEndian.Uint32(b))
}
if b, err := wb.conn.ReadHoldingRegisters(abbRegSocketLock, 2); err == nil {
fmt.Printf("\tSocket lock state:\t%x\n", b)
}
if b, err := wb.conn.ReadHoldingRegisters(abbRegStatus, 2); err == nil {
fmt.Printf("\tStatus:\t%x\n", b)

View file

@ -5,6 +5,9 @@ products:
generic: Terra AC
capabilities: ["mA"]
requirements:
description:
de: Erfordert Firmware >= 1.6.5
en: Requires firmware >= 1.6.5
evcc: ["sponsorship"]
params:
- name: modbus

View file

@ -3,6 +3,8 @@ product:
description: Terra AC
capabilities: ["mA"]
requirements: ["sponsorship"]
description: |
Erfordert Firmware >= 1.6.5
render:
- default: |
type: template