volkszaehler / mbmd

ModBus Measurement Daemon - simple reading of data from ModBus meters and grid inverters
BSD 3-Clause "New" or "Revised" License
233 stars 82 forks source link

How to add additional meters - how to ?? #174

Closed craigcurtin-dev closed 3 years ago

craigcurtin-dev commented 3 years ago

Hi guys, i have a 3phase meter - various names - Acrel DTSD1352, ADL3000E/CT , Solis 3P, etc.

It is an RS485 Modbus compliant device - and i have the Modbus map for it - is there a way to include a translation table or some such going forward (i guess similar to what the SUnspec guys are trying to do) - i do not believe this meter is Sunspec compliant).

I know nothing about Go so if someone could point me to some sample files for other meters i will have a go at modifying one to support this meter

Craig

andig commented 3 years ago

Thank you for the question! The RTU meters are implemented here in this folder: https://github.com/volkszaehler/mbmd/tree/master/meters/rs485. Choose one that looks closest to how your meter is read and start with a copy! PRs are welcome.

craigcurtin-dev commented 3 years ago

Good one thanks – will give it a go and see how I go !

From: andig notifications@github.com Sent: Sunday, October 18, 2020 1:12 AM To: volkszaehler/mbmd mbmd@noreply.github.com Cc: Craig Curtin craigc@prosis.com.au; Author author@noreply.github.com Subject: Re: [volkszaehler/mbmd] How to add additional meters - how to ?? (#174)

Thank you for the question! The RTU meters are implemented here in this folder: https://github.com/volkszaehler/mbmd/tree/master/meters/rs485. Choose one that looks closest to how your meter is read and start with a copy!

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/volkszaehler/mbmd/issues/174#issuecomment-711006443, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AM6OST3KSZS2BFF352YCNDDSLGQZNANCNFSM4SSY47TQ.

craigcurtin-dev commented 3 years ago

Hey AndiG

Not really sure what i am doing here - i have tried to add a new meter ACREL3000 - which is a 3phase meter.

I have used the mbmd commandline with the read option and have been able to successfully query the meter. Not sure how to compile a new version that includes the meter file that i have attempted to put together to enable me to test it ?

regards

Craig

andig commented 3 years ago

See https://github.com/volkszaehler/mbmd#building-from-source for how to build from source. It assumes you have the sources checked out. Run make install once to setup the build tools.

craigcurtin-dev commented 3 years ago

Yes thanks for the quick reply.

I have tried that on the machine that i am using (Ubuntu 18.04) and have gotten it into a terrible mess - each time i try and run the make install as per your directions i am getting an error about other github repos - i have looked in the makefile (which is about the extent of my abilities) and can see it looking for other repos (enumer for instance) and failing on those.

So i have gotten about as far as i can. I was going to run up a new VM and attempt to start from scratch tomorrow and see how i go

Craig

andig commented 3 years ago

Just post your file here and I‘ll wip up a quick PR for you to checkout and test

andig commented 3 years ago

But.. you‘ll still need to be able to compile a PR ;)

craigcurtin-dev commented 3 years ago

package rs485

import . "github.com/volkszaehler/mbmd/meters"

func init() { Register(ACREL3000)

}

const ( METERTYPE_ACREL3000 = "ACREL3000" )

var ops3p Opcodes = Opcodes{

    Frequency:       0x0077, // 32 bit, Hz

    VoltageL1:       0x0061, // 32 bit, V
    CurrentL1:       0x0064, // 32 bit, A
    PowerL1:         0x0067, // 32 bit, kW
    ReactivePowerL1: 0x006B, // 32 bit, kvar
    ApparentPowerL1: 0x006F, // 32 bit, kva

// CosphiL1: 0x0036, // 32 bit, XX,X(literal)

    VoltageL2:       0x0062, // 32 bit, V
    CurrentL2:       0x0065, // 32 bit, A
    PowerL2:         0x0068, // 32 bit, kW
    ReactivePowerL2: 0x006C, // 32 bit, kvar
    ApparentPowerL2: 0x0070, // 32 bit, kva

// CosphiL2: 0x0038, // 32 bit, XX,X(literal)

    VoltageL3:       0x0063, // 32 bit, V
    CurrentL3:       0x0066, // 32 bit, A
    PowerL3:         0x0069, // 32 bit, kW
    ReactivePowerL3: 0x006D, // 32 bit, kvar
    ApparentPowerL3: 0x0071, // 32 bit, kva

// CosphiL3: 0x003A, // 32 bit, XX,X(literal)

    Power:           0x001C, // 32 bit, kW
    ReactivePower:   0x006E, // 32 bit, kvar
    ApparentPower:   0x0072, // 32 bit, kva

// Cosphi: 0x0034, // 32 bit, XX,X(literal)

// Sum: 0x0100, //32 Bit, kwh // SumL1: 0x0102, //32 Bit, kwh // SumL2: 0x0104, //32 Bit, kwh // SumL3: 0x0106, //32 Bit, kwh

// Import: 0x0108, //32 Bit, kwh // ImportL1: 0x010A, //32 Bit, kwh // ImportL2: 0x010C, //32 Bit, kwh // ImportL3: 0x010E, //32 Bit, kwh

// Export: 0x0110, //32 Bit, kwh // ExportL1: 0x0112, //32 Bit, kwh // ExportL2: 0x0114, //32 Bit, kwh // ExportL3: 0x0116, //32 Bit, kwh

// ReactiveSum: 0x0118, //32 Bit, kvarh // ReactiveSumL1: 0x011A, //32 Bit, kvarh // ReactiveSumL2: 0x011C, //32 Bit, kvarh // ReactiveSumL3: 0x011E, //32 Bit, kvarh

// ReactiveImport: 0x0120, //32 Bit, kvarh // ReactiveImportL1:0x0122, //32 Bit, kvarh // ReactiveImportL2:0x0124, //32 Bit, kvarh // ReactiveImportL3:0x0126, //32 Bit, kvarh

// ReactiveExport: 0x0128, //32 Bit, kvarh // ReactiveExportL1:0x012A, //32 Bit, kvarh // ReactiveExportL2:0x012C, //32 Bit, kvarh // ReactiveExportL3:0x012E, //32 Bit, kvarh

// SumT1: 0x0130, //32 Bit, kwh // ImportT1: 0x0132, //32 Bit, kwh // ExportT1: 0x0134, //32 Bit, kwh // ReactiveSumT1: 0x0136, //32 Bit, kvarh // ReactiveImportT1:0x0138, //32 Bit, kvarh // ReactiveExportT1:0x013A, //32 Bit, kvarh

// SumT2: 0x013C, //32 Bit, kwh // ImportT2: 0x013E, //32 Bit, kwh // ExportT2: 0x0140, //32 Bit, kwh // ReactiveSumT2: 0x0142, //32 Bit, kvarh // ReactiveImportT2:0x0144, //32 Bit, kvarh // ReactiveExportT2:0x0146, //32 Bit, kvarh

/* // Curently not supported SumT3: 0x0148, //32 Bit, kwh ImportT3: 0x014A, //32 Bit, kwh ExportT3: 0x014C, //32 Bit, kwh ReactiveSumT3: 0x015E, //32 Bit, kvarh ReactiveImportT3:0x0150, //32 Bit, kvarh ReactiveExportT3:0x0152, //32 Bit, kvarh

    SumT4:           0x0154, //32 Bit, kwh
    ImportT4:        0x0156, //32 Bit, kwh
    ExportT4:        0x0158, //32 Bit, kwh
    ReactiveSumT4:   0x015A, //32 Bit, kvarh
    ReactiveImportT4:0x015C, //32 Bit, kvarh
    ReactiveExportT4:0x015E, //32 Bit, kvarh

*/ }

