The Making of TS-MPPT Remote Monitor Page in Python with Graphs, Part 1: Creating the Data Log CSV file
I have had a Tristar Maximum Power Point Tracker ( TS-MPPT-60 ) made by Morningstar in my solar system for several years. There is a webpage server program built into the charge controller, that displays information on the controller. The charge controller pages can only be accessed while connected to the home network, as it lives inside of my internet firewall and will remain this way for security reasons. While the built in page displays key parameters, it only shows a snap shot of the current time and has a data log function that only shows a single point of key data totals for each day of operation. I really would love to see real time data and long term data charted out in graphs presented on a live webpage that can be accessed from anywhere. This project will implement a web page display somewhat in a very similar style to what was done in the GMC-320 webpage from my previous blog entry.
Note: The current project live monitor webpage can be viewed here : https://madscientisthut.com/TSMPPT_monitor/Solar.html
Here is what the Morningstar TS-MPPT-60 built in liveview page looks like:

I have started to play with Python programming a lot since starting the GMC-320 webpage monitor project back in October. My interest was really peaked when I saw that Python had such great power such as stripping data from HTML. Wondering if that data could be stripped from the built in web sever on the charge controller I started down this path only to find the data in the status page was provided by a built in java script. After futilely messing around with it for a couple of hours I searched online and found a blog written by Takashi Ando here: https://tech.blog.uribou.me/python-driver-for-tsmppt60/ where he had done all of the leg work for getting data back from the Charge controller. No need to reinvent the wheel, thank you so much Takashi for making this great starting point for my next new project!
This Mad Scientist Hut project will be documented in this blog as progress is completed.
A great start: After downloading the driver from Takashi’s Git hub and with these few little tweaks: commented out the SolarArrayStatus, TemperaturesStatus, and OperatingConditions from the Class:SystemStatus the program is already getting data back that is needed for the project. The main data of interest is Battery Voltage, Charging Amps and Amp Hours. A side note, it is snowing today so the solar panels are covered in tons of snow, no charge current today ☹️
My python script to read back from Takashi’s driver:
print("Read…")
tsmppt_dict = SystemStatus("tsmppt12400032").get()
print (tsmppt_dict)
Terminal output:
Read…
{'Battery Voltage': {'group': 'Battery', 'value': 24.69, 'unit': 'V'}, 'Target Voltage': {'group': 'Battery', 'value': 31.64, 'unit': 'V'}, 'Charge Current': {'group': 'Battery', 'value': 0.04, 'unit': 'A'}, 'Amp Hours': {'group': 'Counter', 'value': 224726.5, 'unit': 'Ah'}, 'Kilowatt Hours': {'group': 'Counter', 'value': 6219, 'unit': 'kWh'}}
Done
Hmmm… I have never encountered dictionary variables before in coding, this is different. Well let me see what can be done with this. Okay not so bad, after reading up on nested dictionary variables this is what works:
battery_value = float(tsmppt_dict['Battery Voltage']['value'])
amps_value = float(tsmppt_dict['Charge Current']['value'])
amp_hours_value = float(tsmppt_dict['Amp Hours']['value'])
Now to store the data in a CSV format. This is code from the QuickCPM.py that is being copied over for the CSV file and also later for the HTML file writes.
from pathlib import Path #<--put this with the other imports
def writeToFile(fileName,data, isbinary, ishtml):
if len(data)>1:
fn= fileName # QtGui.QFileDialog.getSaveFileName(self, "Save file", "", "*.*")with open("test.txt", "a") as myfile:
if isbinary:
#print "Writing binary"
with open(fn, "wb") as file:
file.write(data)
#print "{:d} bytes saved to {:s}".format(len(data), fn)
else:
if ishtml:
with open(fn, "w") as file:
file.write(data)
#print "{:d} chars written to {:s}".format(len(data),fn)
else:
with open(fn, "a") as file:
file.write(data)
#print "{:d} chars written to {:s}".format(len(data),fn)
else:
print ("No data to save")
Next step put the tsmppt data read into a call (allows for multiple charge controllers by address later)
def readTSMPPTdata(address):
try:
tsmppt_dict = SystemStatus(address).get()
battery_value = float(tsmppt_dict['Battery Voltage']['value'])
amps_value = float(tsmppt_dict['Charge Current']['value'])
amp_hours_value = float(tsmppt_dict['Amp Hours']['value'])
cl = "{:%y-%m-%d %H:%M}".format(datetime.datetime.now())
Data = "{0},{1},{2},{3}\n".format(battery_value,amps_value,amp_hours_value,cl)
except:
print ("readTSMPPTdata Error")
return (Data)
then start a loop to test the CSV file
try:
#Write log file headers for columns if CSV is not written yet
file_to_check = Path(Data_CSV_FileName)
if not file_to_check.is_file():
cl = "{:%y-%m-%d %H:%M}".format(datetime.datetime.now())
Data_for_CSV = "Battery Voltage,Charge Current, Total Amp Hours,Date Time,,Data_file_started_at: {0}\n".format(cl)
writeToFile(Data_CSV_FileName,Data_for_CSV,0,0)
while True:
Data_for_CSV = readTSMPPTdata("tsmppt12400032")
writeToFile(Data_CSV_FileName,Data_for_CSV,0,0)
except KeyboardInterrupt:
pass
Contents of the CSV:

Okay time to take a break. I will continue this tomorrow. The next step is to add the code that will generate the webpage and add in the canvas gauges.