- PVSM.RU - https://www.pvsm.ru -
Речь пойдет о сборке, настройке и запуске охранной системы с датчиками, камерой, хранением видеозаписей на удаленном домашнем NAS, встроенным UPS, web-интерфейсом и оповещением через Telegram.
Чтобы быть предельно кратким, оформил статью в стилистике hackster.io. Информация рассчитана на читателей с опытом работы с одноплатным компьютером Raspberry и пониманием принципов работы VPN.

После приобретения дачного участка с небольшим деревянным домиком, встал вопрос, а что происходит в мое отсутствие на участке и все ли в порядке в самом доме. Решением было создать видеонаблюдение и охранную систему с сигнализацией и оповещением.
Основными требованиями были:
наличие охранных функций: использовать датчики в качестве пожарной сигнализации и камеру как детектор движения;
возможность хранить видео сроком семь дней и более;
высоковольтное оборудование должно уместиться в существующем электрощитке небольшого размера и не выходить за его пределы;
сигнализация должна быть автономной, работать при отключении электричества;
минимум проводов;
в дальнейшем расширять функциональные возможности;
удаленный доступ;
доступность как по деньгам, так и по оборудованию.
За основу решил взять миникомпьютер Raspberry Pi Zero W. Его легко разместить в небольшом электрощитке. Рабочий вариант выглядит так:
В итоге получилась система, которая умеет постоянно записывать видео с IP-камеры на домашний NAS через VPN, при постановке на охрану оповещать в Telegram о движении на участке и повышении температуры в доме и работать без электричества чуть более часа. Все это умещается в распределительный щит. Также можно следить за разрядом батареи, подключаться удаленно из любой точки мира не только к камере, но и зайти на страницу управления и просмотреть состояние системы, графики изменения температуры и загрузки ЦПУ.
Набросал схему. Система получилась модульная, некоторые компоненты можно исключить.
Нам потребуется:
Raspberry Pi Zero W (wi-fi идет как точка доступа для раздачи интернета на даче);
Raspberry Pi Camera Board (используется в качестве камеры в доме);
Raspberry Pi Ethernet/USB HUB HAT (для подключения 4G-модема и IP-камеры);
UPS Lite v1.2 Power HAT [1] (в качестве источника бесперебойного питания);
антивандальная кнопка с подсветкой и фиксацией [2] (в качестве кнопки постановки на охрану);
модуль датчика температуры DS18B20 [3] (плата с датчиком температуры и встроенным резистором);
IP-камера с POE и дискретными входами/выходами (камера с возможностью подачи дискретного сигнала);
POE-адаптер 220v [4] (для подключения IP-камеры к Raspberry Pi);
4G-модем (для удаленного доступа и отправки уведомлений в интернет);
роутер с возможностью создания VPN-сервера (для удаленного доступа);
NAS с приложением для видеонаблюдения (для записи и хранения видео);
Ethernet-кабель (для подключения камеры к Raspberry);
резистор (для подключения I/O-камеры к Raspberry GPIO).
Некоторые компоненты не являются обязательными и могут быть исключены. Например, можно исключить UPS Lite, если нет проблем с электричеством. Или не использовать роутер и NAS, если не требуется хранить записи удаленно.
Raspberry Pi OS 32-bit;
WireGuard (VPN-сервер на роутере и VPN-клиент на raspberry);
Telegram;
Node-RED (инструмент потокового программирования):
node-red-contrib-boolean-logic [5] (пакет для подключения логических функций);
node-red-contrib-camerapi [6] (пакет для работы с Pi Camera);
node-red-contrib-cpu [7] (пакет для получения нагрузки ЦПУ и температуры процессора);
node-red-contrib-easybotics-ina219-sensor [8] (пакет для получения данных с UPS Lite);
node-red-contrib-edge-trigger [9] (пакет с функциями триггеров);
node-red-contrib-sensor-ds18b20 [10] (пакет для получения данных с датчика температуры);
node-red-contrib-telegrambot [11] (пакет для отправки сообщений в telegram);
node-red-node-pi-gpio [12] (пакет чтения/записи данных GPIO).

Собираем «бутерброд». Подключаем Raspberry Pi Cam к Raspberry Pi Zero W. Снизу подсоединяем UPS Lite, сверху - Raspberry Pi Ethernet/USB HUB HAT. Добавляем датчик температуры DS18B20. Я использовал модуль из набора для Arduino, в нем кроме датчика температуры уже добавлен резистор и выведены контакты для быстрого подключения. Помним, что этот датчик использует 1-Wire протокол, на контроллере это пин GPIO4.
Подключаем согласно архитектурной схеме представленной выше:
минус заводим на пин GND;
плюс заводим на пин 3.3v;
сигнальный вывод на пин GPIO4.
Подключаем ввод-вывод IP-камеры к Raspberry. Я использовал камеру с наличием ввода-вывода, так как считаю этот способ более надежным и гибким в конфигурировании. Принцип следующий: как только камера обнаруживает движение или попытки взлома, формирует на выходе сигнал. В зависимости от настроек он может быть как постоянным, так и импульсным, а контакт - нормально открытым или нормально замкнутым. В моем случае у камеры на выходе будет постоянное напряжение при нормально замкнутом контакте. У Raspberry Pi Zero напряжение на пинах ввода-вывода 3.3v для логической единицы и 0v для нуля. У меня уже была камера Geovision, в описании которой сказано, что максимальный ток 30mA и на дискретном выходе 5v, следовательно мне подойдет резистор на 100 Ом. Возвращаемся к схеме и подключаем:
плюс с камеры на резистор, с резистора на пин GPIO27;
минус с камеры заводим на пин GND.
Теперь кнопка постановки на охрану. Я выбирал кнопку с LED-индикацией. Это позволяет визуально оценить, включена охрана или нет. Также индикация меняется при обнаружении движения. Удобно то, что кнопка рассчитана на питание 3-6V, что существенно облегчает монтаж к Raspberry Pi. Кнопка имеет пять контактов и позволяет подсоединить ее разными способами. Я выбрал тот, при котором подсветка горит при нажатой кнопке. Подключение сделал следующим образом:
GPIO17 подключил к кнопке к контакту С;
минус кнопки заводим на пин GND;
контакт кнопки NO подключил к GPIO22;
минус кнопки заводим на пин GND.

