Victron MPPT data using Arduino serial

From Sensors in Schools
Jump to navigation Jump to search

Reading Victron MPPT Data

References

Parts Required

  • Victron BlueSolar MPPT 75/15 Retail SCC010015050R - $135 (Note that this unit is not Bluetooth enabled) [1]
  • JST Jumper 4 Wire Assembly - $2.75 [2]
  • Arduino Uno

Victron Label descriptions for MPPT Solar Panel Charge Controller

  • This extract is taken from page 6 of the VE.Direct Protocol.
    • V - battery voltage measured in milliVolts (mV)
    • VPV - Solar panel voltage (mV)
    • PPV - Power output from the Solar Panel (Watts). Note that if the battery is fully charged the MPPT will set power to zero to prevent the battery over charging.
    • I - current from the MPPT charge controller to the battery (mA). This will also be zero if the battery if fully charged.
    • LOAD - not being used.

  • The VE.Direct Protocol document page 7.
  • No significant labels for the MPPT.

  • The VE.Direct Protocol document page 8.
    • H19 - Total yield from the MPPT controller in kiloWatt hours (0.01 kWh). Note that this is the actual kWh multiplied by 100. To obtain the true kWh divide the result by 100.
    • H20 - Yield today (0.01 kWh).
    • H21 - Maximum power today (W).
    • H22 - Yield yesterday (0.01 kWh). Remember to divide this result by 100 to get the true value.
    • H23 - Maximum power yesterday (W)
    • ERR - Error codes. Normally 0 when operating correctly.
    • CS - Charge state of battery (3-Bulk, 4-Absorption and 5-Float modes).
    • PID - Product ID - useful for identifying which Victron unit is connected to which USB port.
    • SER - Serial number

  • And here is the output from the Victron MPPT Charge Controller using the command vedirect --port=/dev/ttyUSB0
  • Note that the USB port will be different from different devices and may change if more than one USB device is connected.


Voltage considerations for different Victron hardware

  • Victron MPPT operates at 5V.
  • Arduino Uno also operates at 5V so no digital converter required.

VE.Direct Pinout

Serial Port Configuration - VE.Direct

Fritzing Circuit Diagram

Photos of Circuit


Arduino Code - Image

Serial Monitor - Example Output

Arduino Code - Code

/* Connections:
    MPPT pin     MPPT        Arduino     Arduino pin
    1            GND         GND         GND
    2            RX          TX          -              do not use!
    3            TX          RX          7 (UNO)
    4            Power+      none        -              do not use!
 */

#include <SoftwareSerial.h>

SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;

void setup()  
{
  Serial.begin(9600);
  mySerial.begin(19200);
} 

void loop() 
{
  if (mySerial.available())
   {
        label = mySerial.readStringUntil('\t');      
        val = mySerial.readStringUntil('\r\r\n');
        Serial.println(label + val);
   } 
}

Arduino Code - Code - Reading Battery Voltage - V

/* Connections:
    MPPT pin     MPPT        Arduino     Arduino pin
    1            GND         GND         GND
    2            RX          TX          -              do not use!
    3            TX          RX          7 (UNO)
    4            Power+      none        -              do not use!
 */

#include <SoftwareSerial.h>

SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;

void setup()  
{
  Serial.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT
} 

void loop() 
{
  if (mySerial.available())
   {
        label = mySerial.readStringUntil('\t');      
        val = mySerial.readStringUntil('\r\r\n');
        // V = Battery voltage (mV)
        // VPV = Solar Panel voltage (mV)
        // PPV = Solar panel power (W)
        // I = Battery current (mA)
        // IL = Load Current (mA)
        if (label == "V"){
           Serial.println(label + val);
        }
   } 
}


Arduino Code - Code - Reading other Parameters from MPPT

/* Connections:
    MPPT pin     MPPT        Arduino     Arduino pin
    1            GND         GND         GND
    2            RX          TX          -              do not use!
    3            TX          RX          7 (UNO)
    4            Power+      none        -              do not use!
 */

#include <SoftwareSerial.h>

SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;

void setup()  
{
  Serial.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT
} 

void loop() 
{
  if (mySerial.available())
   {
        label = mySerial.readStringUntil('\t');      
        val = mySerial.readStringUntil('\r\r\n');
        // V = Battery voltage (mV)
        // VPV = Solar Panel voltage (mV)
        // PPV = Solar panel power (W)
        // I = Battery current (mA)
        // IL = Load Current (mA)
        if (label == "V"){
           Serial.println(label + val);  // Battery Voltage
        }
        
        if (label == "VPV"){
          Serial.println(label + val); // Solar Panel Voltage
        }

        if (label == "PPV"){
          Serial.println(label + val); // Solar Panel Power (Watts)
        }
   } 
}

XBee Test - with MPPT

  • Noticed that as soon as Software Serial added for MPPT communication is only one way from Arduino MPPT to XBee receiver for Pi.
