question

seamaster avatar image
seamaster asked

Node red can it write to dbus?

I have generator that is connected to controller DC20-MK2 that “talks” modbus. At this point I can rad the registers via node red. I read RPM, oil pressure temperature etc. Also now I can start / stop the generator with pc writing to modbus register of the DC20-MK2 .

My final goal is to be able to read on the Venus OS screen the generator parameters such as olí pressure temperature rpm etc that I can read from the controller registers, and also start and stop the generator from the screen but not with relay switch

I also posted that question on the modification space but no answer so far.

Can someone help?

Node-REDModbus TCP
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

2 Answers
matt1309 avatar image
matt1309 answered ·

Hi @Seamaster

Think I'm understanding what you're looking to do.

I've had a look at victrons dbus path for generator (below):

https://github.com/victronenergy/venus/wiki/dbus#generator-data


It's definitely doable, but depends on your python ability on how you get it working.

Advanced level:

You create a custom driver that reads modbus data from controller and stores it in dbus on victron system. This will achieve you goal but will require you to write python code to get the modbus data from controller. Victron do provide examples of how to make a custom driver (I'll attach links below).

You will need to modify this to get the modbus data and then pass this into dbus. (the third link is probably most useful, you'll just need to tweak to be generator and populate the generator dbus paths rather than energy meter ones).


Link for explaination around custom driver: https://github.com/victronenergy/venus/wiki/howto-add-a-driver-to-Venus


Example code provided by victron: https://github.com/victronenergy/velib_python/blob/master/dbusdummyservice.py


Another useful example but a shelly energymeter driver with data obtained from HTTP request rather than modbus (but might be a good starting point):

https://github.com/fabian-lauer/dbus-shelly-3em-smartmeter/blob/main/dbus-shelly-3em-smartmeter.py


Easier option (no python code needed):

You still make a custom driver but you barely edit the victron example one, and then you instead use node red to read the modbus data from your controller and then get node red to send this data to victrons mqtt topic (which will now exist given you've added a custom driver). This completes the same goal but rather than reading modbus data in the custom driver's python code and passing directly into dbus. You're using node red to read modbus data, sending that to MQTT which in turn is passed into dbus.



Here is an example of a tank sensor I use (I use a method similar to easy method above, my tank sensor sends it's data via mqtt to victron). To get data into this I would pass values into topics:

W/{vrmID}/tank/{deviceID}/Remaining

Where you replace {vrmID} with your VRM ID and then deviceID with the virtual (generator in your case, tank sensor in mine) device ID.

#!/usr/bin/env python3

"""
A class to put a simple service on the dbus, according to victron standards, with constantly updating
paths. See example usage below. It is used to generate dummy data for other processes that rely on the
dbus. See files in dbus_vebus_to_pvinverter/test and dbus_vrm/test for other usage examples.

To change a value while testing, without stopping your dummy script and changing its initial value, write
to the dummy data via the dbus. See example.

https://github.com/victronenergy/dbus_vebus_to_pvinverter/tree/master/test
"""
from gi.repository import GLib
import platform
import argparse
import logging
import sys
import os

# our own packages
sys.path.insert(1, os.path.join(os.path.dirname(__file__), '../ext/velib_python'))
from vedbus import VeDbusService

class DbusDummyService(object):
    def __init__(self, servicename, deviceinstance, paths, productname='Thunderstorm Tank', connection='Dummy service'):
        self._dbusservice = VeDbusService(servicename)
        self._paths = paths

        logging.debug("%s /DeviceInstance = %d" % (servicename, deviceinstance))

        # Create the management objects, as specified in the ccgx dbus-api document
        self._dbusservice.add_path('/Mgmt/ProcessName', __file__)
        self._dbusservice.add_path('/Mgmt/ProcessVersion', 'Unkown version, and running on Python ' + platform.python_version())
        self._dbusservice.add_path('/Mgmt/Connection', connection)

        # Create the mandatory objects
        self._dbusservice.add_path('/DeviceInstance', deviceinstance)
        self._dbusservice.add_path('/ProductId', 0)
        self._dbusservice.add_path('/ProductName', productname)
        self._dbusservice.add_path('/FirmwareVersion', 0)
        self._dbusservice.add_path('/HardwareVersion', 0)
        self._dbusservice.add_path('/Connected', 1)

        for path, settings in self._paths.items():
            self._dbusservice.add_path(
                path, settings['initial'], writeable=True, onchangecallback=self._handlechangedvalue)

        GLib.timeout_add(1000, self._update)

    def _update(self):
        with self._dbusservice as s:
            for path, settings in self._paths.items():
                if 'update' in settings:
                    update = settings['update']
                    if callable(update):
                        s[path] = update(path, s[path])
                    else:
                        s[path] += update
                    logging.debug("%s: %s" % (path, s[path]))
        return True

    def _handlechangedvalue(self, path, value):
        logging.debug("someone else updated %s to %s" % (path, value))
        return True # accept the change


# === All code below is to simply run it from the commandline for debugging purposes ===

# It will created a dbus service called com.victronenergy.pvinverter.output.
# To try this on commandline, start this program in one terminal, and try these commands
# from another terminal:
# dbus com.victronenergy.pvinverter.output
# dbus com.victronenergy.pvinverter.output /Ac/Energy/Forward GetValue
# dbus com.victronenergy.pvinverter.output /Ac/Energy/Forward SetValue %20
#
# Above examples use this dbus client: http://code.google.com/p/dbus-tools/wiki/DBusCli
# See their manual to explain the % in %20

def main():
    logging.basicConfig(level=logging.DEBUG)

    from dbus.mainloop.glib import DBusGMainLoop
    # Have a mainloop, so we can send/receive asynchronous calls to and from dbus
    DBusGMainLoop(set_as_default=True)

    pvac_output = DbusDummyService(
        servicename='com.victronenergy.thunderstormTank.ttyO1',
        deviceinstance=0,
        paths={
            '/Capacity': {'initial': 1},
            '/Level': {'initial': 0},
            '/Status': {'initial': 0},
            '/Remaining': {'initial': 0},
            '/FluidType': {'initial': 1},
            '/Standard': {'initial': 2}
        })

    logging.info('Connected to dbus, and switching over to GLib.MainLoop() (= event based)')
    mainloop = GLib.MainLoop()
    mainloop.run()


if __name__ == "__main__":
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

andrii-podanenko avatar image
andrii-podanenko answered ·

You may try to use https://www.npmjs.com/package/dbus in node-red

You'd need to include it via node-red service config in order for the library to arrive into function node context

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.