From 7f95e78eb4f6ef34171d82bcba10de9af1127220 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 12 Aug 2021 21:41:19 +0200 Subject: [PATCH] Add ABL eMH1 (#1298) --- charger/abl.go | 178 ++++++++++++++++++++++++++++++++++++++ charger/heidelberg-ec.go | 14 ++- charger/phoenix-em-eth.go | 2 +- charger/phoenix-ev-eth.go | 2 +- charger/phoenix-ev-ser.go | 2 +- charger/simpleevse.go | 2 +- charger/wallbe.go | 2 +- go.mod | 5 +- go.sum | 16 ++-- meter/modbus.go | 8 +- provider/modbus.go | 7 +- util/modbus/modbus.go | 24 ++++- 12 files changed, 229 insertions(+), 33 deletions(-) create mode 100644 charger/abl.go diff --git a/charger/abl.go b/charger/abl.go new file mode 100644 index 000000000..8f0b0334f --- /dev/null +++ b/charger/abl.go @@ -0,0 +1,178 @@ +package charger + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/andig/evcc/api" + "github.com/andig/evcc/util" + "github.com/andig/evcc/util/modbus" + "github.com/andig/evcc/util/sponsor" +) + +// ABLeMH charger implementation +type ABLeMH struct { + log *util.Logger + conn *modbus.Connection + current uint16 +} + +const ( + ablRegFirmware = 0x01 + ablRegVehicleStatus = 0x04 + ablRegAmpsConfig = 0x14 + ablRegStatus = 0x2E + + ablAmpsDisabled uint16 = 0x03E8 + + // ablRegMode = 0x05 + // ablReset uint16 = 0x5A5A + // ablEnable uint16 = 0xA1A1 + // ablDisable uint16 = 0xE0E0 +) + +func init() { + registry.Add("abl", NewABLeMHFromConfig) +} + +// https://www.goingelectric.de/forum/viewtopic.php?p=1550459#p1550459 + +// NewABLeMHFromConfig creates a ABLeMH charger from generic config +func NewABLeMHFromConfig(other map[string]interface{}) (api.Charger, error) { + cc := modbus.Settings{ + ID: 1, + } + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + return NewABLeMH(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID) +} + +// NewABLeMH creates ABLeMH charger +func NewABLeMH(uri, device, comset string, baudrate int, slaveID uint8) (api.Charger, error) { + conn, err := modbus.NewConnection(uri, device, comset, baudrate, modbus.AsciiFormat, slaveID) + if err != nil { + return nil, err + } + + if !sponsor.IsAuthorized() { + return nil, errors.New("abl requires evcc sponsorship, register at https://cloud.evcc.io") + } + + log := util.NewLogger("abl") + conn.Logger(log.TRACE) + + wb := &ABLeMH{ + log: log, + conn: conn, + current: 0x64, // 6A + } + + return wb, nil +} + +// Status implements the api.Charger interface +func (wb *ABLeMH) Status() (api.ChargeStatus, error) { + b, err := wb.conn.ReadHoldingRegisters(ablRegVehicleStatus, 1) + if err != nil { + return api.StatusNone, err + } + + r := rune(b[1]>>4-0x0A) + 'A' + + switch r { + case 'A', 'B', 'C': + return api.ChargeStatus(r), nil + default: + return api.StatusNone, fmt.Errorf("invalid status: %v", r) + } +} + +// Enabled implements the api.Charger interface +func (wb *ABLeMH) Enabled() (bool, error) { + b, err := wb.conn.ReadHoldingRegisters(ablRegAmpsConfig, 1) + if err != nil { + return false, err + } + + enabled := binary.BigEndian.Uint16(b) != ablAmpsDisabled + + return enabled, nil +} + +// Enable implements the api.Charger interface +func (wb *ABLeMH) Enable(enable bool) error { + u := ablAmpsDisabled + if enable { + u = wb.current + } + + _, err := wb.conn.WriteSingleRegister(ablRegAmpsConfig, u) + + return err +} + +// MaxCurrent implements the api.Charger interface +func (wb *ABLeMH) MaxCurrent(current int64) error { + return wb.MaxCurrentMillis(float64(current)) +} + +var _ api.ChargerEx = (*ABLeMH)(nil) + +// MaxCurrent implements the api.ChargerEx interface +func (wb *ABLeMH) MaxCurrentMillis(current float64) error { + // calculate duty cycle according to https://www.goingelectric.de/forum/viewtopic.php?p=1575287#p1575287 + u := uint16(current / 0.06) + + b := make([]byte, 2) + binary.BigEndian.PutUint16(b, u) + + _, err := wb.conn.WriteSingleRegister(ablRegAmpsConfig, u) + if err == nil { + wb.current = u + } + + return err +} + +var _ api.Meter = (*ABLeMH)(nil) + +// CurrentPower implements the api.Meter interface +func (wb *ABLeMH) CurrentPower() (float64, error) { + l1, l2, l3, err := wb.Currents() + return 230 * (l1 + l2 + l3), err +} + +var _ api.MeterCurrent = (*ABLeMH)(nil) + +// Currents implements the api.MeterCurrent interface +func (wb *ABLeMH) Currents() (float64, float64, float64, error) { + b, err := wb.conn.ReadHoldingRegisters(ablRegStatus, 5) + if err != nil { + return 0, 0, 0, err + } + + var currents []float64 + for i := 2; i < 5; i++ { + u := binary.BigEndian.Uint16(b[2*i:]) + if u == ablAmpsDisabled { + u = 0 + } + + currents = append(currents, float64(u)/10) + } + + return currents[0], currents[1], currents[2], nil +} + +var _ api.Diagnosis = (*ABLeMH)(nil) + +// Diagnose implements the api.Diagnosis interface +func (wb *ABLeMH) Diagnose() { + if b, err := wb.conn.ReadHoldingRegisters(ablRegFirmware, 2); err == nil { + fmt.Printf("Firmware: %0 x\n", b) + } +} diff --git a/charger/heidelberg-ec.go b/charger/heidelberg-ec.go index 129f8e05a..28ff68706 100644 --- a/charger/heidelberg-ec.go +++ b/charger/heidelberg-ec.go @@ -39,24 +39,20 @@ func init() { // NewHeidelbergECFromConfig creates a HeidelbergEC charger from generic config func NewHeidelbergECFromConfig(other map[string]interface{}) (api.Charger, error) { - cc := struct { - modbus.Settings `mapstructure:",squash"` - }{ - Settings: modbus.Settings{ - ID: 1, - }, + cc := modbus.Settings{ + ID: 1, } if err := util.DecodeOther(other, &cc); err != nil { return nil, err } - return NewHeidelbergEC(cc.URI, cc.Device, cc.Comset, cc.Baudrate, true, cc.ID) + return NewHeidelbergEC(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID) } // NewHeidelbergEC creates HeidelbergEC charger -func NewHeidelbergEC(uri, device, comset string, baudrate int, rtu bool, slaveID uint8) (api.Charger, error) { - conn, err := modbus.NewConnection(uri, device, comset, baudrate, rtu, slaveID) +func NewHeidelbergEC(uri, device, comset string, baudrate int, slaveID uint8) (api.Charger, error) { + conn, err := modbus.NewConnection(uri, device, comset, baudrate, modbus.RtuFormat, slaveID) if err != nil { return nil, err } diff --git a/charger/phoenix-em-eth.go b/charger/phoenix-em-eth.go index 7a27bb85f..118c45c25 100644 --- a/charger/phoenix-em-eth.go +++ b/charger/phoenix-em-eth.go @@ -73,7 +73,7 @@ func NewPhoenixEMEthFromConfig(other map[string]interface{}) (api.Charger, error // NewPhoenixEMEth creates a Phoenix charger func NewPhoenixEMEth(uri string, id uint8) (*PhoenixEMEth, error) { - conn, err := modbus.NewConnection(uri, "", "", 0, false, id) + conn, err := modbus.NewConnection(uri, "", "", 0, modbus.TcpFormat, id) if err != nil { return nil, err } diff --git a/charger/phoenix-ev-eth.go b/charger/phoenix-ev-eth.go index 4f53d73b3..4999a3111 100644 --- a/charger/phoenix-ev-eth.go +++ b/charger/phoenix-ev-eth.go @@ -74,7 +74,7 @@ func NewPhoenixEVEthFromConfig(other map[string]interface{}) (api.Charger, error // NewPhoenixEVEth creates a Phoenix charger func NewPhoenixEVEth(uri string, id uint8) (*PhoenixEVEth, error) { - conn, err := modbus.NewConnection(uri, "", "", 0, false, id) + conn, err := modbus.NewConnection(uri, "", "", 0, modbus.TcpFormat, id) if err != nil { return nil, err } diff --git a/charger/phoenix-ev-ser.go b/charger/phoenix-ev-ser.go index 8f909ed6b..578c30867 100644 --- a/charger/phoenix-ev-ser.go +++ b/charger/phoenix-ev-ser.go @@ -43,7 +43,7 @@ func NewPhoenixEVSerFromConfig(other map[string]interface{}) (api.Charger, error // NewPhoenixEVSer creates a Phoenix charger func NewPhoenixEVSer(uri, device, comset string, baudrate int, id uint8) (*PhoenixEVSer, error) { - conn, err := modbus.NewConnection(uri, device, comset, baudrate, true, id) + conn, err := modbus.NewConnection(uri, device, comset, baudrate, modbus.RtuFormat, id) if err != nil { return nil, err } diff --git a/charger/simpleevse.go b/charger/simpleevse.go index af41c1362..24ea3d9d5 100644 --- a/charger/simpleevse.go +++ b/charger/simpleevse.go @@ -45,7 +45,7 @@ func NewSimpleEVSEFromConfig(other map[string]interface{}) (api.Charger, error) func NewSimpleEVSE(uri, device, comset string, baudrate int, rtu bool, slaveID uint8) (api.Charger, error) { log := util.NewLogger("evse") - conn, err := modbus.NewConnection(uri, device, comset, baudrate, rtu, slaveID) + conn, err := modbus.NewConnection(uri, device, comset, baudrate, modbus.RtuFormat, slaveID) if err != nil { return nil, err } diff --git a/charger/wallbe.go b/charger/wallbe.go index c9d56e8b7..5dc4af82f 100644 --- a/charger/wallbe.go +++ b/charger/wallbe.go @@ -104,7 +104,7 @@ func NewWallbeFromConfig(other map[string]interface{}) (api.Charger, error) { // NewWallbe creates a Wallbe charger func NewWallbe(uri string) (*Wallbe, error) { - conn, err := modbus.NewConnection(uri, "", "", 0, false, wbSlaveID) + conn, err := modbus.NewConnection(uri, "", "", 0, modbus.TcpFormat, wbSlaveID) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index 82df0a2f1..3f3917a51 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/benbjohnson/clock v1.1.0 github.com/bogosj/tesla v1.0.2 github.com/containrrr/shoutrrr v0.4.4 - github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/denisbrodbeck/machineid v1.0.1 github.com/dustin/go-humanize v1.0.0 github.com/dylanmei/iso8601 v0.1.0 @@ -67,10 +66,10 @@ require ( github.com/spf13/viper v1.8.1 github.com/thoas/go-funk v0.9.0 github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c - github.com/volkszaehler/mbmd v0.0.0-20210706160533-aa268417e0ef + github.com/volkszaehler/mbmd v0.0.0-20210808132733-8235dc7d1327 gitlab.com/bboehmke/sunny v0.14.2 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect - golang.org/x/net v0.0.0-20210716203947-853a461950ff + golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 golang.org/x/text v0.3.6 google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect diff --git a/go.sum b/go.sum index f57083ffb..7c54b4029 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -124,7 +125,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU= github.com/deepmap/oapi-codegen v1.3.9/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.1/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= @@ -319,7 +319,6 @@ github.com/gregdel/pushover v1.1.0 h1:dwHyvrcpZCOS9V1fAnKPaGRRI5OC55cVaKhMybqNsK github.com/gregdel/pushover v1.1.0/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to= github.com/grid-x/modbus v0.0.0-20200108122021-57d05a9f1e1a/go.mod h1:cOS1YDRu+JId5eT7kRulYTlfpACFppj2MpftCvHeiuM= github.com/grid-x/modbus v0.0.0-20200704184817-73c05928caa1/go.mod h1:cOS1YDRu+JId5eT7kRulYTlfpACFppj2MpftCvHeiuM= -github.com/grid-x/modbus v0.0.0-20210224155242-c4a3d042e99b/go.mod h1:YaK0rKJenZ74vZFcSSLlAQqtG74PMI68eDjpDCDDmTw= github.com/grid-x/modbus v0.0.0-20210714071042-7af2b65ec03b h1:ur6X0rPWCe3R5AC5X7qIySVhP/34pM3A1uQVJ0GEdKw= github.com/grid-x/modbus v0.0.0-20210714071042-7af2b65ec03b/go.mod h1:YaK0rKJenZ74vZFcSSLlAQqtG74PMI68eDjpDCDDmTw= github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08 h1:syBxnRYnSPUDdkdo5U4sy2roxBPQDjNiw4od7xlsABQ= @@ -609,11 +608,11 @@ github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52 github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.0 h1:WhlbjwB9EGCc8W5Rxdkus+wmH2ASRwwTJk6tgHKwdqQ= +github.com/spf13/cast v1.4.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.2.0/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -662,8 +661,8 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/volkszaehler/mbmd v0.0.0-20200717102329-c4d965bd1eac/go.mod h1:sldLyJCKVO9GQkit55U5WPNr9U4KmUn1SCdqFN9Gjb4= -github.com/volkszaehler/mbmd v0.0.0-20210706160533-aa268417e0ef h1:OIKfPaGDM8LjDBMyaNfCJ6vgmwbRdj6hsSj2bs2qzJ8= -github.com/volkszaehler/mbmd v0.0.0-20210706160533-aa268417e0ef/go.mod h1:BDNStSIw76tsTTgAmRvdo6o9ILyCK9BNVXbeFf9jjvI= +github.com/volkszaehler/mbmd v0.0.0-20210808132733-8235dc7d1327 h1:GhXik8mWOCLDq7sCJjHjSv49fYUvhxOMMhrGYqAFowI= +github.com/volkszaehler/mbmd v0.0.0-20210808132733-8235dc7d1327/go.mod h1:oh17P6CJFJvVlFfLSgbOh+GdgmtuHpxik7gk7Zr/kxg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -793,8 +792,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds= -golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -974,7 +973,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/meter/modbus.go b/meter/modbus.go index 789d102aa..86bd680ed 100644 --- a/meter/modbus.go +++ b/meter/modbus.go @@ -53,9 +53,12 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) { cc.RTU = &b } - log := util.NewLogger("modbus") + format := modbus.TcpFormat + if cc.RTU != nil && *cc.RTU { + format = modbus.RtuFormat + } - conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, *cc.RTU, cc.ID) + conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, format, cc.ID) if err != nil { return nil, err } @@ -65,6 +68,7 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) { conn.Timeout(cc.Timeout) } + log := util.NewLogger("modbus") conn.Logger(log.TRACE) // prepare device diff --git a/provider/modbus.go b/provider/modbus.go index 232783726..e25756f7d 100644 --- a/provider/modbus.go +++ b/provider/modbus.go @@ -49,7 +49,12 @@ func NewModbusFromConfig(other map[string]interface{}) (IntProvider, error) { cc.RTU = &b } - conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, *cc.RTU, cc.ID) + format := modbus.TcpFormat + if cc.RTU != nil && *cc.RTU { + format = modbus.RtuFormat + } + + conn, err := modbus.NewConnection(cc.URI, cc.Device, cc.Comset, cc.Baudrate, format, cc.ID) if err != nil { return nil, err } diff --git a/util/modbus/modbus.go b/util/modbus/modbus.go index 450e30f02..95826bad4 100644 --- a/util/modbus/modbus.go +++ b/util/modbus/modbus.go @@ -16,6 +16,14 @@ import ( // WriteSingleRegister 16-bit wise write access const WriteSingleRegister = 6 // modbus.FuncCodeWriteSingleRegister +type WireFormat int + +const ( + TcpFormat WireFormat = iota + RtuFormat + AsciiFormat +) + // Settings contains the ModBus settings type Settings struct { ID uint8 @@ -140,7 +148,7 @@ func registeredConnection(key string, newConn meters.Connection) meters.Connecti } // NewConnection creates physical modbus device from config -func NewConnection(uri, device, comset string, baudrate int, rtu bool, slaveID uint8) (*Connection, error) { +func NewConnection(uri, device, comset string, baudrate int, wire WireFormat, slaveID uint8) (*Connection, error) { var conn meters.Connection if device != "" && uri != "" { @@ -151,15 +159,23 @@ func NewConnection(uri, device, comset string, baudrate int, rtu bool, slaveID u if baudrate == 0 || comset == "" { return nil, errors.New("invalid modbus configuration: need baudrate and comset") } - conn = registeredConnection(device, meters.NewRTU(device, baudrate, comset)) + + if wire == RtuFormat { + conn = registeredConnection(device, meters.NewRTU(device, baudrate, comset)) + } else { + conn = registeredConnection(uri, meters.NewASCII(device, baudrate, comset)) + } } if uri != "" { uri = util.DefaultPort(uri, 502) - if rtu { + switch wire { + case RtuFormat: conn = registeredConnection(uri, meters.NewRTUOverTCP(uri)) - } else { + case AsciiFormat: + conn = registeredConnection(uri, meters.NewASCIIOverTCP(uri)) + default: conn = registeredConnection(uri, meters.NewTCP(uri)) } }