/*****************************************************************
XBee_Serial_Passthrough.ino

Set up a software serial port to pass data between an XBee Shield
and the serial monitor.

Hardware Hookup:
  The XBee Shield makes all of the connections you'll need
  between Arduino and XBee. If you have the shield make
  sure the SWITCH IS IN THE "DLINE" POSITION. That will connect
  the XBee's DOUT and DIN pins to Arduino pins 2 and 3.

*****************************************************************/
// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>

//For Atmega328P's
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX
SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;

void setup()
{
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT is 19200
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available())
  { // If data comes in from serial monitor, send it out to XBee
    XBee.write(Serial.read());
  }
  if (mySerial.available())
  { // If data comes in from serial monitor, send it out to XBee
    //XBee.write(mySerial.read());
     label = mySerial.readStringUntil('\t');      
     val = mySerial.readStringUntil('\r\r\n');
     if (label =="V"){
          XBee.write(55); // DEC 55 = HEX #37
     }
  }

  if (XBee.available())
  { // If data comes in from XBee, send it out to serial monitor
    Serial.write(XBee.read());
  }
}


XBee - MPPT Test to send Battery Voltage data using XBee

/*****************************************************************
XBee_Serial_Passthrough.ino

Set up a software serial port to pass data between an XBee Shield
and the serial monitor.

Hardware Hookup:
  The XBee Shield makes all of the connections you'll need
  between Arduino and XBee. If you have the shield make
  sure the SWITCH IS IN THE "DLINE" POSITION. That will connect
  the XBee's DOUT and DIN pins to Arduino pins 2 and 3.

*****************************************************************/
// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>

//For Atmega328P's
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX
SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;
char char_array[10]; // for data from MPPT to XBee

void setup()
{
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT is 19200
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available())
  { // If data comes in from serial monitor, send it out to XBee
    XBee.write(Serial.read());
  }
  if (mySerial.available())
  { // If data comes in from serial monitor, send it out to XBee
    //XBee.write(mySerial.read());
     label = mySerial.readStringUntil('\t');      
     val = mySerial.readStringUntil('\r\r\n');
     if (label =="V"){
          //XBee.write(55); // DEC 55 = HEX #37
          int str_len = val.length() + 1; 
          val.toCharArray(char_array, str_len);
          XBee.write(char_array);
     }
  }

  if (XBee.available())
  { // If data comes in from XBee, send it out to serial monitor
    Serial.write(XBee.read());
  }
}

XBee XCTU Console output


XBee - MPPT Test to send All Data using XBee

/*****************************************************************
XBee_Serial_Passthrough.ino

Set up a software serial port to pass data between an XBee Shield
and the serial monitor.

Hardware Hookup:
  The XBee Shield makes all of the connections you'll need
  between Arduino and XBee. If you have the shield make
  sure the SWITCH IS IN THE "DLINE" POSITION. That will connect
  the XBee's DOUT and DIN pins to Arduino pins 2 and 3.

*****************************************************************/
// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>

//For Atmega328P's
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX
SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;
String V_data = "0";
String VPV_data = "0";
String PPV_data = "0";
String IL_data = "0";
String ALL_data = "";

char ALL_char_array[25];
        // V = Battery voltage (mV)
        // VPV = Solar Panel voltage (mV)
        // PPV = Solar panel power (W)
        // I = Battery current (mA)
        // IL = Load Current (mA)

void setup()
{
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT is 19200
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available())
  { // If data comes in from serial monitor, send it out to XBee
    XBee.write(Serial.read());
  }
  if (mySerial.available())
  { // If data comes in from serial monitor, send it out to XBee
     label = mySerial.readStringUntil('\t');      
     val = mySerial.readStringUntil('\r\r\n');
     if (label =="V"){
          V_data = val;
     }
     
     if (label =="VPV"){
          VPV_data = val;
     }

     if (label =="PPV"){
          PPV_data = val;
     }

     if (label =="IL"){
          IL_data = val;
     }   
     

     if (label == "LOAD"){
          ALL_data = V_data + "," + VPV_data + "," + PPV_data + "," + IL_data;
          int str_len = ALL_data.length() + 1; 
          ALL_data.toCharArray(ALL_char_array, str_len);
          XBee.write(ALL_char_array);
     }
     
  }

  if (XBee.available())
  { // If data comes in from XBee, send it out to serial monitor
    Serial.write(XBee.read());
  }
}


XBee - MPPT Test to send All Data using XBee with End of Line

  • Data stored in Strings trimmed to remove white space
  • New line character added to end of String so that Python can determine end of transmission
  • During programming the XBee was plugged into Arduino.
  • XBee Explorere shield switch permanently on DLine (not UART)
/*****************************************************************
XBee_Serial_Passthrough.ino

Set up a software serial port to pass data between an XBee Shield
and the serial monitor.

Hardware Hookup:
  The XBee Shield makes all of the connections you'll need
  between Arduino and XBee. If you have the shield make
  sure the SWITCH IS IN THE "DLINE" POSITION. That will connect
  the XBee's DOUT and DIN pins to Arduino pins 2 and 3.

*****************************************************************/
// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>

