Thanks for your suggestions folks, I had some spare time over the weekend to switch off various parts to look for the cause. Turns out it isn't the inclusion of the harmless function node, but appears to be the code in node-red-contrib-bluetooth-serial-port sometimes preventing the new flows from being installed (waiting for proper shutdown of a bluetooth connection perhaps?). The reason I suspect this is usually when I deploy my flows I hear an audible disconnection tone from my bluetooth device. But when the flows are not going to be deployed properly (or deleted!) there is no audible disconnection tone and the Node Red flow editing dashboard times out with the deploy, leaving nodes still marked as not deployed. At the backend you can clearly see the deploy request coming in.
This is the bluetooth node:
[{"id":"648570f8.fe2c6","type":"debug","z":"bc8c518c.a3e29","name":"DIVOOM PAYLOAD","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":920,"y":4060,"wires":[]},{"id":"d10dbf12.1a2fd","type":"bt serial in","z":"bc8c518c.a3e29","name":"DIVOOM","btSerial":"7360ab36.341fc4","x":200,"y":4060,"wires":[["648570f8.fe2c6"]]},{"id":"535d71b3.c820d","type":"bt serial out","z":"bc8c518c.a3e29","name":"DIVOOM","btSerial":"7360ab36.341fc4","x":860,"y":4160,"wires":[]},{"id":"cc9b0834.9f1f48","type":"function","z":"bc8c518c.a3e29","name":"Divoom Brightness 5","func":"//msg.payload = toHex(msg.payload)\n//msg.payload = \"4503 04\"\n\n// 0x74 is set brightness (116)\nvar text = \"04007405\"\nvar crc = getCRC(text);\n\nmsg.payload = Buffer.from([0x01, 0x04, 0x00, 0x74, 0x05, 0x7d, 0x00, 0x02]);\n\nreturn msg;\n\n// Format of all Divoom messages is like this:\n//\n// 01 LLLL PAYLOAD CRCR 02\n//\n// All the messages will start with 01 and end with 02\n// LLLL is the length of the PAYLOAD string + the length of the CRC (4) in number of bytes (FF is one byte for example) in LSB First\n// CRCR is the CRC of the message including the length (LLLL PAYLOAD) in LSB First\n//\n// Split the message every 1332 characters and send that as a binary buffer too\n\n// 4503 TT (e.g. 4503 04) for setting theme\n\nfunction toHex(d) {\n return (\"0\"+(Number(d).toString(16))).slice(-2).toUpperCase()\n}\n\nfunction int2hexlittle(value) {\n const byte1 = (value & 0xFF).toString(16).padStart(2, \"0\");\n const byte2 = ((value >> 8) & 0xFF).toString(16).padStart(2, \"0\");\n return `${byte1}${byte2}`;\n}\n\nfunction getLength (payload) {\n // CRC is 4 characters\n // We divide by 2 because 1 byte is 2 Hex char\n const lenght = (payload.length + 4) / 2;\n return int2hexlittle(length);\n}\n\nfunction getCRC(str) {\n let sum = 0;\n for (i = 0, l = str.length; i < l; i += 2) {\n sum += parseInt(str.substr(i, 2), 16)\n }\n return int2hexlittle(sum);\n}\n\nfunction map(x, in_min, in_max, out_min, out_max) {\n return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;\n}\n","outputs":1,"noerr":0,"x":420,"y":4120,"wires":[["535d71b3.c820d"]]},{"id":"e6de22af.ae584","type":"inject","z":"bc8c518c.a3e29","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":4120,"wires":[["cc9b0834.9f1f48"]]},{"id":"f753c258.821e4","type":"function","z":"bc8c518c.a3e29","name":"Divoom Brightness 99","func":"//msg.payload = toHex(msg.payload)\n//msg.payload = \"4503 04\"\n\n// 0x74 is set brightness (116)\nvar text = \"04007463\"\nvar crc = getCRC(text);\n\nmsg.payload = Buffer.from([0x01, 0x04, 0x00, 0x74, 0x63, 0xDB, 0x00, 0x02]);\n\nreturn msg;\n\n// Format of all Divoom messages is like this:\n//\n// 01 LLLL PAYLOAD CRCR 02\n//\n// All the messages will start with 01 and end with 02\n// LLLL is the length of the PAYLOAD string + the length of the CRC (4) in number of bytes (FF is one byte for example) in LSB First\n// CRCR is the CRC of the message including the length (LLLL PAYLOAD) in LSB First\n//\n// Split the message every 1332 characters and send that as a binary buffer too\n\n// 4503 TT (e.g. 4503 04) for setting theme\n\nfunction toHex(d) {\n return (\"0\"+(Number(d).toString(16))).slice(-2).toUpperCase()\n}\n\nfunction int2hexlittle(value) {\n const byte1 = (value & 0xFF).toString(16).padStart(2, \"0\");\n const byte2 = ((value >> 8) & 0xFF).toString(16).padStart(2, \"0\");\n return `${byte1}${byte2}`;\n}\n\nfunction getLength (payload) {\n // CRC is 4 characters\n // We divide by 2 because 1 byte is 2 Hex char\n const lenght = (payload.length + 4) / 2;\n return int2hexlittle(length);\n}\n\nfunction getCRC(str) {\n let sum = 0;\n for (i = 0, l = str.length; i < l; i += 2) {\n sum += parseInt(str.substr(i, 2), 16)\n }\n return int2hexlittle(sum);\n}\n\nfunction map(x, in_min, in_max, out_min, out_max) {\n return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;\n}\n","outputs":1,"noerr":0,"x":420,"y":4160,"wires":[["535d71b3.c820d"]]},{"id":"2c93c0fb.22aa8","type":"inject","z":"bc8c518c.a3e29","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":4160,"wires":[["f753c258.821e4"]]},{"id":"7360ab36.341fc4","type":"bt-serial-port","z":"","name":"Divoom","btAddress":"11:75:58:48:A8:41","serialReconnectTime":"10"}]
Here are the logs showing the request coming in:
Sep 7 10:07:47 raspberrypi Node-RED[339]: 7 Sep 10:07:47 - [audit] {"event":"flows.set","type":"full","level":98,"user":{"username":"xxx","permissions":"*"},"path":"/flows","ip":"192.168.1.218","timestamp":1630973267623}
Sep 7 10:07:48 raspberrypi Node-RED[339]: 7 Sep 10:07:48 - [info] Stopping flows
Sep 7 10:07:48 raspberrypi Node-RED[339]: 7 Sep 10:07:48 - [info] BTSerial - Closed
Sep 7 10:07:48 raspberrypi Node-RED[339]: 7 Sep 10:07:48 - [info] BTSerial - Closed
Sep 7 10:07:48 raspberrypi Node-RED[339]: 7 Sep 10:07:48 - [info] Stopped flows
Sep 7 10:07:48 raspberrypi Node-RED[339]: 7 Sep 10:07:48 - [info] Starting flows
Sep 7 10:07:49 raspberrypi Node-RED[339]: 7 Sep 10:07:49 - [info] Started flows
Sep 7 10:07:53 raspberrypi Node-RED[339]: 7 Sep 10:07:53 - [info] [pushover api:Pushover Notify HTTPS Restarted] pushover POST succeeded:
Sep 7 10:07:53 raspberrypi Node-RED[339]: "{\"status\":1,\"request\":\"aa665c95-9938-427c-81ab-e36f4b5a858f\"}"
Sep 7 10:08:00 raspberrypi Node-RED[339]: 7 Sep 10:08:00 - [info] BTSerial - Open
Sep 7 10:08:02 raspberrypi bluetoothd[527]: 11:75:58:48:A8:41: error updating services: Connection refused (111)
Sep 7 10:09:10 raspberrypi Node-RED[339]: 7 Sep 10:09:10 - [audit] {"event":"flows.set","type":"full","level":98,"user":{"username":"xxx","permissions":"*"},"path":"/flows","ip":"192.168.1.218","timestamp":1630973350815}
Sep 7 10:09:13 raspberrypi Node-RED[339]: 7 Sep 10:09:13 - [error] BTSerial - Didn't find a Serial Port on 'DF:C9:7C:88:8B:46'
Sep 7 10:09:13 raspberrypi Node-RED[339]: 7 Sep 10:09:13 - [info] Stopping flows
Sep 7 10:09:13 raspberrypi Node-RED[339]: 7 Sep 10:09:13 - [info] BTSerial - Closed
Sep 7 10:09:13 raspberrypi Node-RED[339]: 7 Sep 10:09:13 - [info] BTSerial - Closed
Sep 7 10:09:13 raspberrypi Node-RED[339]: 7 Sep 10:09:13 - [info] Stopped flows
Sep 7 10:09:13 raspberrypi Node-RED[339]: 7 Sep 10:09:13 - [info] Starting flows
Sep 7 10:09:14 raspberrypi Node-RED[339]: 7 Sep 10:09:14 - [info] Started flows
Sep 7 10:09:25 raspberrypi Node-RED[339]: 7 Sep 10:09:25 - [info] BTSerial - Open
Sep 7 10:09:25 raspberrypi Node-RED[339]: 7 Sep 10:09:25 - [info] [pushover api:Pushover Notify HTTPS Restarted] pushover POST succeeded:
Sep 7 10:09:25 raspberrypi Node-RED[339]: "{\"status\":1,\"request\":\"e6da88ce-4883-4d5a-8cc3-9715d6d7dc31\"}"
Sep 7 10:09:26 raspberrypi bluetoothd[527]: 11:75:58:48:A8:41: error updating services: Connection refused (111)
Sep 7 10:10:17 raspberrypi Node-RED[339]: 7 Sep 10:10:17 - [audit] {"event":"flows.set","type":"full","level":98,"user":{"username":"xxx","permissions":"*"},"path":"/flows","ip":"192.168.1.218","timestamp":1630973417579}
Sep 7 10:10:59 raspberrypi systemd[1]: Created slice User Slice of UID 1000.
Sep 7 10:10:59 raspberrypi systemd[1]: Starting User Runtime Directory /run/user/1000...
Sep 7 10:10:59 raspberrypi systemd[1]: Started User Runtime Directory /run/user/1000.
Sep 7 10:10:59 raspberrypi systemd[1]: Starting User Manager for UID 1000...
Sep 7 10:10:59 raspberrypi systemd[10239]: Listening on GnuPG cryptographic agent and passphrase cache (restricted).
Sep 7 10:10:59 raspberrypi systemd[10239]: Listening on GnuPG cryptographic agent and passphrase cache (access for web browsers).
Sep 7 10:10:59 raspberrypi systemd[10239]: Listening on GnuPG network certificate management daemon.
Sep 7 10:10:59 raspberrypi systemd[10239]: Listening on GnuPG cryptographic agent (ssh-agent emulation).
Sep 7 10:10:59 raspberrypi systemd[10239]: Listening on GnuPG cryptographic agent and passphrase cache.
Sep 7 10:10:59 raspberrypi systemd[10239]: Reached target Paths.
Sep 7 10:10:59 raspberrypi systemd[10239]: Reached target Sockets.
Sep 7 10:10:59 raspberrypi systemd[10239]: Reached target Timers.
Sep 7 10:10:59 raspberrypi systemd[10239]: Reached target Basic System.
Sep 7 10:10:59 raspberrypi systemd[10239]: Reached target Default.
Sep 7 10:10:59 raspberrypi systemd[10239]: Startup finished in 140ms.
Sep 7 10:10:59 raspberrypi systemd[1]: Started User Manager for UID 1000.
Sep 7 10:10:59 raspberrypi systemd[1]: Started Session c2 of user pi.
At this point no matter how many deploys you do to Node Red no more succeed.
If you reboot with a power cycle, all your nodes are deleted. e.g.
This is what a "good" deploy log looks like:
Sep 7 10:40:02 raspberrypi systemd[1]: Started Session c1 of user pi.
Sep 7 10:40:29 raspberrypi Node-RED[313]: 7 Sep 10:40:29 - [audit] {"event":"flows.set","type":"full","level":98,"user":{"username":"xxx","permissions":"*"},"path":"/flows","ip":"192.168.1.218","timestamp":1630975229085}
Sep 7 10:40:29 raspberrypi Node-RED[313]: 7 Sep 10:40:29 - [info] Stopping flows
Sep 7 10:40:30 raspberrypi Node-RED[313]: 7 Sep 10:40:30 - [info] BTSerial - Closed
Sep 7 10:40:30 raspberrypi Node-RED[313]: 7 Sep 10:40:30 - [info] BTSerial - Closed
Sep 7 10:40:30 raspberrypi Node-RED[313]: 7 Sep 10:40:30 - [info] Stopped flows
Sep 7 10:40:30 raspberrypi Node-RED[313]: 7 Sep 10:40:30 - [info] Starting flows
Sep 7 10:40:31 raspberrypi Node-RED[313]: 7 Sep 10:40:31 - [info] Started flows
Sep 7 10:40:32 raspberrypi Node-RED[313]: 7 Sep 10:40:32 - [info] [pushover api:Pushover Notify HTTPS Restarted] pushover POST succeeded:
Sep 7 10:40:32 raspberrypi Node-RED[313]: "{\"status\":1,\"request\":\"04a68c03-22fe-4ddc-a2ec-00f8aaa18aac\"}"
So I suspect there is some kind of thread locking issue in the Bluetooth code preventing a reinstall of the new flows. I'll hunt around for a different Bluetooth node however my question is more around debugging.
How can I look at Node internals to see where threading problems might be?
Is there an equivalent of a stack dump?
Sorry for delays in replying. Due to work commitments I really only have time to look at it on weekends.
Thanks,
Murray