Живий документ. Оновлюється з кожним етапом проєкту.
Проєкт: DIY IP-камера на embedded Linux
🔧 Характеристики плати
| Параметр | Значення |
|---|---|
| Плата | Luckfox Pico Pro |
| Чіп | Rockchip RV1106 |
| CPU | ARM Cortex-A7 @ 1.2GHz |
| Co-CPU | RISC-V MCU (швидкий старт, low-power) |
| RAM | 128MB DDR2 (доступно ~54MB) |
| Storage | 256MB SPI NAND |
| Мережа | 10/100M Ethernet + USB RNDIS |
| OS | Buildroot Linux (kernel 5.10.110) |
| Архітектура | armv7l (не riscv64!) |
🔌 Підключення до комп’ютера
Перше підключення (USB RNDIS)
Плата підключається через USB і прикидається Ethernet адаптером.
Linux бачить її як інтерфейс enx... — назва генерується з MAC адреси.
Крок 1 — знайти інтерфейс:
|
1 2 3 4 5 |
ip addr show | grep "enx" # Шукаємо enx... — це завжди плата # Приклад: enx0a400188dd9f або enx5a11342ec4ad # Всі інші інтерфейси мають стандартні імена: lo, enp0s25, docker0, virbr0 |
Крок 2 — призначити IP на комп’ютері:
|
1 2 3 4 |
sudo ip addr add 172.32.0.100/24 dev enx5a11342ec4ad sudo ip link set enx5a11342ec4ad up # /24 = маска 255.255.255.0 — означає що обидва пристрої в одній підмережі |
Крок 3 — знайти IP плати:
|
1 2 3 4 |
sudo nmap -sn 172.32.0.0/24 -e enx5a11342ec4ad # -sn = тільки ping scan, без сканування портів # Дефолтний IP з документації 172.32.0.70 — не вір, скануй завжди |
Рівень 1 — скрипт автопідключення
Запускати вручну кожного разу — нудно. Скрипт робить все автоматично.
Зберегти як ~/luckfox-connect.sh:
|
1 2 3 4 5 6 |
# Зберегти як ~/luckfox-connect.sh IFACE=$(ip link | grep "enx" | awk '{print $2}' | tr -d ':') sudo ip addr add 172.32.0.100/24 dev $IFACE sudo ip link set $IFACE up sudo nmap -sn 172.32.0.0/24 -e $IFACE | grep "report for" |
|
1 2 3 |
chmod +x ~/luckfox-connect.sh ~/luckfox-connect.sh |
Рівень 2 — udev правило (TODO)
Система сама призначає IP як тільки бачить плату через USB.
Налаштовується через /etc/udev/rules.d/ — додати пізніше.
Рівень 3 — статичний IP на самій платі ✅
Найкраще рішення — плата завжди має однаковий IP незалежно від комп’ютера.
❌ Що не спрацювало — /etc/network/interfaces:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
cat > /etc/network/interfaces << 'EOF' # interface file auto-generated by buildroot auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.1.102 netmask 255.255.255.0 gateway 192.168.1.1 pre-up killall udhcpc 2>/dev/null; true EOF |
pre-up killall udhcpc— логічно мало б вбивати udhcpc перед підняттям інтерфейсу,
але на Buildrootudhcpcстартує незалежно від ifup і просто приходить знову після хука.
В результаті — після ребуту плата отримує.101від роутера замість нашого.102.
✅ Правильне рішення — S99staticip:
Створюємо init.d скрипт з найвищим пріоритетом (S99 = виконується останнім).
Він приходить вже після udhcpc і мовчки переписує IP.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
cat > /etc/init.d/S99staticip << 'EOF' #!/bin/sh case $1 in start) sleep 5 ifconfig eth0 192.168.1.102 netmask 255.255.255.0 route del default 2>/dev/null route add default gw 192.168.1.1 ;; stop) ;; *) exit 1 ;; esac EOF chmod +x /etc/init.d/S99staticip |
ℹ️
sleep 5— чекаємо покиudhcpcзавершить роботу, тільки потім перезаписуємо IP.
Так, це брутально. Але це єдиний надійний спосіб на Buildroot Luckfox.
Перевірити після ребуту:
|
1 2 3 4 5 6 7 8 9 |
ip addr show eth0 # Шукаємо: inet 192.168.1.102/24 ... valid_lft forever ip route show # Має бути: default via 192.168.1.1 dev eth0 ping -c3 192.168.1.1 # роутер ping -c3 8.8.8.8 # інтернет |
⚠️ Важливо: після
rebootякщо кабель не підключений фізично під час старту —
S99staticip все одно виставить IP, алеNO-CARRIERможе заважати маршрутизації.
Якщо щось не так:ifdown eth0 && ifup eth0
Перевірити що udhcpc не заважає:
|
1 2 3 |
ps aux | grep udhcpc # Якщо крутиться після S99staticip — вбити: kill $(pgrep udhcpc) |
ℹ️ Чому плата зникла зі списку DHCP клієнтів роутера?
Зі статичним IP плата більше не робить DHCP запит.
Роутер бачить трафік але не реєструє в таблиці клієнтів — це нормально.
🩺 Діагностика мережі — алгоритм
Щось не працює? Йди по кроках зверху вниз — знайдеш де обривається.
Крок 1 — фізика
|
1 2 3 4 |
ip link show eth0 # Шукаємо: LOWER_UP — кабель підключений і link є # Якщо NO-CARRIER — проблема фізична: кабель, роз'єм, порт роутера |
⚠️ Перший підозрюваний завжди кабель — пошевели роз’єми!
Крок 2 — IP адреса
|
1 2 3 4 |
ip addr show eth0 # Має бути: inet 192.168.1.102/24 ... valid_lft forever # Якщо немає IP — ifdown eth0 && ifup eth0 # Якщо IP інший (наприклад .101) — udhcpc перебив статичний |
Крок 3 — таблиця маршрутів
|
1 2 3 4 5 6 |
ip route show # Має бути: # default via 192.168.1.1 dev eth0 ← вихід в інтернет # 192.168.1.0/24 dev eth0 ← локальна мережа # Якщо немає default — ip route add default via 192.168.1.1 dev eth0 |
Крок 4 — роутер
|
1 2 3 4 |
ping -c3 192.168.1.1 # Якщо не пінгується — проблема між платою і роутером # Перевір: кабель, порт роутера, IP в одній підмережі |
Крок 5 — інтернет
|
1 2 3 |
ping -c3 8.8.8.8 # Якщо роутер пінгується але 8.8.8.8 ні — проблема з gateway або DNS |
Крок 6 — сервіс
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# Чи запущений сервер? ps aux | grep http-server # Чи слухає порт? netstat -tln | grep 8080 # Перевірити локально на платі wget -O- http://127.0.0.1:8080/api/stats # Перевірити з мережі wget -O- http://192.168.1.102:8080/api/stats |
Гарячі команди — швидка діагностика
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Все одразу — стан мережі за 3 секунди ip addr show eth0 && ip route show && ping -c1 192.168.1.1 && ping -c1 8.8.8.8 # Перезапустити мережу ifdown eth0 && ifup eth0 # Вбити udhcpc якщо перебиває статичний IP kill $(pgrep udhcpc) # Перевірити хто слухає порти netstat -tln # Перевірити процеси які їдять ресурси top # Запустити http-server у фоні /root/http-server & # Зупинити http-server kill $(pgrep http-server) |
|
1 2 3 4 5 6 7 8 9 |
# Стандартне підключення (може впасти з "Too many auth failures") ssh root@172.32.0.93 # Правильне підключення — примусово через пароль ssh -o IdentitiesOnly=yes -o PubkeyAuthentication=no root@172.32.0.93 # Пароль за замовчуванням luckfox |
⚠️
Too many authentication failures— ssh-agent перебирає всі ключі.
Фікс:-o PubkeyAuthentication=no
🌐 Мережа на платі
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Перевірити інтерфейси ip addr show # USB RNDIS (підключення до комп'ютера) ip addr show usb0 # IP: 172.32.0.93/16 # Ethernet (роутер) ip addr show eth0 # IP: отримує через DHCP # Отримати IP через DHCP вручну udhcpc -i eth0 # Перевірити інтернет ping -c3 8.8.8.8 |
💻 Корисні команди всередині плати
Система
|
1 2 3 4 5 6 |
uname -a # версія ядра і архітектура cat /proc/cpuinfo | grep model # інфо про процесор free -h # пам'ять df -h # диск uptime # час роботи |
Процеси і модулі
|
1 2 3 4 5 6 7 |
top # процеси в реальному часі ps aux # всі процеси lsmod # завантажені модулі ядра dmesg | tail -30 # системний лог ядра dmesg | grep -i usb # USB події dmesg | grep -i video # відео пристрої |
Відео (V4L2)
|
1 2 3 4 5 |
ls /dev/video* # перевірити чи є камера v4l2-ctl --list-devices # всі відео пристрої v4l2-ctl -d /dev/video0 --all # все про пристрій v4l2-ctl -d /dev/video0 --list-formats-ext # підтримувані формати |
🐍 Python на платі
|
1 2 3 |
python3 --version # Python 3.11.6 which python3 # /usr/bin/python3 |
Запуск HTTP сервера
|
1 2 3 4 5 6 |
# ⚠️ Без -b 0.0.0.0 впаде з помилкою IPv6 python3 -m http.server 8080 -b 0.0.0.0 # Відкрити в браузері: # http://192.168.1.102:8080/назва_файлу.html |
⚠️
OSError: [Errno 97] Address family not supported— це IPv6 проблема.
Фікс: додати-b 0.0.0.0
📁 Копіювання файлів на плату
|
1 2 3 4 5 6 7 8 9 10 |
# З комп'ютера на плату (SCP) scp -o PubkeyAuthentication=no /шлях/до/файлу root@172.32.0.93:/root/ # Приклад scp -o PubkeyAuthentication=no ~/dashboard.html root@172.32.0.93:/root/ # ⚠️ Кирилиця в шляху ламає SCP — копіюй файл в /tmp спочатку cp ~/Завантаження/файл.html /tmp/файл.html scp -o PubkeyAuthentication=no /tmp/файл.html root@172.32.0.93:/root/ |
🧩 Модулі ядра (lsmod)
| Модуль | Призначення |
|---|---|
video_rkisp |
Rockchip ISP — обробка зображення |
video_rkcif |
Rockchip Camera Input Framework |
mpp_vcodec |
Hardware відео енкодер/декодер |
rknpu |
Neural Processing Unit (0.5 TOPS) |
rga3 |
2D графічний прискорювач |
rockit |
Rockchip media framework |
os04a10 |
Драйвер камери OS04A10 (CSI) |
ℹ️ UVC (USB камери) — не вбудований в цю прошивку.
Рідна камера: MIS5001 5MP або SC3336 3MP
📷 Камера
| Параметр | Значення |
|---|---|
| Модель | MIS5001 5MP Camera (A) |
| Інтерфейс | MIPI CSI |
| Сумісність | Luckfox Pico Pro/Max/Ultra (RV1106) |
| Статус | ✅ Підключена, працює |
Перевірити що камера розпізнана:
|
1 2 3 4 |
ls /userdata/rkipc.ini # є файл → камера ініціалізована ls /etc/iqfiles/ | grep mis5001 # є IQ файл → сенсор підтримується dmesg | grep -i "mis5001\|csi\|isp" | tail -10 # детальніше |
RTSP стрім
|
1 2 3 |
rtsp://172.32.0.93/live/0 ← через USB rtsp://192.168.1.102/live/0 ← через Ethernet |
|
1 2 3 4 5 6 7 8 9 |
# Перегляд (працює) ffplay rtsp://192.168.1.102/live/0 # Якщо затримка відео велика (10+ секунд) — мінімальний буфер: ffplay -fflags nobuffer -flags low_delay -framedrop rtsp://192.168.1.102/live/0 # ⚠️ VLC 3.0.16 — не працює з цим RTSP сервером, використовуй ffplay # sudo apt install ffmpeg |
ℹ️ Нормальна затримка RTSP/H.264 — 1-3 секунди. Якщо 10+ сек — ffplay буферизує занадто агресивно, допомагає
-fflags nobuffer -flags low_delay -framedrop.
🔍 Діагностика камери / RTSP — алгоритм
“Все працювало, перезавантажив — і стрім зник”. Йди по кроках:
Крок 1 — rkipc запущений?
|
1 2 3 4 |
ps aux | grep rkipc # Має бути: rkipc -a /oem/usr/share/iqfiles # Якщо нема — запустити вручну (крок 4) |
Крок 2 — порт 554 слухається?
|
1 2 3 |
netstat -tln 2>/dev/null | grep 554 # Має бути: tcp 0.0.0.0:554 ... LISTEN # Якщо нема — rkipc ще не ініціалізувався, зачекай 15 сек і перевір знову |
Крок 3 — порт доступний ззовні?
|
1 2 3 4 5 |
# З комп'ютера telnet 192.168.1.102 554 # Має бути: Connected to 192.168.1.102 # Якщо нема — перевір мережу/firewall |
Крок 4 — запустити rkipc вручну і дивитись лог
|
1 2 3 4 |
killall rkipc 2>/dev/null sleep 1 rkipc -a /oem/usr/share/iqfiles 2>&1 & sleep 15 netstat -tln 2>/dev/null | grep 554 |
Нормальний вивід містить:
|
1 2 3 4 |
m00_b_mis5001 4-0031 ← сенсор знайдений rk_aiq_uapi_sysctl_start success ← ISP запущений rkipc_server_thread ← RTSP сервер стартує |
Крок 5 — перевірити стрім
|
1 2 3 |
# З комп'ютера ffplay rtsp://192.168.1.102/live/0 |
Відомі проблеми з RTSP
| Проблема | Причина | Фікс |
|---|---|---|
| VLC: “Failed to setup RTSP session” | VLC 3.0.16 несумісний з rkipc RTSP | Використовувати ffplay |
| Порт 554 не слухається відразу | rkipc ініціалізується ~15 сек | Зачекати і перевірити ще раз |
| rkipc запущений але стрім не йде | Гонка старту — rkipc піднявся до того як ISP готовий | killall rkipc і запустити знову |
Recv failure: Вузол розірвав з'єднання |
curl не підтримує RTSP | Використовувати ffplay або telnet для перевірки |
🐹 Go — крос-компіляція для Luckfox
✅ Перевірено: Hello World + HTTP сервер + JSON API успішно запущені на платі
Чому Go а не Node.js?
| Node.js | Go | |
|---|---|---|
| Runtime розмір | ~50MB | 0 (статичний бінарник) |
| RAM при старті | ~40MB | ~2MB |
| Крос-компіляція | складна | одна команда |
| Є в Buildroot | ні | не потрібен |
ℹ️
statically linked— Go компілює всі залежності в один бінарник.
Копіюєш один файл на плату і він просто працює. Безapt install, без.soбібліотек.
Крок 1 — Підготовка Dockerfile
|
1 2 3 4 5 6 7 |
# Використовуємо Dockerfile.go-luckfox на базі Alpine # Alpine замість Debian Bookworm — суттєво менше вразливостей docker build -f Dockerfile.go-luckfox -t go-arm-dev . # Перевірити що образ є docker images | grep go-arm-dev |
Dockerfile.go-luckfox:
|
1 2 3 4 5 6 7 8 9 10 |
FROM golang:1.23-alpine RUN apk add --no-cache \ git curl vim bash \ iputils net-tools procps \ openssh-client file WORKDIR /go-dev CMD ["tail", "-f", "/dev/null"] |
Крок 2 — Запуск контейнера
|
1 2 3 |
# З volume — папка проєкту доступна всередині як /go-dev docker run -it --rm -v $(pwd):/go-dev go-arm-dev bash |
Крок 3 — Крос-компіляція
|
1 2 3 4 5 6 7 8 |
# Всередині контейнера cd /go-dev/luckfox GOOS=linux GOARCH=arm GOARM=7 go build -o http-server server.go # Перевірити бінарник file http-server # → ELF 32-bit LSB executable, ARM, EABI5, statically linked ✅ |
ℹ️
GOOS=linux— цільова ОС
ℹ️GOARCH=arm— архітектура ARM
ℹ️GOARM=7— версія ARM (Cortex-A7 = ARMv7)
Крок 4 — Копіювання і запуск на платі
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# З комп'ютера (не в Docker) scp -o PubkeyAuthentication=no ./luckfox/http-server root@192.168.1.102:/root/ # На платі chmod +x /root/http-server # Запуск у фоні — консоль залишається вільною /root/http-server & # Зупинити kill $(pgrep http-server) |
API endpoints
| Endpoint | Опис |
|---|---|
GET / |
список endpoints |
GET /api/stats |
системні метрики (JSON) |
GET /metrics |
raw /proc дані |
/api/stats — приклад відповіді
|
1 2 3 4 5 6 7 8 9 10 |
{ "hostname": "luckfox", "arch": "arm", "uptime_seconds": 4500.48, "load_avg": 6.17, "mem_total_kb": 55852, "mem_free_kb": 14112, "mem_available_kb": 37504 } |
ℹ️
Access-Control-Allow-Origin: *— CORS увімкнено, дашборд може робитиfetch()
Підключення дашборду до API
|
1 2 3 4 5 6 7 8 |
// В браузері — замість хардкоду fetch('http://192.168.1.102:8080/api/stats') .then(r => r.json()) .then(data => { console.log(data.mem_available_kb) console.log(data.uptime_seconds) }) |
🔤 Мови програмування — шпаргалка
| Мова | Крос-компіляція | Для чого |
|---|---|---|
| Python | не потрібна (вже є) | прототип, скрипти |
| Go | GOOS=linux GOARCH=arm GOARM=7 |
HTTP сервер, стрімер |
| C/C++ | arm-linux-gnueabihf-gcc |
системні речі, v4l2 |
| Rust | --target armv7-unknown-linux-musleabihf |
безпека пам’яті |
ℹ️ Node.js — не рекомендується: runtime ~50MB, RAM всього ~33MB вільно
🗺️ Roadmap проєкту
- [x] Етап 1 — Flash Linux, SSH, мережа
- [x] Go Hello World — крос-компіляція через Docker ✅
- [x] Go HTTP сервер —
/api/statsJSON API ✅ - [ ] Підключити дашборд до реального API
- [ ] Етап 2 — підключення камери MIS5001
- [ ] Етап 3 — MJPEG стрім (Python → Go)
- [ ] Етап 4 — Watchdog, autostart, init.d
🐛 Відомі проблеми і фікси
| Проблема | Причина | Фікс |
|---|---|---|
Too many authentication failures |
ssh-agent перебирає ключі | -o PubkeyAuthentication=no |
OSError: [Errno 97] в http.server |
немає IPv6 на ядрі | додати -b 0.0.0.0 |
| SCP не копіює файл з кирилицею | unicode в шляху | скопіювати в /tmp спочатку |
| Дефолтний IP 172.32.0.70 не пінгується | плата бере інший IP | сканувати через nmap |
loadavg постійно ~6.x |
NPU/RISC-V MCU kernel threads чекають на hardware | норма для цієї прошивки, %CPU 0% — система вільна |
| Samba запущена з коробки | Luckfox прошивка включає smbd/nmbd | killall smbd nmbd — звільняє ~3MB RAM |
| http-server VSZ 523MB | Go runtime резервує віртуальний адресний простір | норма, реальна RAM нормальна |
| Після ребуту eth0 без IP | ifup спрацьовує до підняття PHY link (NO-CARRIER) |
підключити кабель до ребуту, або ifdown eth0 && ifup eth0 |
| udhcpc перебиває статичний IP після ребуту | Buildroot запускає udhcpc до будь-якого нашого конфігу, /etc/network/interfaces не допомагає |
Використати /etc/init.d/S99staticip — скрипт з sleep 5 + ifconfig що приходить після udhcpc |
| Плата зникла з DHCP списку роутера | статичний IP не реєструється в DHCP таблиці | норма, трафік ходить, просто не видно в списку |
| Нестабільний ping (53ms, 715ms, 759ms) | поганий контакт кабелю або роз’єму | замінити кабель або пошевелити роз’єми |
netstat -tlnp не працює |
BusyBox версія не підтримує -p |
використовувати netstat -tln без -p |
| Камера не розпізнається, rkipc.ini не з’являється | прошивка не містить IQ файл для сенсора | перепрошити новим образом з підтримкою потрібного сенсора |
| SSH: “WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED” | після перепрошивки плата генерує нові SSH ключі | ssh-keygen -f ~/.ssh/known_hosts -R "172.32.0.93" |
Два IP одночасно після ifup |
udhcpc встиг отримати IP до pre-up killall |
kill $(pgrep udhcpc) + ip addr del 192.168.1.101/24 dev eth0 + ifdown eth0 && ifup eth0 |
🔄 Перепрошивка (Linux)
Потрібно коли: камера не підтримується, треба оновити систему, або щось зламалось.
Що знадобиться
| Ресурс | Посилання |
|---|---|
| Образи прошивок | Google Drive Luckfox → buildroot → Flash |
| upgrade_tool для Linux | wiki.luckfox.com → розділ “Image Flashing (Linux Environment)” |
⚠️ upgrade_tool офіційно підтримує тільки Ubuntu 22.04 x86_64
Крок 1 — встановити upgrade_tool
|
1 2 3 4 5 6 7 8 9 10 |
# Скачати з wiki.luckfox.com розділ 5.1.2 unzip upgrade_tool_v2.17.zip cd upgrade_tool_v2.17_for_linux/ sudo cp upgrade_tool /usr/local/bin sudo chmod +x /usr/local/bin/upgrade_tool # Перевірка sudo upgrade_tool -v # Upgrade Tool v2.17 |
Крок 2 — скачати образ
Йдемо на Google Drive (посилання вище):
firmware → buildroot → Flash → Luckfox_Pico_Pro_Flash_XXXXXX.zip- Розпакувати — всередині має бути
update.img
Крок 3 — перевірити підтримку сенсора (до прошивки)
Назва файлу не каже про сенсори — дізнатись можна тільки після прошивки:
|
1 2 3 |
ls /etc/iqfiles/ | grep mis5001 # mis5001_CMK-OT2115-PC1_30IRC-F16.json ← є! |
Крок 4 — увійти в Maskrom режим
- Затиснути кнопку BOOT на платі
- Підключити USB до комп’ютера
- Відпустити BOOT
|
1 2 3 |
lsusb | grep -i rock # Bus 003 Device 018: ID 2207:110c Fuzhou Rockchip Electronics Company |
Якщо бачимо Rockchip — плата в Maskrom, можна прошивати.
Крок 5 — прошити
|
1 2 3 4 5 6 7 |
cd ~/Downloads/Luckfox_Pico_Pro_Flash_XXXXXX/ sudo upgrade_tool uf update.img # ... # Download Image... (100%) # Download Firmware Success # Upgrade firmware ok. |
Займає ~2 хвилини. Після завершення плата сама перезавантажується.
Крок 6 — після прошивки
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# Очистити старий SSH ключ ssh-keygen -f ~/.ssh/known_hosts -R "172.32.0.93" # Підключитись через USB (статичний IP ще не налаштований) ./luckfox-connect.sh ssh -o PubkeyAuthentication=no root@172.32.0.93 # Перевірити камеру ls /userdata/ # має бути rkipc.ini ls /etc/iqfiles/ # шукаємо свій сенсор # Налаштувати статичний IP (див. розділ вище) # Скопіювати http-server назад на плату |
Перевірка RTSP стріму
|
1 2 3 |
rtsp://172.32.0.93/live/0 ← через USB rtsp://192.168.1.102/live/0 ← через Ethernet (після статичного IP) |
|
1 2 3 4 5 6 7 8 9 |
# Звичайний перегляд ffplay rtsp://192.168.1.102/live/0 # З мінімальною затримкою (якщо затримка 10+ сек) ffplay -fflags nobuffer -flags low_delay -framedrop rtsp://192.168.1.102/live/0 # ⚠️ VLC 3.0.16 — не працює з цим RTSP сервером # sudo apt install ffmpeg |
ℹ️ Норма затримки — 1-3 сек.
-fflags nobuffer -flags low_delay -framedropзменшує з 12 сек до 1-2.
Оновлено: прошивка + камера MIS5001 + RTSP стрім працює Наступне: autostart http-server + інтеграція стріму в дашборд