//For Atmega328P's
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX
SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;
String V_data = "0";
String VPV_data = "0";
String PPV_data = "0";
String IL_data = "0";
String ALL_data = "";

char ALL_char_array[25];
        // V = Battery voltage (mV)
        // VPV = Solar Panel voltage (mV)
        // PPV = Solar panel power (W)
        // I = Battery current (mA)
        // IL = Load Current (mA)

void setup()
{
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT is 19200
  //Serial.begin(9600);
}

void loop()
{
  if (Serial.available())
  { // If data comes in from serial monitor, send it out to XBee
    XBee.write(Serial.read());
  }
  if (mySerial.available())
  { // If data comes in from serial monitor, send it out to XBee
     label = mySerial.readStringUntil('\t');      
     val = mySerial.readStringUntil('\r\r\n');

     if (label =="V"){
          V_data = val;
          V_data.trim();
     }
     
     if (label =="VPV"){
          VPV_data = val;
          VPV_data.trim();
     }

     if (label =="PPV"){
          PPV_data = val;
          PPV_data.trim();
     }

     if (label =="IL"){
          IL_data = val;
          IL_data.trim();
     }   
     

     if (label == "LOAD"){
          ALL_data = V_data + "," + VPV_data + "," + PPV_data + "," + IL_data + "\n";
          int str_len = ALL_data.length() + 1; 
          ALL_data.toCharArray(ALL_char_array, str_len);
          XBee.write(ALL_char_array);
          //Serial.println(ALL_data);
     }
     
  }

  if (XBee.available())
  { // If data comes in from XBee, send it out to serial monitor
    Serial.write(XBee.read());
  }
}

XBee - MPPT Test - Printing data to the Arduino IDE Serial Monitor

  • Serial monitor uses pins 0 and 1


/*****************************************************************
XBee_Serial_Passthrough.ino

Set up a software serial port to pass data between an XBee Shield
and the serial monitor.

Hardware Hookup:
  The XBee Shield makes all of the connections you'll need
  between Arduino and XBee. If you have the shield make
  sure the SWITCH IS IN THE "DLINE" POSITION. That will connect
  the XBee's DOUT and DIN pins to Arduino pins 2 and 3.

*****************************************************************/
// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>

//For Atmega328P's
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX
SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;
String V_data = "0";
String VPV_data = "0";
String PPV_data = "0";
String IL_data = "0";
String ALL_data = "";

char ALL_char_array[25];
        // V = Battery voltage (mV)
        // VPV = Solar Panel voltage (mV)
        // PPV = Solar panel power (W)
        // I = Battery current (mA)
        // IL = Load Current (mA)

void setup()
{
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT is 19200
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available())
  { // If data comes in from serial monitor, send it out to XBee
    XBee.write(Serial.read());
  }
  if (mySerial.available())
  { // If data comes in from serial monitor, send it out to XBee
     label = mySerial.readStringUntil('\t');      
     val = mySerial.readStringUntil('\r\r\n');

     if (label =="V"){
          V_data = val;
          V_data.trim();
     }
     
     if (label =="VPV"){
          VPV_data = val;
          VPV_data.trim();
     }

     if (label =="PPV"){
          PPV_data = val;
          PPV_data.trim();
     }

     if (label =="IL"){
          IL_data = val;
          IL_data.trim();
     }   
     

     if (label == "LOAD"){
          ALL_data = V_data + "," + VPV_data + "," + PPV_data + "," + IL_data + "\n";
          int str_len = ALL_data.length() + 1; 
          ALL_data.toCharArray(ALL_char_array, str_len);
          XBee.write(ALL_char_array);
          Serial.println(ALL_data);
     }
  }

  if (XBee.available())
  { // If data comes in from XBee, send it out to serial monitor
    Serial.write(XBee.read());
  }
}


XBee - MPPT Test - Python code

#!/usr/bin/env python3
import serial
import time


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            print(count)
            time.sleep(0.1)
            try: 
                #line = ser.readline()
                line = ser.readline().decode('utf-8').rstrip()
                print(line)
            except:
                print("fail")
            count += 1


XBee - MPPT - Separating incoming data into separate variables

  • Incoming data separated into separate variable.
    • V = Battery voltage (mV)
    • VPV = Solar Panel voltage (mV)
    • PPV = Solar panel power (W)
    • IL = Load Current (mA)


#!/usr   /bin/env python3
import serial
import time


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            #print(count)
            time.sleep(0.1)
            try: 
                #line = ser.readline()
                line = ser.readline().decode('utf-8').rstrip()

                print(f"Data separated using commas {line.split(',')}")
                mylist = line.split(',')
                #print(f'My List {mylist}')

                V = mylist[0]  ## Battery Voltage (nV)
                VPV = mylist[1] ## Solar Panel Voltage (mV)
                PPV = mylist[2] ## Solar Panel Power (W)
                IL = mylist[3] ## Load Current (mA)

                print(f'Battery voltage {V}, Solar Panel voltage {VPV}')
                print(f'Solar Panel power {PPV}, Load current {IL}')
            except:
                V = "0"
                VPV = "0"
                PPV = "0"
                IL = "0"
                print("Read serial fail")
            #count += 1

