Running python in node-red, passing back a msg

I want to thank everyone that help me on this project. THANK YOU, hopefully I'll be able in time to help others on this forum. In light of that here is what I have completed.

I've got a PI 4 machine running nodered reading 2 temperature probes(I'll be adding more) and displaying the data on a nodered dashboard. I have 2 DS18B20 probes, seen in the picture(black wire's with silver ends) running to a breadboard. A 3.3v and GND from the probes are attached to + and GND. The yellow wire sends the data to the PI in a serial 1-wire format(https://en.wikipedia.org/wiki/1-Wire) that writes a file w1_slave to a directory on the PI. Commands modprobe w1-gpio, and modprobe w1-therm are the modules that do this. That file(w1_slave) contains the temperature in Celsius(along with other data). I parse this data with a javascript function . I've included the flow and pictures with this post.

Thanks again.

[{"id":"2f0d7d46.0c05c2","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"21c219a8.7d6cd6","type":"file in","z":"2f0d7d46.0c05c2","name":"","filename":"/sys/bus/w1/devices/28-0301a279830e/w1_slave","format":"utf8","chunk":false,"sendError":false,"encoding":"ascii","x":510,"y":120,"wires":[["8ff94a5b.ba6008","66124819.afbb98"]]},{"id":"69acf5b6.b123bc","type":"inject","z":"2f0d7d46.0c05c2","name":"","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":100,"wires":[["21c219a8.7d6cd6","43a136d5.040208"]]},{"id":"b27cd728.f61298","type":"debug","z":"2f0d7d46.0c05c2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":870,"y":320,"wires":[]},{"id":"8ff94a5b.ba6008","type":"function","z":"2f0d7d46.0c05c2","name":"","func":"var filelinein = msg.payload;\nvar pos = filelinein.indexOf(\"YES\"); \nmsg.payload = pos;\nif (pos > 0) {\n    var postequal = filelinein.indexOf(\"t=\");\n    tempstartpos = postequal + 2;\n    tempendpos = postequal + 7;\n    tempstring = filelinein.slice(tempstartpos, tempendpos);\n    temp_c = tempstring / 1000.0;\n    temp_f = temp_c * 9.0 /5.0 +32;\n    temp_fT = Math.trunc( temp_f);\n    msg.payload = temp_fT;\n}\nreturn msg;\n//var myTrunc = Math.trunc( myFloat );\n//temp_string = lines[1][equals_pos+2:]\n//        temp_c = float(temp_string) / 1000.0\n//       temp_f = temp_c *9.0 /5.0 + 32.0\n//        return temp_c, temp_f","outputs":1,"noerr":0,"initialize":"","finalize":"","x":880,"y":120,"wires":[["b27cd728.f61298","9d928244.42887"]]},{"id":"66124819.afbb98","type":"debug","z":"2f0d7d46.0c05c2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":850,"y":40,"wires":[]},{"id":"9d928244.42887","type":"ui_gauge","z":"2f0d7d46.0c05c2","name":"","group":"2a9abbac.830534","order":0,"width":0,"height":0,"gtype":"gage","title":"Engine Room","label":"degree F ","format":"{{value}}","min":0,"max":"100","colors":["#03b300","#e6e600","#ca3838"],"seg1":"60","seg2":"90","x":680,"y":220,"wires":[],"inputLabels":["engine room temp"]},{"id":"43a136d5.040208","type":"file in","z":"2f0d7d46.0c05c2","name":"","filename":"/sys/bus/w1/devices/28-3c01b55678cf/w1_slave","format":"utf8","chunk":false,"sendError":false,"encoding":"ascii","x":500,"y":360,"wires":[["f12b35e2.3f3a08"]]},{"id":"f12b35e2.3f3a08","type":"function","z":"2f0d7d46.0c05c2","name":"","func":"var filelinein = msg.payload;\nvar pos = filelinein.indexOf(\"YES\"); \nmsg.payload = pos;\nif (pos > 0) {\n    var postequal = filelinein.indexOf(\"t=\");\n    tempstartpos = postequal + 2;\n    tempendpos = postequal + 7;\n    tempstring = filelinein.slice(tempstartpos, tempendpos);\n    temp_c = tempstring / 1000.0;\n    temp_f = temp_c * 9.0 /5.0 +32;\n    temp_fT = Math.trunc( temp_f);\n    msg.payload = temp_fT;\n}\nreturn msg;\n//var myTrunc = Math.trunc( myFloat );\n//temp_string = lines[1][equals_pos+2:]\n//        temp_c = float(temp_string) / 1000.0\n//       temp_f = temp_c *9.0 /5.0 + 32.0\n//        return temp_c, temp_f","outputs":1,"noerr":0,"initialize":"","finalize":"","x":420,"y":460,"wires":[["c53343b4.6e6d1"]]},{"id":"c53343b4.6e6d1","type":"ui_gauge","z":"2f0d7d46.0c05c2","name":"","group":"2a9abbac.830534","order":0,"width":0,"height":0,"gtype":"gage","title":"Port Packing","label":"degree F ","format":"{{value}}","min":0,"max":"150","colors":["#03b300","#e6e600","#ca3838"],"seg1":"60","seg2":"90","x":610,"y":460,"wires":[],"inputLabels":["engine room temp"]},{"id":"2a9abbac.830534","type":"ui_group","z":"","name":"moonstruck","tab":"89f27b00.97ada8","order":1,"disp":true,"width":"6","collapse":false},{"id":"89f27b00.97ada8","type":"ui_tab","z":"","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]
1 Like

Hi.

Just wondering if you have worked out the MQTT part?

I have recently gone through a very similar course and I have the basic Python code if you want to go down that path.

Did you realise that there are several DS18B20 nodes in the node red flows site that could save some of the heavy lifting? Personally I use owserver with node-red-contrib-owfs as I find that more flexible that using the w1_therm based ones, and moves the measurement out to a separate process. Also it allows sensors distributed across a number of devices (mostly Pis in my case) to be accessed from any device in the network. For simpler systems the dedicated nodes are fine though.

get a 1-Wire Busmaster / USB Hostadapter DS9490R (about 25€), owserver and node-red-contrib-owfs.
Works perfect for me.
have a look at

and 2 more one wire videos from this guy and you are done.
A very good guide line to run ow on your raspi.

No need to use a bus master provided you only have two or three 1 wire sensors and they are not on leads longer than a few metres. Direct connection should work fine for such situations. You might have problems if you are in an electrically noisy environment though. Try it and see. If you have problems then you can always add the master later.

direct connection is also explained in these videos.

I spent 30-45 minutes on it this morning with a youtube video. I'll probably spend an hour or two on it each day for the next week before I start working on coding it up. I'm got some ADC sensor that will probably be a good fit for it.

That doesn't really tell me if you want the scripts or not.

Ok, no I did not know this and didn't even look. I'm too new to nodered, but leaning fast. Thanks for the info.

Yes, I would like to see your scripts. I never pass up MQ stuff ! ....lol

The environment is trawler running up and down the ICW on the east coast. Electrically noisy, probably, definitely loud and hot, distance is going to be around 10 meters. Learning ever day, thanks

(drats! Now I've done it!)

Um.....

Hang on.

Understand this is written for what I was doing.

Sorry, but that's just how it is.

The idea is the script is invoked from NR in an exec node.

It then replies on a MQTT topic saying the script is running.

You then send stuff to the topic and it does the python stuff there.

You will see that.

Most of the code is not needed by you. Sorry maybe I should have removed the other stuff, but I think you will work it out.

Oh, when / if you want you send a command and the script stops/exits.

What I do is: every now and then I send a "heartbeat" command to it and it replies telling me the script is still running.

I'll expand more on that later. It is 22:56 local here and I think I am going to go to bed soon.

#  Imports
##  This must be the first line.

from __future__ import print_function
import sys
import os
import time
import paho.mqtt.client as mqtt
import qwiic_serlcd




def on_connect(client, userdata, flags, rc):
    print("Connected:", rc)
    client.subscribe("alarm_clock/#", 0) 


def on_subscribe(client, userdata, mid, granted_qos):
    print ('Subscribed:', userdata, mid, granted_qos)
    print ('We are here, waiting for commands...')


def on_message(client, userdata, msg):
    global abort
    if msg.payload.decode("utf-8") == 'Heartbeat':
        client.publish("alarm_clock/response", "Script is alive")

    if (msg.topic == "alarm_clock/ctl"):
        print(msg.payload.decode("utf-8"))
        if msg.payload.decode("utf-8") == 'Stop':
            print ("Script terminates..")
            client.publish("alarm_clock/response", "Script is stopping")
            abort = True
##  Now put other commands here.
        if (msg.payload.decode("utf-8") == 'CLS'):
            print("CLS")
            myLCD.clearScreen() # clear the screen - this moves the cursor to the home position as well
        ###  Also need contrasts adjustments in here.
        #  work needed on backlight stuff.  Not touched for now
        if (msg.payload.decode("utf-8") == 'BKL-ON'):
            print("Backlight On")
            cad.lcd.backlight_on()
        if (msg.payload.decode("utf-8") == 'BKL-OFF'):
            print("Backlight Off")
            cad.lcd.backlight_off()
        #  done
        if (msg.payload.decode("UTF-8") == 'BLK-OFF'):
            print("Blink Off")
            myLCD.noBlink()
        if (msg.payload.decode("UTF-8") == 'BLK-ON'):
            print("Blink On")
            myLCD.blink()
        #  done
        if (msg.payload.decode("UTF-8") == 'C-ON'):
            print("Cursor On")
            myLCD.cursor()
        if (msg.payload.decode("UTF-8") == 'C-OFF'):
            print("Cursor Off")
            myLCD.noCursor()
        if (msg.payload.decode("UTF-8") == 'D-OFF'):
            print("Display Off")
            myLCD.noDisplay()
        if (msg.payload.decode("UTF-8") == 'D-ON'):
            print("Display On")
            myLCD.Display()



########################
#  Text part
    if (msg.topic == "alarm_clock/text"):
        txt = msg.payload.decode("utf-8")
        print ("=============")
        print (txt)
        try:
            text = txt.split("@")
            print ("Text to display is ",text[1])
            print ("Raw co-ordinates are ",text[0])
            print ("=============")
            cord = text[1].split(",")
            print ("X ",cord[0])
            print ("Y ",cord[1])
            x = int(cord[0])
            y = int(cord[1])
            myLCD.setCursor(x,y)
            myLCD.print(text[0])
        except:
            myLCD.print(text[0])








# inits
myLCD = qwiic_serlcd.QwiicSerlcd()
mqtt_broker = "192.168.0.99"
mqtt_port = 1883
abort = False
client = mqtt.Mosquitto()
client.on_connect = on_connect
client.on_message = on_message
client.on_subscribe = on_subscribe
resp = client.connect(mqtt_broker, mqtt_port, 60)
client.loop_start()

while abort == False:
    time.sleep(1)
    
client.loop_stop()
client.disconnect()
del client
print ('Script has terminated')
exit(0)

hmmm, new to nodered world of nodes. so this could be a user error, or just a bug. This DS19B20 node is supposed to return the timestamp in the message. But no matter how I beat it up I can't get the timestamp to appear, either in the string mode or array mode. So I added another debug node and that still didn't' work . Still trying to get a handle on msg.xxxx fields, I'm missing something.

here's the node https://flows.nodered.org/node/node-red-contrib-ds18b20-sensor
here's my flow

[{"id":"bda1ba45.58aab8","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"29b02423.b502ec","type":"rpi-ds18b20","z":"bda1ba45.58aab8","topic":"","array":true,"name":"","x":390,"y":200,"wires":[["66516c1b.e84bb4","7c148fdc.a3e83"]],"outputLabels":["timestamp"]},{"id":"6a9b6af5.d03f94","type":"inject","z":"bda1ba45.58aab8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":300,"wires":[["29b02423.b502ec"]]},{"id":"66516c1b.e84bb4","type":"debug","z":"bda1ba45.58aab8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":660,"y":200,"wires":[]},{"id":"7c148fdc.a3e83","type":"debug","z":"bda1ba45.58aab8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"timestamp","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":320,"wires":[]}]

When posting flows you have to use the </> button at the top of the edit box here and paste the flow in. Otherwise the forum interprets it as markdown and messes it up.

Feed the output of the node into a Debug node set to Show Complete Message (the default is just to show the payload contents) and see what it shows.

Have looked at the official documentation on handling messages in NR? Let see, I think 4 or 5 people asked me that one when I asked a question early on in my NR experience. :slight_smile:
https://nodered.org/docs/user-guide/messages

Adding to the 'message' for variable tracking purposes is as simple as...

msg.myvalue = X where you want in a function node for example, so your added property is appended to the 'msg' object returned.

Then in the debug node, later in the same connected flow, set it to COMPLETE, so that you get ALL the msg parts or properties.

Ok, this old COBOL programmer is struggling....I believe I've got the complete message set. then I set the array checkbox, although the doc says it not required, as either way it should work. Still not finding the timestamp. Now lets see if I can post correctly.... :slight_smile: !

[{"id":"bda1ba45.58aab8","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"29b02423.b502ec","type":"rpi-ds18b20","z":"bda1ba45.58aab8","topic":"","array":true,"name":"","x":390,"y":200,"wires":[["66516c1b.e84bb4"]],"outputLabels":["timestamp"]},{"id":"6a9b6af5.d03f94","type":"inject","z":"bda1ba45.58aab8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":300,"wires":[["29b02423.b502ec"]]},{"id":"66516c1b.e84bb4","type":"debug","z":"bda1ba45.58aab8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":640,"y":200,"wires":[]}]


Briefly looked at messages doc a few nights ago, need to dig in deeper now that I'm trying to get things going. Need to get the debug stuff working so probably should just work on that first. What ever happened to "ready trace" from the COBOL days

Oh no... someone invoked COBOL. Of evil things, such not speak, the darkness release the reaper... all because of a missing period on card 17, column 47, row 102. LOL. Only a COBOL programmer will see the humor in this!

2 Likes

Well that does look like an issue with the node. You could raise an issue on the node's github page. However I have to ask what do you want the timestamp for? Is it not always the current time?

I do, however I did take some C and C++ courses in the early 90's after 15 years of COBOL programming.