Дело за малым - подключить к USB-порту LTE-модем, Ethernet-кабель от Raspberry Pi Ethernet/USB HUB HAT к POE-адаптеру, а от адаптера к IP-камере. Запитать UPS Lite от стандартного блока питания для Raspberry и включить POE-адаптер в розетку. Как все это выглядит живьем, можно посмотреть на самом первом фото.
Подготавливаем Raspberry Pi к работе. Первым делом установим Raspberry Pi OS 32-bit. Установка подробно описана на официальном сайте [13]. После успешной установки и загрузки OS включаем поддержку протокола 1-Wire и активируем Raspberry Pi Camera Board. Делаем это через утилиту raspi-config запуском в терминале команды:
sudo raspi-config
В открывшемся окне настроек переходим в раздел Interfacing options.

Активируем P1 Camera и 1-Wire.

После сохранения настроек, скорее всего, понадобится перезагрузить Raspberry Pi.
Далее устанавливаем инструмент потокового программирования Node-RED. На Хабре много статей о самом продукте и его установке. В моем случае никаких особенностей нет, выполняем команду:
$ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
После успешного выполнения скрипта, устанавливаем модули:
node-red-contrib-boolean-logic$ npm install node-red-contrib-boolean-logic
node-red-contrib-camerapi$ npm install node-red-contrib-camerapi
node-red-contrib-cpu$ npm install node-red-contrib-cpu
node-red-contrib-easybotics-ina219-sensor$ npm install node-red-contrib-easybotics-ina219-sensor
node-red-contrib-edge-trigger$ npm install node-red-contrib-edge-trigger
node-red-contrib-sensor-ds18b20$ npm install node-red-contrib-sensor-ds18b20
node-red-contrib-telegrambot$ npm install node-red-contrib-telegrambot
node-red-node-pi-gpio$ npm install node-red-node-pi-gpio
Включаем автозапуск и запускаем Node-RED.
$ sudo systemctl enable nodered.service
$ sudo systemctl start nodered.service
Как только Node-RED запустится, подключаемся через браузер по адресу: http://<ip raspberry>:1880.

