FM80 solar charge controller datalogger

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
https://www.outbackpower.com/downloads/documents/charge_controllers/flexmax_6080/owner_manual.pdf
The FLEXmax Charge Controllers use continuous Maximum Power Point Tracking (MPPT), which seeks out the maximum power available from a PV array and uses it to recharge the batteries. Without this feature, the PV array does not operate at the ideal voltage and can only operate at the level of the battery voltage itself. (See page 97 for a description of MPPT.) Each charge controller continuously tracks the array’s maximum operating power.
They use a 9N1 proprietary interface protocol that's been reversed engineered several times.
Most of the protocol and interface adapter information came from here.
https://github.com/jorticus/pymate

That software was written in Python so I re-implemented it using XC8 for the PIC18 series of controllers so I can pull important system data for logging to the local system without the expensive and limited Outback MATE remote display that has an ASCII port.
https://www.outbackpower.com/downlo...ment/mate/mate_serial_communicationsguide.pdf
The Mate controller is designed to report status and control the operating modes of OutBack Power Systems power conversion equipment. The Mate communicates with OutBack products through a proprietary serial communication link, and receives pre defined status pages from whatever type of OutBack product it is connected to. The Mate is also capable of issuing commands to OutBack products. At this point, commands are limited to controlling an FX inverter. The Mate can be directly connected to a single OutBack device, or to multiple devices using an OutBack HUB.
By connecting directly to the CC I have complete control and access to the device.

1685848368105.png
A example data capture from the prototype.
1685848671749.png
You can see the MPPT moving the panel operating voltage to increase panel power as the sunsets.

The new monitoring system will use the original front-end board from another solar 24VDC monitor project and a newly designed PIC18F47Q84 processing board.
1685848928572.png
https://forum.allaboutcircuits.com/...c-controlled-battery-array.32879/post-1460084
 

Attachments

Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
For the software serial interface @ 9N1 the EUSART is a little too smart. It can be configured for 9-bit date in address or parity mode but only to a 8-bit data stream and I need 9-bit data into and from a 16-bit array. 9-bit address mode works but you need to push the 9-th bit high using the UxP1L register instead of the normal UxTXB register. For receive you need to check the UxERRIRbits.PERIF bit for 9 bit high. It's better just to disable EUSART interrupts and poll at several times the 9600 baud rate using a 500us timer interrupt ISR to check for TX/RX flags and stuff the data arrays as needed.

1685852388798.png
C:
/*
 * serial I/O ISR, TMR4 500us I/O sample rate
 * polls the required UART registers for 9-bit send and receive into 16-bit arrays
 */
void FM_io(void)
{
    static uint8_t pace = 0;  // the charge controller doesn't like back to back bytes

    MISC_SetHigh(); // serial CPU usage signal

    if (pace++ > 3) {  
        if (dcount-- > 0) {
            if (tbuf[dstart] > 0xff) { // Check for bit-9
                U1P1L = (uint8_t) tbuf[dstart]; // send with bit-9 high
            } else {
                UART1_Write((uint8_t) tbuf[dstart]); // send with bit-9 low
            }
            dstart++;
        } else {
            dstart = 0;
            dcount = 0;
        }
        pace = 0;
    }

    /*
     * handle framing errors
     */
    if (U1ERRIRbits.RXFOIF) {
        rbuf[0] = U1RXB; // read bad data to clear error
        U1ERRIRbits.RXFOIF = 0;
        rdstart = 0; // reset buffer to start
    }

    /*
     * read serial data if polled interrupt flag is set
     */
    if (PIR4bits.U1RXIF) {
        if (U1ERRIRbits.FERIF) {
        }

        if (rdstart > FM_BUFFER - 1) { // overload buffer index
            rdstart = 0; // reset buffer to start
            MLED_SetHigh();
        }
        if (U1ERRIRbits.PERIF) {
            rbuf[rdstart] = 0x0100;
        } else {
            rbuf[rdstart] = 0x00;
        }
        rbuf[rdstart] += U1RXB;
        rdstart++;
    }
    MISC_SetLow();
}
Pacing (gaps between words) on transmit 9N1 data (yellow) and the CC reply (purple).
1685852463319.png
1685852545576.png
For the status command:
C:
    const uint16_t cmd_id[] = {0x100, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02};
    const uint16_t cmd_status[] = {0x100, 0x02, 0x01, 0xc8, 0x00, 0x00, 0x00, 0xcb};  // multi-value status command
    const uint16_t cmd_mx_status[] = {0x100, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05};
    const uint16_t cmd_panelv[] = {0x100, 0x02, 0x01, 0xc6, 0x00, 0x00, 0x00, 0xc9};
    const uint16_t cmd_batteryv[] = {0x100, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a};
    const uint16_t cmd_batterya[] = {0x100, 0x02, 0x01, 0xc7, 0x00, 0x00, 0x00, 0xca};
    const uint16_t cmd_watts[] = {0x100, 0x02, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x6d};
    const uint16_t cmd_misc[] = {0x100, 0x02, 0x01, 0xc8, 0x00, 0x00, 0x00, 0xcb}; // example MX80 command request status
