Disclaimer first: everything I mention here is at your own risk. It works on my system, but I can’t tell if it will work on yours, use your judgement.
You’ll need SSH access to your GX device (as a new user somehow I can’t post links here): victronenergy. com/live/ccgx:root_access
Proof of concept
I’ve attached a proof of concept script you can run on the GX device to read the data.
First you’ll need to identify which port the shunt is on. It should be one of these ports:
ls /service | grep vedirect
Normally, vedirect service reads the shunt data, and it doesn’t let other processes read it at the same time. So the correct port will be the one you can’t read from (for now). In that case the script outputs “SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)”.
In my case on Cerbo GX MK2, the port labeled “VE.Direct 1” turned out to be /dev/ttyS7.
I first tried a simple option: stop the service, run the script to see if I can read the data, and then start the service again:
svc -d /service/vedirect-interface.ttyS7
python ~/read_shunt.py --port /dev/ttyS7
svc -u /service/vedirect-interface.ttyS7
It should show something like in the attached output.txt, repeating every second.
Reading alongside vedirect service
If you’re ok with vedirect service not running for the shunt, that’s all then and you can skip to the Integration section below, but I still wanted it to run, so I split the TX and GND wires from the shunt into a separate USB TTL adapter, so that I get a mirror of the data on a different port. I used an isolated adapter based on FT232RL. I bought one that supports both 5V and 3.3V. Turned out in my case the shunt is using 3.3V. The connectors are JST PH with 2mm pitch, so I just made my own cable, but I guess splitting an existing one would work too. GND and V+ are easy to find with a multimeter, they’re on the sides. TX and RX wires cross from one end of the cable to the other end. I found TX from the shunt by checking which wire had non-zero fluctuating voltage. To the adapter I connected only TX from the shunt (to adapter’s RX) and GND, nothing else.
Resolving interference issues
After everything is connected, the GX device should still see the shunt as usual, but now the data is also readable from the port the adapter is plugged in. In my case it was /dev/ttyUSB0. However, when you run the script you might notice it frequently outputs “Interference detected” errors. I believe serial-starter is the culprit, it tries to run various services over and over again to see which one can identify the plugged in device. I tried reprogramming the vendor and product IDs on the adapter so that it doesn’t get recognized by serial-starter, but even if I use some obscure IDs and change the adapter description, serial-starter still interferes, so I suspect it starts for all IDs that the FTDI driver supports. The remaining option was to disable serial-starter for this particular adapter, using the adapter’s serial number (or you can try some other attributes if your adapter doesn’t have a serial number):
Get the serial number of the adapter:
udevadm info -a -n /dev/ttyUSB0 | grep ‘{serial}’
Create a rule to stop serial-starter from interfering with a device with this particular serial number, and optionally add a /dev/ttySH symlink. Make sure to replace YOUR_SERIAL_HERE!
In theory since this is in /data directory, it should survive software upgrades, but I haven’t tested that.
mkdir -p /data/etc/udev/rules.d
cat > /data/etc/udev/rules.d/10-ignore-shunt-secondary-adapter.rules << ‘EOF’
ACTION==“add”, SUBSYSTEM==“tty”, ATTRS{serial}==“YOUR_SERIAL_HERE”, ENV{VE_SERVICE}=“ignore”, SYMLINK+=“ttySH”
EOF
Then run nano /data/rc.local to add the following lines to /data/rc.local, so that the rule gets applied on startup and force-triggers udev to re-process tty devices. This applies the new rule to the already-connected USB adapter.
ln -sf /data/etc/udev/rules.d/10-ignore-shunt-secondary-adapter.rules /etc/udev/rules.d/
udevadm control --reload-rules
udevadm trigger --action=add --subsystem-match=tty
Reboot the GX device:
reboot
Now you should see a new symlink /dev/ttySH, and the script should be able to read from it without interference (or from the original /dev/ttyUSB0 if you prefer that).
Integration
The code from the script can be integrated into the system, for example into dbus-serialbattery or dbus-aggregate-batteries that already implement the Battery Monitor code. They’re open source, so instead of the current value they use, the code can be edited to pass the high-resolution shunt current (and SOC or other values if needed). Then the script becomes a part of a proper battery monitor with all the privileges of getting these measurements to the rest of the system. The current shows up on the GX device and in the VRM portal. One note is that even if you pass the current with mA resolution, GX device rounds it to one decimal place and VRM to two. This is a separate UI limitation of the GX device and VRM themselves. I don’t know if there are any solutions to it, but I’m ok with it. Edit: for example, here’s the integration for dbus-aggregate-batteries: Add support for reading SmartShunt current with 1 mA resolution · dmitrych5/dbus-aggregate-batteries@b96f5ef · GitHub
With this setup, it should be easy to compensate for wrong shunt resistance in software and add other custom logic or calculations.
What I’m really curious now is what accuracy can be achieved on practice, both at very small and large currents, by reading this high-resolution data and compensating for shunt resistance and maybe temperature (I don’t know if this is needed). At what point would it get limited by noise and other factors out of our control.
output.txt (281 Bytes)
read_shunt.py.txt (5.2 KB)