Raspberry Pi - contrib-python-function won't work

Hello all,

My name is Pawel and this is my first post on this forum.
I would like to set up node, to take MQTT input and display on the MAX7219 device using Node-Red and Raspberry Pi4. I have python script downloaded from GitHub that working very well, but I would like to take message from MQTT node and display on the screen.

Please see below python script: I would like to replace msg = "Test Message" with MQTT input.
So I have tried

msg = msg.payload
msg = msg

But no luck.

Working Script:

import sys
import re
import time
import argparse

from luma.led_matrix.device import max7219
from luma.core.interface.serial import spi, noop
from luma.core.render import canvas
from luma.core.virtual import viewport
from luma.core.legacy import text, show_message
from luma.core.legacy.font import proportional, CP437_FONT, TINY_FONT, SINCLAIR_FONT, LCD_FONT



def demo(n, block_orientation, rotate, inreverse):
    # create matrix device
    serial = spi(port=0, device=0, gpio=noop())
    device = max7219(serial, cascaded=n or 0, block_orientation=block_orientation,
                     rotate=rotate or 0, blocks_arranged_in_reverse_order=inreverse)


    # start demo


    msg = "Test Message"

    show_message(device, msg, fill="white", font=proportional(CP437_FONT))


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='matrix_demo arguments',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('--cascaded', '-n', type=int, default=4, help='Number of cascaded MAX7219 LED matrices')
    parser.add_argument('--block-orientation', type=int, default=-90, choices=[0, 90, -90], help='Corrects block orientation when wired vertically')
    parser.add_argument('--rotate', type=int, default=0, choices=[0, 1, 2, 3], help='Rotate display 0=0┬░, 1=90┬░, 2=180┬░, 3=270┬░')
    parser.add_argument('--reverse-order', type=bool, default=False, help='Set to true if blocks are in reverse order')

    args = parser.parse_args()

    try:
        demo(args.cascaded, args.block_orientation, args.rotate, args.reverse_order)
    

    except KeyboardInterrupt:
        pass

[{"id":"d65ba77d.7a5d98","type":"python3-function","z":"c60843c1.d6316","name":"python3_LCD","func":"import sys\nimport re\nimport time\nimport argparse\n\nfrom luma.led_matrix.device import max7219\nfrom luma.core.interface.serial import spi, noop\nfrom luma.core.render import canvas\nfrom luma.core.virtual import viewport\nfrom luma.core.legacy import text, show_message\nfrom luma.core.legacy.font import proportional, CP437_FONT, TINY_FONT, SINCLAIR_FONT, LCD_FONT\n\n\n\ndef demo(n, block_orientation, rotate, inreverse):\n    # create matrix device\n    serial = spi(port=0, device=0, gpio=noop())\n    device = max7219(serial, cascaded=n or 0, block_orientation=block_orientation,\n                     rotate=rotate or 0, blocks_arranged_in_reverse_order=inreverse)\n\n\n    # start demo\n\n\n    msg = \"Test Message\"\n\n    show_message(device, msg, fill=\"white\", font=proportional(CP437_FONT))\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser(description='matrix_demo arguments',\n        formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n\n    parser.add_argument('--cascaded', '-n', type=int, default=4, help='Number of cascaded MAX7219 LED matrices')\n    parser.add_argument('--block-orientation', type=int, default=-90, choices=[0, 90, -90], help='Corrects block orientation when wired vertically')\n    parser.add_argument('--rotate', type=int, default=0, choices=[0, 1, 2, 3], help='Rotate display 0=0┬░, 1=90┬░, 2=180┬░, 3=270┬░')\n    parser.add_argument('--reverse-order', type=bool, default=False, help='Set to true if blocks are in reverse order')\n\n    args = parser.parse_args()\n\n    try:\n        demo(args.cascaded, args.block_orientation, args.rotate, args.reverse_order)\n    \n\n    except KeyboardInterrupt:\n        pass\n","outputs":1,"x":620,"y":180,"wires":[["365e3b6.71afcc4"]],"inputLabels":["payload"]},{"id":"365e3b6.71afcc4","type":"debug","z":"c60843c1.d6316","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":830,"y":180,"wires":[]},{"id":"fcefdc2e.ec1ae","type":"inject","z":"c60843c1.d6316","name":"","topic":"test","payload":"testpayload","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":400,"y":180,"wires":[["d65ba77d.7a5d98"]]}]

Best regards
Pawel

I'm not familiar with the MAX7219 device, but to get MQTT data into python you need to import an mqtt client. I use:
import paho.mqtt.client as mqtt

Its well documentated, tutorial:
http://www.steves-internet-guide.com/into-mqtt-python-client/

"official" docs:
https://www.eclipse.org/paho/clients/python/docs/

HTH.

Is not clear if you want the python code running in a python function node (node-red-contib-python-function) or if you want to run it outside of Node-RED

(if you try to run it the node, you will have some more problems to try to handle and it might not work at all, has to be tested)

I suggest, to allow people to give and if you wish quality support, share your flow

I run my python AI in a separate process generally launched by node-red on startup or as required by a "watchdog timer". But it can run "stand-alone".

The python subscribes to MQTT topics published by the node-red controller and publishes MQTT messages that node-red controller subscribes to for pushing alerts & notifications and some simple dashboard displays to help with initial camera setup.

The python is totally self-contained and doesn't require any MQTT messages to run (although it needs a connection to a broker to start). The node-red provides the system integration that really makes it useful.

I think this is generally the best "model" the parts are kept as independent as possible glued together by MQTT.

I have the MQTT broker setup and node subscribing no problem.
I just don't know how to pass payload from mqtt node to python script node as variable.
Whatever I have tried it give me an error.