You get this is return.
1685852993509.png

TX: 100 02 00 01 00 00 00 03 (Command)
RX: 102 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF (Response)
| |…………………………………………………| |………|
| | Checksum
| Status page
Device Type



81 22 33 44 55 66 77 88 99 AA BB CC DD
|| | | | | | | | | |---| |---|
|| | | | | | | | | | +- in_voltage (uint16 / 10.0)
|| | | | | | | | | +------- out_voltage (uint16 / 10.0)
|| | | | | | | | +------------- kwh (int16 / 10.0, lower byte)
|| | | | | | | +---------------- error (bit field)
|| | | | | | +------------------- status (01..04)
|| | | | | +---------------------- aux mode / state
|| | | | +------------------------- AH lower byte (int12)
|| | | +---------------------------- kwh (int16 / 10.0, upper byte)
|| | +------------------------------- out_amps_dc (int8, 0x80=0.0)
|| +---------------------------------- in_amps_dc (int8, 0x80=0.0)
|+------------------------------------- out_amps_dc (tenths, 0x01=0.1A, 0x0F=1.5A) FM80/FM60 only
+-------------------------------------- AH upper nibble (int12, 0x800=0.0)
This status packet is quite tightly packed, and the signed integers are not your typical 2's complement.
[/quote]
that can be parsed in to something usable like this with that received data in the abuf array:
/*
* display div 10 integer to fraction without FP
* %d.%01d volt_whole, volt_fract
*/
void volt_f(uint16_t voltage)
{
volt_fract = (uint16_t) abs(voltage % 10);
volt_whole = voltage / 10;
}


void state_mx_status_cb(void)
{
uint16_t vf, vw;
static uint8_t log_pace = 0;

volt_f((abuf[11] + (abuf[10] << 8)));
vw = volt_whole;
vf = volt_fract;
volt_f((abuf[13] + (abuf[12] << 8)));
if ((abuf[1] &0x0f) > 9) { // check for whole Amp
abuf[2]++; // add extra Amp for fractional overflow.
abuf[1] = (abuf[1]&0x0f) - 10;
}
#ifdef debug_data
printf("%5d: %3x %3x %3x %3x %3x SDATA: MX80 Data mode %3x %3x %3x %3x %3x %3x %3x %3x %3x\r\n",
rx_count++, abuf[0], abuf[1], abuf[2], abuf[3], abuf[4], abuf[5], abuf[6], abuf[7], abuf[8], abuf[9], abuf[10], abuf[11], abuf[12], abuf[13]);
#endif
if (log_pace++ == 0) {
/*
* log CSV values to the serial port for data storage and processing
*/
printf("^^^,%d.%01d,%d.%01d,%d,%d.%01d,%d,%d,%d\r\n", abuf[3] - 128, abuf[1]&0x0f, vw, vf, abuf[2] - 128, volt_whole, volt_fract, panel_watts, cc_mode, rx_count++);
}
if (log_pace > 5) {
log_pace = 0;
}
state = state_misc;
}
It's total kludge of a data sequence from the Charge controller but it's reliable and matches the data from the unit panel display.
1685853560982.png
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
A start of the morning plot from about 5:30AM to 1:30PM
1685915060431.png
Up until sample ~2400 you can see the FM80 checking the panels for sun power during 'Snooze' mode at dawn. Around 2800 (sun shine on the panels) you see it start to 'Bulk' recharge the battery then idle for a bit until ~3100 where I add 200W load and it starts to recharge the battery and power the load. From 3400 to 3600 you see the effects of clouds. At 4100 the battery charge mode switched to float and is steady until 5100 when I add another 200W load for the rest of the day. The large spikes down to zero watts are where the FM80 restarts the MPPT. I need to adjust a few parameters and modes on the CC.

1685915599379.png 1685915629103.png
1685915666243.png
Current from FM80
1685915730352.png
Current to inverter. Very little current is being supplied by the battery while the FM80 is in float mode.

