Kill Exec Python Script NeoPixels

Hi community, it's that time of year again and I've got a few Pis around the place controlling strings of NeoPixels, I've modified the stringiest.py

I can initiate the python script no problem with the Exec node however I do not seem to be able to stop the script even when the node says killed, I've tried all combinations of SIGINT , SIGQUIT, SIGHUP and SIGTERM sadly no luck.

Is it something to do with needing to do sudo kill? if so how does one do that?

As ever I'm sure it's something blindly obvious I've missed, any help would be greatly appreciated.

[{"id":"a82c1e1b.dbe","type":"exec","z":"95b8f1c0.e06bf","command":"sudo python /home/pi/neopix.py","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":420,"y":400,"wires":[["6b08cd62.01fb84"],["916ca703.ef5588"],["4db11d4f.a5e754"]]},{"id":"6b08cd62.01fb84","type":"debug","z":"95b8f1c0.e06bf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":650,"y":360,"wires":[]},{"id":"916ca703.ef5588","type":"debug","z":"95b8f1c0.e06bf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":650,"y":400,"wires":[]},{"id":"4db11d4f.a5e754","type":"debug","z":"95b8f1c0.e06bf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":650,"y":440,"wires":[]},{"id":"9d7b5683.af8a08","type":"inject","z":"95b8f1c0.e06bf","name":"Go","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":400,"wires":[["a82c1e1b.dbe"]]},{"id":"cfb8a6d2.798938","type":"status","z":"95b8f1c0.e06bf","name":"","scope":["a82c1e1b.dbe"],"x":140,"y":540,"wires":[["f357f47d.d37178","7c8433be.8a479c"]]},{"id":"7c8433be.8a479c","type":"debug","z":"95b8f1c0.e06bf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"status.text","targetType":"msg","x":420,"y":540,"wires":[]},{"id":"f357f47d.d37178","type":"function","z":"95b8f1c0.e06bf","name":"Set & Check LED PID","func":"//msg.payload =  Number(msg.status.text.substring(4, 8))\n\nvar led_pid = Number(msg.status.text.substring(4, 8))\n\nif (led_pid === isNaN){return}\n\nglobal.set(\"ledpid\",led_pid)\n\nmsg.payload = led_pid\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":200,"y":600,"wires":[["d901617b.6d72a"]]},{"id":"d901617b.6d72a","type":"debug","z":"95b8f1c0.e06bf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":410,"y":600,"wires":[]},{"id":"1512d427.9caf6c","type":"function","z":"95b8f1c0.e06bf","name":"Get LED PID & Kill","func":"msg.pid = global.get(\"ledpid\")\nmsg.payload = global.get(\"ledpid\")\nmsg.kill = \"SIGTERM\"\nreturn msg\n\n//SIGINT, SIGQUIT or SIGHUP. Defaults to SIGTERM","outputs":1,"noerr":0,"initialize":"","finalize":"","x":150,"y":440,"wires":[["ce2149a7.c811e8","a82c1e1b.dbe"]]},{"id":"92a7204c.1ef76","type":"inject","z":"95b8f1c0.e06bf","name":"Stop","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":480,"wires":[["1512d427.9caf6c"]]},{"id":"ce2149a7.c811e8","type":"debug","z":"95b8f1c0.e06bf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"pid","targetType":"msg","statusVal":"pid","statusType":"auto","x":340,"y":460,"wires":[]}]

What version of node-red are you running? There was fix for this a little time ago.

1.2.6 I clocked that and updated this morning, didn't make a difference.

I launch a script like this to shutdown my python AI code with an exec node, I don't need to mess around with remembering the PID.

#!/bin/bash
export DISPLAY=:0
export XAUTHORITY=/home/ai/.Xauthority

# should be clean shutdown -2 is SIGINT, as if typing Ctrl-C in the program.
/usr/bin/pkill -2 -f "programName.py" > /dev/null 2>&1
sleep 3

# but, make sure it goes away before retrying -9 is tactical nuke, removes it even if python interpreter has crashed.
/usr/bin/pkill -9 -f "programName.py" > /dev/null 2>&1
sleep 1
type or paste code here

I call it stop.sh and run it in an exec node. Has worked with all versions of node-red I've used, including one installed yesterday.

1 Like

You don't need to set the pid in the kill message unless you want to kill a different process, try removing that and see if it helps, though I suspect that is not the problem. I suspect the problem is that you are using sudo so the process will, I think, be owned by root, in which case it will not be possible to kill it as the non-root user running node-red.
It should not be necessary to use sudo, instead give the node-red user permission to do whatever the python command is doing.

Thanks for the pointer @wb666greene sadly no lucky my lights keep flashing away.

#!/bin/bash
export DISPLAY=:0
export XAUTHORITY=/home/ai/.Xauthority

# should be clean shutdown -2 is SIGINT, as if typing Ctrl-C in the program.
/usr/bin/pkill -2 -f "/home/pi/neopix.py" > /dev/null 2>&1
sleep 3

# but, make sure it goes away before retrying -9 is tactical nuke, removes it even if python inter$
/usr/bin/pkill -9 -f "/home/pi/neopix.py" > /dev/null 2>&1
sleep 1```

Hi @Colin thank you, you were correct removing the PID made no change, I'll work out how to drive the NeoPixel WS2801 sequence without the sudo privileges

Screenshot 2020-12-24 at 23.49.11

Hi @Brew
I have a similar thing on my machine/s.

I got a lot of help from @ krambriw

Their trick was a bit different.

My script gets it messages from MQTT. So all I need to do is send the script a message in MQTT to exit and it stops that way.

Do you need to me elaborate on that?

I can post the script is you want, and how I start/stop it.

1 Like

@Trying_to_learn, how exciting yes please, I've got Mosquito running on another Pi and my grand plan was to send MQTT messages from my master pi to the remotes, which are running NR controlling the sequence, if I need to fire that straight into the remote sequence, yes please how.

This is the main part of the flow to set up the script running.

[{"id":"d9ce4613.926b2","type":"inject","z":"a1911aa3.c45be8","name":"Start script","repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"true","payloadType":"bool","x":290,"y":410,"wires":[["286e3fcd.eaeba","32f9a84c.cdfd78"]]},{"id":"286e3fcd.eaeba","type":"exec","z":"a1911aa3.c45be8","command":"export DISPLAY=:0 && lxterminal -e python3 /home/pi/Pimoroni/blinkt/blinkt_ctl.py","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Blinkt Ctl script","x":570,"y":410,"wires":[["41a6bc68.4626cc"],["4d9b096d.536c3"],["4e10c0c4.1d04d8"]]}]

This is the script.

#

import time
import sys
import paho.mqtt.client as mqtt
import blinkt

#blinkt.set_clear_on_exit()

def on_connect(client, userdata, flags, rc):
    print("Connected:", rc)
    client.subscribe("tele_led/#", 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
    global blinkt
    print ("===============================")
    print ("New loop invocation")
    my_string = (msg.payload.decode("utf-8"))
    print ("my_String")
    print (my_string)
    dummy = my_string.split(",")
    print ("Dummy ",dummy)


#   [int(x) & 0xff for x in data]
    command = dummy[0]
    print ("Command ",command)

    if command == 'Heartbeat':
        print ("I am alive")
        client.publish("led-response", "Script is alive")

    if command == 'Stop':
        print ("Script terminates..")
        client.publish("led-response", "Script is stopping")
        blinkt.set_all(0, 0, 0)
        blinkt.show()
        abort = True

    print (len(dummy))

#
    if command == 'rgb' and len(dummy) == 5:
        pixel = dummy[1]
        red = dummy[2]
        green = dummy[3]
        blue = dummy[4]

        pixel = int(pixel)
        red = int(red)
        green = int(green)
        blue = int(blue)

        print("--==  values  ==--")
        print ("Command >",command,"<")
        print ("Pixel ", pixel)
        print ("Red ", red)
        print ("Green ",green)
        print ("Blue ",blue)

        print ("Data length ",len(dummy))

        if pixel == 8:
            print ("Oops")
            blinkt.set_all(red,green,blue)
            blinkt.show()

#            for x in range(blinkt.NUM_PIXELS):
#                blinkt.set_pixel(pixel,red,greed,blue)
        else:
            print ("setting pixel")
            print ("Led ",pixel," Red ",red," Green ",green," Blue ",blue)
            blinkt.set_pixel(pixel,red,green,blue)
            print("Done")
            blinkt.show()
        return
#
#    if command == "dim":



# inits
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)

This is another part of the flow which tells me the script is alive.

The input is a pulse which happens every 20 seconds and is used to cancel a signal that will indicate the script has failed/died if no reset signal is received.

Oh, the fan node can be simply deleted and negated. It does little else than is a point in the flow.

[{"id":"5d851e45.a249d","type":"mqtt in","z":"a1911aa3.c45be8","name":"","topic":"led-response/#","qos":"2","datatype":"auto","broker":"1ecd3a9e.dc5bfd","x":300,"y":730,"wires":[["25d40ed0.c6d1a2","a3c380a5.beac08"]]},{"id":"b6efb5ce.91a248","type":"link in","z":"a1911aa3.c45be8","name":"Heart Beat","links":["b4aa1ba4.4dc758"],"x":280,"y":800,"wires":[["7b5ed2a7.b5f364","418e7b5d.001a9c"]],"l":true},{"id":"418e7b5d.001a9c","type":"change","z":"a1911aa3.c45be8","name":"Heartbeat","rules":[{"t":"set","p":"payload","pt":"msg","to":"Heartbeat","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":800,"wires":[["9d723d25.1e545","a3c380a5.beac08","94559244.5faee"]]},{"id":"a3c380a5.beac08","type":"trigger","z":"a1911aa3.c45be8","name":"Alive?","op1":"","op2":"LED script failure","op1type":"nul","op2type":"str","duration":"5","extend":false,"units":"s","reset":"Script is alive","bytopic":"all","outputs":1,"x":600,"y":760,"wires":[["25d40ed0.c6d1a2"]]},{"id":"25d40ed0.c6d1a2","type":"fan","z":"a1911aa3.c45be8","name":"","x":780,"y":730,"wires":[["63d15087.b1f22"]]},{"id":"63d15087.b1f22","type":"function","z":"a1911aa3.c45be8","name":"RBE","func":"var newval = msg.payload;\nvar last = context.get(\"last\");\nif (msg.topic == \"CONTROL\")\n{\n    //\n    //  Set how node works.\n    //\n    context.set(\"PASS\",msg.payload);\n    node.status({fill:\"yellow\",shape:\"dot\",text:\"Changed\"});\n    return;\n}\nvar pass = context.get(\"PASS\") || 0;\nif (pass === 0)\n{\n    if (newval != last)\n    {\n       context.set(\"last\", newval);\n       node.status({fill:\"green\",shape:\"dot\",text:\"Passed\"});\n       return msg;\n    }\n    node.status({fill:\"red\",shape:\"dot\",text:\"Blocked\"});\n    return;\n}\nelse\n{\n    node.status({fill:\"green\",shape:\"dot\",text:\"All\"});\n    return msg;\n}\n","outputs":1,"noerr":0,"x":910,"y":730,"wires":[["a31b073c.fcacf","45f667ea.d01f28"]]},{"id":"45f667ea.d01f28","type":"switch","z":"a1911aa3.c45be8","name":"Script fail?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"LED script failure","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":1070,"y":730,"wires":[[]]},{"id":"1ecd3a9e.dc5bfd","type":"mqtt-broker","name":"TIMEPI MQTT","broker":"192.168.0.99","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"SOM","birthQos":"0","birthPayload":"TelePi comms up","closeTopic":"EOM","closePayload":"TelePi shutting down","willTopic":"EOM","willQos":"0","willPayload":"TelePi Comms Failure"}]

Does that help?

Oh, and here is the part to stop the script.

[{"id":"645522c7.2bff44","type":"inject","z":"a1911aa3.c45be8","name":"Stop script","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Stop","payloadType":"str","x":280,"y":590,"wires":[["3ef2d6b1.89130a"]]},{"id":"3ef2d6b1.89130a","type":"fan","z":"a1911aa3.c45be8","name":"","x":460,"y":590,"wires":[["54c5b879.c98a2"]]},{"id":"54c5b879.c98a2","type":"fan","z":"a1911aa3.c45be8","name":"","x":780,"y":590,"wires":[["68e550b0.329d18","6c3e3b77.faa4ec"]]},{"id":"68e550b0.329d18","type":"mqtt out","z":"a1911aa3.c45be8","name":"","topic":"tele_led","qos":"","retain":"","broker":"1ecd3a9e.dc5bfd","x":980,"y":590,"wires":[]},{"id":"1ecd3a9e.dc5bfd","type":"mqtt-broker","name":"TIMEPI MQTT","broker":"192.168.0.99","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"SOM","birthQos":"0","birthPayload":"TelePi comms up","closeTopic":"EOM","closePayload":"TelePi shutting down","willTopic":"EOM","willQos":"0","willPayload":"TelePi Comms Failure"}]

I’ll give it a try tomorrow, if time allows, thank you for the pointers. Merry Christmas

No problems.

Sorry if it is a bit confusing. I did this a few months ago and so as it is happily working, I have kind of forgotten some of the stuff.

You will have to change the path to the python script - obviously.

This is to drive some LEDs (not Neo-pixels) (a hat) that plugs onto the GPIO pins,

The message structure is LED,R,G,B.
Then there is the "kill" command to quit the script.

The alive part isn't complete - I just realised - but it may help you with what you want to do.
(I edited this a while after posting. I was wrong with what I originally said.)

Yes, all the best to you too.

Are you launching your neopixel.py as root? Try
sudo /usr/bin/pkill -9 -f "/home/pi/neopix.py"

1 Like

Andy that’s great thank you! Result, this Christmas brings much more led automation!!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.