Проект представляет собой json-файл. Так как для отправки уведомлений используется модуль Telegram, в настройках которого указывается id Telegram-канала, в который отправляются оповещения, то понадобиться зарегистрировать Telegram-бота и создать чат. Для регистрации бота воспользуйтесь служебным ботом @BotFather. Узнать id-номер чата поможет бот @getmyid_bot, добавленный в созданный Telegram-чат. Чат позволит добавить пользователей, которые будут получать сообщения от системы. Номер созданного чата необходимо подставить в json-файле вместо текста CHANGE_CHAT_ID.
[
{
"id": "35700ef4.b4cc62",
"type": "tab",
"label": "Dacha",
"disabled": false,
"info": ""
},
{
"id": "21ee0549ab80c1b3",
"type": "group",
"z": "35700ef4.b4cc62",
"name": "Temperature external",
"style": {
"fill": "#c8e7a7",
"label": true,
"stroke": "#000000",
"color": "#000000"
},
"nodes": [
"1b33f0bb.ddc0af",
"8cb14a63.63e588",
"bf42d115.5f8dc",
"f826e2b9.18e4c",
"37299a0b.442dc6",
"589f8077.70b05",
"7744d782.93a8f8",
"cd44f017.139e6",
"d9e7f0b2.9c11a"
],
"x": 34,
"y": 479,
"w": 1172,
"h": 302
},
{
"id": "3a19bea8.e27032",
"type": "group",
"z": "35700ef4.b4cc62",
"name": "Camera external motion alarm",
"style": {
"stroke": "#000000",
"fill": "#c8e7a7",
"label": true,
"color": "#000000"
},
"nodes": [
"5d05a379.500dcc",
"636c36b0.9db488",
"391ad295.c56a6e",
"14425883.8a8027",
"c14e914c.160e1",
"57894684.68d3c8",
"f7c59ddc.2f24c",
"c411c632.3dd2f8",
"48255a26.189904",
"78cfa48.1eae25c",
"ce092f2e.fb6ff",
"3b1e904c.649e9",
"48d31990.641c08",
"1d8f6830.1c7338",
"7f12a359.8f4dfc",
"6299cce8.7b20a4",
"7f956fcc.36206",
"e9b9f2a1a8421187",
"548c306e6e2c111f"
],
"x": 34,
"y": 19,
"w": 1192,
"h": 422
},
{
"id": "6e2ba88d.60c498",
"type": "group",
"z": "35700ef4.b4cc62",
"name": "CPU Load and temperature",
"style": {
"stroke": "#000000",
"fill": "#c8e7a7",
"label": true,
"color": "#000000"
},
"nodes": [
"b8c052bb.44535",
"d6fd679.6caa398",
"dab61cbf.cfa87",
"fdb3a1b6.cfb71",
"a0e62b1a.757c48"
],
"x": 34,
"y": 819,
"w": 452,
"h": 282
},
{
"id": "72f5dbe4bc2de2b5",
"type": "group",
"z": "35700ef4.b4cc62",
"name": "Capture photo",
"style": {
"fill": "#c8e7a7",
"label": true,
"stroke": "#000000",
"color": "#000000"
},
"nodes": [
"9aca7759.d95a48",
"9ca8d5ff.3857d8",
"24ee77ab.2e8738",
"ea0b75b0.94f058",
"c452f057.ca63a"
],
"x": 44,
"y": 1159,
"w": 1182,
"h": 142
},
{
"id": "8c22d8c9580d2858",
"type": "group",
"z": "35700ef4.b4cc62",
"name": "",
"style": {
"label": true,
"fill": "#c8e7a7"
},
"nodes": [
"cc5bc2c3a2c509fa",
"e291c889c1d4e186",
"0d8935ca87af5a1b",
"a067feaf7d06fabf",
"2686f0fe374cc52d",
"d6992fd1eb26d528",
"589c720b5a2be2f2",
"4ecdc496c0ecd286",
"b9d28942538208e4",
"bd842c6db6853e79",
"26d9630fb660bc9d",
"0d4f374253a9f8d4",
"65fe44dc0bb4ca1d"
],
"x": 514,
"y": 819,
"w": 692,
"h": 282
},
{
"id": "9aca7759.d95a48",
"type": "camerapi-takephoto",
"z": "35700ef4.b4cc62",
"g": "72f5dbe4bc2de2b5",
"filemode": "1",
"filename": "photo1.JPEG",
"filedefpath": "0",
"filepath": "/home/pi/Pictures/",
"fileformat": "jpeg",
"resolution": "3",
"rotation": "0",
"fliph": "0",
"flipv": "0",
"brightness": "50",
"contrast": "0",
"sharpness": "0",
"quality": "80",
"imageeffect": "none",
"exposuremode": "auto",
"iso": "0",
"agcwait": "1.0",
"led": "0",
"awb": "auto",
"name": "",
"x": 860,
"y": 1260,
"wires": [
[
"24ee77ab.2e8738",
"ea0b75b0.94f058"
]
]
},
{
"id": "9ca8d5ff.3857d8",
"type": "ui_template",
"z": "35700ef4.b4cc62",
"g": "72f5dbe4bc2de2b5",
"group": "fddd6117.b012f",
"name": "",
"order": 1,
"width": "10",
"height": "13",
"format": "<script>nvar value = "1";n// or overwrite value in your callback function ...nthis.scope.action = function() { return value; }nnfunction updateF() {n var source = '/photo1.JPEG',n timestamp = (new Date()).getTime(),n newUrl = source + '?_=' + timestamp;n document.getElementById("photo").src = newUrl;n}n</script>nn<md-button ng-click="send({payload:action()})" onclick="setTimeout(updateF, 2500);" style="padding:40px; margin-bottom: 40px; color: #000000; background-color: #888981;" >n <ui-icon icon="camera"></ui-icon>n Take a photo<br>n</md-button>nn<div style="margin-bottom:40px;">n <img src="/photo1.JPEG" id="photo" width="100%" height="100%">n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 130,
"y": 1260,
"wires": [
[
"9aca7759.d95a48"
]
]
},
{
"id": "24ee77ab.2e8738",
"type": "debug",
"z": "35700ef4.b4cc62",
"g": "72f5dbe4bc2de2b5",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1110,
"y": 1260,
"wires": []
},
{
"id": "5d05a379.500dcc",
"type": "inject",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "Test sms",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 760,
"y": 340,
"wires": [
[
"e9b9f2a1a8421187"
]
]
},
{
"id": "636c36b0.9db488",
"type": "function",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Motion detected! [myDacha]"nreturn msg;n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 980,
"y": 320,
"wires": [
[
"f5b3043a.2a3ff8"
]
]
},
{
"id": "391ad295.c56a6e",
"type": "rpi-gpio in",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "camera grpio 7",
"pin": "13",
"intype": "down",
"debounce": "25",
"read": true,
"x": 140,
"y": 220,
"wires": [
[
"f7c59ddc.2f24c",
"78cfa48.1eae25c",
"48d31990.641c08",
"548c306e6e2c111f"
]
]
},
{
"id": "14425883.8a8027",
"type": "rising-edge",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"threshold": "0",
"x": 770,
"y": 280,
"wires": [
[
"636c36b0.9db488"
]
]
},
{
"id": "c14e914c.160e1",
"type": "ui_button",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"group": "371d08d2.682078",
"order": 3,
"width": "6",
"height": "1",
"passthru": false,
"label": "Test msg",
"tooltip": "",
"color": "#000000",
"bgcolor": "#888981",
"icon": "",
"payload": "1",
"payloadType": "str",
"topic": "topic",
"topicType": "msg",
"x": 760,
"y": 400,
"wires": [
[
"e9b9f2a1a8421187"
]
]
},
{
"id": "57894684.68d3c8",
"type": "ui_switch",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"label": "Лампочка",
"tooltip": "",
"group": "371d08d2.682078",
"order": 1,
"width": "6",
"height": "1",
"passthru": true,
"decouple": "false",
"topic": "topic",
"topicType": "msg",
"style": "",
"onvalue": "true",
"onvalueType": "bool",
"onicon": "",
"oncolor": "",
"offvalue": "false",
"offvalueType": "bool",
"officon": "",
"offcolor": "",
"animate": false,
"x": 1100,
"y": 60,
"wires": [
[
"316ad35.6698d2c"
]
]
},
{
"id": "f7c59ddc.2f24c",
"type": "ui_text",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"group": "189e2e48.d109d2",
"order": 1,
"width": 0,
"height": 0,
"name": "",
"label": "Движение",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 310,
"y": 300,
"wires": []
},
{
"id": "c411c632.3dd2f8",
"type": "ui_text",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"group": "189e2e48.d109d2",
"order": 3,
"width": 0,
"height": 0,
"name": "",
"label": "Охрана",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 300,
"y": 60,
"wires": []
},
{
"id": "48255a26.189904",
"type": "rpi-gpio in",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "Security Button",
"pin": "15",
"intype": "up",
"debounce": "250",
"read": true,
"x": 140,
"y": 120,
"wires": [
[
"c411c632.3dd2f8",
"78cfa48.1eae25c",
"3b1e904c.649e9"
]
]
},
{
"id": "78cfa48.1eae25c",
"type": "BooleanLogic",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"operation": "AND",
"inputCount": "2",
"topic": "result",
"x": 500,
"y": 220,
"wires": [
[
"14425883.8a8027",
"6299cce8.7b20a4"
]
]
},
{
"id": "ce092f2e.fb6ff",
"type": "inject",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "Blink 1",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "0.5",
"crontab": "",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "true",
"payloadType": "bool",
"x": 480,
"y": 60,
"wires": [
[
"3b1e904c.649e9"
]
]
},
{
"id": "3b1e904c.649e9",
"type": "BooleanLogic",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"operation": "AND",
"inputCount": "3",
"topic": "result",
"x": 720,
"y": 120,
"wires": [
[
"7f12a359.8f4dfc"
]
]
},
{
"id": "1b33f0bb.ddc0af",
"type": "inject",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "1",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 170,
"y": 640,
"wires": [
[
"8cb14a63.63e588"
]
]
},
{
"id": "8cb14a63.63e588",
"type": "sensor-ds18b20",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"topic": "",
"sensorid": "28-011632edb7ee",
"timer": "1",
"repeat": false,
"x": 390,
"y": 640,
"wires": [
[
"bf42d115.5f8dc",
"cd44f017.139e6",
"d9e7f0b2.9c11a"
]
]
},
{
"id": "bf42d115.5f8dc",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "Above 40",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "gte",
"v": "40",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 600,
"y": 520,
"wires": [
[
"37299a0b.442dc6"
]
]
},
{
"id": "f826e2b9.18e4c",
"type": "function",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Temperature HI! [myDacha]"nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1120,
"y": 520,
"wires": [
[
"f5b3043a.2a3ff8"
]
]
},
{
"id": "37299a0b.442dc6",
"type": "trigger",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"op1": "1",
"op2": "0",
"op1type": "str",
"op2type": "str",
"duration": "5",
"extend": true,
"overrideDelay": false,
"units": "s",
"reset": "",
"bytopic": "all",
"topic": "topic",
"outputs": 1,
"x": 770,
"y": 520,
"wires": [
[
"7744d782.93a8f8"
]
]
},
{
"id": "589f8077.70b05",
"type": "debug",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 670,
"y": 600,
"wires": []
},
{
"id": "7744d782.93a8f8",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 950,
"y": 520,
"wires": [
[
"f826e2b9.18e4c"
]
]
},
{
"id": "cd44f017.139e6",
"type": "ui_gauge",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"group": "189e2e48.d109d2",
"order": 5,
"width": "6",
"height": "6",
"gtype": "gage",
"title": "Temperature ext",
"label": "units",
"format": "{{value}}",
"min": "-40",
"max": "60",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 860,
"y": 680,
"wires": []
},
{
"id": "d9e7f0b2.9c11a",
"type": "ui_chart",
"z": "35700ef4.b4cc62",
"g": "21ee0549ab80c1b3",
"name": "",
"group": "172f5cc2.238383",
"order": 1,
"width": "18",
"height": "4",
"label": "Temperature ext",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "-40",
"ymax": "60",
"removeOlder": "1",
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#3f16d4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"x": 860,
"y": 740,
"wires": [
[]
]
},
{
"id": "ea0b75b0.94f058",
"type": "function",
"z": "35700ef4.b4cc62",
"g": "72f5dbe4bc2de2b5",
"name": "",
"func": "var payload = { nchatId: CHANGE_CHAT_ID,n type: "photo",n content: "/home/pi/Pictures/photo1.JPEG" };nreturn {payload};",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1100,
"y": 1200,
"wires": [
[
"f5b3043a.2a3ff8"
]
]
},
{
"id": "c452f057.ca63a",
"type": "inject",
"z": "35700ef4.b4cc62",
"g": "72f5dbe4bc2de2b5",
"name": "Test web cam img",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "1",
"payloadType": "num",
"x": 850,
"y": 1200,
"wires": [
[
"ea0b75b0.94f058"
]
]
},
{
"id": "b8c052bb.44535",
"type": "cpu",
"z": "35700ef4.b4cc62",
"g": "6e2ba88d.60c498",
"name": "",
"msgCore": true,
"msgOverall": false,
"msgArray": false,
"msgTemp": false,
"x": 170,
"y": 860,
"wires": [
[
"d6fd679.6caa398"
]
]
},
{
"id": "d6fd679.6caa398",
"type": "ui_chart",
"z": "35700ef4.b4cc62",
"g": "6e2ba88d.60c498",
"name": "",
"group": "172f5cc2.238383",
"order": 3,
"width": "18",
"height": "4",
"label": "CPU usage",
"chartType": "line",
"legend": "true",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "100",
"removeOlder": "1",
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#b33c1e",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"x": 390,
"y": 880,
"wires": [
[]
]
},
{
"id": "dab61cbf.cfa87",
"type": "inject",
"z": "35700ef4.b4cc62",
"g": "6e2ba88d.60c498",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 170,
"y": 960,
"wires": [
[
"b8c052bb.44535",
"fdb3a1b6.cfb71"
]
]
},
{
"id": "fdb3a1b6.cfb71",
"type": "cpu",
"z": "35700ef4.b4cc62",
"g": "6e2ba88d.60c498",
"name": "CPU Temp",
"msgCore": false,
"msgOverall": false,
"msgArray": false,
"msgTemp": true,
"x": 170,
"y": 1060,
"wires": [
[
"a0e62b1a.757c48"
]
]
},
{
"id": "a0e62b1a.757c48",
"type": "ui_chart",
"z": "35700ef4.b4cc62",
"g": "6e2ba88d.60c498",
"name": "",
"group": "172f5cc2.238383",
"order": 5,
"width": "18",
"height": "4",
"label": "CPU Temp",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": "1",
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#30b31e",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"x": 370,
"y": 1020,
"wires": [
[]
]
},
{
"id": "f5b3043a.2a3ff8",
"type": "telegram sender",
"z": "35700ef4.b4cc62",
"name": "",
"bot": "d9de0dcc.3d2a6",
"haserroroutput": false,
"outputs": 1,
"x": 1430,
"y": 320,
"wires": [
[]
]
},
{
"id": "316ad35.6698d2c",
"type": "rpi-gpio out",
"z": "35700ef4.b4cc62",
"name": "LED",
"pin": "11",
"set": true,
"level": "0",
"freq": "",
"out": "out",
"x": 1330,
"y": 140,
"wires": []
},
{
"id": "48d31990.641c08",
"type": "Invert",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "Invert",
"x": 490,
"y": 160,
"wires": [
[
"3b1e904c.649e9"
]
]
},
{
"id": "1d8f6830.1c7338",
"type": "trigger",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"op1": "",
"op2": "false",
"op1type": "pay",
"op2type": "str",
"duration": "250",
"extend": false,
"overrideDelay": false,
"units": "ms",
"reset": "",
"bytopic": "all",
"topic": "topic",
"outputs": 1,
"x": 1120,
"y": 120,
"wires": [
[
"316ad35.6698d2c"
]
]
},
{
"id": "7f12a359.8f4dfc",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 890,
"y": 120,
"wires": [
[
"1d8f6830.1c7338"
]
]
},
{
"id": "6299cce8.7b20a4",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 770,
"y": 200,
"wires": [
[
"7f956fcc.36206"
]
]
},
{
"id": "7f956fcc.36206",
"type": "trigger",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"op1": "",
"op2": "true",
"op1type": "pay",
"op2type": "str",
"duration": "100",
"extend": false,
"overrideDelay": false,
"units": "ms",
"reset": "",
"bytopic": "all",
"topic": "topic",
"outputs": 1,
"x": 1020,
"y": 200,
"wires": [
[
"316ad35.6698d2c"
]
]
},
{
"id": "cc5bc2c3a2c509fa",
"type": "ina-sensor",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"handle": "93c1182fe25c4f4a",
"x": 670,
"y": 940,
"wires": [
[
"e291c889c1d4e186",
"0d4f374253a9f8d4"
],
[
"0d8935ca87af5a1b",
"26d9630fb660bc9d"
]
]
},
{
"id": "65fe44dc0bb4ca1d",
"type": "inject",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "1",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payloadType": "date",
"x": 650,
"y": 880,
"wires": [
[
"cc5bc2c3a2c509fa"
]
]
},
{
"id": "e291c889c1d4e186",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "Less 3.5v",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "lt",
"v": "3.5",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 900,
"y": 900,
"wires": [
[
"d6992fd1eb26d528"
]
]
},
{
"id": "0d8935ca87af5a1b",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "Less 30%",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "lt",
"v": "30",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 900,
"y": 1060,
"wires": [
[
"589c720b5a2be2f2"
]
]
},
{
"id": "a067feaf7d06fabf",
"type": "function",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Voltage low 3.5v! [myDacha]"nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1120,
"y": 920,
"wires": [
[
"f5b3043a.2a3ff8"
]
]
},
{
"id": "2686f0fe374cc52d",
"type": "function",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Battery low 30%! [myDacha]"nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1120,
"y": 1000,
"wires": [
[
"f5b3043a.2a3ff8"
]
]
},
{
"id": "d6992fd1eb26d528",
"type": "trigger",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"op1": "1",
"op2": "0",
"op1type": "str",
"op2type": "str",
"duration": "5",
"extend": true,
"overrideDelay": false,
"units": "s",
"reset": "",
"bytopic": "all",
"topic": "topic",
"outputs": 1,
"x": 910,
"y": 960,
"wires": [
[
"4ecdc496c0ecd286"
]
]
},
{
"id": "589c720b5a2be2f2",
"type": "trigger",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"op1": "1",
"op2": "0",
"op1type": "str",
"op2type": "str",
"duration": "5",
"extend": true,
"overrideDelay": false,
"units": "s",
"reset": "",
"bytopic": "all",
"topic": "topic",
"outputs": 1,
"x": 910,
"y": 1000,
"wires": [
[
"b9d28942538208e4"
]
]
},
{
"id": "4ecdc496c0ecd286",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 1090,
"y": 860,
"wires": [
[
"a067feaf7d06fabf"
]
]
},
{
"id": "b9d28942538208e4",
"type": "switch",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 1110,
"y": 1060,
"wires": [
[
"2686f0fe374cc52d"
]
]
},
{
"id": "bd842c6db6853e79",
"type": "debug",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 870,
"y": 860,
"wires": []
},
{
"id": "26d9630fb660bc9d",
"type": "ui_gauge",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"group": "189e2e48.d109d2",
"order": 3,
"width": "3",
"height": "2",
"gtype": "gage",
"title": "Battery",
"label": "units",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#ff0000",
"#e6e600",
"#008000"
],
"seg1": "30",
"seg2": "70",
"x": 720,
"y": 1060,
"wires": []
},
{
"id": "0d4f374253a9f8d4",
"type": "ui_gauge",
"z": "35700ef4.b4cc62",
"g": "8c22d8c9580d2858",
"name": "",
"group": "189e2e48.d109d2",
"order": 4,
"width": "3",
"height": "2",
"gtype": "gage",
"title": "Voltage",
"label": "units",
"format": "{{value}}",
"min": 0,
"max": "5",
"colors": [
"#ff0000",
"#e6e600",
"#008000"
],
"seg1": "",
"seg2": "",
"x": 620,
"y": 1000,
"wires": []
},
{
"id": "e9b9f2a1a8421187",
"type": "function",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Test sms! [myDacha]"nreturn msg;n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 990,
"y": 400,
"wires": [
[
"f5b3043a.2a3ff8"
]
]
},
{
"id": "548c306e6e2c111f",
"type": "debug",
"z": "35700ef4.b4cc62",
"g": "3a19bea8.e27032",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 340,
"y": 400,
"wires": []
},
{
"id": "fddd6117.b012f",
"type": "ui_group",
"name": "Camera",
"tab": "39cad93e.df1b16",
"order": 3,
"disp": true,
"width": "10",
"collapse": false
},
{
"id": "371d08d2.682078",
"type": "ui_group",
"name": "Проверки",
"tab": "39cad93e.df1b16",
"order": 4,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "189e2e48.d109d2",
"type": "ui_group",
"name": "Состояние",
"tab": "39cad93e.df1b16",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "172f5cc2.238383",
"type": "ui_group",
"name": "Показатели",
"tab": "39cad93e.df1b16",
"order": 2,
"disp": true,
"width": "18",
"collapse": false
},
{
"id": "d9de0dcc.3d2a6",
"type": "telegram bot",
"botname": "house_bot",
"usernames": "",
"chatids": "",
"baseapiurl": "",
"updatemode": "polling",
"pollinterval": "300",
"usesocks": false,
"sockshost": "",
"socksport": "6667",
"socksusername": "anonymous",
"sockspassword": "",
"bothost": "",
"botpath": "",
"localbotport": "8443",
"publicbotport": "8443",
"privatekey": "",
"certificate": "",
"useselfsignedcertificate": false,
"sslterminated": false,
"verboselogging": false
},
{
"id": "93c1182fe25c4f4a",
"type": "ina-sensor-manager",
"address": "0x36",
"delay": "1000",
"ohms": "0.01",
"customResistor": false
},
{
"id": "39cad93e.df1b16",
"type": "ui_tab",
"name": "Dacha",
"icon": "dashboard",
"order": 2,
"disabled": false,
"hidden": false
}
]
В правом верхнем углу вызываем меню и выбираем «Импорт».