XBee - MPPT - Add Datetime Stamp

  • Add DateTime stamp
  • import datetime required.
#!/usr   /bin/env python3
import serial
import time
import datetime


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            #print(count)
            time.sleep(0.1)
            print()
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'The current date and time is {date_stamp}')
            
            try: 
                line = ser.readline().decode('utf-8').rstrip()

                print(f"Data separated using commas {line.split(',')}")
                mylist = line.split(',')
                #print(f'My List {mylist}')

                V = mylist[0]  ## Battery Voltage (nV)
                VPV = mylist[1] ## Solar Panel Voltage (mV)
                PPV = mylist[2] ## Solar Panel Power (W)
                IL = mylist[3] ## Load Current (mA)

                print(f'Battery voltage {V}, Solar Panel voltage {VPV}')
                print(f'Solar Panel power {PPV}, Load current {IL}')
            except:
                V = "0"
                VPV = "0"
                PPV = "0"
                IL = "0"
                print("Read serial fail")
            
            #count += 1

XBee - MPPT - Saving Data every Five Minutes

  • Set the statement if count > 300 to save data every 5 minutes (300 seconds).
  • For testing purposes the count can be set to 10.
            count += 1
            if count > 300:
                # Save data
                data = date_stamp + "," + V + "," + VPV + "," + PPV + "," + IL + "\n"
                print(f'DATA =  {data}')
                f = open('/home/pi/MPPT/MPPT_data.txt','a')
                f.write(data)
                f.close()
                print("Data saved")
                count = 0


Full code

#!/usr   /bin/env python3
import serial
import time
import datetime


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            #print(count)
            time.sleep(0.1)
            print()
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'The current date and time is {date_stamp}')
            
            try: 
                line = ser.readline().decode('utf-8').rstrip()

                print(f"Data separated using commas {line.split(',')}")
                mylist = line.split(',')
                #print(f'My List {mylist}')

                V = mylist[0]  ## Battery Voltage (nV)
                VPV = mylist[1] ## Solar Panel Voltage (mV)
                PPV = mylist[2] ## Solar Panel Power (W)
                IL = mylist[3] ## Load Current (mA)

                print(f'Battery voltage {V}, Solar Panel voltage {VPV}')
                print(f'Solar Panel power {PPV}, Load current {IL}')
            except:
                V = "0"
                VPV = "0"
                PPV = "0"
                IL = "0"
                print("Read serial fail")
            
            count += 1
            if count > 300:
                # Save data
                data = date_stamp + "," + V + "," + VPV + "," + PPV + "," + IL + "\n"
                print(f'DATA =  {data}')
                f = open('/home/pi/MPPT/MPPT_data.txt','a')
                f.write(data)
                f.close()
                print("Data saved")
                count = 0


XBee - MPPT - Dweet data

  • import requests required to Dweet data
                # Dweet data
                try:
                    print("Preparing dweet")
                    dweet_dict = {}
                    dweet_dict.update({"V": str(V)})
                    dweet_dict.update({"VPV": str(VPV)})
                    dweet_dict.update({"PPV": str(PPV)})
                    dweet_dict.update({"IL": str(IL)})
                    url = "https://dweet.io/dweet/for/3083-MPPT-Xbee1?"
                    x = requests.post(url, json=dweet_dict)
                    print(x.text)
                except:
                    print("Dweet failed")
                # check dweet with - https://dweet.io/get/latest/dweet/for/3083-MPPT-Xbee1

Full code

#!/usr   /bin/env python3
import serial
import time
import datetime
import requests


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            #print(count)
            time.sleep(0.1)
            print()
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'The current date and time is {date_stamp}')
            
            try: 
                line = ser.readline().decode('utf-8').rstrip()

                print(f"Data separated using commas {line.split(',')}")
                mylist = line.split(',')
                #print(f'My List {mylist}')

                V = mylist[0]  ## Battery Voltage (nV)
                VPV = mylist[1] ## Solar Panel Voltage (mV)
                PPV = mylist[2] ## Solar Panel Power (W)
                IL = mylist[3] ## Load Current (mA)

                print(f'Battery voltage {V}, Solar Panel voltage {VPV}')
                print(f'Solar Panel power {PPV}, Load current {IL}')
            except:
                V = "0"
                VPV = "0"
                PPV = "0"
                IL = "0"
                print("Read serial fail")
            
            count += 1
            if count > 300:
                # Save data
                data = date_stamp + "," + V + "," + VPV + "," + PPV + "," + IL + "\n"
                print(f'DATA =  {data}')
                f = open('/home/pi/MPPT/MPPT_data.txt','a')
                f.write(data)
                f.close()
                print("Data saved")
                count = 0
                
                # Dweet data
                try:
                    print("Preparing dweet")
                    dweet_dict = {}
                    dweet_dict.update({"V": str(V)})
                    dweet_dict.update({"VPV": str(VPV)})
                    dweet_dict.update({"PPV": str(PPV)})
                    dweet_dict.update({"IL": str(IL)})
                    url = "https://dweet.io/dweet/for/3083-MPPT-Xbee1?"
                    x = requests.post(url, json=dweet_dict)
                    print(x.text)
                except:
                    print("Dweet failed")
                # check dweet with - https://dweet.io/get/latest/dweet/for/3083-MPPT-Xbee1


