Skip to content

Commit

Permalink
Update for EM2GO chargers (#7099)
Browse files Browse the repository at this point in the history
* Changed brand 'D-Parts' to 'EM2GO', Updated template docs and definition for em2go, Updated em2go.go: completed ModBus registers, added Status F, Voltages, Powers, extended diagnosis.

* Fix test

* Fix test

* Removed PhasePowers as it´s not being unused by chargers

* Fix build, fix CurrentPower

---------

Co-authored-by: Dominik Niakamal <dominik.niakamal@d-parts.de>
  • Loading branch information
2 people authored and andig committed Mar 30, 2023
1 parent 31183e9 commit 4674066
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 29 deletions.
99 changes: 78 additions & 21 deletions charger/em2go.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,32 @@ type Em2Go struct {
}

const (
em2GoRegStatus = 0
em2GoRegCurrents = 6
em2GoRegPower = 12
em2GoRegEnergy = 28
em2GoRegCurrent = 32
em2GoRegSerial = 38
em2GoRegChargedEnergy = 72
em2GoRegChargeDuration = 78
em2GoRegChargeMode = 93
em2GoRegChargeCommand = 95
em2GoRegStatus = 0 // Uint16 RO ENUM
em2goRegConnectorState = 2 // Uint16 RO ENUM
em2goRegErrorCode = 4 // Uint16 RO ENUM
em2GoRegCurrents = 6 // 3xUint16 RO 0.1A
em2GoRegPower = 12 // Uint32 RO 1W
em2GoRegPowers = 16 // 3xUint16 RO 1W
em2GoRegEnergy = 28 // Uint16 RO 0.1KWh
em2GoRegMaxCurrent = 32 // Uint16 RO 0.1A
em2GoRegMinCurrent = 34 // Uint16 RO 0.1A
em2goRegCableMaxCurrent = 36 // Uint16 RO 0.1A
em2GoRegSerial = 38 // Chr[16] RO UTF16
em2GoRegChargedEnergy = 72 // Uint16 RO 0.1kWh
em2GoRegChargeDuration = 78 // Uint32 RO 1s
em2goRegSafeCurrent = 87 // Uint16 WR 0.1A
em2goRegCommTimeout = 89 // Uint16 WR 1s
em2goRegCurrentLimit = 91 // Uint16 WR 0.1A
em2GoRegChargeMode = 93 // Uint16 WR ENUM
em2GoRegChargeCommand = 95 // Uint16 WR ENUM
em2goRegVoltages = 109 // 3xUint16 RO 0.1V
)

func init() {
registry.Add("em2go", NewEm2GoFromConfig)
}

// NewEm2GoFromConfig creates a Mennekes Em2Go charger from generic config
// NewEm2GoFromConfig creates a Em2Go charger from generic config
func NewEm2GoFromConfig(other map[string]interface{}) (api.Charger, error) {
cc := modbus.TcpSettings{
ID: 0xff,
Expand Down Expand Up @@ -105,8 +114,10 @@ func (wb *Em2Go) Status() (api.ChargeStatus, error) {
return api.StatusA, nil
case 2, 3:
return api.StatusB, nil
case 4:
case 4, 6:
return api.StatusC, nil
case 5, 7:
return api.StatusF, nil
default:
return api.StatusNone, fmt.Errorf("invalid status: %0x", b[1])
}
Expand Down Expand Up @@ -145,7 +156,7 @@ func (wb *Em2Go) MaxCurrentMillis(current float64) error {
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, uint16(10*current))

_, err := wb.conn.WriteMultipleRegisters(em2GoRegCurrent, 1, b)
_, err := wb.conn.WriteMultipleRegisters(em2goRegCurrentLimit, 1, b)
return err
}

Expand All @@ -170,21 +181,35 @@ func (wb *Em2Go) TotalEnergy() (float64, error) {
return 0, err
}

return rs485.RTUUint32ToFloat64(b) / 10, nil
return float64(binary.BigEndian.Uint16(b)) / 10, nil
}

var _ api.PhaseCurrents = (*Em2Go)(nil)

// Currents implements the api.MeterCurrent interface
// Currents implements the api.PhaseCurrents interface
func (wb *Em2Go) Currents() (float64, float64, float64, error) {
b, err := wb.conn.ReadHoldingRegisters(em2GoRegCurrents, 3)
b, err := wb.conn.ReadHoldingRegisters(em2GoRegCurrents, 6)
if err != nil {
return 0, 0, 0, err
}

return float64(binary.BigEndian.Uint16(b)) / 10,
float64(binary.BigEndian.Uint16(b[2:])) / 10,
float64(binary.BigEndian.Uint16(b[4:])) / 10, nil
float64(binary.BigEndian.Uint16(b[4:])) / 10,
float64(binary.BigEndian.Uint16(b[8:])) / 10, nil
}

var _ api.PhaseVoltages = (*Em2Go)(nil)

// Currents implements the api.PhaseVoltages interface
func (wb *Em2Go) Voltages() (float64, float64, float64, error) {
b, err := wb.conn.ReadHoldingRegisters(em2goRegVoltages, 6)
if err != nil {
return 0, 0, 0, err
}

return float64(binary.BigEndian.Uint16(b)) / 10,
float64(binary.BigEndian.Uint16(b[4:])) / 10,
float64(binary.BigEndian.Uint16(b[8:])) / 10, nil
}

var _ api.ChargeRater = (*Em2Go)(nil)
Expand All @@ -196,7 +221,7 @@ func (wb *Em2Go) ChargedEnergy() (float64, error) {
return 0, err
}

return rs485.RTUUint32ToFloat64(b) / 10, nil
return float64(binary.BigEndian.Uint16(b)) / 10, nil
}

