I've just spent much of the day trying to do what I thought should be simple: query the system to determine the charge state... and then programmatically do something about that.
It honestly took hours to figure out that the MODBUS interface could be useful for this. It took another hour or two to read how to query this type of (overly-engineered) third-party interface.
- I created a Raspberry Pi 3B with the VenusOS system on it and configured it for my wi-fi
- I plugged a VE.Direct USB interface from the Pi to my SmartSolar MPPT 150/100 Charge Controller
- I powered ON the Pi in my solar shed
- I visited the web interface from my browser via http://venus.local
- Under Settings -> Services, I enabled Modbus TCP and noted under Available Services that com.victronenergy.system is Unit ID 100 and com.victronenergy.solarcharger is Unit ID 239
- Under Device List, I review the information available under SmartSolar Charger MPPT 150/100 rev 2, (not that this is important for this tutorial)
- On https://victronenergy.com I visit Downloads -> Technical information and download the Excel spreadsheet called "Modbus-TCP register list" and review the first worksheet "Field list", find the section for com.victronenergy.solarcharger, look for Charge State in the Description field and see that it's under address = 775.
On my MacBook, I create a folder ~/sites/venusos/dbus, change to it and create a text file in this called GetMode.py for running this code.
I run the following command to install the library.
pip install pymodbus
I create the Python file I mentioned before, noting that I've hard-coded the Unit ID for my SmartSolar charger as well as the hostname:
#!/usr/local/bin/python # GetMode is a command line utility to query the current charge state # on a VenusOS system. # Requirements: python 2.7 and the pymodbus module (https://github.com/bashwork/pymodbus). from pymodbus.client.sync import ModbusTcpClient client = ModbusTcpClient(host='venus.local', port=502) result = client.read_holding_registers(address=775, count=1, unit=239) if result.function_code >= 0x80: print('Error in function_code: {} {}'.format(hex(result.function_code), hex(result.exception_code))) else: print(result.registers)
I mark the script so that it can be executed:
chmod +x GetMode.py
Finally, I run the command in this folder:
./GetMode.py [3]
Referring back to the Excel spreadsheet from before, here's how to interpret that response.
0=Off, 2=Fault, 3=Bulk, 4=Absorption, 5=Float, 6=Storage, 7=Equalize, 11=Other, 252=External Control
I confirm that my system is now in the Bulk charging mode as was seen from the MODBUS query I created.
-------
Okay, so why would I want to do this? What I intend to do is to migrate this code over to the VenusOS—based Raspberry Pi computer and run it as a service. I might run this so that once my batteries are fully charged that I can toggle on something that would use the (otherwise) wasted electricity that my panels are generating. This service could then toggle off that something once the solar has gone below a threshold.
I'm fond of the TP-Link brand of programmable power outlets and I have several of these in my home. What I usually do manually is to dump my excess power into subsystems of batteries with their own dedicated inverters which look something like this:
Power outlet on my primary system -> TP-Link -> Battery Charger -> 12VDC battery -> Third-Party Inverter
And then of course I could install another TP-Link to that downstream inverter and programmatically turn on, say, something to grow plants.
Note that you could use the script as-is by forwarding its output to something like awk, for example:
./GetMode.py | awk '$1 == "[5]" {print "float"}'