XBee - MPPT - Plotting data

  • The following imports are required to plot data
    • import plotly
    • import plotly.graph_objects as go
    • import pandas
                # import and plot data text file
                df = pandas.read_csv('/home/pi/MPPT/MPPT_data.txt')

                fig = go.Figure(data = go.Scatter(mode='markers',
                                                    x=df.date_time,
                                                    y=df.V,
                                                    marker=dict(
                                                        color="LightSkyBlue",
                                                        size=20)
                                                    )
                                            )
                fig.update_layout(title="Voltage of Solar MPPT Battery (mV)",
                                    xaxis_title = 'Time',
                                    yaxis_title = 'Battery voltage (mV)',
                                font=dict(
                                    size=20,
                                    color="RebeccaPurple"
                                )
                            )

                plotly.offline.plot(fig,
                                    filename="/home/pi/MPPT/MPPT_data_plot.html",
                                    auto_open=False)

                print("Battery Voltage data plotted")

Full code

#!/usr   /bin/env python3
import serial
import time
import datetime
import requests
import plotly
import plotly.graph_objects as go
import pandas


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            #print(count)
            time.sleep(0.1)
            print()
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'The current date and time is {date_stamp}')
            
            try: 
                line = ser.readline().decode('utf-8').rstrip()

                print(f"Data separated using commas {line.split(',')}")
                mylist = line.split(',')
                #print(f'My List {mylist}')

                V = mylist[0]  ## Battery Voltage (nV)
                VPV = mylist[1] ## Solar Panel Voltage (mV)
                PPV = mylist[2] ## Solar Panel Power (W)
                IL = mylist[3] ## Load Current (mA)

                print(f'Battery voltage {V}, Solar Panel voltage {VPV}')
                print(f'Solar Panel power {PPV}, Load current {IL}')
            except:
                V = "0"
                VPV = "0"
                PPV = "0"
                IL = "0"
                print("Read serial fail")
            
            count += 1
            if count > 300:
                # Save data
                data = date_stamp + "," + V + "," + VPV + "," + PPV + "," + IL + "\n"
                print(f'DATA =  {data}')
                f = open('/home/pi/MPPT/MPPT_data.txt','a')
                f.write(data)
                f.close()
                print("Data saved")
                count = 0
                
                # Dweet data
                try:
                    print("Preparing dweet")
                    dweet_dict = {}
                    dweet_dict.update({"V": str(V)})
                    dweet_dict.update({"VPV": str(VPV)})
                    dweet_dict.update({"PPV": str(PPV)})
                    dweet_dict.update({"IL": str(IL)})
                    url = "https://dweet.io/dweet/for/3083-MPPT-Xbee1?"
                    x = requests.post(url, json=dweet_dict)
                    print(x.text)
                except:
                    print("Dweet failed")
                # check dweet with - https://dweet.io/get/latest/dweet/for/3083-MPPT-Xbee1
                
                
                # import and plot data text file
                df = pandas.read_csv('/home/pi/MPPT/MPPT_data.txt')

                fig = go.Figure(data = go.Scatter(mode='markers',
                                                    x=df.date_time,
                                                    y=df.V,
                                                    marker=dict(
                                                        color="LightSkyBlue",
                                                        size=20)
                                                    )
                                            )
                fig.update_layout(title="Voltage of Solar MPPT Battery (mV)",
                                    xaxis_title = 'Time',
                                    yaxis_title = 'Battery voltage (mV)',
                                font=dict(
                                    size=20,
                                    color="RebeccaPurple"
                                )
                            )

                plotly.offline.plot(fig,
                                    filename="/home/pi/MPPT/MPPT_data_plot.html",
                                    auto_open=False)

                print("Battery Voltage data plotted")


XBee - MPPT - Error Logs

  • Noticed that solar data was missing in this plot.
  • Not sure what the error was so decided to add an Error Log to help troubleshoot.

  • An example of an error log is included below.
  • The date_time stamp is recorded together with a simple text message.
  • The information is saved in a text file named MPPT_error_log.txt
                print("Read serial fail")
                data = date_stamp + "," + "Read serial fail" + "\n"
                f = open('/home/pi/MPPT/MPPT_error_log.txt','a')
                f.write(data)
                f.close()

Full code

