Earth Notes: MODBUS and Raspberry Pi for Off-grid Storage Monitoring

Updated 2022-09-18.
Using my SS-MPPT-15L solar charge controller as a smart remote battery monitor.
My off-grid RPi measures nominal 12V 'battery' voltage near itself with a plug-in ADC card to estimate how charged the battery bank is. From that it works out if it can run extra jobs, or has to conserve. But even the relatively short run from battery in the shed outside to the RPi in the house drops enough voltage to make that estimation difficult. This is especially hard when taking extra load from the grid onto the battery. An elegant fix is to take readings from the smart solar/battery controller, calibrated and very close to the battery...

Measuring Battery Voltage at the Bank

2016-08-08: I will try to measure battery voltage at the battery using the SS-MPPT-15L controller over MODBUS over RS232. So I will be ordering a PC MeterBus Adapter. This should largely eliminate the effects of voltage drops in the supply cables when estimating SoC (leaving genuine sag from the battery bank itself). This should also allow measuring (some of) the power flow into the bank, and out to the loads. The are various solutions for having the RPi talk MODBUS to the SS-MPPT-15L though I may prefer to keep everything in C for efficiency and use the LGPL libmodbus (see RPi build notes).

(Minutes after finally relenting and buying the Meterbus RS232 adapter that I had been considering for years, and working out how to interface it to USB for my RPi for example, I noticed that there is a USB version of the Meterbus adapter which would be simpler and probably more power efficient. Oh well.)

Meterbus Arrives

2016-08-10: Meterbus adapter arrived today. Got slightly spendy on a 5m FTDI USB 2.0 Cable Male USB to Male RS232 to reach directly from my RPi to the battery bank. (Possibly I should sheathe the cable with something UV resistant to protect the 1m or so outside, exposed to direct sunlight.)

I will first try out the connection to the SS-MPPT-15L from a Windows laptop using Morningstar's MS View application to try to maximise the chance of success.

SS-MPPT-15L solar controller

Then I will probably put together a stand-alone C or C++ program to access the SS-MPPT-15L from the RPi. This may continue to be used to gather stats beyond those needed for the power management function.

Finally I will aim to integrate the MODBUS code into the powermng code. There may be an interlock between this and the stand-alone code above to help avoid clashes.

2016-08-12: FTDI USB/Serial cable arrived, MS View tested on W10 laptop, all seems to be working with a couple of small surprises: the controller "hourmeter" is reading over 60,000 (installed 2009-09); and the 1st CS panel just apparently in full sunlight was showing ~120W rather than nearer nameplate even allowing for some very minor shading, probably because the battery is near enough full that that is all it can absorb. (Array power has been dropping steadily, and voltage rising over Vmp.)

Tested MODBUS access in stand-alone MODBUS access C++ source code, then integrated enough to fetch controller's (smoothed) notion of battery voltage in place of value measured at RPi. Lots of other things may need tweaking, eg assumptions about slump under load. It will also be good to gather load wattage and display it.

