Многие уже слышали о проекте OpenIPC, который представляет собой альтернативную универсальную прошивку для камер видеонаблюдения. Сегодняшний пост для тех, кто хочет попробовать этот софт, кто дружит с Linux, но далёк от программатора, паяльника, фена и многих подобных вещей, которые, как правило, сопровождают смену прошивки и путь обратно к заводской. Ведь многие боятся, что залив новую прошивку, она не заработает, а дороги к родной уже не будет. И эти опасения вполне оправданы, потому как на большинстве плат производители всеми средствами ограничивают возможности создания бэкапов. С каждой новой версией это становится сделать всё сложнее. Ещё такой вариант загрузки будет удобен администраторам, кому требуется обслуживать огромное количество одинаковых камер, ведь одно изменение в директории обновит сразу все камеры.
Из дополнительных железок нам понадобится мультиметр, USB UART-адаптер на 3.3 вольта и dupont-проводки от макетной платы, чтобы подключиться к камере. Сегодня будем настраивать загрузку OpenIPC на камере с сервера вместо классической загрузки с микросхемы флэш-памяти. Если понравится, можно даже оставить работать камеру таким образом навсегда.
Особенно такой вариант будет удобен тем, кто разрабатывает программы для камеры, и нужно быстрым и удобным способом подкидывать в систему программы, модули ядра, менять ядро и саму корневую файловую систему. А также тем, кто использует камеру как одноплатный компьютер вместо плодово-ягодных плат.
Для сегодняшнего эксперимента я покопался в коробочке и выудил PTZ WiFi-камеру на процессоре XM530 от вендора XiongMai. На плате камеры написано IPC-RM1-BLK530AI-0235E-S-ETH-v1.02, но кодовое название, под которым её определяют такие программы, как IPCam_DMS, это XM530_RA50X20_8M. Дополнительная плата промаркирована как IPC-RM530-WIFI713G 4G IF-MINI v 2.02. На самом процессоре написано 50WX2, но практика показывает, что там может быть написано что угодно. Самый надёжный способ узнать всё о компонентах камеры — это запустить IPCTool на заводской прошивке.
Поворотная WiFi-камера
Выглядит моя камера вот так, но подобный корпус вовсе не говорит ни о процессоре, ни о плате камеры абсолютно ничего. Внутри может быть всё что угодно.
Вы, конечно, можете взять любую другую, которая поддерживается OpenIPC. Но тут вам понадобится некоторая доля везения, потому что сюрпризы ожидают на каждом шагу. Все камеры очень и очень различаются как софтом, так и железом.
В первую очередь, камера от XiongMai «попалась» мне из коробочки по той причине, что на ней не запаролен доступ в командную строку загрузчика u-boot, и именно это нам даст возможность загрузить ядро Linux по протоколу TFTP и подключить rootfs через сетевой протокол NFS.
▍ Разбираемся, как подключиться к UART консоли камеры
Хоть ничего паять не нужно, всё равно вы должны осознавать, что есть риск испортить оборудование. Вся ответственность только на вас. Ведь тут даже одним неловким движением щупа мультиметра можно замкнуть что-либо и сжечь плату.
Оборудование камеры состоит из двух плат: основная и плата для подключения кабеля питания, LAN, а также модуль Wi-Fi
Для удобного доступа к плате вытаскиваем её из корпуса, отключив все лишние провода. К плате должны быть подключены кабель питания и сеть. Иногда это два разных коннектора на 4 пина и на 6 пин, иногда совмещены в одном коннекторе на 8 пин (на современных камерах XiongMai чаще всего именно последний вариант). Всё остальное отключайте. Операционная система запускается, даже когда не подключены PTZ-моторы, микрофон, спикер, IR-CUT, подсветка и датчики. Они сейчас будут только мешать, потому как на этапе загрузки они ничего не смогут сказать о состоянии системы. Нам поможет только UART-консоль.
Крайне не рекомендую без крайней на то необходимости снимать с платы объектив и IR-CUT фильтр, как это сделал я. Светочувствительный сенсор покрывается пылью и грязью ровно за 1 секунду, и отмыть его будет стоить немалых усилий. Но если уж сняли, то гуглите изопропанол.
Подключение GND, RX, TX к плате камеры
Как я и обещал, никаких паяльников. Но чтобы попасть в меню u-boot, нам во что бы то ни стало нужно подключиться к контактам UART, которые обозначаются RX, TX и GND. Если на плате они не обозначены, то придётся вызванивать мультиметром. Обычно эти контакты выглядят как группа контактов от 2 до 4 в ряд. Один из них — это земля (GND) и, как правило, он должен быть замкнут на посадочные места от болтов (но далеко не всегда!). Если контактов только два, то землю берём с любого места платы.
RX и TX определяют по характерной особенности: каждый из них должен показать 3.3 вольта. Чтобы замерять, выставляем мультиметр в режим замера постоянного напряжения с пределом до 20 вольт. Чёрный щуп мультиметра зажимаем на землю, а красный на обследуемый контакт. RX или TX покажут 3,3 вольта.
Внимание! Никогда не подключайте UART-адаптер к контактам, где напряжение составляет 12 вольт — это гарантированно сожжёт USB UART-адаптер.
Дальше выяснять, кто из двух братцев является TX, будем экспериментально. TX — transmit. RX — receive. Таким образом, камера с контакта TX будет плеваться логами загрузки, даже если RX мы никуда не подключали.
Подключаем UART-адаптер в USB компьютера и запускаем программу (конечно, её предварительно нужно установить):
$ picocom -b 115200 -r -l /dev/ttyUSB0
- b — baud rate. Обычно он 115200 на камерах, но крайне редко может отличаться;
- r — no reset;
- l — no lock.
Убедитесь, что после включения адаптера у вас появился файл /dev/ttyUSB0. Вместо picocom можно использовать minicom.
Тянем проводом от RX (приём) UART-адаптера по очереди до каждого из контактов камеры, которые показали 3.3 вольта, и включаем камеру. Если это контакт TX, то на консоли на компьютере вы увидите лог загрузки. Контакт RX находим по остаточному принципу. Он даст возможность передавать от компьютера нажатия клавиш в консоли в камеру.
Вот такая конструкция у меня получилась
Очень рекомендую удлинителем вывести на стол USB, чтобы не возиться в неудобной позе.
Так, если всё правильно сделано, то в консоли отобразится следующее:
CPU: XM530
DRAM: 64 MiB
MMC: arasan: 0
In: serial
Out: serial
Err: serial
Net: dwmac.10010000
Press Ctrl+C to stop autoboot
Если быстро нажимать CTRL+C, то попадём в командную строку загрузчика, который имеет свой набор команд, и из которых можно даже писать скрипты загрузки.
U-Boot> help
? - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
cramfsload- load binary file from a filesystem image
cramfsls- list files in a directory (default /)
crc32 - checksum calculation
dhcp - boot image via network using DHCP/TFTP protocol
echo - echo args to console
editenv - edit environment variable
env - environment handling commands
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
flwrite - SPI flash sub-system
go - start application at address 'addr'
help - print command description/usage
imxtract- extract a part of a multi-image
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
md - memory display
mm - memory modify (auto-incrementing address)
mmc - MMC sub system
mmcinfo - display MMC info
mw - memory write (fill)
nfs - boot image via network using NFS protocol
nm - memory modify (constant address)
ping - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
reset - Perform RESET of the CPU
run - run commands in an environment variable
saveenv - save environment variables to persistent storage
setenv - set environment variables
setexpr - set environment variable as the result of eval expression
sf - SPI flash sub-system
sleep - delay execution for some time
source - run script from memory
tftpboot- boot image via network using TFTP protocol
version - print monitor, compiler and linker version
▍ Сохраняем настройки U-Boot
Хоть саму прошивку мы не собираемся менять, но настройки загрузки изменить придётся. Поэтому в первую очередь нужно увидеть и сохранить все текущие настройки, с которыми камера работает на заводской прошивке.
U-Boot> printenv
appProducerID=000
baudrate=115200
bootargs=mem=35M console=ttyAMA0,115200 root=/dev/mtdblock2 rootfstype=cramfs mtdparts=xm_sfc:256K(boot),1536K(kernel),1280K(romfs),4544K(user),256K(custom),320K(mtd)
bootcmd=sf probe 0;sf read 80007fc0 40000 180000;bootm 80007fc0
bootdelay=1
cramfsaddr=0x60040000
da=mw.b 0x81000000 ff 800000;tftp 0x81000000 u-boot.bin.img;sf probe 0;flwrite
dc=mw.b 0x81000000 ff 800000;tftp 0x81000000 custom-x.cramfs.img;sf probe 0;flwrite
dd=mw.b 0x81000000 ff 800000;tftp 0x81000000 mtd-x.jffs2.img;sf probe 0;flwrite
dr=mw.b 0x81000000 ff 800000;tftp 0x81000000 romfs-x.cramfs.img;sf probe 0;flwrite
du=mw.b 0x81000000 ff 800000;tftp 0x81000000 user-x.cramfs.img;sf probe 0;flwrite
dw=mw.b 0x81000000 ff 800000;tftp 0x81000000 web-x.cramfs.img;sf probe 0;flwrite
ethact=dwmac.10010000
ethaddr=00:12:41:00:00:01
ipaddr=192.168.1.10
netmask=255.255.255.0
serverip=192.168.1.107
stderr=serial
stdin=serial
stdout=serial
tk=mw.b 0x81000000 ff 800000;tftp 0x81000000 uImage; bootm 0x81000000
ua=mw.b 0x81000000 ff 800000;tftp 0x81000000 upall_verify.img;sf probe 0;flwrite
up=mw.b 0x81000000 ff 800000;tftp 0x81000000 update.img;sf probe 0;flwrite
verify=n
Environment size: 1259/65532 bytes
Хоть u-boot это не Linux, но тут мы можем даже убедиться, что сеть работает:
U-Boot> ping 192.168.1.1
Speed: 100, full duplex
Using dwmac.10010000 device
host 192.168.1.1 is alive
Загрузка u-boot начинается командами, определёнными в переменной bootcmd. В будущем я её переопределю, чтобы камера при включении загружалась по сети, но пока откладываем плату в сторону и приступим к настройке серверов. Нам понадобится два сервиса, которые могут быть установленными как на одной машине, так и на разных.
▍ Настраиваем TFT-сервер
Я не буду заострять внимание на мелочах по настройке в расчёте на то, что в поисковиках вы не забанены. Разберитесь, как настроить TFTP-сервер. Я установил на машину 192.168.1.1 сервис atftpd со следующими настройками, которые задал в файле /etc/conf.d/atftp
TFTPD_ROOT="/mnt/tftproot"
TFTPD_OPTS="--daemon --user nobody --group nobody"
В эту директорию TFTP-сервера я сохраню ядро Linux под именем uImage.xm530. Его можно достать из архива, скачав с сайта OpenIPC файл openipc.xm530-nor-lite.tgz.
Второй вариант — это скомпилировать самостоятельно:
github.com/openipc//firmware.
Обязательно проверяю клиентом atftp (без буквы d в конце), что сервер TFTP отдаёт файлы:
atftp --get -r uImage.xm530 -l /tmp/uImage.xm530 192.168.1.1
Эта команда скачает файл, указанный после опции -r (remote) и сохранит локально под именем указанным -l (local).
▍ Настраиваем NFS-сервер
Сервер NFS я настроил на машине 192.168.1.3, добавив в /etc/exports следующую строку:
/mnt/nfsroot 192.168.0.0/16(async,rw,no_subtree_check,no_root_squash)
В эту директорию я распаковал корневую систему из того же архива: rootfs.squashfs.xm530, используя программу unsquashfs. Если скомпилировать самостоятельно, то корневая система будет лежать в TAR-архиве rootfs.tar.
Обязательно убедитесь, что ваша директория теперь доступна по протоколу NFS:
# mount -o nolock,rw 192.168.1.3:/mnt/nfsroot /mnt/cdrom/
# touch /mnt/cdrom/111
touch: cannot touch '/mnt/cdrom/111': Read-only file system
Если она доступна в режиме только для чтения, то OpenIPC всё равно загрузится и будет работать, но с некоторыми нюансами и ограничениями. В NFS есть такая специфика — директория экспортируется как RO, даже если указано экспортировать RW, если вышестоящая директория уже была эспортирована как RO. Дальше я покажу, что система OpenIPC работает, даже если корень системы в режиме только для чтения.
▍ Добавляем скрипты загрузки bootargsnfs и nfsroot
Теперь нужно сообщить загрузчику, какие компоненты системы откуда нужно загрузить.
Обратите внимание, что после имени переменной не ставится знак '=' для того, чтобы задать значение этой переменной.
setenv serverip 192.168.1.1
setenv ipaddr 192.168.1.10
setenv bootargsnfs 'mem=35M console=ttyAMA0,115200 panic=20 root=/dev/nfs rootfstype=nfs ip=dhcp nfsroot=192.168.1.3:/mnt/nfsroot,v4,nolock,tcp rw ip=192.168.1.10:192.168.1.3:192.168.1.1:255.255.255.0::eth0'
setenv nfsboot 'tftp 0x81000000 uImage.xm530;setenv setargs setenv bootargs ${bootargsnfs};run setargs;bootm 0x81000000'
run nfsboot
Коротко о том, что тут происходит:
- printenv — выводит значение всех переменных или одной заданной переменной;
- setenv — задаёт значение переменной.
Все переменные задаются только в памяти до тех пор, пока не сохраните их командой saveenv. Пока не сохраните, ваша камера будет оставаться в нетронутом первозданном виде и при включении будет себя вести как и раньше.
Через точку с запятой перечисляются команды, которые будут выполнены последовательно при запуске 'run nfsboot'.
Самая сложная тут конструкция:
setenv setargs setenv bootargs ${bootargsnfs}
Она означает, что нужно сохранить в переменную setargs значение «setenv bootargs ${bootargsnfs}», которое при запуске команды «run setargs» создаст или переопределит переменную bootargs, а её значение будет взято из переменной bootargsnfs (то есть скопирована одна переменная в другую).
Такие конструкции в u-boot используются для того, чтобы один раз настроить среду окружения и изменением лишь одной команды и, возможно, нескольких дополнительных настроек можно было бы переключать загрузку камеры с различных источников. Так, например, адреса NFS и TFTP-серверов было бы удобно вынести в отдельные переменные. Впрочем, именно так и сделано в загрузчиках проекта OpenIPC. Но, к сожалению, именно для платформы XM530 православного загрузчика от OpenIPC пока не существует.
Ещё немного остановлюсь на аргументе для передачи ядру ip. Его структура довольно сложная:
bootcmd=bootp; setenv bootargs root=/dev/nfs nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; bootm
Настройте эти параметры под свои условия:
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>
Чтобы увидеть, что загрузка идёт не с микросхемы, а по сети, сохраните (или сфотографируйте в крайнем случае) вывод загрузки. У меня стоковое ядро не выводило ничего после сообщения «Uncompressing Linux… done, booting the kernel.», а на ядре OpenIPC всегда есть вывод. Особенно стоит обратить внимание на сообщение о том, что есть попытка подключиться к серверу NFS.
IP-Config: Complete:
device=eth0, hwaddr=00:12:41:09:9c:1b, ipaddr=192.168.1.10, mask=255.255.255.0, gw=192.168.1.1
host=192.168.1.10, domain=, nis-domain=(none)
bootserver=192.168.1.3, rootserver=192.168.1.3, rootpath=
У меня на этом этапе была потеряна уйма времени, потому как ядро никак не могло подключиться к NFS-серверу. Оказалось, что по какой-то причине NFS никак не хотел работать через протокол по умолчанию UDP, хотя нигде он не режется файрволом. Решилось всё передачей ещё одного дополнительного параметра ядру 'tcp'.
Когда OpenIPC загрузится, можно залогиниться в систему.
Welcome to OpenIPC
openipc-xm530 login: root
Password:
Cannot open config file '/etc/fw_env.config': Read-only file system
/###### /###### /####### /######
/##__ ## |_ ##_/ | ##__ ## /##__ ##
| ## ## /###### /###### /####### | ## | ## ## | ## __/
| ## | ## /##__ ## /##__ ## | ##__ ## | ## | #######/ | ##
| ## | ## | ## ## | ######## | ## ## | ## | ##____/ | ##
| ## | ## | ## | ## | ##_____/ | ## | ## | ## | ## | ## ##
| ######/ | #######/ | ####### | ## | ## /###### | ## | ######/
______/ | ##____/ _______/ |__/ |__/ |______/ |__/ ______/
| ##
| ## build
|__/ local+build, 2023-11-16
Логинимся в систему через root:12345 (пароль по умолчанию).
Тут нужно быстро выгрузить из памяти модуль watchdog чтобы камера не ребутилась в случае неудачи по запуску стримера majestic:
rmmod xm_wdt
Теперь можно выдохнуть и медленно с наслаждением заняться настройкой.
На платформе XM, чтобы заработал majestic-скрипт /usr/bin/load_xiongmai при загрузке ОС на камере, должен определить сенсор через утилиту ipcinfo. В зависимости от результатов создаётся символьная ссылка:
ln -s /usr/lib/sensors/libsns_X123_XM530.so /usr/lib/libsns.so
ln -s /usr/lib/sensors/libsns_X50_XM530.so /usr/lib/libsns.so
Так как rootfs у меня подмонтирован read-only, то симлинк не создался и majestic не запустился.
На сервере создаю симлинк и запускаю majestic на камере:
root@openipc-xm530:~# majestic
LIBH264: Complied at Apr 30 2019 16:55:22
17:07:28 < majestic> [ main] main@123 Majestic Lite for Xiongmai, version master+3093a9b, built on 2023-11-11
17:07:28 < majestic> [app_conf] load_config@108 Using /etc/majestic.yaml as main configuration
17:07:28 < majestic> [watchdog] watchdog_start@82 Watchdog device (/dev/wdt) not found
DEBUG: [ISP_SDK]BUILD TIME:Sep 21 2021 11:58:04
DEBUG: OSC:12M
17:07:28 < majestic> [ log] printf@268 SYS/VI set in [ Sofia ]
17:07:28 < majestic> [ log] printf@268 Vstd_init [ PAL ]
17:07:28 < majestic> [ log] printf@268 Rslt [ 0 ]
17:07:28 < majestic> [ log] printf@268 IPC_Venc: [ H.264 ]
17:07:28 < majestic> [ log] printf@268 Sns_IF: [ By src ]
DEBUG: SC2235E!
DEBUG: sensor_get_chip:
17:07:29 < majestic> [ log] printf@268 0x30
17:07:29 < majestic> [ log] printf@268 0x2
17:07:29 < majestic> [ log] printf@268 0x1
17:07:29 < majestic> [ log] printf@268 0x35
17:07:29 < majestic> [ puts]
ERR: open /mnt/mtd/Config/SensorType.bat failed
DEBUG: Venc(264):495
DEBUG: DspChip:XM530
DEBUG: SnsIF:MIPI
DEBUG: Vstd:PAL[25fps]
DEBUG: ------------- SC2235E (@20190110_mipi) ----------------
DEBUG: sensor_init Ok!
DEBUG: Init over ~~~~~~~~
DEBUG: ProductType: 0x0
DEBUG: libfvideo: SC2235E!
DEBUG: isp_sample end!
DEBUG: imageStyle XM!
17:07:29 < majestic> [ sdk] ViChnConfig@164 ViChn = 0, enSize = 14
17:07:29 < majestic> [ sdk] COMM_VENC_Start@249 VencChn = 0, enType = 265, enSize = 14 (1920x1080), enRcMode = 1
17:07:29 < majestic> [ sdk] COMM_VENC_Start@249 VencChn = 2, enType = 26, enSize = 0 (704x576), enRcMode = 1
17:07:29 < majestic> [ httpd] new_http_server@373 HTTP server started on 0.0.0.0:80
17:07:29 < majestic> [ rtsp] rtsp_init@31 RTSP server started on port 554
DEBUG: black and white
DEBUG: EShutter 1/1
DEBUG: color
17:07:34 < majestic> [discover] udp_read@48 onvif discovery probe from 192.168.1.23:3702
DEBUG: Delay Over !!!
Камера теперь выполняет свои функции как камера и стримит видеопоток по RTSP. Веб-интерфейс работает по адресу http://192.168.1.10:85/. Но если корневая файловая система в режиме только для чтения, то сохранить что-либо будет проблематично. Зато это можно использовать, чтобы загрузить хоть миллион одинаковых камер через NFS с одной сетевой директории в одном экземпляре. Очень удобно для администрирования.
Если хотите, чтобы камера всегда загружалась по сети, то переопределите переменную bootcmd:
U-Boot> setenv bootcmd 'tftp 0x81000000 uImage.xm530;setenv setargs setenv bootargs ${bootargsnfs};run setargs;bootm 0x81000000'
▍ Добавлю-ка я поддержку камеры в Coupler
Я убедился, даже не затирая заводскую прошивку, что эта плата XM поддерживается текущей системой OpenIPC. Плата называется XM530_RA50X20_8M. Это я выяснил, запустив через wine IPCam_DMS.exe. Там же я посмотрел номер заводской прошивки для этой платы: 00030665. Оказалось, что поддержки этой платы нет в Coupler. А ведь это самый простой и надёжный способ начать использовать OpenIPC для самых начинающих, о котором я уже рассказывал раньше.
IPCam_DMS показывает название платы и ID прошивки
Пройти мимо проекта, имея на руках всю информацию для улучшения, я не могу, чего и вам желаю. Тем более, чтобы законтрибьютить в Coupler, вообще требуется минимум усилий. По уму, нужно сделать форк проекта, всё скомпилировать и проверить на локальной машине, но так как я уже это делал и проект понимаю, то сокращаю свои приложенные усилия до добавления одной строки через веб-интерфейс GitHub в этот файл.
SOC="xm530" OSMEM="35M" TOTALMEM="64M" HARDWARE="XM530_RA50X20_8M" DEVID="00030665" FLASH_SIZE="0x800000" COMPAT="3" $RUN
Все эти переменные в строке служат параметрами для скрипта _xm2oipc.sh из этого проекта. Кто хочет скомпилировать локально, чтобы собрать прошивку самостоятельно и убедиться, что всё работает до создания пул-реквеста, передайте эти переменные окружения скрипту, и он соберёт всё. Но не забывайте убедиться, что в систему установлены все необходимые скрипту программы.
А у меня после пул-реквеста всё готово! Через денёк можно заглянуть и увидеть, что в сборках появится прошивка ещё для одной платы XiongMai.
▍ Возврат к заводской прошивке
После экспериментов, если вдруг захочется вернуть камеру к работе на заводской прошивке, нужно опять попасть в меню uboot и вернуть как было переменную со скриптом загрузки bootcmd:
setenv bootcmd 'sf probe 0;sf read 80007fc0 40000 180000;bootm 80007fc0'
saveenv
Автор: Новак Сергей