Python logging of PCM 60x charge controller

Python logging of PCM 60x charge controller

My MPP Solar PCM 60x charge controller has a serial-port connection, but the software that “comes with it” only runs on Windows (I need something for Linux). It turns out that a number of people have already invented this wheel, and I was able to use their efforts to hack a simple python script for crude logging.

I got the serial query string from a piece of code in the solarsnoop project on GitHub, and the details of the return string are explained in a documentation pdf that I found on another GitHub project. I also had a look at the solar-sis project, and it might be worth coming back to when I want fancier things later on. For now I made a Jupyter notebook with the following code:

import serial
import time
import datetime
import numpy as np

QPIGS = b"\x51\x50\x49\x47\x53\xB7\xA9\x0D"
ser = serial.Serial(port='/dev/ttyACM0',baudrate=2400,timeout=2)

log_duration = 10*60*60  # seconds

data = []
times = []

start = datetime.datetime.now()
while (datetime.datetime.now() - start).seconds < log_duration:
    ser.write(QPIGS)
    result = ser.read(70)
    ser.read()
    ser.read()
    times.append(datetime.datetime.now())
    data.append(result)
    time.sleep(60)

# Processing the logged strings into useful data
numdata = np.zeros((len(data), 7))
nptimes = np.zeros(len(times), dtype='datetime64[ms]')

for i, entry in enumerate(data):
    nptimes[i] = np.datetime64(times[i])

    # When it gets dark the PCM 60x goes to sleep and returns 
    # empty strings
    if len(entry) > 40:
        numdata[i, 0] = float(entry[1:6].decode())
        numdata[i, 1] = float(entry[7:12].decode())
        numdata[i, 2] = float(entry[13:18].decode())
        numdata[i, 3] = float(entry[19:24].decode())
        numdata[i, 4] = float(entry[25:30].decode())
        numdata[i, 5] = float(entry[31:35].decode())
        numdata[i, 6] = float(entry[36:40].decode())

# Keep only the lines that have data
data_present = np.where(numdata[:,1] > 0)
numdata = numdata[data_present]
nptimes = nptimes[data_present]

# Save to log files
np.savetxt('20180919_solar_charge_log.csv', numdata)
np.savetxt('20180919_solar_charge_log_times.csv', nptimes, fmt='%s')

This gave me what I needed – the ability to “look at” the solar charging process despite being away from home during the sunny part of the day. The next step is nice visualisation, and Google Sheets gives me a fast and easy way to put logged data on the cloud. I followed this helpful tutorial for setting up Google Docs API and interacting with a Google Sheets spreadsheet from python. It didn’t work immediately, and I think the authentication details may have changed slightly since that tutorial was written. I found the following works nicely.

import gspread
from oauth2client.service_account import ServiceAccountCredentials

scope = ['https://spreadsheets.google.com/feeds',
        'https://www.googleapis.com/auth/drive']
creds = ServiceAccountCredentials.from_json_keyfile_name('my_secret.json', scope)
client = gspread.authorize(creds)

sheet = client.open("my_solar_charge_log").sheet1

header = ["Time", "Battery V", "Charging A", "Charging W"]
sheet.insert_row(header, 1)

for i, line in enumerate(numdata):
    row = [str(nptimes[i])[11:19],
           line[1],
           line[2],
           line[5]
          ]
    sheet.insert_row(row, i+2)
    time.sleep(1.5)  # Avoid the 100 queries in 100s access limit

Once the data was in Google Sheets I could make nice interactive plots to embed here. I found a nice trick for adding arbitrary text labels to a plot. I hope to set up a live chart that can track the charging in real-time.

One thought on “Python logging of PCM 60x charge controller

Leave a Reply

Your email address will not be published. Required fields are marked *