I've got an issue with the Python AI detection for which I seem to have found a work-around, but the node-red part that controls everything is looking pretty solid, so I thought it'd be nice to share.
Basically a Pi 2B (or better) with network connection and a NoIR camera module and some IR LED illumination can push a highly reliable Email notification (with attached photo) when a person is in the field of view when the system is enabled. While it only achieves ~1 frame every two seconds when using CPU only MobileNetSSD AI, this can be surprisingly effective because of the near zero false alarm rate.
This node red flow starts the Python AI, has a simple Dashboard UI to set the mode (Idle, Audio, Email), send the notifications and power off the system. Once I've solved the AI issue, or my work-around passes a burn in I've started today for a week or more, I'll put the project up on GitHub
But I hope posting this could get me some ideas and help with a few improvements on the node-red side, particularly the Dasboard UI.
[
{
"id": "244fb09e.f4344",
"type": "tab",
"label": "PiCam Notification",
"disabled": false,
"info": ""
},
{
"id": "566659f.6cc25a8",
"type": "e-mail",
"z": "244fb09e.f4344",
"server": "smtp.gmail.com",
"port": "465",
"secure": true,
"name": "",
"dname": "Email Notification",
"x": 1250,
"y": 460,
"wires": []
},
{
"id": "a101e221.13455",
"type": "mqtt out",
"z": "244fb09e.f4344",
"name": "Set AI Mode",
"topic": "AI/Control/Mode",
"qos": "2",
"retain": "true",
"broker": "f4ac4236.1ce18",
"x": 690,
"y": 260,
"wires": []
},
{
"id": "11224d26.5fc6c3",
"type": "debug",
"z": "244fb09e.f4344",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"x": 670,
"y": 220,
"wires": []
},
{
"id": "fc6db738.6e8ac8",
"type": "mqtt in",
"z": "244fb09e.f4344",
"name": "Person Detected",
"topic": "AI/Detection",
"qos": "2",
"broker": "f4ac4236.1ce18",
"x": 120,
"y": 320,
"wires": [
[
"8e22e2d8.169e"
]
]
},
{
"id": "8e22e2d8.169e",
"type": "function",
"z": "244fb09e.f4344",
"name": "Notification Filter",
"func": "var Idle = context.get('Idle')||0;\nvar Audio = context.get('Audio')||0;\nvar Notify = context.get('Notify')||0;\n\nif (msg.topic === \"AI/Control/Mode\"){\n if(msg.payload.includes(\"Idle\")){\n context.set('Idle',1); \n context.set('Audio',0);\n context.set('Notify',0);\n }else if(msg.payload.includes(\"Audio\")){\n context.set('Idle',0);\n context.set('Audio',1);\n context.set('Notify',0);\n }else if(msg.payload.includes(\"Notify\")){\n context.set('Idle',0);\n context.set('Audio',0);\n context.set('Notify',1);\n }\n return [ msg, null, null ]; // change null to msg for debugging.\n}\n\nif (msg.topic === \"AI/Detection\"){\n if (Idle == 1){\n return [ null, null, null ];\n }\n if (Audio == 1){\n return [ null, msg, null ];\n }\n if (Notify == 1){\n return [ null, null, msg ];\n }\n}\nreturn [ null, null, null ];",
"outputs": 3,
"noerr": 0,
"x": 390,
"y": 320,
"wires": [
[
"11224d26.5fc6c3",
"a101e221.13455"
],
[
"be1df75f.243188"
],
[
"250f61c0.0ccc3e",
"2f0c485e.3d3168",
"4a55e864.97cc18"
]
]
},
{
"id": "f9aaacef.4b5cc",
"type": "debug",
"z": "244fb09e.f4344",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"x": 1210,
"y": 400,
"wires": []
},
{
"id": "be1df75f.243188",
"type": "exec",
"z": "244fb09e.f4344",
"command": "/usr/bin/espeak-ng \"Person Detected!\"",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "Espeak Person Detected",
"x": 730,
"y": 320,
"wires": [
[],
[],
[]
]
},
{
"id": "c724c5f7.e02138",
"type": "file in",
"z": "244fb09e.f4344",
"name": "File Attachment",
"filename": "",
"format": "",
"chunk": false,
"sendError": false,
"x": 1040,
"y": 460,
"wires": [
[
"f9aaacef.4b5cc",
"566659f.6cc25a8"
]
]
},
{
"id": "250f61c0.0ccc3e",
"type": "change",
"z": "244fb09e.f4344",
"name": "Setup Email",
"rules": [
{
"t": "move",
"p": "payload",
"pt": "msg",
"to": "filename",
"tot": "msg"
},
{
"t": "set",
"p": "to",
"pt": "msg",
"to": "",
"tot": "str"
},
{
"t": "set",
"p": "cc",
"pt": "msg",
"to": "",
"tot": "str"
},
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "PiCam AI Detection",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 650,
"y": 460,
"wires": [
[
"8eb6413b.57abe"
]
]
},
{
"id": "2f0c485e.3d3168",
"type": "debug",
"z": "244fb09e.f4344",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"x": 670,
"y": 380,
"wires": []
},
{
"id": "8cfb64ed.b3e868",
"type": "change",
"z": "244fb09e.f4344",
"name": "Setup SMS",
"rules": [
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": " AI Person Detected! Check Email.",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 870,
"y": 580,
"wires": [
[
"a1c26563.b8eb58"
]
]
},
{
"id": "a1c26563.b8eb58",
"type": "debug",
"z": "244fb09e.f4344",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"x": 1030,
"y": 540,
"wires": []
},
{
"id": "4a55e864.97cc18",
"type": "delay",
"z": "244fb09e.f4344",
"name": "",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "minute",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"x": 650,
"y": 580,
"wires": [
[
"8cfb64ed.b3e868"
]
]
},
{
"id": "4e0e3a2c.b37524",
"type": "inject",
"z": "244fb09e.f4344",
"name": "Send Test Email",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 180,
"y": 460,
"wires": [
[
"e05145bb.5faf68"
]
]
},
{
"id": "e05145bb.5faf68",
"type": "change",
"z": "244fb09e.f4344",
"name": "set file attachment",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "/home/pi/Pictures/patio8_test.jpg",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 410,
"y": 460,
"wires": [
[
"250f61c0.0ccc3e"
]
]
},
{
"id": "51610077.527a4",
"type": "inject",
"z": "244fb09e.f4344",
"name": "Send Test SMS",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 440,
"y": 580,
"wires": [
[
"4a55e864.97cc18"
]
]
},
{
"id": "3a5c696c.c58786",
"type": "inject",
"z": "244fb09e.f4344",
"name": "Set AI Mode Idle",
"topic": "AI/Control/Mode",
"payload": "Idle",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 120,
"y": 200,
"wires": [
[
"8e22e2d8.169e"
]
]
},
{
"id": "bb220fea.6d8eb",
"type": "inject",
"z": "244fb09e.f4344",
"name": "Set AI Mode Audio",
"topic": "AI/Control/Mode",
"payload": "Audio",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 130,
"y": 240,
"wires": [
[
"8e22e2d8.169e"
]
]
},
{
"id": "9d066d18.d9816",
"type": "inject",
"z": "244fb09e.f4344",
"name": "Set AI Mode Notify",
"topic": "AI/Control/Mode",
"payload": "Notify",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 130,
"y": 280,
"wires": [
[
"8e22e2d8.169e"
]
]
},
{
"id": "6112cd17.f577c4",
"type": "comment",
"z": "244fb09e.f4344",
"name": "PiCam Notification",
"info": "## Set AI Mode\n1. Idle -- nothing happens, set on startup\n2. Audio -- AI detection plays audio and saves image\n3. Notify -- AI detection saves image and Emails notification\n\n",
"x": 110,
"y": 40,
"wires": []
},
{
"id": "d8dca35b.10f45",
"type": "ui_dropdown",
"z": "244fb09e.f4344",
"name": "Set AI Mode",
"label": "",
"place": "Select option",
"group": "cd24dd47.cbbab",
"order": 1,
"width": 0,
"height": 0,
"passthru": true,
"options": [
{
"label": "Idle",
"value": "Idle",
"type": "str"
},
{
"label": "Audio Alerts",
"value": "Audio",
"type": "str"
},
{
"label": "Email Alerts",
"value": "Notify",
"type": "str"
}
],
"payload": "",
"topic": "AI/Control/Mode",
"x": 110,
"y": 120,
"wires": [
[
"8e22e2d8.169e"
]
]
},
{
"id": "7c5d4415.df641c",
"type": "ui_form",
"z": "244fb09e.f4344",
"name": "",
"label": "WiFi",
"group": "1c41ca31.7c7d26",
"order": 1,
"width": 0,
"height": 0,
"options": [
{
"label": "SSID",
"value": "ssid",
"type": "text",
"required": true
},
{
"label": "WPA2 PSK",
"value": "psk",
"type": "text",
"required": true
}
],
"formValue": {
"ssid": "",
"psk": ""
},
"payload": "",
"topic": "",
"x": 430,
"y": 920,
"wires": [
[
"c8b9b1c2.ca0b1"
]
]
},
{
"id": "c8b9b1c2.ca0b1",
"type": "debug",
"z": "244fb09e.f4344",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"x": 590,
"y": 920,
"wires": []
},
{
"id": "be1316f9.e51568",
"type": "exec",
"z": "244fb09e.f4344",
"command": "kill -2 `pidof python` ; sleep 5 ; sudo /sbin/shutdown -h now",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "shutdown",
"x": 640,
"y": 800,
"wires": [
[],
[],
[]
]
},
{
"id": "cf927d58.2a3d7",
"type": "ui_button",
"z": "244fb09e.f4344",
"name": "",
"group": "1c41ca31.7c7d26",
"order": 2,
"width": 0,
"height": 0,
"passthru": false,
"label": "",
"color": "",
"bgcolor": "#83ed7b",
"icon": "",
"payload": "",
"payloadType": "str",
"topic": "",
"x": 430,
"y": 860,
"wires": [
[]
]
},
{
"id": "1051d806.3f13a8",
"type": "ui_button",
"z": "244fb09e.f4344",
"name": "",
"group": "cd24dd47.cbbab",
"order": 2,
"width": 0,
"height": 0,
"passthru": false,
"label": "",
"color": "",
"bgcolor": "#83ed7b",
"icon": "",
"payload": "",
"payloadType": "str",
"topic": "",
"x": 430,
"y": 740,
"wires": [
[]
]
},
{
"id": "e8f8f06c.0325b",
"type": "ui_button",
"z": "244fb09e.f4344",
"name": "PowerOff",
"group": "1c41ca31.7c7d26",
"order": 3,
"width": 0,
"height": 0,
"passthru": false,
"label": "Power Off",
"color": "",
"bgcolor": "#793042",
"icon": "",
"payload": "",
"payloadType": "date",
"topic": "",
"x": 440,
"y": 800,
"wires": [
[
"be1316f9.e51568"
]
]
},
{
"id": "6e2cd714.96e078",
"type": "inject",
"z": "244fb09e.f4344",
"name": "Launch AI",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"x": 170,
"y": 1060,
"wires": [
[
"2b00d737.406308"
]
]
},
{
"id": "2b00d737.406308",
"type": "exec",
"z": "244fb09e.f4344",
"command": "/home/pi/startAI.sh ",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "",
"x": 630,
"y": 1060,
"wires": [
[],
[],
[]
]
},
{
"id": "e2f5e883.11f458",
"type": "mqtt in",
"z": "244fb09e.f4344",
"name": "AI Status",
"topic": "AI/Status",
"qos": "2",
"broker": "f4ac4236.1ce18",
"x": 140,
"y": 1140,
"wires": [
[
"cc9e4370.34bb2"
]
]
},
{
"id": "cc9e4370.34bb2",
"type": "switch",
"z": "244fb09e.f4344",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "cont",
"v": "died",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 330,
"y": 1140,
"wires": [
[
"6092dcd9.a44a94",
"2b00d737.406308"
]
]
},
{
"id": "6092dcd9.a44a94",
"type": "debug",
"z": "244fb09e.f4344",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"x": 530,
"y": 1140,
"wires": []
},
{
"id": "a50a93f8.39f89",
"type": "comment",
"z": "244fb09e.f4344",
"name": "WiFi setup, incomplete.",
"info": "This is a work in progress.\nplanning to use the command:\n\n# wpa_passphrase \"testing\" \"testingPassword\" >> /etc/wpa_supplicant/wpa_supplicant.conf\n\nBut am having some issues. Obviously it must already be network connected to use this, so its main purpose is to enter the creds on the current network before moving it to the new one.\n",
"x": 780,
"y": 920,
"wires": []
},
{
"id": "8eb6413b.57abe",
"type": "delay",
"z": "244fb09e.f4344",
"name": "",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "5",
"nbRateUnits": "3",
"rateUnits": "minute",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"x": 840,
"y": 460,
"wires": [
[
"c724c5f7.e02138"
]
]
},
{
"id": "1fd3eb96.21e914",
"type": "comment",
"z": "244fb09e.f4344",
"name": "SMS is Incomplete",
"info": "SMS is incomplete since it needs to talk to an external MQTT Broker in order to send an SMS text message.\n\nI use node-red running on an Android phone with termux-api and it is listening for a message from the Alarmbone MQTT Broker.\n",
"x": 1070,
"y": 580,
"wires": []
},
{
"id": "f4ac4236.1ce18",
"type": "mqtt-broker",
"z": "",
"name": "localhost:1883",
"broker": "localhost",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": true,
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthRetain": "false",
"birthPayload": "",
"closeTopic": "",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": ""
},
{
"id": "cd24dd47.cbbab",
"type": "ui_group",
"z": "",
"name": " AI Mode",
"tab": "4f9dd3ff.26cdfc",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "1c41ca31.7c7d26",
"type": "ui_group",
"z": "",
"name": "Setup",
"tab": "4f9dd3ff.26cdfc",
"disp": true,
"width": "6",
"collapse": true
},
{
"id": "4f9dd3ff.26cdfc",
"type": "ui_tab",
"z": "",
"name": "PiCam AI Controller",
"icon": "dashboard"
}
]
What additions do I envision?
-
some kind of UI image display for camera setup, monitoring, and image review
-
complete the UI functionality for setting WiFi SSID and pass-phrase, although this is sort of a chicken and egg problem since a working network connection or attached KVM is required to use the Dashboard UI. The main goal here would be to avoid manually editing wpa_supplicant.conf so non-geeks might use it.
-
consolidate the email messages so it can send multiple attachments in a single message instead of one attachment per Email message.
-
move the cronjob from the Pi root crontab and run it from node-red so the days of retention could be changed from the UI. This is essential to avoid filling up the USB stick with detected images.
I have a more complicated version of this using a Movidius Neural Compute Stick that gets snapshot images from my existing FLIR Lorex security DVR that handles 9 cameras and has been running 24/7 for over a month now with essentially zero false alarms -- got a few audio messages after my wife put some hand washed clothes over a chair on the patio to dry, and some others when she put a floppy sun had on the tower fan on the patio -- both kind of look like a person at a quick glance I've created this project as a much simpler way to share the idea and to give to my neighbor next time he asks me to "watch our house" while he is away
I really appreciate all the help I've received here, and all the great work of the developers. Node-red and MQTT really are the perfect plumbing for the Internet of Things!
This system works far better than anything I'd put together in the past using PIR motion sensors, video based motion detection, and traditional image processing like Harr Cascades, HOG, etc.