#!/usr   /bin/env python3
import serial
import time
import datetime
import requests
import plotly
import plotly.graph_objects as go
import pandas


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            #print(count)
            time.sleep(0.1)
            print()
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'The current date and time is {date_stamp}')
            
            try: 
                line = ser.readline().decode('utf-8').rstrip()

                print(f"Data separated using commas {line.split(',')}")
                mylist = line.split(',')
                #print(f'My List {mylist}')

                V = mylist[0]  ## Battery Voltage (nV)
                VPV = mylist[1] ## Solar Panel Voltage (mV)
                PPV = mylist[2] ## Solar Panel Power (W)
                IL = mylist[3] ## Load Current (mA)

                print(f'Battery voltage {V}, Solar Panel voltage {VPV}')
                print(f'Solar Panel power {PPV}, Load current {IL}')
            except:
                V = "0"
                VPV = "0"
                PPV = "0"
                IL = "0"
                print("Read serial fail")
                data = date_stamp + "," + "Read serial fail" + "\n"
                f = open('/home/pi/MPPT/MPPT_error_log.txt','a')
                f.write(data)
                f.close()
            
            count += 1
            if count > 300:
                # Save data
                data = date_stamp + "," + V + "," + VPV + "," + PPV + "," + IL + "\n"
                print(f'DATA =  {data}')
                f = open('/home/pi/MPPT/MPPT_data.txt','a')
                f.write(data)
                f.close()
                print("Data saved")
                count = 0
                
                # Dweet data
                try:
                    print("Preparing dweet")
                    dweet_dict = {}
                    dweet_dict.update({"V": str(V)})
                    dweet_dict.update({"VPV": str(VPV)})
                    dweet_dict.update({"PPV": str(PPV)})
                    dweet_dict.update({"IL": str(IL)})
                    dweet_dict.update({"Time": str(date_stamp)})
                    url = "https://dweet.io/dweet/for/3083-MPPT-Xbee1?"
                    x = requests.post(url, json=dweet_dict)
                    print(x.text)
                except:
                    print("Dweet failed")
                    data = date_stamp + "," + "Dweet fail" + "\n"
                    f = open('/home/pi/MPPT/MPPT_error_log.txt','a')
                    f.write(data)
                    f.close()
                # check dweet with - https://dweet.io/get/latest/dweet/for/3083-MPPT-Xbee1
                
                
                # import and plot data text file
                df = pandas.read_csv('/home/pi/MPPT/MPPT_data.txt')
                fig = go.Figure(data = go.Scatter(mode='markers',
                                                    x=df.date_time,
                                                    y=df.V,
                                                    marker=dict(
                                                        color="LightSkyBlue",
                                                        size=20)
                                                    )
                                            )
                fig.update_layout(title="Voltage of Solar MPPT Battery (mV)",
                                    xaxis_title = 'Time',
                                    yaxis_title = 'Battery voltage (mV)',
                                font=dict(
                                    size=20,
                                    color="RebeccaPurple"
                                )
                            )

                plotly.offline.plot(fig,
                                    filename="/home/pi/MPPT/MPPT_data_plot.html",
                                    auto_open=False)

                print("Battery Voltage data plotted")

XBee - MPPT - Freeboard IO

Add Data Source

Design Dashboard

Share Dashboard

Reading all Victron MPPT Charge Controller Data Values


/*****************************************************************
XBee_Serial_Passthrough.ino

Set up a software serial port to pass data between an XBee Shield
and the serial monitor.

Hardware Hookup:
  The XBee Shield makes all of the connections you'll need
  between Arduino and XBee. If you have the shield make
  sure the SWITCH IS IN THE "DLINE" POSITION. That will connect
  the XBee's DOUT and DIN pins to Arduino pins 2 and 3.

*****************************************************************/
// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>

//For Atmega328P's
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX
SoftwareSerial mySerial(7, 8); // RX, TX

String label, val;
String V_data = "0";
String VPV_data = "0";
String PPV_data = "0";
String I_data = "0";
String IL_data = "0";
String LOAD_data = "0";
String H19_data = "0";
String H20_data = "0";
String H21_data = "0";
String H22_data = "0";
String H23_data = "0";
String ERR_data = "0";
String CS_data = "0";
String HSDS_data = "0";

String ALL_data = "";

char ALL_char_array[250];
        // V = Battery voltage (mV)
        // VPV = Solar Panel voltage (mV)
        // PPV = Solar panel power (W)
        // I = Main Battery current (mA)
        // IL = Load Current (mA)
        // LOAD = Load output state (ON/OFF)
        // H19 = Yield total (0.01 kWh)
        // H20 = Yield today (0.01 kWh)
        // H21 = Maximum power today (W)
        // H22 = Yield yesterday (0.01 kWh)
        // H23 = Maximum power yesterday (W)
        // ERR = Error code
        // CS = State of operation (3-Bulk, 4-Absorption, 5-Float)
        // HSDS = Day sequence number (0..364)