One of the functions of the CC datalogger/battery monitor is to add a current sensor in the battery string so I can calculate the energy moving into and out of the battery to track the battery SOC as loads and panel energy dynamically change.

The prototype 24volt system had this for monitor points but the FM80 data will reduce the number uC data ports to only a few critical point related to the battery bank like the Hall BC and a few voltage points to mainly check for excessive voltage drops and when to turn on the AC charger.
1685916219867.png
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Full harvest day chart limited by the 600W inverter.
1685930319886.png
1685930408150.png
I have an extra 4 100W panels to make two 6 panel strings to up the VOC to over 100VDC. (Open-Circuit Voltage(Voc) 21.6V per panel) This should add another ~25VDC to the current panel voltage under load with 10 gauge panel feed wiring. I have 3 gauge wiring but I don't think it's needed. The FM80 has a VOC max of 150VDC so the higher voltages are OK to reduce line losses.
1685931895326.png

I'll add a remote panel voltage sense wire inputs (with a isolation amp) to check for panel line losses on the system monitor.

https://www.kg4cyx.net/under-the-hood-the-outback-fm80/
Warning: Engineering porn ahead. All images are clickable to view in full resolution.
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Received the 4000W inverter. Seems pretty solid so far with some typical loads that should always be under 2000W total.

1686186212692.png
No load idle-current looks good.

1686186318718.png
Inverter remote display power matches the incoming solar power
1686186412326.png
Battery and Inverter sending power to the load (Air Conditioner)

Outdoor PV disconnect and feed-wring to inverter upgrade.
1686186527318.png
My only DC wiring color code rule is that Black should never be the POSITIVE polarity wire.
https://assets.new.siemens.com/siem...d15eae5c4/sie-cs-vbii-pf-60a-3r-gd-switch.pdf
250DC 60A
1686186815761.png
Overcast morning but turned sunny later.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
The panel remote feed is done with about 50ft of 6 gauge wire round-trip, still need to dig the trench and bury it. . Should be about 1% voltage loss at full-power panel power with the third row added.
1686264711302.png1686264742812.png

Have the 40-pin DIP Q84 controller boards from JLC, $2 for 5 with a $20 shipping charge LOL.
1686264895896.png
 

Jerry-Hat-Trick

Joined Aug 31, 2022
547
Respect. Thanks for sharing.

I've been looking at building something similar, albeit significantly simpler, to charge batteries using the really inexpensive ESP32 development board with WiFi and Bluetooth for communication and logging. The ESP32 is fast enough to switch the mark/space of the DC/DC converter directly without using a conventional converter IC but this may be taking things too far.

My maximum power point tracking is a simple perturbation and observation algorithm. Measure the voltage across the battery under charge, try a small change in mark/space ratio and see if the battery voltage goes up or down. If it goes up, that's an increase in current and voltage, i.e. power and vice versa.

Typically batteries expect a charge routine based on their open circuit voltage and charge current as a function of "C" the mAhr capacity of the battery. So far I'm using a brief disconnect every 5 minutes to check battery voltage and if the solar panel is able to deliver more power than the battery can take I'll need to deliberately move away from the maximum power point. Adding an inverter will make the whole process more complicated but you've got me thinking!
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Respect. Thanks for sharing.

I've been looking at building something similar, albeit significantly simpler, to charge batteries using the really inexpensive ESP32 development board with WiFi and Bluetooth for communication and logging. The ESP32 is fast enough to switch the mark/space of the DC/DC converter directly without using a conventional converter IC but this may be taking things too far.

My maximum power point tracking is a simple perturbation and observation algorithm. Measure the voltage across the battery under charge, try a small change in mark/space ratio and see if the battery voltage goes up or down. If it goes up, that's an increase in current and voltage, i.e. power and vice versa.

Typically batteries expect a charge routine based on their open circuit voltage and charge current as a function of "C" the mAhr capacity of the battery. So far I'm using a brief disconnect every 5 minutes to check battery voltage and if the solar panel is able to deliver more power than the battery can take I'll need to deliberately move away from the maximum power point. Adding an inverter will make the whole process more complicated but you've got me thinking!
My system will do (command the FM80) a MPPT restart sweep if it detects a large change in operating conditions to optimize output to the load. As currently designed, the system is very storage lean to quickly generate load instabilities for testing. The batteries are the intermittent base load generator for when solar energy is dipping quickly. The current battery bank is not currently designed for long duration energy storage. The primary function is to run day-time opportunity loads like the AC or server power that have dual redundant auto-load switching power supplies.
An HPE ProLiant server solutions configured with 2 Flex Slot Power Supplies – 500W, 800W, 1000W, 1600W, or 1800W-2200W
- supports the following three power scenarios:
• Operation with a single power supply
• Operation with redundant power supplies in load-balanced mode
• Operation with redundant power supplies in high-efficiency mode
A single Flex Slot Power Supply supporting the entire load of the server can achieve the highest efficiency when operating in the
middle range (50%) of its capacity