krambriw
It is not important for me. I can have node that call python code outside.

So temporarily I have set up inject node with string. Connected to pythone function. But it gave me an error.

Sample Flow:

[{"id":"d65ba77d.7a5d98","type":"python3-function","z":"c60843c1.d6316","name":"python3_LCD","func":"import sys\nimport re\nimport time\nimport argparse\n\nfrom luma.led_matrix.device import max7219\nfrom luma.core.interface.serial import spi, noop\nfrom luma.core.render import canvas\nfrom luma.core.virtual import viewport\nfrom luma.core.legacy import text, show_message\nfrom luma.core.legacy.font import proportional, CP437_FONT, TINY_FONT, SINCLAIR_FONT, LCD_FONT\n\n\n\ndef demo(n, block_orientation, rotate, inreverse):\n    # create matrix device\n    serial = spi(port=0, device=0, gpio=noop())\n    device = max7219(serial, cascaded=n or 0, block_orientation=block_orientation,\n                     rotate=rotate or 0, blocks_arranged_in_reverse_order=inreverse)\n\n\n    # start demo\n\n\n    msg = \"Test message\"\n\n    show_message(device, msg, fill=\"white\", font=proportional(CP437_FONT))\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser(description='matrix_demo arguments',\n        formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n\n    parser.add_argument('--cascaded', '-n', type=int, default=4, help='Number of cascaded MAX7219 LED matrices')\n    parser.add_argument('--block-orientation', type=int, default=-90, choices=[0, 90, -90], help='Corrects block orientation when wired vertically')\n    parser.add_argument('--rotate', type=int, default=0, choices=[0, 1, 2, 3], help='Rotate display 0=0┬░, 1=90┬░, 2=180┬░, 3=270┬░')\n    parser.add_argument('--reverse-order', type=bool, default=False, help='Set to true if blocks are in reverse order')\n\n    args = parser.parse_args()\n\n    try:\n        demo(args.cascaded, args.block_orientation, args.rotate, args.reverse_order)\n    \n\n    except KeyboardInterrupt:\n        pass\n","outputs":1,"x":620,"y":180,"wires":[["365e3b6.71afcc4"]],"inputLabels":["payload"]},{"id":"365e3b6.71afcc4","type":"debug","z":"c60843c1.d6316","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":830,"y":180,"wires":[]},{"id":"fcefdc2e.ec1ae","type":"inject","z":"c60843c1.d6316","name":"","topic":"test","payload":"testpayload","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":400,"y":180,"wires":[["d65ba77d.7a5d98"]]}]

There have been problems reported here with python nodes previously, if I remember correctly the solution is often to call python via an exec node (use the full path for the file to run) rather than mess about with those nodes.

1 Like

Seems to me you have Python code that does what you want but you need to pass in a variable.

Modify the python code and launch it when you start your flow and have it run in the background waiting for MQTT input. Have the flow send the variable value to the Python as an MQTT message.

Have the python code do its thing with the variable value and then loop to wait for the next value or an exit signal. The docs I linked should show you now to do it.

You'd use an mqtt callback sorta like this:

# The callback for when a PUBLISH message is received from the server, aka message from SUBSCRIBE topic.
def on_message(client, userdata, msg):
    global message
    if str(msg.topic) == "myVariable":       # whatever your MQTT publish topic is
        currentDT = datetime.datetime.now()       # logfile entry
        message = str(msg.payload.decode('utf-8'))
        print(str(msg.topic)+":  " + message + currentDT.strftime(" ... %Y-%m-%d %H:%M:%S"))
        return

Replace msg with message in your show_mesage() call
Get the python and mqtt working separately by running the python in one terminal window and using mosquitto_pub command in another terminal window. Then add the mqtt publishing to your node red flow.

You should post the error message here that you get

I did not want to touch your python code but instead give you an exaple how you can pass messages from mqtt to your python node

We send the message "testpayload" to the mqtt broker (you have to change the ip of the broker) topic "test". It is received and passed on to the python node. Inside we extract the message in payload with m= msg['payload']

You have to do similar in your code but I would restructure it a bit also, but if it works already, fine

However I cannot test it, I have no such display device so you would have to test if you want me to modify it

[{"id":"5ff0e975.6c27a8","type":"mqtt out","z":"dbc3addc.853ef","name":"","topic":"","qos":"","retain":"","broker":"75eba16c.094f9","x":450,"y":1170,"wires":[]},{"id":"bc161f4b.ae985","type":"mqtt in","z":"dbc3addc.853ef","name":"","topic":"test","qos":"2","datatype":"auto","broker":"75eba16c.094f9","x":450,"y":1090,"wires":[["3cd611ca.23305e"]]},{"id":"5dd00586.e0943c","type":"inject","z":"dbc3addc.853ef","name":"","topic":"test","payload":"testpayload","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":260,"y":1170,"wires":[["5ff0e975.6c27a8"]]},{"id":"3cd611ca.23305e","type":"python3-function","z":"dbc3addc.853ef","name":"","func":"m = msg['payload']\nnode.warn(m)\n\n# Here you add code that shall do the stuff\n\nmsg['payload'] = \"We are finished\"\nreturn msg\n","outputs":1,"x":640,"y":1090,"wires":[["4ddf265f.2e7bf8"]]},{"id":"4ddf265f.2e7bf8","type":"debug","z":"dbc3addc.853ef","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":840,"y":1090,"wires":[]},{"id":"75eba16c.094f9","type":"mqtt-broker","z":"","name":"","broker":"127.0.0.1","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

Thanks a lot krambriw. This is all what I need.

m = msg['payload']
node.warn(m)

And it's working perfect now.
Best regards
Pawel