type ORNO3PProducer struct { Opcodes }

func NewORNO3PProducer() Producer { return &ORNO3PProducer{Opcodes: ops3p} }

// Type implements Producer interface func (p *ORNO3PProducer) Type() string { return METERTYPE_ORNO3p }

// Description implements Producer interface func (p *ORNO3PProducer) Description() string { return "ACREL3000 3Phase" }

// snip creates modbus operation func (p *ORNO3PProducer) snip(iec Measurement, readlen uint16) Operation { return Operation{ FuncCode: ReadHoldingReg, OpCode: p.Opcode(iec), // adjust according to docs ReadLen: readlen, IEC61850: iec, } }

// snip32 creates modbus operation for double register func (p *ORNO3PProducer) snip32(iec Measurement, scaler ...float64) Operation { snip := p.snip(iec, 2)

    snip.Transform = RTUIeee754ToFloat64 // default conversion
    if len(scaler) > 0 {
            snip.Transform = MakeScaledTransform(snip.Transform, scaler[0])
    }

    return snip

}

func (p *ORNO3PProducer) Probe() Operation { return p.snip32(VoltageL1,1) }

// Produce implements Producer interface func (p *ORNO3PProducer) Produce() (res []Operation) {

// These values are stored as literals for _, op := range []Measurement{ Frequency, VoltageL1, CurrentL1, VoltageL2, CurrentL2, VoltageL3, CurrentL3, // Sum, SumL1, SumL2, SumL3, // Import, ImportL1, ImportL2, ImportL3, // Export, ExportL1, ExportL2, ExportL3, ReactiveSum, ReactiveSumL1, ReactiveSumL2, ReactiveSumL3, // ReactiveImport, ReactiveImportL1, ReactiveImportL2, ReactiveImportL3, // ReactiveExport, ReactiveExportL1, ReactiveExportL2, ReactiveExportL3, // SumT1, ImportT1, ExportT1, ReactiveSumT1, ReactiveImportT1, ReactiveExportT1, // SumT2, ImportT2, ExportT2, ReactiveSumT2, ReactiveImportT2, ReactiveExportT2, } { res = append(res, p.snip32(op, 1)) }

// For Power values, we need to scale by 1000 (aka convert kW/kva -> W/va) for _, op := range []Measurement{ PowerL1, ReactivePowerL1, ApparentPowerL1, PowerL2, ReactivePowerL2, ApparentPowerL2, PowerL3, ReactivePowerL3, ApparentPowerL3, Power, ReactivePower, ApparentPower, } { res = append(res, p.snip32(op, 0.001)) } return res }

craigcurtin-dev commented 3 years ago

This is where i am at so far but need to test it before i flesh it out and rename all of the internal variables etc

Hmm, the whole GO compile thing is a bit of a nightmare !

Craig

craigcurtin-dev commented 3 years ago

Thanks AndiG - now trying to work out how to get this PR onto my system and then compile !!

Craig

andig commented 3 years ago

Ping @craigcurtin-dev would appreciate some feedback after putting everything together for you ;)

craigcurtin-dev commented 3 years ago

Yeah i am struggling with the whole compile the PR in go thing at the moment and it has slipped off my urgent list (so many things to do - so little time !)

I have a weekly reminder to get back to it but will be another couple of weeks until i free up the time.

thanks for the follow up

Craig