For redundant Flex Slot Power Supplies operating in load-balanced mode (the default mode when adding redundant power supplies), the load is shared equally between the two power supplies. In general, the load-balanced mode offers better efficiency for loads requiring more than 60 percent of the primary power supply capacity. When high-efficiency mode is enabled for redundant supplies (via the server's ROM-based setup utility under System options -> Redundancy options), each power supply in the server is designated as either a primary or secondary supply, and the entire server load is shifted to the primary power supply. This allows the primary power supply to operate at higher efficiency points on the load curve while the secondary power supply operates in idle mode, providing no output power and consuming very little energy (typically two to four watts per supply). The user can also specify that odd or even power supplies will be designated manually or automatically as secondary supplies. This flexibility allows users to balance the load across a rack manually or automatically.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Time to build the first 5 for $2, low cost JLCPCB, Q84 board.
1686443066826.png
40MHz cpu clock with 4xPLL. Very simple two layer build.
1686443091114.png
1686445292365.png
One or two small buglets (pin reconfiguration) means another $2 version 1.1 for a clean build but nothing major, it works.
1686443255995.png
1686443311018.png
4x20 LCD display using SPI https://newhavendisplay.com/content/specs/NHD-0420D3Z-FL-GBW.pdf

Powered from the USB to TTL adapter 5VDC. The board has an internal 5VDC switching regulator that can take up to 36VDC on the input.

Source code: https://github.com/nsaspook/Q84vtouch
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Now that those bugs are cured it's time to move on to the next thing.

First add a MODBUS master to the Q84 controller PCB next revision using UART5 on the chip. The target is a AC power monitor I've already written a simple driver for.
https://forum.allaboutcircuits.com/threads/pic32mk-mc-qei-example.150351/post-1730490
https://gavazzi.se/app/uploads/2022/03/em500-cp-v1r3-eng.pdf

I've added the MODBUS C code to the Q84 but need an external TTL to RS-485 transceiver for testing. I should have one somewhere in the junk-box.
https://forum.allaboutcircuits.com/...c-controlled-battery-array.32879/post-1485220
1686520311790.png

Have the full set of panels installed. Two strings of six panels in three rows. Looks good voltage wise in the heat (under 150vdc VOC). If the cold weather in winter VOC is too high I'll just remove one panel per string.
1686520626368.png
1686520648313.png
Load power.
1686520677415.png
Battery recharge and load power.
1686532040117.png
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
REV 1.1 PCB in black and gold :D with TTL serial, CANBUS and MODBUS interface channels with LCD display.
1686861800047.png
Remote panel voltage interface. Voltage divider for 160VDC max panel voltage to 4.095 max ADC input voltage.
1686862881793.png
1686861878878.png

1686862241677.png
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Have all the interfaces working on the REV 1.1 board. Outback Mate network interface RJ45, CANBUS to another MATE board, MODBUS to the AC line power monitor (EM540), serial ttl to the USB for data logging and SPI to the LCD.
1687144301620.png
LCD screen SCM: FM80 in Sleep mode, Canbus online, Modbus online.
1687144604256.png
CANBUS FD to the another board and MODBUS to the AC power monitor.
1687144639015.png
1687144667880.png
MODBUS TX/RX half-duplex data.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
More detail on the MODBUS interface to the energy/AC power monitor.
https://www.gavazzionline.com/pdf/EM540_DS_ENG.pdf
Measure active, reactive and apparent energy
• Measure the main electrical variables
• Measure the load run hours and of the analyser
• Measure the total harmonic distortion (THD) of current and voltages
• Transmit data to other systems through Modbus RTU or M-Bus
• Manage a digital output for pulses or alarm transmission
• Visualize the measured variables on the display

System and phase variables (V L-L, V L-N, A, W/var, VA, PF, Hz)
• Displaying of the consumed active energy with a resolution of 0.001 kWh
• The frequency value is available via Modbus, with a resolution of 0.001 Hz
• Average value calculation (dmd) for current and power (kW / kVA)
• Streamlined user interface featuring 3 mechanical buttons
• Modbus RTU RS485 (data refresh every 100 ms)
• Continuous sampling of each voltage and current
Test equipment with similar capabilities can cost thousands.

I'm monitoring and logging WATTS, VA , VAR and VOLTS from the inverter to loads.
1687287052976.png
1687286984649.png
Some of the unit MODBUS registers.
1687287122459.png
Last line data from the EM540 energy monitor: 119.1W, 133.7VA, -2.1VAR, 119.8VAC
1687287173852.png
I think the Chinese inverter power displayed is a little high, not surprised. :cool:

1687287585481.png
1687287550680.png
The scope only power factor is pretty crappy.
C:
const uint8_t
// transmit frames for commands
modbus_em_id[] = {MADDR, READ_HOLDING_REGISTERS, 0x00, 0x0b, 0x00, 0x01}, // Carlo Gavazzi Controls identification code
modbus_em_version[] = {MADDR, READ_HOLDING_REGISTERS, 0x03, 0x02, 0x00, 0x01}, // Firmware version and revision code
modbus_em_data[] = {MADDR, READ_HOLDING_REGISTERS, 0x00, 0x00, 0x00, 33}, // last number is 16-bit words wanted from the start register address
modbus_em_config[] = {MADDR, WRITE_SINGLE_REGISTER, 0x10, 0x02, 0x00, 0x02}, // System configuration, Value 2 = ?2P? (2-phase with neutral)
modbus_em_passwd[] = {MADDR, WRITE_SINGLE_REGISTER, 0x10, 0x00, 0x00, 0x00}, // Password configuration, set to no password = 0
// receive frames prototypes for received data checking
em_id[] = {MADDR, READ_HOLDING_REGISTERS, 0x00, 0x00, 0x00, 0x00, 0x00},
em_version[] = {MADDR, READ_HOLDING_REGISTERS, 0x00, 0x00, 0x00, 0x00, 0x00},
em_data[] = {MADDR, READ_HOLDING_REGISTERS, 0x00, // number of 16-bit words returned, IN BYTES
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00}, // crc
em_config[] = {MADDR, WRITE_SINGLE_REGISTER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
em_passwd[] = {MADDR, WRITE_SINGLE_REGISTER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
We send and receive the em_data frames then parse the data into the needed variables.
C:
/*
* reorder 16-bit word bytes for int32_t
* https://control.com/forums/threads/endianness-for-32-bit-data.48584/
* https://ctlsys.com/support/common_modbus_protocol_misconceptions/
* https://iotech.force.com/edgexpert/s/article/Byte-and-Word-Swapping-in-Modbus
*
* "Little Endian" slaves or "Big Endian" slaves
* Byte endianness with Word endianness?
* Lions and Tigers and Bears!
*/
int32_t mb32_swap(int32_t value)
{
    uint8_t i;
    union MREG32 dvalue;

    // program it simple and easy to understand way, let the compiler optimize the expressions
    dvalue.value = value;
    i = dvalue.bytes[0];
    dvalue.bytes[0] = dvalue.bytes[1];
    dvalue.bytes[1] = i;
    i = dvalue.bytes[2];
    dvalue.bytes[2] = dvalue.bytes[3];
    dvalue.bytes[3] = i;
    return dvalue.value;
}


    /*
     * fake good received bytes from client
     * a null string with not debugging
     */
#define    DBUG_R    //    true ||


            case G_DATA: // check for controller data codes
#ifdef    MB_EM540
                client->req_length = sizeof(em_data);
                if (DBUG_R((M.recv_count >= client->req_length) && (cc_buffer[0] == MADDR) && (cc_buffer[1] == READ_HOLDING_REGISTERS))) {
                    c_crc = crc16(cc_buffer, client->req_length - 2);
                    c_crc_rec = crc16_receive(client);
                    if (DBUG_R c_crc == c_crc_rec) {
                        client->data_ok = true;
                        /*
                         * move from receive buffer to data structure and munge the data into the correct local 32-bit format from MODBUS client
                         */
                        memcpy((void*) &em, (void*) &cc_buffer[3], sizeof(em));
                        em.vl1l2 = mb32_swap(em.vl1l2);
                        em.vl2l3 = mb32_swap(em.vl2l3);
                        em.vl3l1 = mb32_swap(em.vl3l1);
                        em.al1 = mb32_swap(em.al1);
                        em.al2 = mb32_swap(em.al2);
                        em.al3 = mb32_swap(em.al3);
                        em.wl1 = mb32_swap(em.wl1);
                        em.wl2 = mb32_swap(em.wl2);
                        em.wl3 = mb32_swap(em.wl3);
                        em.val1 = mb32_swap(em.val1);
                        em.val2 = mb32_swap(em.val2);
                        em.val3 = mb32_swap(em.val3);
                        em.varl1 = mb32_swap(em.varl1);
                        em.varl2 = mb32_swap(em.varl2);
                        em.varl3 = mb32_swap(em.varl3);
                        client->data_prev = client->data_count;
                        client->data_count++;
                        MM_ERROR_C;
                    } else {
                        MM_ERROR_C;
                        client->data_ok = false;
                        log_crc_error(c_crc, c_crc_rec);
                        MM_ERROR_S;
                    }
                    client->cstate = CLEAR;
                } else {
                    if (get_500hz(false) >= RDELAY) {
                        client->cstate = CLEAR;
                        MM_ERROR_C;
                        client->mcmd = G_ID;
                        M.to_error++;
                        M.error++;
                        MM_ERROR_S;
                    }
                }
#endif
                break;
C:
    if (B.ten_sec_flag) {
        B.ten_sec_flag = false;
        if (B.mx80_online) {
            /*
             * log CSV values to the serial port for data storage and processing
             */
            printf("^^^,%d.%01d,%d.%01d,%d,%d.%01d,%d,%d,%.1f,%.1f,%.1f,%4.1f,%d\r\n", abuf[3] - 128, abuf[1]&0x0f, vw, vf, abuf[2] - 128, volt_whole, volt_fract, panel_watts, cc_mode, ((float) em.wl1) / 10.0f, ((float) em.val1) / 10.0f, ((float) em.varl1) / 10.0f, ((float) em.vl1l2) / 10.0f, B.rx_count++);
            snprintf(can_buffer, MAX_B_BUF, "^^^,%d.%01d,%d.%01d,%d,%d.%01d,%d,%d,%.1f,%.1f,%.1f,%4.1f,%d\r\n", abuf[3] - 128, abuf[1]&0x0f, vw, vf, abuf[2] - 128, volt_whole, volt_fract, panel_watts, cc_mode, ((float) em.wl1) / 10.0f, ((float) em.val1) / 10.0f, ((float) em.varl1) / 10.0f, ((float) em.vl1l2) / 10.0f, B.rx_count);
            snprintf(buffer, MAX_B_BUF, "%d Watts %d.%01d Volts   ", panel_watts, volt_whole, volt_fract);
            eaDogM_WriteStringAtPos(2, 0, buffer);
            snprintf(buffer, MAX_B_BUF, "%d.%01d Amps %d.%01d Volts   ", abuf[3] - 128, abuf[1]&0x0f, vw, vf);
            eaDogM_WriteStringAtPos(3, 0, buffer);
            snprintf(buffer, MAX_B_BUF, "%6.1fW %6.1fVA %c%c%c   ", ((float) em.wl1) / 10.0f, ((float) em.val1) / 10.0f, state_name[cc_mode][0], canbus_name[B.canbus_online][0], modbus_name[B.modbus_online][0]);
            eaDogM_WriteStringAtPos(0, 0, buffer);
            can_fd_tx(); // send the logging packet via CANBUS
        }
    }
Ten second data logging and display updates. Printf and non-buffer-overflow family functions are fine to use if the 8-bit chip has the code/memory space and speed is not an issue.
1687288632706.png
No need for FreeRTOS or any OS emulating multitasking. Just Plain-Jane interrupt, state machine and stack programming using real hardware module multi-tasking.
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
I should have all the bugs and wiring issues resolved with AC power monitoring from the EM540. All of the power indications look reasonable now across systems (FM80, Inverter, MATE_monitor)
1687385215407.png
PXL_20230621_213400747.NIGHT.jpg
With AC fan and server Power Supply loads, the leading and lagging reactive power Q (load_ac_var) is close to balanced. With the mainly capacitive PS load only, the Q is slightly more negative. When the main AC compressor load kicks in, the Q increases positive to a sizable level as the motor inductive current load pulls power.
https://en.wikipedia.org/wiki/AC_power
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
I have my energy bank battery finally.
1687745049242.png

I've been working on the battery energy tracking functions without using coulomb counting (energy source and drain accounting). It seem to work OK with the much smaller testing battery set.
The WH number is the remaining battery energy left in the small set.
1687745402560.png
 
Top