Standalone Raspberry Pi Security Camera with AI "person detection"

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?

  1. some kind of UI image display for camera setup, monitoring, and image review

  2. 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.

  3. consolidate the email messages so it can send multiple attachments in a single message instead of one attachment per Email message.

  4. 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 :slight_smile: 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 :smile:

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.

4 Likes

I can look at your flow but please, format it accordingly to make it possible to import

What is the preferred way? I cut and pasted my exported formatted json file into another instance of node-red running on a second system and it imported just fine. Does the blockquote on this forum mess up the cut and paste? Or is it because I turned the strings containing Email addresses and phone numbers into empty strings before posting?

I hate Python's significant white-space "feature" because of all the grief it causes sharing code snippets.

Well, your code has a CRLF after each single line so your code cannot be imported unless I first clean it up

I do it like the example below. Honestly, it might be even better ways, edit your previous post, mark your flow text, then select "Preformatted text"

For Python, yes, you have to consider that "feature", I use an editor (UltraEdit) where you can configure tabs to be 4 spaces instead, that helped me. But as you say, you receive & paste some snippet from somewhere into your code where ordinary tabs has been used and to get the whole thing working, you need to make a search and replace, kind of annoying, But as language and possibilities, I still consider Python very much superior to Javascript, especially the multi-threading possibilities that I have used a lot during the years when writing sw

[{"id":"b32ef6f4.3c8f78","type":"comment","z":"bccd3f77.b2e57","name":"Operation mode control","info":"","x":160,"y":60,"wires":[]}]

Looks like a moderator has already changed it to "preformatted text".

I copied it again and pasted it into a new flow and it looks fine now. I'm skeptical of CR-LF being in cut and paste of my flow as I've only used Linux editors, maybe part of the problem is the variety -- thonny & nano on the Pi, Puma and Gedit on my Linux Mate desktop. I'm not committed to any particular editor or IDE but am committed to using what is immediately available whenever possible.

Thanks for the tip!

The hard part of setting this system up is getting openCV 3.3.x compiled or installed on the Raspberry Pi. I don't know of any openCV 3.3.x binaries available for the Pi2/3 B, although I've found one for the PiZero, but I've not tried it.

Good instructions are here: Optimizing openCV 3.3.0 for Raspberry Pi an earlier article on the site give much more detailed instructions on building it, but you definitely want the "optimized" version for this application for its nearly 50% speedup. This site also provided the python example code I started with in some other Deep Learning tutorials.

Actually it should work with a web cam on most any computer that can run python, node-red, and openCV 3.3.x, although most web cams have an IR filter and thus won't work under IR illumination.

If anyone really wants to give this a try I can Email the python script and startAI.sh script used int the flow. Although I will put it up on GitHub next week if my 24/7 testing completes successfully. It does appear I needed an apt-get dist-upgrade as the WiFi seemed to drop out on the Pi3, but so far no issues on the Pi2 version since my work-around.

Yeah, this is pain, I did it on a Pi3, still takes ages

Also Adrians site with his articles is wonderful, he is a master in video analytics I must admit, I also use the Deep Learning stuff he showed, currently the MobileNetSSD_deploy.caffemodel, seems to be working very well.

My approach;

  • I use Motion connected to two cameras (the Pi USB port is limiting me to add more cameras to one single Pi somehow) and tuned "to the outmost perfection", using the version 4.1.1 that I had to build as well, took also some time but is a must, much more reliable in terms of noise and basic detection
  • When Motion detects movement, it saves pictures, starts a Python scripts that copies those to a target dir on a usb stick (out_1 for the first camera, out_2 for the second)
  • When the Event ends, another Python script is started, this takes the pics one by one, analyses them,
    looking for persons, when found sends a configurable number of compressed small images with the detection frame to my iPhone via Telegram. Finally it cleans up the target dir, all prepared for next event
  • Full set of stored pictures are kept for 7 days, then automatically cleaned up

So far, no false alarms but really accurate when a real person is showing up

I have also tried a solution using AWS Rekognition, it was just as accurate, fast, maybe slightly better, but it is only free the first year and depends on the cloud so I have put that in the drawer

In all, I have 4 RPi3's, with 7 cameras

2 Likes