void setup()
{
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  mySerial.begin(19200); // baud rate for MPPT is 19200
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available())
  { // If data comes in from serial monitor, send it out to XBee
    XBee.write(Serial.read());
  }
  if (mySerial.available())
  { // If data comes in from serial monitor, send it out to XBee
     label = mySerial.readStringUntil('\t');      
     val = mySerial.readStringUntil('\r\r\n');

     // V = Battery voltage (mV)
     if (label =="V"){
          V_data = val;
          V_data.trim();
     }

     // VPV = Solar Panel voltage (mV)
     else if (label =="VPV"){
          VPV_data = val;
          VPV_data.trim();
     }

     // PPV = Solar panel power (W)
     else if (label =="PPV"){
          PPV_data = val;
          PPV_data.trim();
     }

     // I = Main Battery current (mA)
     else if (label =="I"){
          I_data = val;
          I_data.trim();
     }

     // IL = Load Current (mA)
     else if (label =="IL"){
          IL_data = val;
          IL_data.trim();
     }   

     // LOAD = Load output state (ON/OFF)
     else if (label =="LOAD"){
          LOAD_data = val;
          LOAD_data.trim();
     }

     // H19 = Yield total (0.01 kWh)
     else if (label =="H19"){
          H19_data = val;
          H19_data.trim();
     }

     // H20 = Yield today (0.01 kWh)
     else if (label =="H20"){
          H20_data = val;
          H20_data.trim();
     }     

     // H21 = Maximum power today (W)
     else if (label =="H21"){
          H21_data = val;
          H21_data.trim();
     } 

     // H22 = Yield yesterday (0.01 kWh)
     else if (label =="H22"){
          H22_data = val;
          H22_data.trim();
     } 

     // H23 = Maximum power yesterday (W)
     else if (label =="H23"){
          H23_data = val;
          H23_data.trim();
     } 

     // ERR = Error code
     else if (label =="ERR"){
          ERR_data = val;
          ERR_data.trim();
     } 

     // CS = State of operation (3-Bulk, 4-Absorption, 5-Float)
     else if (label =="CS"){
          CS_data = val;
          CS_data.trim();
     } 

     else if (label == "HSDS"){
          HSDS_data = val;
          HSDS_data.trim();
      
          ALL_data = V_data + "," + 
                      VPV_data + "," + 
                      PPV_data + "," + 
                      I_data + "," +
                      IL_data + "," +
                      LOAD_data + "," +
                      H19_data + "," +
                      H20_data + "," +
                      H21_data + "," +
                      H22_data + "," +
                      H23_data + "," +
                      ERR_data + "," +
                      CS_data + "," +
                      HSDS_data + "\n";
          int str_len = ALL_data.length() + 1; 
          ALL_data.toCharArray(ALL_char_array, str_len);
          XBee.write(ALL_char_array);
          Serial.println(ALL_data);
     }
  }

  if (XBee.available())
  { // If data comes in from XBee, send it out to serial monitor
    Serial.write(XBee.read());
  }
}

State of Charge Calibration for Victron 12V 8Ah Deep Cycle AGM Battery

  • Battery was fully charged using Swallow Battery Charger.
  • The battery is fully charged when the battery voltage is 14.00V and the charge current drops to 0.23A.
  • The aim of the experiment is to apply a constant load (approximately 70mA - using the Arduino and the MPPT as loads) and monitoring the voltage drop on the battery with time.
  • The battery voltage readings will be transmitted using XBee and stored on a Raspberry Pi.
  • A 12V battery rated at 8Ah has a theoretical capacity of 96 Ah (12V x 8Ah).
  • A 70mAh (0.07Ah) load should last for approx 114 Hours (8Ah / 0.07Ah)
  • However, with we only deplete the battery to 75% of the full capacity - the battery should be able to support this fixed load for 28.5 hours (114 hours / 4).
  • So for this experiment we will drain the battery for 28.4 hours using this set load and then note the final voltage.
  • This final voltage will become our reading for 75% SOC (State of Charge).
  • To maintain good battery health and longevity it will be important to stay above this battery voltage reading (SOC).
  • Note that this battery voltage reading is only relevant for the 70mA load. A greater load will cause the battery voltage to drop lower.

Python code on Raspberry Pi to monitor Battery Voltage

  • In this example the Python code on the Raspberry Pi will be modified to read all Victron MPPT Charge Controller Data Values as provided by the Arduino code above.