var _ api.ChargeTimer = (*Em2Go)(nil)
Expand All @@ -215,6 +240,24 @@ var _ api.Diagnosis = (*Em2Go)(nil)

// Diagnose implements the api.Diagnosis interface
func (wb *Em2Go) Diagnose() {
if b, err := wb.conn.ReadHoldingRegisters(em2GoRegStatus, 1); err == nil {
fmt.Printf("\tCharging Station Status:\t%d\n", binary.BigEndian.Uint16(b))
}
if b, err := wb.conn.ReadHoldingRegisters(em2goRegConnectorState, 1); err == nil {
fmt.Printf("\tConnector State:\t%d\n", binary.BigEndian.Uint16(b))
}
if b, err := wb.conn.ReadHoldingRegisters(em2goRegErrorCode, 1); err == nil {
fmt.Printf("\tError Code:\t%d\n", binary.BigEndian.Uint16(b))
}
if b, err := wb.conn.ReadHoldingRegisters(em2GoRegMaxCurrent, 1); err == nil {
fmt.Printf("\tEVSE Max. Current:\t%.1fA\n", float64(binary.BigEndian.Uint16(b)/10))
}
if b, err := wb.conn.ReadHoldingRegisters(em2GoRegMaxCurrent, 1); err == nil {
fmt.Printf("\tEVSE Min. Current:\t%.1fA\n", float64(binary.BigEndian.Uint16(b)/10))
}
if b, err := wb.conn.ReadHoldingRegisters(em2goRegCableMaxCurrent, 1); err == nil {
fmt.Printf("\tCable Max. Current:\t%.1fA\n", float64(binary.BigEndian.Uint16(b)/10))
}
var serial []byte
for reg := 0; reg < 8; reg++ {
b, err := wb.conn.ReadHoldingRegisters(em2GoRegSerial+2*uint16(reg), 2)
Expand All @@ -223,6 +266,20 @@ func (wb *Em2Go) Diagnose() {
}
serial = append(serial, b...)
}

fmt.Printf("Serial: %s\n", string(serial))
fmt.Printf("\tSerial: %s\n", string(serial))
if b, err := wb.conn.ReadHoldingRegisters(em2goRegSafeCurrent, 1); err == nil {
fmt.Printf("\tSafe Current:\t%.1fA\n", float64(binary.BigEndian.Uint16(b)/10))
}
if b, err := wb.conn.ReadHoldingRegisters(em2goRegCommTimeout, 1); err == nil {
fmt.Printf("\tConnection Timeout:\t%d\n", binary.BigEndian.Uint16(b))
}
if b, err := wb.conn.ReadHoldingRegisters(em2goRegCurrentLimit, 1); err == nil {
fmt.Printf("\tCurrent Limit:\t%.1fA\n", float64(binary.BigEndian.Uint16(b)/10))
}
if b, err := wb.conn.ReadHoldingRegisters(em2GoRegChargeMode, 1); err == nil {
fmt.Printf("\tCharge Mode:\t%d\n", binary.BigEndian.Uint16(b))
}
if b, err := wb.conn.ReadHoldingRegisters(em2GoRegChargeCommand, 1); err == nil {
fmt.Printf("\tCharge Command:\t%d\n", binary.BigEndian.Uint16(b))
}
}
11 changes: 8 additions & 3 deletions templates/definition/charger/em2go.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
template: em2go
products:
- brand: D-Parts
- brand: EM2GO
description:
generic: Em2Go
generic: Pro Power/OCPP/ONC
capabilities: ["mA", "rfid"]
requirements:
description:
de: Für die Nutzung mit evcc muss die Wallbox mit einer aktuellen Firmware mit Modbus-Unterstützung betrieben werden. Bitte mit dem Hersteller abklären.
en: For use with evcc, the wallbox must be operated with a recent firmware including Modbus support. Please contact vendor.
params:
- name: modbus
choice: ["tcpip"]
id: 255
render: |
type: em2go
{{- include "modbus" . }}
{{- include "modbus" . }}
9 changes: 5 additions & 4 deletions templates/docs/charger/em2go_0.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
product:
brand: D-Parts
description: Em2Go
brand: EM2GO
description: Pro Power/OCPP/ONC
capabilities: ["mA", "rfid"]
render:
- default: |
type: template
template: em2go
template: em2go
# Modbus TCP
modbus: tcpip
id: 255
host: 192.0.2.2 # Hostname
port: 502 # Port
port: 502 # Port
2 changes: 1 addition & 1 deletion templates/evcc.io/brands.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
"Bender",
"BMW",
"cFos",
"D-Parts",
"Dadapower",
"DaheimLaden",
"E.ON Drive",
"Easee",
"Ebee",
"echarge",
"Elli",
"EM2GO",
"Ensto",
"Etrel",
"EVBox",
Expand Down

0 comments on commit 4674066

Please sign in to comment.