Предисловие
Наступал новый 2018 год. Кто-то собрался уезжать праздновать его наступление в тёплые края за границу, а кто-то остался праздновать новый год дома, в серых стенах каменного Санкт-Петербурга. Пожалуй, несомненно то, что новогодние праздники – особенная пора, от которой ждут волшебства, единения с семьёй и друзьями, и, разумеется, подарков, ведь все мы в той или иной степени в душе немного дети. Просто те, что помладше, ждут Деда Мороза, а те, что постарше, идут за подарками в магазин сами. Вот и я решил зайти на eBay за подарком самому себе на новый год.
Дело в том, что сам по себе настольный компьютер на базе архитектуры Intel x86 уже не вызывает большого удивления, однако всё больше и больше появляется энтузиастов, ковыряющих различные SoC-решения, которые зачастую базируются на архитектуре ARM либо MIPS. А учитывая то, что относительно незадолго до новогодних праздников ко мне на почту прилетело письмо с вопросом о том, буду ли я поддерживать архитектуру ARM в одном из своих проектов, я однозначно решил найти себе платформу на базе ARM. И одной из самых популярных, на мой взгляд, является платформа Raspberry Pi, которая к этому моменту уже успела выйти в третьей версии.
Комплектация
Вот он, мой подарок на Новый год! Изучив подробно предложения на eBay, я остановился на варианте стоимостью в $77, который включает в себя:
Разумеется, хотелось ещё и какой-нибудь мини-экран, чтобы не ворочать головой к телевизору и не переключать постоянно основной монитор с DVI на HDMI и обратно. Поэтому я нашёл очень забавный семидюймовый HDMI-дисплей разрешением 1024x600 пикселей, который, помимо всего, ещё и имеет ёмкостную сенсорную поверхность. И такое чудо обошлось всего лишь в какие-то 50$.
- компьютер Raspberry Pi 3 Model B;
- медные радиаторы для пассивного охлаждения чипов;
- прозрачный пластиковый корпус;
- MicroSD-карта SanDisk Ultra ёмкостью 16 Гбайт с предустановленной операционной системой;
- мини-клавиатура совмещённая с тачпадом, работающая по радиоканалу;
- блок питания, дающий 5 вольт постоянного напряжения и максимальный ток 2,5 ампер;
- usb-шнур с выключателем для питания платформы, который подключается к блоку питания;
- синий патч-корд с разъёмами RJ-45 для проводного ethernet;
- HDMI-кабель для подключения монитора.
Итого мы имеем компьютер по цене дешёвого мобильника, с достаточно внушительными характеристиками:
Но меня в первую очередь интересует именно процессор и его набор команд, а остальное – по мере желания и возможностей.
- процессор – Broadcom BCM2837 на базе архитектуры Cortex-A53 (ARM v8);
- тактовая частота – 1200 МГц;
- количество ядер – 4 физических ядра;
- объём памяти – 1 Гб SDRAM;
- видео – Broadcom Videocore 4, видеопамять выделяется из ОЗУ;
- разъёмы:
- 4 разъёма USB 2.0;
- разъём RJ 45 (ethernet);
- разъём HDMI;
- разъём 3.5 mm Jack, комбинированный с композитным видеовыходом;
- разъём для подключения камеры CSI;
- разъём для подключения дисплея DSI;
- разъём для карты MicroSD;
- 40-пиновый разъём GPIO;
- разъём для питания, оформленный в виде micro USB.
Первый взгляд
Новогодние праздники прошли, а я довольный вернулся из почтового отделения с коробками, в которых лежали долгожданные компьютер и экран. На самом деле, я ожидал, что коробки будут несколько больше, но производители действительно не обманули, заявив, что компьютер по размерам не больше кредитной карточки. Упаковано всё достаточно аккуратно, без множества вычурных бумажек, хотя, честно, хотелось увидеть какой-нибудь многостраничный рекламный буклетик. Всё по-минимуму, сам компьютер лежит в такой бумажной коробочке:
Рисунок 1. Коробочка Raspberry Pi.
Не буду долго рассказывать про распаковку, этим занимаются отдельные люди. В целом, всё без проблем размещается на одном столе.
Рисунок 2. Вся комплектация одним снимком.
И, прежде, чем что-либо подключать, на всякий случай сделаем резервную копию флеш-карты.
Смотрим по dmesg устройство, которое было подключено к кард-ридеру:
Отлично, снимаем копию утилитой dd куда-нибудь в рабочий каталог:Код (Text):
[ 6262.662987] sd 8:0:0:2: [sdf] 31116288 512-byte logical blocks: (15.9 GB/14.8 GiB) [ 6262.663993] sd 8:0:0:2: [sdf] Write Protect is on [ 6262.663995] sd 8:0:0:2: [sdf] Mode Sense: 03 00 80 00 [ 6262.671625] sdf: sdf1 sdf2
В целом, скорость около 20 Мбайт в секунду, не супер-быстро, но и не медленно.Код (Text):
dd if=/dev/sdf of=/home/work/rpi/20180113-native.img 31116288+0 записей получено 31116288+0 записей отправлено 15931539456 байт (16 GB, 15 GiB) скопирован, 819,005 s, 19,5 MB/s
Теперь давайте смонтируем разделы sdf1 и sdf2 и посмотрим, что на них.
Странно. Почему-то карточка защищена от записи. Демонтируем разделы и cмотрим на саму карту…Код (Text):
mkdir /mnt/1 mkdir /mnt/2 mount /dev/sdf1 /mnt/1 mount: /dev/sdf1 is write-protected, mounting read-only mount /dev/sdf2 /mnt/2 mount: /dev/sdf2 is write-protected, mounting read-only
Вытаскиваем карту, осматриваем, вставляем. Теперь в dmesg:Код (Text):
umount /mnt/1 umount /mnt/2
По всей видимости, проблема в контактах кард-ридера. Тем не менее, теперь мы можем исследовать разделы:Код (Text):
[ 8202.595233] sd 8:0:0:2: [sdf] 31116288 512-byte logical blocks: (15.9 GB/14.8 GiB) [ 8202.596229] sd 8:0:0:2: [sdf] Write Protect is off [ 8202.596230] sd 8:0:0:2: [sdf] Mode Sense: 03 00 00 00 [ 8202.603872] sdf: sdf1 sdf2
Код (Text):
mount /dev/sdf1 /mnt/1 mount /dev/sdf2 /mnt/2 Смотрим содержимое разделов:Код (Text):
df -h Файловая система Размер Использовано Дост Использовано% Cмонтировано в ... /dev/sdf1 512M 117M 396M 23% /mnt/1 /dev/sdf2 27M 396K 26M 2% /mnt/2
По всей видимости, первый раздел содержит EFI. Второй же раздел, как видим, пуст:Код (Text):
ls -1 /mnt/1 bcm2709-rpi-2-b.dtb bcm2710-rpi-3-b.dtb bootcode.bin cmdline.txt config.txt fixup.dat kernel.img overlays start.elf SYSTEM System Volume Information
Теперь мы готовы запустить сам микрокомпьютер и посмотреть, что произойдёт.Код (Text):
ls -l /mnt/2 итого 13 drwx------ 2 root root 12288 мар 1 2016 lost+found -rw-r--r--. 1 root root 0 мар 1 2016 .please_resize_me
Собираем тестовый стенд. В первую очередь вставляем SD-карту в соответствующий слот, подключаем мини-клавиатуру к порту USB, соединяем устройство с тачскрином проводом HDMI, и, помимо этого, ещё дополнительным USB-проводом, чтобы было можно пользоваться тачскрином. Также кидаем идущий в комплекте сетевой патч-корд от микрокомпьютера до роутера.Код (Text):
umount /mnt/1 umount /mnt/2
Рисунок 3. Взаимное подключение устройств.
Включаем питание, и… ничего, экран мигнул, потом заполнился чёрным, и перестал что-либо показывать. По всей видимости, что-то с конфигурацией, поэтому приходится всё-таки подключить свой основной рабочий монитор. И теперь, похоже, понятно, в чём дело.
Рисунок 4. Экран приветствия от openELEC.
Рисунок 5. Интерфейс openELEC.
Как видим, по умолчанию на карте был записан образ openELEC, то есть программное обеспечение для медиацентра. Нам же это совсем не подходит, поэтому будем ставить свой полноценный Linux-дистрибутив. Для этого начнём с официально поддерживаемого сообществом дистрибутива Raspbian, базирующегося на Debian Linux.
Ставим Raspbian
Скачиваем образ диска утилитой wget:
Распаковываем:Код (Text):
wget https://downloads.raspberrypi.org/raspbian_latest 2018-01-13 01:31:58 (2,31 MB/s) - «raspbian_latest» сохранён [1764972666/1764972666]
Смотрим реальный размер образа:Код (Text):
unzip raspbian_latest Archive: raspbian_latest inflating: 2017-11-29-raspbian-stretch.img
Итого имеем образ размером 4.6 Гбайт. Теперь нам предстоит залить его на SD-карту. Официальная документация рекомендует писать блоками по 4 мегабайта, что мы и сделаем.Код (Text):
du -hs 2017-11-29-raspbian-stretch.img 4,6G 2017-11-29-raspbian-stretch.img
На запись ушло где-то около восьми минут на скорости 10 Мбайт в секунду. Похоже, SD-карта действительно держит скорость соответственно заявленному десятому классу, китайцы не обманули.Код (Text):
dd if=2017-11-29-raspbian-stretch.img of=/dev/sdf bs=4M conv=fsync 1173+0 записей получено 1173+0 записей отправлено 4919918592 байт (4,9 GB, 4,6 GiB) скопирован, 464,473 s, 10,6 MB/s
На всякий случай говорим системе произвести сканирование партиций на разделе:
Получаем перечень партиций:Код (Text):
partprobe /dev/sdf
В данном случае, как и в предыдущем, две партиции. Давайте посмотрим, что на них:Код (Text):
ls -1 /dev/sdf* /dev/sdf /dev/sdf1 /dev/sdf2
Содержимое первого раздела:Код (Text):
mount /dev/sdf1 /mnt/1 mount /dev/sdf2 /mnt/2
Содержимое второго раздела.Код (Text):
ls -1 /mnt/1 bcm2708-rpi-0-w.dtb bcm2708-rpi-b.dtb bcm2708-rpi-b-plus.dtb bcm2708-rpi-cm.dtb bcm2709-rpi-2-b.dtb bcm2710-rpi-3-b.dtb bcm2710-rpi-cm3.dtb bootcode.bin cmdline.txt config.txt COPYING.linux fixup_cd.dat fixup.dat fixup_db.dat fixup_x.dat issue.txt kernel7.img kernel.img LICENCE.broadcom LICENSE.oracle overlays start_cd.elf start_db.elf start.elf start_x.elf
Как видно, в первом случае это также загрузочный раздел, во втором – основной раздел операционной системы. Отлично, давайте посмотрим, как эти разделы были смонтированы:Код (Text):
ls -1 /mnt/2 bin boot dev etc home lib lost+found media mnt opt proc root run sbin srv sys tmp usr var
В первом случае видим, что используется vfat, что справедливо для раздела EFI, во втором случае – вполне себе современная ext4.Код (Text):
mount | grep /dev/sdf /dev/sdf1 on /mnt/1 type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro) /dev/sdf2 on /mnt/2 type ext4 (rw,relatime,data=ordered)
Демонтируем разделы, вставляем SD-карту в слот мини-компьютера и включаем кнопку питания.
При первом старте операционная система сама расширяет корневой раздел до максимального значения и повторно перезагружается. Время загрузки – считанные секунды, отличный пример пользователям настольных систем на базе процессоров Intel, ведь есть куда стремиться.Код (Text):
umount /mnt/1 umount /mnt/2
Так или иначе, мы сходу попадаем в графический интерфейс Raspbian:
Рисунок 6. Raspbian в комплектации по умолчанию.
Проверяем работу мини-клавиатуры – система адекватно реагирует на нажатия клавиш и прикосновение к тачпаду. Однако всё же пользоваться ей не совсем удобно, поэтому я вынужден подключить к устройству дополнительно клавиатуру и мышь, которые так же распознаются и работают вполне адекватно.
Разумеется, в первую очередь у меня возникло желание сделать скриншот. Однако, когда я нажал клавишу Print Screen, ничего не произошло. Чуть позже я понял, что скриншоты просто сохраняются как отдельные файлы в домашний каталог. Вот, собственно говоря, один из них.
Рисунок 7. Первый скриншот.
Ура, теперь можно отложить фотоаппарат подальше. По свойствам скриншота видно, что система стартует в разрешении 1824x984, что несколько меньше FullHD (1920x1080), но с первого раза в глаза не бросается, ведь есть куда смотреть и помимо этого.
Первым делом я решил запустить терминал и убедиться, что под корневую систему отдана действительно вся флеш-карта:
Как видим, корневой раздел монтируется со специального устройства /dev/root, в то время как для загрузки используется обычная партиция, представленная устройством /dev/mmcblk0p1. При этом, сам терминал выглядит достаточно симпатично:Код (Text):
df -h Filesystem Size Used Avail Use% Mounted on /dev/root 15G 4.1G 9.9G 30% / devtmpfs 460M 0 460M 0% /dev tmpfs 464M 0 464M 0% /dev/shm tmpfs 464M 18M 446M 4% /run tmpfs 5.0M 4.0K 5.0M 1% /run/lock tmpfs 464M 0 464M 0% /sys/fs/cgroup /dev/mmcblk0p1 41M 21M 21M 51% /boot tmpfs 93M 0 93M 0% /run/user/1000
Рисунок 8. Окно терминала
В общем, первое впечатление можно озвучить одним междометием - «вау!»
Конфигурируем Raspbian
Отлично. Теперь надо выполнить первоначальную конфигурацию Raspbian. Для этого находим в меню соответствующий пункт, который запускает приложение-конфигуратор:
Рисунок 9. Окно приложения Raspberry Pi Configuration.
Конечно, интерфейс не блещет обилием настроек, но основные и самые важные с его помощью можно сделать. Для начала изменяем пароль, заданный по умолчанию, на свой при помощи кнопки «Change Password...». Это будет необходимо при дальнейшей удалённой работе с устройством. Также я решил изменить имя хоста на «rpi3b». Во вкладке «Interfaces» я разрешил SSH, который идёт из коробки. Очень важно настроить параметры локали и раскладки клавиатуры во вкладке «Localisation», так как по умолчанию стоят настройки английской раскладки вместо привычной нам американской.
Рисунок 10. Вкладка «Localisation»
Теперь настраиваем WiFi. Для этого редактируем соответствующий конфигурационный файл:
Содержимое файла должно иметь приблизительно следующий вид:Код (Text):
nano /etc/wpa_supplicant/wpa_supplicant.conf
Сохраняем содержимое файла и запускаем переконфигурацию интерфейса:Код (Text):
country=RU ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 network={ ssid="homelinux" scan_ssid=1 psk="password" }
Убеждаемся, что компьютер подключился к WiFi:Код (Text):
wpa_cli -i wlan0 reconfigure
Как видим, сеть подхватилась, всё замечательно, и патчкорд нам теперь использовать совсем необязательно.Код (Text):
/sbin/ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.0.162 netmask 255.255.255.0 broadcast 192.168.0.255 inet6 fe80::83aa:5565:8698:e6f6 prefixlen 64 scopeid 0x20<link> ether b8:27:eb:ec:98:f8 txqueuelen 1000 (Ethernet) RX packets 12790 bytes 15196006 (14.4 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8322 bytes 940424 (918.3 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.0.183 netmask 255.255.255.0 broadcast 192.168.0.255 inet6 fe80::c1b4:3674:4dbe:643 prefixlen 64 scopeid 0x20<link> ether b8:27:eb:b9:cd:ad txqueuelen 1000 (Ethernet) RX packets 20 bytes 3445 (3.3 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 23 bytes 3922 (3.8 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Собираем информацию
Что ж, теперь важно собрать информацию о новоприобретённой железке. Для этого выполним ряд команд. Прежде всего, узнаем версию операционной системы:
и ядра:Код (Text):
cat /etc/issue Raspbian GNU/Linux 9 \n \l
Как видим, несмотря на то что процессор базируется на Cortex-A53, то есть архитектуре ARM 8, которая, в свою очередь, является уже 64-разрядной, операционная система всё же работает на базе 32-раздрядного ядра. Это говорит о том, что программировать в ближайшее время придётся всё же с использованием набора команд ARM 7, нежели ARM 8, которые претерпели достаточно внушительные изменения.Код (Text):
uname -a Linux raspberrypi 4.9.59-v7+ #1047 SMP Sun Oct 29 12:19:23 GMT 2017 armv7l GNU/Linux
Теперь выведем информацию о самом процессоре:
Как видим, процессор распознаётся как процессор архитектуры ARM 7 (что неудивительно, если сделать поправку на ядро операционной системы), имеет четыре ядра, каждое из которых поддерживает ряд следующих расширений:Код (Text):
cat /proc/cpuinfo processor : 0 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 processor : 1 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 processor : 2 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 processor : 3 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 Hardware : BCM2835 Revision : a22082 Serial : 0000000019ec98f8
Из всего этого набора наибольший интерес, разумеется, представляют расширения vfp различных версий, edsp и neon, с которыми я непременно ознакомлюсь в процессе изучения архитектуры ARM.
- half – поддержка чтения и записи данных размером с половину машинного слова;
- thumb – поддержка набора сокращённых thumb-команд, в котором каждая команда кодируется всего лишь шестнадцатью битами;
- fastmult – поддержка умножения 32-битных чисел с расширением до 64 бит;
- vfp – поддержка векторизованных инструкций с числами одинарной точности (ранний SIMD) – Vectorized Floating Point;
- edsp – поддержка расширений цифровой обработки сигналов (DSP);
- neon – поддержка нового набора SIMD-расширений NEON;
- vfpv3 – поддержка VFP версии 3;
- tls – наличие регистра TLS (Thread Local Storage);
- vfpv4 – поддержка VFP версии 4;
- idiva – поддержка инструкций SDIV и UDIV в режиме ARM;
- idivt – поддержка инструкций SDIV и UDIV в режиме Thumb;
- vfpd32 – поддержка 32 D-регистров со стороны VFP;
- lpae – поддержка расширенного физического адреса (Large Physical Address Extension), позволяющая адресовать больше 4 Гбайт физического адресного пространства в 32-разрядном режиме;
- evtstrm – использование потока событий ядра с использованием общего системного таймера;
- crc32 – поддержка инструкций, ускоряющих подсчёт контрольных сумм CRC32.
Что касается памяти – то её действительно 1 Гбайт, 64 Мбайт из которых отданы под видеопамять:
Если посмотреть на предустановленный набор средств, то он рассчитан на каких-то новичков. Мы же люди серьёзные, и нам нужны серьёзные средства разработки. Проверяем, есть ли таковые:Код (Text):
free total used free shared buff/cache available Mem: 949580 98792 456552 39892 394236 758952 Swap: 102396 0 102396
Вот это удивляет не меньше – GCC версии 6.3 уже из коробки. Однако не хватает моего любимого файлового менеджера. Давайте его поставим штатными средствами дистрибутива семейства Debian:Код (Text):
gcc --version gcc (Raspbian 6.3.0-18+rpi1) 6.3.0 20170516 Copyright (C) 2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Всё замечательно, и теперь мы наблюдаем наш файловый менеджер во всей красе:Код (Text):
apt-get install mc
Рисунок 11. файловый менеджер Midnight Commander.
Сенсорный экран
А что же всё-таки с нашим мини-экраном? Давайте попробуем его завести. Выключаем систему, подключаем всё по изначальной схеме. Простое подключение экрана здесь не поможет, необходимо провести дополнительную настройку, отредактировав файл /boot/config.txt и прописать в него следующие параметры:
Первый параметр hdmi_group задаёт группу HDMI-выхода. Могут быть использованы следующие значения:Код (Text):
hdmi_group=2 hdmi_mode=87 hdmi_cvt=1024 600 60 0 0 0 hdmi_force_hotplug=1
В зависимости от параметра hdmi_group устанавливается следующий параметр hdmi_mode – целое число, означающее режим работы графической системы. Однако в нашем случае дисплей имеет нестандартное разрешение, и поэтому мы вынуждены прибегнуть к вручную задаваемым параметрам графического режима, а параметр hdmi_mode в связи с этим выставить в значение 87.
- 0 – автоматическое определение по EDID;
- 1 – CEA (Consumer Electronics Association) – стандарт, используемый обычно в телевизорах;
- 2 – DMT (Display Monitor Timings) – стандарт, обычно используемый мониторами.
Чтобы явно задать параметры графического режима, нужно прописать параметр hdmi_cvt, который представлен шестью числами в следующем порядке:
Осталось разобраться с последним параметром – hdmi_force_hotplug. Если этот параметр выставлен в 1, то графическая подсистема считает, что к HDMI-порту дисплей подключен всегда и использует параметры именно заданного графического режима.
- ширина экрана в пикселах, в нашем случае 1024;
- высота экрана в пикселах, в нашем случае 600;
- частота обновления кадров, в нашем случае 60;
- соотношение сторон: 1 – 4:3, 2 – 14:9, 3 – 16:9, 4 – 5:4; 5 – 16:10, 6 – 15:9, в нашем случае явно не задаётся;
- наличие краёв в изображении: 0 – отсутствуют, 1 – присутствуют;
- развёртка: 0 – прогрессивная развёртка; 1 – черезстрочная развёртка;
- яркость: 0 – обычная; 1 – пониженная.
Теперь выключаем операционную систему, подключаем дисплей, и включаем снова. Теперь всё работает, как и ожидалось.
Рисунок 12. Операционная система Raspbian на тачскрине.
Заключение
В целом, поигравшись несколько часов, не могу не отметить, что платформа Raspberry Pi 3 произвела на меня куда более положительные впечатления, нежели я ожидал. Штатная операционная система Raspbian достаточно легковесна и шустра и не имеет каких-либо проблем с установкой дополнительного программного обеспечения. Тачскрин также успешно завёлся. Немного, конечно, расстроило, что для Raspberry Pi 3 пока нет официально поддерживаемой 64-разрядной операционной системы, но, тем не менее, это не умаляет всей интересности проекта. Следите за обновлениями!
Знакомимся с Raspberry Pi
Дата публикации 13 янв 2018
| Редактировалось 14 янв 2018