Skip to content
This repository has been archived by the owner on Jun 25, 2019. It is now read-only.

Implement Saia Burgess ALE3 #54

Merged
merged 4 commits into from
Sep 20, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add Saia Burgess Controls ALE3 meter
  • Loading branch information
andig committed Sep 20, 2018
commit a6cfa4a9b0ac0fa8d3c4e301de04b8f792490237
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ manuals for yourself, I could be wrong):
| SDM630 v2 | 3 | + | + | + | + | + | + | + | + |
| Janitza B23-312 | 3 | + | + | + | + | + | + | - | - |
| DZG DVH4013 | 3 | + | + | - | - | + | + | - | - |
| SBC ALE3 | 3 | + | + | + | + | + | + | - | - |

Please note that voltage, current, power and power factor are always
reported for each connected phase.
Expand All @@ -50,6 +51,9 @@ reported for each connected phase.
serial number (top right of the device), e.g. 23, and add one (24).
Assume this is a hexadecimal number and convert it to decimal (36). Use
this as the meter ID.
* SBC ALE3: This compact Saia Burgess Controls meter is comparable to the SDM630:
two tariffs, both import and export depending on meter version and compact (4TE).
It's often used with Viessmann heat pumps.

Some of my test devices have been provided by [B+G
E-Tech](http://bg-etech.de/) - please consider to buy your meter from
Expand Down
5 changes: 3 additions & 2 deletions cmd/sdm630/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ func main() {
Usage: `MODBUS device type and ID to query, separated by comma.
Valid types are:
"SDM" for Eastron SDM meters
"JANITZA" for Janitza B-Series DIN-Rail meters
"DZG" for the DZG Metering GmbH DVH4013 DIN-Rail meter
"JANITZA" for Janitza B-Series meters
"DZG" for the DZG Metering GmbH DVH4013 meters
"SBC" for the Saia Burgess Controls ALE3 meters
Example: -d JANITZA:1,SDM:22,DZG:23`,
},
cli.StringFlag{
Expand Down
5 changes: 3 additions & 2 deletions cmd/sdm630_httpd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ func main() {
Usage: `MODBUS device type and ID to query, separated by comma.
Valid types are:
"SDM" for Eastron SDM meters
"JANITZA" for Janitza B-Series DIN-Rail meters
"DZG" for the DZG Metering GmbH DVH4013 DIN-Rail meter
"JANITZA" for Janitza B-Series meters
"DZG" for the DZG Metering GmbH DVH4013 meters
"SBC" for the Saia Burgess Controls ALE3 meters
Example: -d JANITZA:1,SDM:22,DZG:23`,
},
cli.StringFlag{
Expand Down
5 changes: 5 additions & 0 deletions meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ func NewMeterByType(
measurements as the other meters. Only limited functionality is
implemented.`)
p = NewDZGProducer()
case METERTYPE_SBC:
log.Println(`WARNING: The SBC ALE3 does not report the same
measurements as the other meters. Only limited functionality is
implemented.`)
p = NewSBCProducer()
default:
return nil, fmt.Errorf("Unknown meter type %s", typeid)
}
Expand Down
114 changes: 114 additions & 0 deletions sbc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package sdm630

import (
"math"
)

const (
METERTYPE_SBC = "SBC"

/***
* Opcodes for Saia Burgess ALE3
* http://datenblatt.stark-elektronik.de/saia_burgess/DE_DS_Energymeter-ALE3-with-Modbus.pdf
*/
OpCodeSaiaTotalImport = 28 - 1 // double, scaler 100
OpCodeSaiaPartialImport = 30 - 1 // double, scaler 100
OpCodeSaiaTotalExport = 32 - 1 // double, scaler 100
OpCodeSaiaPartialExport = 34 - 1 // double, scaler 100

OpCodeSaiaL1Voltage = 36 - 1
OpCodeSaiaL1Current = 37 - 1 // scaler 10
OpCodeSaiaL1Power = 38 - 1 // scaler 100
OpCodeSaiaL1ReactivePower = 39 - 1 // scaler 100
OpCodeSaiaL1Cosphi = 40 - 1 // scaler 100

OpCodeSaiaL2Voltage = 41 - 1
OpCodeSaiaL2Current = 42 - 1 // scaler 10
OpCodeSaiaL2Power = 43 - 1 // scaler 100
OpCodeSaiaL2ReactivePower = 44 - 1 // scaler 100
OpCodeSaiaL2Cosphi = 45 - 1 // scaler 100

OpCodeSaiaL3Voltage = 46 - 1
OpCodeSaiaL3Current = 47 - 1 // scaler 10
OpCodeSaiaL3Power = 48 - 1 // scaler 100
OpCodeSaiaL3ReactivePower = 49 - 1 // scaler 100
OpCodeSaiaL3Cosphi = 50 - 1 // scaler 100

OpCodeSaiaTotalPower = 51 - 1 // scaler 100
OpCodeSaiaTotalReactivePower = 52 - 1 // scaler 100
)

type SBCProducer struct {
}

func NewSBCProducer() *SBCProducer {
return &SBCProducer{}
}

func (p *SBCProducer) GetMeterType() string {
return METERTYPE_SBC
}

func (p *SBCProducer) snip(devid uint8, opcode uint16, iec string, readlen uint16) QuerySnip {
return QuerySnip{
DeviceId: devid,
FuncCode: ReadHoldingReg,
OpCode: opcode,
ReadLen: readlen,
Value: math.NaN(),
IEC61850: iec,
}
}

// snip16 creates modbus operation for single register
func (p *SBCProducer) snip16(devid uint8, opcode uint16, iec string, scaler ...float64) QuerySnip {
snip := p.snip(devid, opcode, iec, 1)

snip.Transform = RTU16ToFloat64 // default conversion
if len(scaler) > 0 {
snip.Transform = MakeRTU16ScaledIntToFloat64(scaler[0])
}

return snip
}

// snip32 creates modbus operation for double register
func (p *SBCProducer) snip32(devid uint8, opcode uint16, iec string, scaler ...float64) QuerySnip {
snip := p.snip(devid, opcode, iec, 2)

snip.Transform = RTU32ToFloat64 // default conversion
if len(scaler) > 0 {
snip.Transform = MakeRTU32ScaledIntToFloat64(scaler[0])
}

return snip
}

func (p *SBCProducer) Probe(devid uint8) QuerySnip {
return p.snip16(devid, OpCodeSaiaL1Voltage, "VolLocPhsA")
}

func (p *SBCProducer) Produce(devid uint8) (res []QuerySnip) {
res = append(res, p.snip16(devid, OpCodeSaiaL1Voltage, "VolLocPhsA"))
res = append(res, p.snip16(devid, OpCodeSaiaL2Voltage, "VolLocPhsB"))
res = append(res, p.snip16(devid, OpCodeSaiaL3Voltage, "VolLocPhsC"))

res = append(res, p.snip16(devid, OpCodeSaiaL1Current, "AmpLocPhsA", 10))
res = append(res, p.snip16(devid, OpCodeSaiaL2Current, "AmpLocPhsB", 10))
res = append(res, p.snip16(devid, OpCodeSaiaL3Current, "AmpLocPhsC", 10))

res = append(res, p.snip16(devid, OpCodeSaiaL1Power, "WLocPhsA", 100))
res = append(res, p.snip16(devid, OpCodeSaiaL2Power, "WLocPhsB", 100))
res = append(res, p.snip16(devid, OpCodeSaiaL3Power, "WLocPhsC", 100))

res = append(res, p.snip16(devid, OpCodeSaiaL1Cosphi, "AngLocPhsA", 100))
res = append(res, p.snip16(devid, OpCodeSaiaL2Cosphi, "AngLocPhsB", 100))
res = append(res, p.snip16(devid, OpCodeSaiaL3Cosphi, "AngLocPhsC", 100))

// res = append(res, p.snip16(devid, OpCodeSaiaTotalPower, "WLoc", 100))

res = append(res, p.snip32(devid, OpCodeSaiaTotalImport, "TotkWhImport", 100))
res = append(res, p.snip32(devid, OpCodeSaiaTotalExport, "TotkWhExport", 100))

return res
}