Далее можно вставить содержимое json-файла из буфера обмена или указать файл для импорта.

После успешного импорта json-файла в основном окне отобразятся все цепочки потока:
Проект разбит на пять блоков (сверху вниз, слева направо):
постановка на охрану и обработка сигналов с IP-камеры;
логика работы с датчиком температуры;
отображение системных метрик Raspberry Pi;
обработка данных с UPS Lite;
управление Raspberry Pi Camera.
Для удобства просмотра сделал все отдельными блоками. В случае, если функционал не используется, например, отсутствует UPS Lite, блок можно легко удалить.
После загрузки потоки существуют только в редакторе, осталось загрузить их на сервер нажатием кнопки «Развернуть» в правом верхнем углу Node-RED.
На этом этапе система уже может выполнять охранные функции, оповещать в Telegram и предоставлять локальное онлайн-наблюдение.
Далее будет описаны настройки, если требуется хранение видео на удаленном носителе или управление системой через интернет.
Для себя решил, что хранить видео с IP-камеры буду удаленно на своем NAS. Это позволит собирать записи в высоком качестве за долгий период, а также обеспечит быстрый просмотр нужного фрагмента видео (все-таки домашний интернет значительно быстрее дачного).
Так как захват видео с IP-камеры инициализируется самим NAS, а LTE-модем не имеет белого IP-адреса, нам потребуется VPN. В качестве VPN-сервера я выбрал Wireguard. Его преимуществом является быстрая работа (что важно для мобильного интернета) и простая настройка. Мой роутер от Mikrotik умеет работать с Wireguard.
Настройка роутера сводится к добавлению интерфейса Wireguard, генерации приватного и публичного ключей сервера, плюс добавлении клиентов и маршрутов. Настройка Wireguard на роутерах Mikrotik вариант Site to Site подробно описана на официальном сайте [14]. Вариант настройки через графический интерфейс можно найти на Хабре, например тут [15]. Мой вариант настройки, где локальная сеть имеет адрес 192.168.0.255, VPN адрес 10.10.10.1, а локальная сеть на даче - 192.168.1.255 и VPN адрес 10.10.10.2 имеет такие настройки:
/interface wireguard
add comment="WG Server" listen-port=51820 mtu=1420 name=wg_server
/interface wireguard peers
add allowed-address=10.10.10.2/32,192.168.1.0/24 comment="WG Client" interface=wg_server persistent-keepalive=25s public-key=
"публичный ключ из файла wg-public.key"
/ip route
add disabled=no distance=2 dst-address=192.168.1.0/24 gateway=10.10.10.2 pref-src="" scope=30 target-scope=10
Для записи и хранения видео с IP-камеры NAS имеет программный модуль Surveillance Station. Это приложение, которое позволяет управлять IP-камерами из перечня поддерживаемых. Если вашей нет в списке, это не проблема, все IP-камеры имеют протокол ONVIF, который также поддерживает приложение.
Устанавливаем Wireguard:
$ sudo apt install wireguard
Генерируем приватный и публичный ключи клиента:
$ (umask 077 && wg genkey > wg-private.key)
$ wg pubkey < wg-private.key > wg-public.key
Создаем файл /etc/wireguard/wg0.conf с содержимым:
[Interface]
Address = 10.10.10.2/24
PrivateKey = Приватный ключ из файла wg-private.key
PostUp = /etc/wireguard/scripts/add-nat-routing.sh
PostDown = /etc/wireguard/scripts/delete-nat-routing.sh
[Peer]
PublicKey =
AllowedIPs = 10.10.10.0/24, 192.168.1.0/24б 192.168.0.0/24
Endpoint = белый IP роутера:51820
Содержимое файла wg-public.key надо вставить в настройки клиента в процессе настройки Wireguard на роутере.
В параметры PostUp и PostDown указал путь к скриптам, которые проставляют маршруты и инициализируют LTE-модем при запуске VPN соединения, а при отключении VPN - удаляют маршруты.
Скрипт add-nat-routing.sh
#!/bin/bash
IPT="/sbin/iptables"
IN_FACE="br0" # NIC connected to the internet
WG_FACE="wg0" # WG NIC
SUB_NET="10.10.10.0/24" # WG IPv4 sub/net aka CIDR
WAN_FACE="wan"
curl -H "Referer: http://192.168.99.1/index.html" "http://192.168.99.1/goform/goform_set_cmd_process?goformId=SET_CONNECTION_MODE&ConnectionMode=auto_dial"
## IPv4 ##
$IPT -t nat -A POSTROUTING -o $WG_FACE -j MASQUERADE
$IPT -t nat -A POSTROUTING -o $WAN_FACE -j MASQUERADE
$IPT -t nat -A POSTROUTING -s $SUB_NET -o $IN_FACE -j MASQUERADE
Скрипт delete-nat-routing.sh
#!/bin/bash
IPT="/sbin/iptables"
## IPv4 ##
$IPT -t nat -F
Настройки и названия интерфейсов будут описаны в разделе о конфигурировании точки доступа. Команда curl в скрипте add-nat-routing.sh выполняет инициализацию LTE-модема, команду взял из документации к моему LTE-модему.
Так как у Raspberry Pi Zero W есть wi-fi модуль, то я решил этим воспользоваться и сделать полноценную точку доступа для раздачи интернета на даче. Процесс создания точки доступа довольно сложный, руководствовался несколькими статьями тут [16] и тут [17]. Первым делом устанавливаем пакеты.
Для настройки точки доступа:
sudo apt-get install hostapd
Для создания моста между wi-fi и ethernet интерфейсами:
sudo apt-get install bridge-utils
Описал интерфейсы.
Интерфейс для локальной сети lan описал в файле /etc/network/interfaces.d/lan:
iface lan inet manual
Интерфейс для беспроводной сети wifi описал в файле /etc/network/interfaces.d/wifi:
iface wifi inet manual
Интерфейс LTE-модема wan описал в файле /etc/network/interfaces.d/wan:
iface wan inet static
address 192.168.99.100
netmask 255.255.255.0
gateway 192.168.99.1
Создаем мост и включаем в него интерфейсы lan и wifi:
sudo brctl addbr br0
sudo brctl addif br0 lan
sudo brctl addif br0 wifi
Мост br0 описал в файле /etc/network/interfaces.d/br0:
auto br0
iface br0 inet static
bridge_ports wifi lan
address 192.168.1.1
broadcast 192.168.1.255
netmask 255.255.255.0
Чтобы мост заработал, исключаем из DHCP интерфейс wan, добавив в конец файла /etc/dhcpcd.conf:
denyinterfaces wan
Добавляем диапазон для раздачи адресов в файл /etc/dhcp/dhcpd.conf:
subnet 192.168.1.0 netmask 255.255.255.0 {
range 192.168.1.100 192.168.1.254;
option routers 192.168.1.1;
option subnet-mask 255.255.255.0;
}
Осталось настроить саму точку доступа. Для этого создадим файл /etc/hostapd/hostapd.conf со следующими параметрами:
## Wireless network interface ###
interface=wifi
### Driver ###
driver=nl80211
### Network name SSID ###
ssid=НАЗВАНИЕ_WIFI_СЕТИ
### Set frequency to 2.4 Ghz ###
hw_mode=g
### Channel number ###
channel=4
### Enable Wi-Fi N ###
ieee80211n=1
### Enable WMM ###
wmm_enabled=1
### Enable 40 Mhz channels ###
ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]
### Allow all MAC Address ###
macaddr_acl=0
### Use WPA Auth ###
auth_algs=1
### Require clients to know the network name ###
ignore_broadcast_ssid=0
### Use WPA2 ###
wpa=2
### Enable Pre-Shared Key ###
wpa_key_mgmt=WPA-PSK
### Network key ###
wpa_passphrase=ПАРОЛЬ_СЕТИ_WIFI
### Use AES ###
rsn_pairwise=CCMP
bridge=br0
Обратите внимание, что там где указано НАЗВАНИЕ_СЕТИ_WIFI и ПАРОЛЬ_СЕТИ_WIFI следует придумать свои.
Указываем расположение конфигурационного файла для точки доступа в файле /etc/default/hostapd:
DAEMON_CONF="/etc/hostapd/hostapd.conf"
Осталось включить переадресацию трафика, раскомментировав в файле /etc/sysctl.conf строку:
net.ipv4.ip_forward=1
Выполняем рестарт Raspberry. После рестарта мы получим полноценный роутер с раздачей IP-адресов как по wi-fi, так и по ethernet, плюс подключенный интернет через LTE-модем и настроенный VPN-клиент.
Проверяем доступ в интернет. Проверяем сетевую связность между домашним роутером и Raspberry по локальным адресам.
В итоге получилось Site-to-Site соединение между домашней и дачной сетями. Это позволяет осуществлять доступ ко всем устройствам и сервисам на даче по внутренним адресам из домашней локальной сети через VPN, и доступ в интернет и в домашнюю сеть из локальной сети на даче.
В таком виде система работает уже более трех лет, сейчас появилась потребность добавить еще одну IP-камеру. Для этого достаточно подключить USB Ethernet-адаптер или добавить еще один Raspberry Pi Ethernet/USB HUB HAT к Raspberry Pi. Входов-выходов у Raspberry достаточно, чтобы добавить датчик дыма, а датчик температуры использовать только для наблюдения. Есть возможность добавить релейный модуль для включения света на участке при срабатывании движения от IP-камеры. Главное, что модернизация не потребует замены распределительного щитка и легко уместится в существующем.
Raspberry Pi Zero был выбран исключительно из-за его размеров. Если размеры распределительного щитка позволяют, то проще будет использовать Raspberry Pi 3 и новее. В нем уже есть Ethernet и USB-порты.
Конечно, можно обойтись и без I/O в IP-камере и использовать камеру с wi-fi, а управление и оповещение осуществлять из приложения через облако вендора. Для себя я исключил этот вариант только потому, что считаю проводное соединение надежнее, а облака в последнее время все чаще становятся платными.
Что еще? Недавно на рынок вышел интересный продукт - Mikrotik KNOT. Это полноценный роутер с wi-fi и POE, но самое главное - у него есть GPIO, что позволит подключить не только вход-выход IP-камеры, но и датчики, в том числе аналоговые.
*Статья написана в рамках ХабраЧелленджа 0.1, который прошел в ЛАНИТ осенью 2023 года
Автор:
Vutshi
Источник [18]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/diy/388456
Ссылки в тексте:
[1] UPS Lite v1.2 Power HAT: https://aliexpress.ru/item/1005001733452340.html?spm=a2g2w.detail.rcmdprod.0.5599b413VvZlEZ&mixer_rcmd_bucket_id=aerabtestalgoRecommendAbV4_testPdpMoreCandSources&ru_algo_pv_id=c18dd8-476f2e-ce797c-0fa459&scenario=pcDetailBottomMoreOtherSeller&sku_id=12000017383301011&traffic_source=recommendation&type_rcmd=core
[2] антивандальная кнопка с подсветкой и фиксацией: https://aliexpress.ru/item/32956631402.html?spm=a2g2w.orderdetail.0.0.5ee74aa6jl1KYd&sku_id=12000020988158385
[3] модуль датчика температуры DS18B20: https://arduino-kit.ru/product/modul-datchika-temperaturyi-ds18b20
[4] POE-адаптер 220v: https://www.ozon.ru/product/poe-inzhektor-esvi-poe-11-standart-poe-ieee-802-3af-s-byudzhetom-26w-521557533/?asb=F8srikrZT9qcynhLBvc5SYEsOv8Cp7ttJA6POdlFe98%253D&asb2=pvVZvGv-TFyCuUhOdf0DgZgV-eedKGvmOV2K9BG1S_1C9UPTulawUgES0l9GTwy4&avtc=1&avte=2&avts=1696443690&keywords=poe+220
[5] node-red-contrib-boolean-logic: https://flows.nodered.org/node/node-red-contrib-boolean-logic
[6] node-red-contrib-camerapi: https://flows.nodered.org/node/node-red-contrib-camerapi
[7] node-red-contrib-cpu: https://flows.nodered.org/node/node-red-contrib-cpu
[8] node-red-contrib-easybotics-ina219-sensor: https://flows.nodered.org/node/node-red-contrib-easybotics-ina219-sensor
[9] node-red-contrib-edge-trigger: https://flows.nodered.org/node/node-red-contrib-edge-trigger
[10] node-red-contrib-sensor-ds18b20: https://flows.nodered.org/node/node-red-contrib-sensor-ds18b20
[11] node-red-contrib-telegrambot: https://flows.nodered.org/node/node-red-contrib-telegrambot
[12] node-red-node-pi-gpio: https://flows.nodered.org/node/node-red-node-pi-gpio
[13] сайте: https://www.raspberrypi.com/documentation/computers/getting-started.html
[14] сайте: https://help.mikrotik.com/docs/display/ROS/WireGuard
[15] тут: https://habr.com/ru/articles/702164/
[16] тут: https://thepi.io/how-to-use-your-raspberry-pi-as-a-wireless-access-point/
[17] тут: https://learn.sparkfun.com/tutorials/setting-up-a-raspberry-pi-3-as-an-access-point/all
[18] Источник: https://habr.com/ru/companies/lanit/articles/774410/?utm_source=habrahabr&utm_medium=rss&utm_campaign=774410
Нажмите здесь для печати.