Pi 3B secure video recorder with LUKS + LAN-only wipe (fork of bluebird-works/rpi5-recorder)
Відео-реєстратор на Raspberry Pi 4 / Pi 5 з будь-якою офіційною CSI-камерою (Camera Module v1/v2/v3, HQ, Global Shutter — код сам визначає сенсор при старті). Два незалежні режими:
Обидва режими пишуть mp4 в ~/recordings/ з ротацією (макс 50 файлів). Довжина одного файлу задається змінною SEGMENT_SEC (сек). Дефолт 0 = не різати, писати одним файлом на всю сесію.
Енкодер обирається автоматично під залізо:
h264_v4l2m2m) — rpicam-vid віддає готовий elementary stream, ffmpeg тільки муксить його в mp4 без перекодування. CPU майже вільний.Обидва шляхи дають однаковий результат: 1080p30 без дропів, сегменти рівно по SEGMENT_SEC. Фокус за замовчуванням — фіксована безкінечність (тільки для Camera Module 3 з моторним фокусом; решта сенсорів фіксовані апаратно).
Веб-сторінка клієнта: https://bluebird-works.github.io/rpi5-recorder/
Для тих, хто хоче побачити запис за 5 хвилин, без розуміння деталей.
ssh user@<ip-pi>
git clone https://github.com/bluebird-works/rpi5-recorder.git
cd rpi5-recorder
sudo bash pi/install_ble.sh
RPi5-CAM → «Pair» → ● Запис.Все. Файли лежать в ~/recordings/ на Pi. Забрати:
scp user@<ip-pi>:~/recordings/*.mp4 ./
Якщо щось не так — див. розділ 8. Діагностика.
| Компонент | Мінімум |
|---|---|
| Raspberry Pi 4 або 5 | 2 GB+, Raspberry Pi OS / Debian 12+ |
| SD-карта | 32 GB+ (краще A2) |
| CSI-камера | Будь-яка офіційна: Camera Module v1 (OV5647), v2 (IMX219), v3 (IMX708), HQ (IMX477), GS (IMX296). Перевірка: rpicam-hello --list-cameras — має вивести сенсор у першому рядку |
| Живлення | офіційний БЖ (Pi 4 — 5 V / 3 A) |
| Мережа | тільки для першого налаштування (SSH). Далі BLE — офлайн. |
| Смартфон/ноутбук | Android Chrome/Edge або desktop Chrome/Edge/Opera. iOS Safari та Firefox — не працюють (немає Web Bluetooth). |
Ставиться автоматично інсталяторами, руками нічого доставляти не треба:
| Пакет | Навіщо | Хто ставить |
|---|---|---|
rpicam-apps |
rpicam-vid — захоплення з CSI-камери + апаратний H.264 |
обидва режими |
ffmpeg |
муксинг elementary stream у mp4, нарізка на сегменти | обидва режими |
bluez |
bluetoothd (GATT-сервер) + btmgmt |
режим A |
python3-bluezero |
Python-обгортка BlueZ через D-Bus. Немає в apt Debian 13 → ставиться через pip3 --break-system-packages |
режим A |
python3-dbus, python3-gi |
залежності bluezero | режим A |
Плюс інсталятор сам:
video (доступ до камери);rfkill з Bluetooth і піднімає hci0 (на свіжому образі адаптер часто лежить DOWN);Перевірити все разом — setup/verify.sh.
Реєстратор автономний — після налаштування живе без інтернету. Код на Pi треба залити один раз (плюс зрідка git pull коли міняється). GitHub є публічним, тож клон анонімно через HTTPS — без токенів і без ключів:
cd ~
git clone https://github.com/bluebird-works/rpi5-recorder.git
cd rpi5-recorder
Оновлення потім:
cd ~/rpi5-recorder && git pull
SSH-ключ до GitHub на Pi не потрібен — з реєстратора нічого туди не пушиться. (SSH між твоїм ноутом і Pi — це інша річ, потрібна щоб взагалі залізти на Pi, див. пункт 6.)
Обери один режим. Обидва одночасно ставити не варто — будуть битися за камеру.
cd ~/rpi5-recorder
sudo bash pi/install_ble.sh
Що робить скрипт:
python3-bluezero, ffmpeg, rpicam-apps, bluezble_recorder.py в ~/rpi5-ble/rpi5-ble-recorder.serviceПеревірка:
sudo systemctl status rpi5-ble-recorder
journalctl -u rpi5-ble-recorder -f
Має бути advertising as 'RPi5-CAM'.
cd ~/rpi5-recorder
sudo bash pi/install_autostart.sh
Що робить скрипт:
ffmpeg, rpicam-appsautostart.sh в ~/rpi5-auto/rpi5-auto-recorder.serviceПеревірка:
sudo systemctl status rpi5-auto-recorder
ls -la ~/recordings/
Файли повинні зʼявлятись одразу після старту сервісу.
RPi5-CAM.RPi5-CAM → «Pair».● Запис / ■ Стоп.● Запис — Pi починає писати. Червона лампочка блимає, статус «запис…».■ Стоп — Pi коректно закриває mp4 (moov-атом на місці, файл програється).Скинути UUID до дефолтних або задати свої — розгорни <details> «Налаштування BLE» на сторінці, вписуй значення, тисни «Зберегти» (лежить в localStorage браузера).
Радіус BLE: 5-10 м без перешкод. Через стіну ≈ 3-5 м.
Reload сторінки = розконект. Chrome не памʼятає пристрій між сесіями (privacy). Треба знову тиснути «Підключити».
Плагін-плей:
rpicam-vid, ffmpeg добиває mp4 по EOF → shutdown. Останній файл не зіпсований.Забрати відео:
scp pi@<pi-ip>:~/recordings/*.mp4 ./
Рекордер побудований так, щоб такі ситуації не втрачали більше 1 секунди відео і продовжували запис після повернення живлення.
Три сценарії, що покриті:
Ти закрила вкладку в браузері / телефон вимкнувся.
Pi продовжує писати. BLE-розʼєднання не зупиняє запис. Відкриваєш сторінку знову, тиснеш «Підключити» — UI сам покаже що йде запис. Тиснеш ■ коли треба.
Pi здохла (kernel panic, SD-карта збоїть).
Після рестарту systemd за 3 секунди підіймає сервіс, той бачить ~/recordings/.recording_state і автоматично продовжує запис новим файлом (rec_YYYYMMDD_HHMMSS.mp4 з поточним часом). Файл, який лишився від крешу, лишається програвабельним до останнього keyframe (втрачається до ~1 секунди хвоста) — завдяки fragmented mp4 (+frag_keyframe+empty_moov+default_base_moof).
Обірвалось живлення.
Те саме, що п.2, плюс атомарний запис state-файла (tmp + fsync + rename) переживає torn write. Наступний бут = продовження запису.
Як воно взагалі знає, що треба продовжити?
При натисканні ● Запис рекордер пише файл-маркер ~/recordings/.recording_state (маленький JSON з номером пресета). При чистому стопі через ■ — цей файл видаляється. Якщо сервіс піднімається і бачить маркер — значить, він упав під час запису, і треба відновити.
Як зупинити «намертво» щоб більше не відновлювало:
Тиснеш ■ Стоп на телефоні — це прибирає state-файл. Або руками:
rm ~/recordings/.recording_state
sudo systemctl restart rpi5-ble-recorder
В Raspberry Pi Imager перед записом натисни ⚙ Advanced options:
id_ed25519.pub з ноута)pi + сильний пароль)Після першого бута — з ноута:
ssh pi@<pi-ip>
Створити порожній файл ssh в /boot/firmware/ (або підключитись з клавою+моніком і sudo raspi-config → Interface Options → SSH → Enable).
Якщо Pi в тій же мережі:
# з ноута
nmap -sn 192.168.1.0/24 | grep -B2 -i raspberry
# або
arp -a | grep -i raspberry
Або тимчасово підключити моніторчик і виконати hostname -I на самій Pi.
На ноуті:
ssh-copy-id pi@<pi-ip>
# або якщо ключа немає:
ssh-keygen -t ed25519 -C "$USER@$(hostname)"
ssh-copy-id -i ~/.ssh/id_ed25519.pub pi@<pi-ip>
Потім вимкнути парольний вхід (на Pi):
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart ssh
Обидва режими читають однакові змінні оточення — задаються при старті запису, код правити не треба.
| Змінна | Дефолт | Що робить |
|---|---|---|
WIDTH / HEIGHT |
1920 / 1080 |
роздільність. Вище 1920 по ширині HW-енкодер не вміє |
FPS |
30 |
кадрів за секунду |
BITRATE |
10000000 |
10 Mbps |
AUTOFOCUS_MODE |
manual |
manual, auto або continuous. Ігнорується для камер без моторного фокусу (v1, v2, HQ, GS). Тільки Camera Module v3 підтримує. |
LENS_POSITION |
0 |
тільки для manual на v3. 0 = безкінечність, default = гіперфокал |
ENCODER |
auto |
auto / hardware / software. auto визначає наявність bcm2835-codec — Pi 4 → апаратний, Pi 5 → софтверний |
SEGMENT_SEC |
0 |
0 = один файл на сесію; >0 = чанки по N сек |
REC_DIR |
~/recordings |
куди писати |
MAX_FILES |
50 |
скільки файлів тримати |
FREE_MB_MIN |
500 |
нижче цього вільного місця — примусова ротація (тільки режим B) |
Змінити на живому сервісі — писати чанки по 5 хвилин:
sudo systemctl edit rpi5-auto-recorder
# додати:
# [Service]
# Environment=SEGMENT_SEC=300
sudo systemctl restart rpi5-auto-recorder
Разово, без systemd:
REC_DIR=/tmp/test SEGMENT_SEC=10 LENS_POSITION=0 bash pi/autostart.sh
AUTOFOCUS_MODE=manual + LENS_POSITION=0 — лінза жорстко на безкінечність, автофокус не смикається під час запису. Це дефолт: для реєстратора «різко все, що далі кількох метрів» кращий за автофокус, який перефокусовується на кожну зміну сцени.
Якщо треба різкість на близькій дистанції — LENS_POSITION задається як обернена відстань (діоптрії): 0.5 ≈ 2 м, 1 ≈ 1 м, 2 ≈ 0.5 м.
Апаратний енкодер Pi 4 обмежений 1920 по ширині, тому для ширших кадрів треба явно ENCODER=software:
WIDTH=2304 HEIGHT=1296 ENCODER=software bash pi/autostart.sh
Заміряно на Pi 4 / 2304x1296: софтверний libx264 тримає 30 fps, але грів чіп до 76°C проти 62°C на апаратному — без активного охолодження на довгому записі впреться в тротлінг. Повний сенсорний режим 4608x2592 видав 7.9 fps замість 14 — непридатний. На Pi 5 запас більший, але окремо не міряний.
Дефолтні placeholder-и — у трьох місцях:
pi/ble_recorder.py → SERVICE_UUID, CHAR_UUID, STATUS_UUIDindex.html → DEFAULTSCLAUDE.mdЗгенерувати нові — uuidgen. Замінити всюди.
Веб-сторінка каже «Web Bluetooth недоступний» — браузер не той. Треба Chrome/Edge/Opera, не Firefox/Safari. На iOS — жодна опція не працює.
Pi не зʼявляється в списку сканера — перевір з іншого телефону через nRF Connect чи Pi взагалі рекламується:
sudo systemctl status rpi5-ble-recorder
sudo hciconfig # має бути UP RUNNING
sudo journalctl -u rpi5-ble-recorder -n 50
Запис не стартує (BLE підʼєднаний, кнопка натиснута, але файлів нема) — типово камера не бачиться:
rpicam-hello --list-cameras # має показати сенсор: imx708 / imx219 / ov5647 / imx477 / imx296
groups | grep -o video # має вивести "video"
Якщо video немає в групах — sudo usermod -aG video $USER && sudo reboot.
Якщо камери нема в списку — перевірити шлейф CSI (контактами до плати, при вимкненому живленні!) і dmesg | grep -iE 'imx|ov5647'.
У логах no CSI camera detected — рекордер запустив rpicam-vid --list-cameras, але не зміг зпарсити відповідь. Найчастіше — камера справді не підключена або шлейф болтається. Якщо rpicam-hello --list-cameras руками таки показує сенсор, а рекордер його не бачить — відкрий issue на GitHub із виводом обох команд.
ERROR: *** no cameras available *** — камеру вже тримає інший процес. Обидва сервіси одночасно не ставити: systemctl status rpi5-ble-recorder rpi5-auto-recorder.
У логах Timestamps are unset in a packet — косметично, ігнорувати. Вилазить один раз на перший пакет raw h264; тривалість і fps у готовому файлі коректні.
Файли є, але не програються (moov-атом відсутній) — станеться якщо процес вбити через SIGKILL замість SIGTERM. Systemd надсилає SIGTERM за замовчуванням, тож проблема тільки при hard-power-off посеред запису. Пом’якшено фрагментованим mp4 (+frag_keyframe+empty_moov) — файл лишається програвабельним і без moov.
Пристрій RPi5-CAM не видно у сканері телефона. Спершу перевір, що реклама реально в ефірі:
sudo btmgmt advinfo | grep -i "instances list" # має бути "1 item", а не "0 items"
Якщо 0 items — не піднявся обхід із pi/ble_advertise.sh, дивись логи journalctl -u rpi5-ble-recorder. У нормі там є рядок Instance added: 1.
Чому взагалі обхід: контролер Pi 4 (CYW43455) не підтримує LE Extended Advertising, а BlueZ 5.82 реєструє рекламу тільки через розширений mgmt-шлях і отримує від ядра Invalid Parameters (0x0d). Через D-Bus рекламу підняти неможливо — bluezero отримує org.bluez.Error.Failed. Тому інстанс створюється напряму legacy-шляхом через btmgmt, а GATT-сервер лишається за bluetoothd. Деталі — в шапці pi/ble_advertise.sh.
У логах сервісу через це завжди є нешкідливий рядок від bluezero про невдалу реєстрацію реклами — це очікувано, GATT при цьому працює.
BLE — це радіо на ~10 метрів. Ні Tailscale, ні SSH тут не допоможуть: щоб підключитись зі смартфона, треба фізично бути поруч із Pi.
Web Bluetooth debugger від Google — лінк прямо на сторінці клієнта внизу.
cd ~/rpi5-recorder
git pull
# режим A:
sudo bash pi/install_ble.sh && sudo systemctl restart rpi5-ble-recorder
# режим B:
sudo bash pi/install_autostart.sh && sudo systemctl restart rpi5-auto-recorder
rpicam-vid, входу з /dev/video0 немає.AUTOFOCUS_MODE / LENS_POSITION для них ігноруються (авто) — так задумано, інакше rpicam-vid падає.