Forgot to mention, NR is used for the following:

  • Setting the AI mode (based on the arm state of our home security system)
  • Monitoring loss of cameras
  • Communication with Telegram
  • Integration with Owntracks, allows me to approach the house without causing pics to be sent, the AI mode is actually changed for ten minutes when I approach the house, gives me time to disarm our home security system
  • Health control (checking that my various services are running correctly in all my Motion Pi's)

I looked into telegram, it was either immediate or very late to notify depending on the state of my phone -- generally asleep in my pocket as I'm not much of a smartphone user.

Seems phones are setup for fast SMS notifications so that is what I use, generally by the time I get the SMS and open the Email the photo is there sending via Gmail.

Have you found USB cameras that work with IR? The "event" approach is pretty much standard and seems to add significantly to the detection and notification latency which I am trying to minimize, particularly for the audio alerts. This suggests perhaps I should send the first detection and then start an "event" to consolidate further images that arrive within the event time window.

My next attempt to cut down the latency will be to change out the python script to use the Movidius NCS, but this nearly doubles the cost of setting up a stand-alone system.

As to your two camera limit with the Pi, its been quite a while since I've messed with v4l2, but usually there is some limit on the number of video devices so you need to do some mknod commands or edit /etc/modules to create the extra /dev/video files, I recall this was not all that well documented.

As to automatically changing the alarm mode when you arrive and depart, I use Bluetooth beacons and a couple of strategically placed PiZeroW running node-red and a shell script to poll for the beacons. The beacons are key fobs, so if both sets of keys are gone the system goes to Notification mode via MQTT. So far no notifications when we arrive or depart.

I'm trying to avoid smartphone dependence beyond getting SMS alerts and Emailed images. This makes it easy to redirect or add notifications to our neighbors when we are away for significant amounts of time.

Before I learned about the beacons, I tried Home Automation and its "presence detection" via cellphone connections to the home WiFi network. It appeared to work great until the phones started going to sleep. I couldn't find a solution that didn't massacre the time the phone could go between charges.

I have sensors in the deadbolts to automatically arm/disarm the system when we lockup -- I couldn't do the hard wiring for this now. but back when we first got the house I was much younger and rooting around in the attic was not the issue for me that it is now :frowning: The original system was discrete CMOS logic and its evolved over the years. It became "computerized" after hurricane Alicia thunderstorms destroyed the logic along with a few other electronic devices, and I had an unused Rockwell AIM65 computer :slight_smile: . I beefed up the interface with MOV surge suppressors, protection diodes and bipolar transistor buffers. Its since survived Hurricanes Rita, Ike, Harvey, and a host of random thunderstorms so I got something right :slight_smile:

If this ~$80 stand-alone system, pans out, I plan to present it eventually to our neighborhood watch group to maybe get a network of neighbors to monitor each other's property and notify the security patrol if necessary.

So far, for over a month of 24/7 usage going on about our normal lives with my Lorex add-on, its been very unobtrusive. No crimes so far, our area tends to see only minor thefts particularly from unlocked vehicles and garages left open, but I consider it a feature that I get a message when USPS, UPS, FedEx, etc. deliver.

Yes, I use these, fixed cameras, decent night vision with built-in IR LED's:
https://www.aliexpress.com/item/2Megapixel-1920-1080-CMOS-OV2710-usb-Video-Surveillance-camera-24pcs-IR-LED-night-vision-bullet-Outdoor/32771791898.html?spm=a2g0s.9042311.0.0.77d64c4dONZsPB

This I need to investigate further

Yeah, me too, I have a "long range" USB powered, well, it gives a radius of approx 150m, is good enough

There are other things more amusing :wink:

Here i s a snapshot from one of the cameras, it is now evening here but the sun is still up. I use a lower resolution, just 640 x 480 since i did not get a stable picture with higher resolutions (though the camera supports much better, but the Pi did not like that, or Motion). It is acceptable I think for an overview camera
Namnlo%CC%88st

Nice find on the USB camera. I'd been thinking about how to weatherproof a Pi setup, this pretty much solves the issue although, a hole through the wall for a USB connector is a bit larger than I'd prefer.

Is there perhaps a smaller connector at the camera? Didn't look like it in the photos on the website.

No, it comes with cable already fitted but you can ask for longer (or shorter). After I drilled the hole, I used a rubber seal around the cable that "closed the gap around", worked fine for me. It might be possible to open up the housing and make a customization but I did not try.

As for temperature, my Pi3's are mounted "on the other side" of the wall and connected to our WiFi network. In winter time, it is pretty cold where they sit (non-heated areas) but the self-heating from the Pi seems to be enough, it worked perfect during the previous winter

PS In my country the customs started an activity regarding direct import from China so my last order took a bit longer (long handling time in customs) and Swedish VAT was added :frowning: Earlier everything slipped thru fast and easy

I've decided to complete the project here by uploading the Python script that runs the AI and is controlled by the node-red flow I've shared, and the startup script that the flow uses to start the Python on bootup and restart it if it should crash (I've not yet observed this happen). Just remove the .txt extensions I had to add to make the upload work.

startAI.sh.txt (206 Bytes)
PiCam_AI_detection.py.txt (10.2 KB)

You still need to download the MobileNetSSD model and prototext files for the AI network from the original tutorial code I started with: PyImageSearch Tutorial

Its working well enough in my tests and I won't have time to do anything with Github for a while so its time to complete this thread.

There is an issue with the OpenCV dnn module where the net.forward() function sometimes fails to do anything on a blob and returns the previous successful detection as "new" results. You can see the effects here: New Images, Old Detection

I've found a work-around in that there always seems to be variations in the detections on sequential images no matter how stationary the background is so I filter out the frames where no real detection happened by comparing the previous detection with the current detection and if they are identical abort to the top of the loop for the next frame. In a couple of days of 24/7 running its not happened since adding the work-around.

My test location is about the worst camera position I could come up with. It looks through a window and its outer screen, the window is not the cleanest since the get cats up on the sill and rub their noses on the glass from time time, and it faces the west getting blinded by the setting sun. This has exposed the first true false detections I've seen from the AI. With really bad lighting its called cars, and the house & tree behind it as "person" . I filter these out by using the aspect ratio of the detection box A real person should be taller than wide. So far this seems to have solved this problem.

I've also noticed any big out of focus blob near the camera tends to detect as "person" I haven't seen it happen yet, but maybe it will also filter out when bugs fly in towards the camera lens. When using video motion detection serious false alarm issues were caused by wasps that seem to really like building nests near the cameras. This was about the only real improvement I got from the AND of video motion detection and PIR motion detection -- the wasps never seemed to trigger the PIR.

If you play with this and have problems, I'll be happy to try and help. The imutils package makes it trivial to use a USB camera instead of the PiCamera module. Its not confined to the Pi, as long as you have openCV 3.3.x and the required python modules it should run.

I do more or less the same with my scripts as you do. There are some difference in functionality but in general, the outcome is the same

  1. Since Motion is handling the recordings to disk, I do not save the dnn analyzed pictures, I just send those directly to my phone using Telegram as mentioned (after compressing them using PIL). Sample code:
def send_pic(pic, myChat_id):
    # prepare & send images with Telegram
    img = Image.fromarray(pic)
    bio = BytesIO()
    img.save(bio, 'JPEG', optimize=True, quality=25)
    bio.seek(0)
    try:
        bot.send_photo(myChat_id, photo=bio)
    except:
        pass
    bio.close()
  1. There is an issue with the OpenCV dnn module where the net.forward() function sometimes fails. Never seen that myself. Could it be related to the OpenCV version? I'm using a later version, 3.4.1

  2. I have no gui for configuring variables, all necessary values are passed to my scripts from Motion as parameters

Could be. I've not found any OpenCV binaries for the Pi2 or better, Cross-compiling for some reason left me with broken highgui which I use when running with GUI or via ssh -X. Compiling on the Pi3 was many hours, I didn't time it. I'm using 3.3.0

I may try cross-compiling again, I think v 3.3.4 is available.

I found a gituub with a link to a deb for it, but no mention if the dnn stuff was built or not. That site is using the Movidius so it didn't need the dnn modules which used to be in the contrib section. Its hard to keep up, and its too easy to burn time chasing changes that don't matter.

I don't care about saving images without dnn detections which saves a lot of space. Where are you saving images to with Motion, SD card, USB stick, external harddrive/SSD?

I've heard about "wear out" issues with SD cards. I've had a few mysterious SD card failures in long running systems which concern me, but so far they always seem to happen (or get discovered) after a power failure. I'm writing to USB sticks so I can use smaller SD cards. Now once I hit a "release" point I keep a clone of the SD card I can swap in for fast recovery or use as a "master" for making copies. I got burned by this very early on with my initial Beaglebone system

Yes, agree, it took very long to compile, I started with the 3.4.1 since that was the latest at that time but I rather leave it unless I have to upgrade for some reason or another feature is available that I need.

In my setup, each RPi has a 16G USB stick. Motion is saving pictures to the stick. I mounted them using samba to easily access them from Windows and my MacBook. They are all mounted as /mnt/pics.

When Motion detects an event, a "reference" picture is saved to /mnt/pics and a python script starts to save a "working copy" to /mnt/pics/out_1 for camera1.

When the event ends, there is a number of pictures taken. Now another python script for analyzing starts processing each picture in /mnt/pics/out_1 until it finds "person" using dnn. If so, it sends information to my phone and stops further analyzing. When finished, all pictures in /mnt/pics/out_1 are deleted, ready for next event when and if it happens.

The reference directory /mnt/pics keeps all pictures with an age up until 7 days, a simple house keeping script is handling this automatically

My directory settings and script executions for Motion (sample for camera1 in one of the pi's)

target_dir = /mnt/pics

on_event_start = (not defined)
on_event_end = python /home/pi/det_people_dnn.py -i '/mnt/pics/out_1' -n 1 -c 0.3
on_picture_save = python /home/pi/on_pic_save.py -p %f -d '/mnt/pics/out_1'
on_motion_detected = (not defined)
on_area_detected = (not defined)
on_movie_start = (not defined)
on_movie_end = (not defined)
on_camera_lost = python /home/pi/on_camera_lost.py -c '31'
on_camera_found = (not defined)

I do the same, I use the "SD Card Copier" tool from time to time

It'll be interesting to see how well our USB sticks stands up to all the writing. It may be misinformation or only an issue with older ones, but I've seen reports of short (~1 yr) lifespans in "dash cam" usage, generally with smaller cards. If the "wear leveling" algorithms work, larger capacity devices should last longer.

I'll certainly followup if my USB sticks develop issues. The one in my "real" system that uses the Movidius for my FLIR Lorex DVR (9 cameras writing to it) is a 256GB San Disk brand. The ones I'm using for the standalone and playing around systems are Microcenter "house brand" 32GB that they had on sale a while back.

I don't know of any real reason why USB sticks would be any different than SD cards, but it was the easiest way to keep all those writes off the SD card that boots the system. May not matter.

We are clearly thinking along similar lines. My "standalone" system idea was mostly to loan to friends and neighbors when they ask us to "keep and eye on their house" while they are away.

My main idea was the realization that most any commercial video security DVR can ftp images to a server somewhere and that node-red can be that server and pipe the images to a Pi with the Movidius for AI detection and notifications.

Yes, agree. But they can be replaced much easier, there is nothing else on them in my case. If something happens of REAL interest, some unexpected visitor in the middle of the night, I will copy such evidence to another place anyway

And what about SSD disks? Will they also face a wearing issue then?

Of course, this makes sense and is key I think

Is a good and very friendly & caring thought I think. Having a number of (small) portable suit cases, everything inside prepared and then a few wireless connected cams in a "neighbouring care kit"

SD cards can be very unreliable. Especially unbranded ones. I always buy the Samsung Evo's or the Evo Pro's - I've had one of these running on my Pi2 for several years now. It gets written to on average once per second and has held up really well. I also make sure I buy oversized ones since that gives a lot more room for wear levelling. I typically use 32GB cards.

Also worth noting that USB sticks are often less reliable than SD cards.

In general, you should never rely on either an SD card nor a USB stick for critical data. I use my Synology NAS's server backup app to back up critical files and folders from the Pi.

SD cards are also sensitive to power failures and this can cause catastrophic errors. My Pi's are powered off a PC UPS along with my switch, router, WiFi AP and the NAS (I have 2 UPSs).

If you want to reduce wear, there are a number of blog posts that show how to redirect log files to in-memory RAM drive.

My IOT devices are on a UPS but when the outage is longer than the battery lasts it ends up being a power failure event. I guess I could try nut or one of the other "smart" UPS packages but I want the system up as long a possible, Cloning a new SD card from my master copy is a trade off for keeping the system up as long a possible.

Power issues here are getting worse every year, we are looking into one of those "whole house" natural gas powered backup generators.

As to brand names SD cards, I've has as many SanDisk and Samsung fail as the Microcenter "noname" house brand cards and I use more of the noname cards.

Log files in RAM kind of defeats the purpose of having them to look at after a failure, If that is acceptable why not just turn off all logging.

Depending on the UPS, you can generally get the data from the UPS and feed it into Node-RED. Then you can create a flow that monitors for low battery levels then to shut down accordingly. Not especially pleasant to recover from a corrupt database (assuming you are storing data).

That would help of course though you will still want your UPS to smooth over the power transition. Also note that my Pi2 rarely recovers its network connection after a power swap. It is annoying but I've not got to the bottom of why it does it. It is running Jesse and the Pi3 running a newer version of Rasbian doesn't have the issue but then the Pi2 didn't used to do it either. Again, I can use Node-RED to force a reboot when it can't reach the local network.

Odd, none of my Samsung cards have ever failed on me but plenty of others have.

I agree. I don't do that myself but it is an option for people having issues.

I actually think that most issues are caused by a bad USB power supply. Best to use a decent power supply.