Smoothed/filtered battery voltage as seen by the controller now being used in powermng ~9pm. Locally measured LA/supply voltage being logged as 'Vbus', and can be used as a fallback in case of loss of comms with the controller. (The MODBUS connection seems slightly unreliable, so my code has built-in reties and pauses to improve that. But I'd still like the system to work reliably if, for example, the MODBUS link fails entirely, eg the plug is yanked out.)

2016/08/12T19:00:06Z AL -1 B1 12891 B2 -1 P -1 BV -1 ST OK D T
2016/08/12T19:10:06Z AL -1 B1 12863 B2 -1 P -1 BV -1 ST OK D e
2016/08/12T19:20:06Z AL -1 B1 12431 B2 -1 P -1 BV -1 ST OK D e
2016/08/12T19:30:06Z AL -1 B1 12855 B2 -1 P -1 BV 12534 ST OK D e
2016/08/12T19:40:06Z AL -1 B1 12845 B2 -1 P -1 BV 12516 ST OK D e
2016/08/12T19:50:07Z AL -1 B1 12845 B2 -1 P -1 BV 12544 ST OK D e
2016/08/12T20:00:06Z AL -1 B1 12845 B2 -1 P -1 BV 12562 ST OK D e

2016-08-13: now computing effective load power (from battery volts and load amps) and added them to the log for display. The difference between battery voltage and supply voltage seen at the RPi shows (eg) 0.5V drop at 70W (Mac recharging), or 100mΩ supply impedance. Possibly time to swap out that fuse for another DC circuit breaker. (The 20A MAXI fuse should only be ~3mΩ.)

2016/08/13T13:20:06Z AL -1 B1 13276 B2 -1 P 16715 BV 13135 ST H D H
2016/08/13T13:30:06Z AL -1 B1 13239 B2 -1 P 16668 BV 13079 ST H D H
2016/08/13T13:40:09Z AL -1 B1 14057 B2 -1 P 19498 BV 13923 ST VH D H
2016/08/13T13:50:07Z AL -1 B1 14060 B2 -1 P 73956 BV 13566 ST VH D H
2016/08/13T14:00:07Z AL -1 B1 14060 B2 -1 P 71172 BV 13595 ST VH D H
2016/08/13T14:10:06Z AL -1 B1 14060 B2 -1 P 55650 BV 13670 ST VH D V
2016/08/13T14:20:06Z AL -1 B1 14060 B2 -1 P 42602 BV 13773 ST VH D V
2016/08/13T14:30:06Z AL -1 B1 14060 B2 -1 P 34855 BV 13820 ST VH D V

2016-08-15: now displaying charge and load power as of today, alongside battery and RPi supply ('bus') volts.

2016/08/15T16:20:07Z AL 1634 B1 13465 B2 -1 P 23820 BV 13238 ST H D H A1P 22180
2016/08/15T16:30:06Z AL 1363 B1 13343 B2 -1 P 25112 BV 13116 ST OK D r A1P 18300
2016/08/15T16:40:06Z AL 1213 B1 13306 B2 -1 P 29447 BV 13069 ST OK D r A1P 16065
2016/08/15T16:50:06Z AL 1100 B1 13276 B2 -1 P 23711 BV 13088 ST OK D r A1P 14586
2016/08/15T17:00:06Z AL 998 B1 13257 B2 -1 P 24287 BV 13060 ST OK D r A1P 13136
2016/08/15T17:10:06Z AL 841 B1 13239 B2 -1 P 26968 BV 13041 ST OK D e A1P 11022
20160815: load, charge W; battery, bus V

Generation from the 510Wp array (A1P) starts about 30 minutes after official sunrise (05:48 BST, 04:48Z), and drops to zero about 30 minutes before official sunset (20:22 BST). (Note that this array is on the west of the house and in shade early morning.) According to the controller it generated/collected ~500Wh during the day.

At the winter solstice, ie ~20 Dec, out of ~7:49 of nominal daylight, the array may be generating for a shade under 7h vs 13.5h today.

2016/08/15T04:50:06Z AL 0 B1 12589 B2 -1 P 15938 BV 12412 ST OK D e A1P 0
2016/08/15T05:00:06Z AL 0 B1 12558 B2 -1 P 16514 BV 12412 ST OK D e A1P 0
2016/08/15T05:10:06Z AL 0 B1 12565 B2 -1 P 16762 BV 12403 ST OK D e A1P 0
2016/08/15T05:20:06Z AL 44 B1 12549 B2 -1 P 16502 BV 12403 ST OK D e A1P 499
2016/08/15T18:40:06Z AL 92 B1 12964 B2 -1 P 23336 BV 12722 ST OK D e A1P 1088
2016/08/15T18:50:06Z AL 42 B1 12931 B2 -1 P 22306 BV 12750 ST OK D e A1P 559
2016/08/15T19:00:07Z AL 0 B1 12928 B2 -1 P 25145 BV 12722 ST OK D e A1P 0
2016/08/15T19:10:06Z AL 0 B1 12897 B2 -1 P 22248 BV 12694 ST OK D e A1P 0

2016-08-19: a pretty cloudy day but ~200Wh captured by the primary array; compare with the grid-tie generation graph below. Here it is fairly evident that generation/capture is not being limited by the controller or the battery or the load.

20160819: load, charge W; battery, bus V
20160819: grid-tie output

2016-08-23: a good generation day with no interruptions. The battery rises very fast to absorption over ~90m. About 500Wh is generated off-grid with no manual loads added; the small automatic software based 'dump' load is visible ~10:00--15:00Z.

2016/08/23T09:50:06Z AL 974 B1 12665 B2 -1 P 16655 BV 12544 ST OK D e A1P 12261
2016/08/23T10:00:07Z AL 1063 B1 12678 B2 -1 P 16672 BV 12553 ST OK D e A1P 13408
2016/08/23T10:10:06Z AL 1179 B1 12702 B2 -1 P 16704 BV 12572 ST OK D e A1P 14933
2016/08/23T10:20:06Z AL 1332 B1 12708 B2 -1 P 16712 BV 12572 ST OK D c A1P 16941
2016/08/23T10:30:06Z AL 2829 B1 12784 B2 -1 P 17770 BV 12684 ST OK D c A1P 36388
2016/08/23T10:40:06Z AL 6204 B1 13199 B2 -1 P 17357 BV 13135 ST OK D c A1P 81865
2016/08/23T10:50:06Z AL 9891 B1 13694 B2 -1 P 17145 BV 13595 ST VH D h A1P 135450
2016/08/23T11:00:06Z AL 10098 B1 13846 B2 -1 P 17100 BV 13745 ST VH D h A1P 139768
2016/08/23T11:10:06Z AL 10886 B1 14054 B2 -1 P 17962 BV 13998 ST VH D h A1P 153040
2016/08/23T11:20:06Z AL 9316 B1 14060 B2 -1 P 16746 BV 13960 ST VH D h A1P 131192
2016/08/23T11:30:06Z AL 8374 B1 14060 B2 -1 P 16648 BV 13960 ST VH D V A1P 11758
20160823: load, charge W; battery, bus V
20160823: grid-tie output

2016-08-25: MODBUS is proving to be a bit flaky at the moment. I increased the number of retries from 3 to 4 last night but it made the problem worse, with more drop-outs. In this case most obviously to locally-measured voltage (Vbus) rather that the battery voltage measured by the SS-MPPT-15L.

20160825: LA V keeps dropping out to bus V

Wrapper

PC MeterBus Adapter in situ

I have put a simple wrapper around the single-register read in its own file to cache the libmodbus context (etc), as closing/freeing and reopening did not actually seem to help much. I will have to see if that is more reliable, though I am still at a loss as to the underlying failure cause given a clean opto-isolated USB/serial link from RPi to controller.

An initial problem was results getting mixed up between different registers, apparently picking up data already on the wire from a previous request. I have added some flush calls to attempt to mitigate that issue.

To try to avoid some (initial) read calls timing out, a 750ms delay after 'connecting' seems good (500ms not enough):

// Get process-scope cached MODBUS context/connection to SS-MPPT-15L.
// Flushes any pending input.
// Returns NULL in case of failure.
static modbus_t * const getCachedContext()
    {
    static modbus_t * const ctx = modbus_new_rtu(DEFAULT_MODBUS_DEV, 9600, 'N', 8, 2);
    if(NULL != ctx)
        {
        // Set the SS-MPPT-15L as the device go talk to.
        modbus_set_slave(ctx, SUNSAVERMPPT);
        // Attempt to connect; return NULL if failed.
        if(-1 == modbus_connect(ctx))
            {
            fprintf(stderr, "MODBUS: connection failed: %s\n", modbus_strerror(errno));
            return(NULL);
            }
        // Pause to allow things to settle and previous responses to clear.
        // 750ms seems enough to avoid most read timeouts following (500ms not).
        do { usleep(750000ul); } while(0 < modbus_flush(ctx));
        }
    return(ctx);
    }

Today's snapshot of the code does seem to be reasonably reliable for MODBUS again.

2016-09-25: improved code that has been working well for a while now that is much faster after the first call and does not open() the underlying device multiple times:

// Get process-scope cached MODBUS context/connection to SS-MPPT-15L.
// Flushes any pending input.
// Returns NULL in case of failure.
static modbus_t * const getCachedContext()
    {
    static modbus_t * const ctx = modbus_new_rtu(DEFAULT_MODBUS_DEV, 9600, 'N', 8, 2);
    if(NULL == ctx) { return(NULL); } // Failed.

    // First time only, set slave and connect to avoid multiple open() calls.
    static bool inited;
    if(!inited)
        {
        // Set the SS-MPPT-15L as the device to talk to.
        modbus_set_slave(ctx, SUNSAVERMPPT);
        // Attempt to connect; return NULL if failed.
        if(-1 == modbus_connect(ctx))
            {
            fprintf(stderr, "MODBUS: connection failed: %s\n", modbus_strerror(errno));
            return(NULL);
            }
        // Allow time for power to stabilise, etc.
        // (Our circs with RS232 inteface powered over USB...)
        // 750ms seems enough to avoid initial read timeout (500ms not).
        usleep(750000ul);
        inited = true;
        }

    // Pause/flush to allow things to settle and previous responses to clear.
    // 10ms is ~10 character times at 9600 bps;
    // typical single-register read is 7 or 8 chars.
    do { usleep(10000ul); } while(0 < modbus_flush(ctx));

    return(ctx);
    }

2016-10-01: sampling power parameters every few seconds shows that numbers can change fast. There was rain, cloud and sunshine in the log of ~5s samples captured with powermng -nl and graphed below with gnuplot -e "infilename='fast.dat'" -e "outfilename='out/fast.png'" -e "xafmt='%H%M'" graphing/gnuplotBattVRPi.txt supporting the old joke that if you don't like the British weather then just wait ten minutes...

20161001 afternoon fast sampling

Note that the battery voltage is smoothed over ~25s, the charge and load power ~1s, and Vbus not at all.

Another sample, after sun-down, showing the fairly variable and spiky load from networking and laptop (and RPi, a little).

20161001 evening fast sampling