#!/usr   /bin/env python3
import serial
import time
import datetime
import requests
import plotly
import plotly.graph_objects as go
import pandas


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()
    count = 0

    while True:
        if ser.in_waiting > 0:
            #print(count)
            time.sleep(0.1)
            print()
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'The current date and time is {date_stamp}')
            
            try: 
                line = ser.readline().decode('utf-8').rstrip()

                print(f"Data separated using commas {line.split(',')}")
                mylist = line.split(',')
                #print(f'My List {mylist}')

                V = mylist[0]  ## Battery Voltage (nV)
                VPV = mylist[1] ## Solar Panel Voltage (mV)
                PPV = mylist[2] ## Solar Panel Power (W)
                I = mylist[3] ## Main Battery Current (mA)
                IL = mylist[4] ## Load Current (mA)
                LOAD = mylist[5] ## Load output state (ON/OFF)
                H19 = mylist[6] ## H19 = Yield total (0.01 kWh)
                H20 = mylist[7] ## H20 = Yield today (0.01 kWh)
                H21 = mylist[8] ## H21 = Maximum power today (W)
                H22 = mylist[9] ## H22 = Yield yesterday (0.01 kWh)
                H23 = mylist[10] ## H23 = Maximum power yesterday (W)
                ERR = mylist[11] ## ERR = Error code
                CS = mylist[12] ## CS = State of operation (3-Bulk, 4-Absorption, 5-Float)
                HSDS = mylist[13] ## HSDS = Day sequence number (0..364)

                print(f'Battery voltage {V}')
                print(f'Solar Panel voltage {VPV}')
                print(f'Solar Panel power {PPV}')
                print(f'Main battery current {I}')
                print(f'Load current {IL}')
                print(f'Load output state {LOAD}')
                print(f'H19 Yield total (0.01kWh) {H19}')
                print(f'H20 Yield total (0.01kWh) {H20}')
                print(f'H21 Maximum power today (W) {H21}')
                print(f'H22 Yield yesterday (0.01kWh) {H22}')
                print(f'H23 Maximum power yesterday (W) {H23}')
                print(f'ERR Error code {ERR}')
                print(f'CS State of operation {CS}')
                print(f'HSDS Day sequence number {HSDS}')
                
            except:
                V = "0"
                VPV = "0"
                PPV = "0"
                I = "0"
                IL = "0"
                LOAD = "0"
                H19 = "0"
                H20 = "0"
                H21 = "0"
                H22 = "0"
                H23 = "0"
                ERR = "0"
                CS = "0"
                HSDS = "0"
                print("Read serial fail")
                data = date_stamp + "," + "Read serial fail" + "\n"
                f = open('/home/pi/MPPT/MPPT_error_log.txt','a')
                f.write(data)
                f.close()
            
            count += 1
            if count > 10:
                # Save data
                data = date_stamp + "," + V + "," + VPV + "," + PPV + "," + I + "," + \
                       IL + "," + LOAD + "," + H19 + "," + H20 + "," + \
                       H21 + "," + H22 + "," + H23 + "," + ERR + "," + \
                       CS + "," + HSDS + "\n"

                print(f'DATA =  {data}')
                f = open('/home/pi/MPPT/MPPT_data.txt','a')
                f.write(data)
                f.close()
                print("Data saved")
                count = 0
                
                # Dweet data
                try:
                    print("Preparing dweet")
                    dweet_dict = {}
                    dweet_dict.update({"V": str(V)})
                    dweet_dict.update({"VPV": str(VPV)})
                    dweet_dict.update({"PPV": str(PPV)})
                    dweet_dict.update({"I": str(I)})
                    dweet_dict.update({"IL": str(IL)})
                    dweet_dict.update({"LOAD": str(LOAD)})
                    dweet_dict.update({"H19": str(H19)})
                    dweet_dict.update({"H20": str(H20)})
                    dweet_dict.update({"H21": str(H21)})
                    dweet_dict.update({"H22": str(H22)})
                    dweet_dict.update({"H23": str(H23)})
                    dweet_dict.update({"ERR": str(ERR)})
                    dweet_dict.update({"CS": str(CS)})
                    dweet_dict.update({"HSDS": str(HSDS)})
                    dweet_dict.update({"Time": str(date_stamp)})
                    url = "https://dweet.io/dweet/for/3083-MPPT-Xbee1?"
                    x = requests.post(url, json=dweet_dict)
                    print(x.text)
                except:
                    print("Dweet failed")
                    data = date_stamp + "," + "Dweet fail" + "\n"
                    f = open('/home/pi/MPPT/MPPT_error_log.txt','a')
                    f.write(data)
                    f.close()
                # check dweet with - https://dweet.io/get/latest/dweet/for/3083-MPPT-Xbee1
                
                
                # import and plot data text file
                df = pandas.read_csv('/home/pi/MPPT/MPPT_data.txt')
                fig = go.Figure(data = go.Scatter(mode='markers',
                                                    x=df.date_stamp,
                                                    y=df.V,
                                                    marker=dict(
                                                        color="LightSkyBlue",
                                                        size=20)
                                                    )
                                            )
                fig.update_layout(title="Voltage of Solar MPPT Battery (mV)",
                                    xaxis_title = 'Time',
                                    yaxis_title = 'Battery voltage (mV)',
                                font=dict(
                                    size=20,
                                    color="RebeccaPurple"
                                )
                            )

                plotly.offline.plot(fig,
                                    filename="/home/pi/MPPT/MPPT_data_plot.html",
                                    auto_open=False)

                print("Battery Voltage data plotted")


Modify the file MPPT_data.txt

  • Modify the file MPPT_data.txt to include the following header date_stamp,V,VPV,PPV,I,IL,LOAD,H19,H20,H21,H22,H23,ERR,CS,HSDS