Пингвин в гостях у Дельфина, или UNIX‑like система на Flipper Zero

от автора

Экран заставки FlippixOS

Экран заставки FlippixOS

Когда на экране терминала появилось приглашение #, Flipper Zero уже сложно было назвать просто устройством для работы с радиопротоколами, NFC и инфракрасными пультами. Передо мной находился маленький Unix‑подобный компьютер: с ядром, процессами, командной оболочкой и файловой системой на microSD.

Правда, до этого момента Flipper был опутан кучей проводов, прошивка загружалась через ST‑Link, консоль работала по внешнему UART, а малейшая ошибка в обмене с SD‑картой превращала загрузку в бесконечную последовательность, вселяющую страх:

sd: failed, retrying.sd: failed, retrying.sd: failed, retrying.sd: failed, retrying.sd: failed, retrying.sd: failed, retrying.

Всё началось с типичного желания айтишника запускать DOOM Linux на всём, что хотя бы теоретически способно его запустить. Flipper не стал исключением. У него есть 32-битный микроконтроллер, экран, кнопки, USB, Bluetooth и слот для microSD — неплохой набор для маленького карманного компьютера.

При этом было очевидно, что «взрослый» Linux на Flipper не запустить. Как минимум этому мешают отсутствие MMU и совсем небольшое по меркам современных операционных систем количество оперативной памяти — всего 256 КБ.

В этот момент я вспомнил, как годом ранее запускал FUZIX на Raspberry Pi Pico по гайду

Прошлогоднее фото: запускал FUZIX с кучей костылей - дисплей с разъемом microSD и flipper как UART мост

Прошлогоднее фото: запускал FUZIX с кучей костылей — дисплей с разъемом microSD и flipper как UART мост

По характеристикам Flipper оказался не так уж далёк от Pico. У RP2040 — 264 КиБ SRAM и два ядра Cortex‑M0+, а у STM32WB55 внутри Flipper — 256 КиБ SRAM и основное ядро Cortex‑M4. Разумеется, это разные микроконтроллеры, но с точки зрения возможности запуска небольшой Unix‑подобной системы они находятся примерно в одной весовой категории.

После непродолжительного поиска выяснилось, что готового порта FUZIX для Flipper Zero в интернете нет. Поэтому, пока Павел Жовнер пилит нативный linux на Flipper One, было решено попробовать адаптировать систему под железо Flipper самостоятельно.

Теперь немного о FUZIX

FUZIX — это компактная Unix‑подобная операционная система для маломощных микроконтроллеров. Её создал Алан Кокс, много лет участвовавший в разработке ядра Linux. Изначально FUZIX была ориентирована на 8-битные процессоры вроде Z80, но со временем получила порты для 6502, 68000, MSP430, ESP8266, ARM и других архитектур.

Несмотря на скромные требования к железу, это не просто командная оболочка, стилизованная под Unix. FUZIX имеет процессы, системные вызовы, файловую систему, устройства в /dev, перенаправление ввода‑вывода, каналы и привычный минимальный набор консольных утилит. В ней можно создавать файлы, запускать программы и работать с каталогами почти так же, как в старых Unix‑системах.

Разумеется, сравнение FUZIX с современным Linux напрямую абсолютно бессмысленно. Здесь нет MMU, виртуальной памяти как таковой, графического окружения и минимального набора драйверов. Размер процесса ограничен доступной памятью (1 процесс — 64 КБ), а многие программы приходится специально собирать или хотя бы адаптировать под систему. Но именно благодаря этим ограничениям FUZIX способна работать на маломощных устройствах, где «взрослое» Linux‑ядро даже не начнёт загрузку.

Для Flipper это выглядело подходящим компромиссом: достаточно компактная система, чтобы поместиться в доступную память (264 КБ), и при этом достаточно похожая на Linux, чтобы эксперимент не сводился к одному лишь приглашению командной строки.

Оставалось решить небольшую проблему: FUZIX ничего не знала ни о дисплее Flipper, ни о его кнопках, USB, microSD, контроллерах питания. Сначала нужно было хотя бы заставить ядро загрузиться и вывести хоть какой‑то вменяемый текст через UART.

Вдохновившись вузовским курсом по Технологиям программирования, я принялся за работу. Правда, между лабораторной работой с учебным ядром и портированием FUZIX на настоящее устройство обнаружилась некоторая разница. Здесь никто заранее не подготовил загрузочный код, таблицу прерываний и драйверы, а в случае ошибки Flipper чаще всего просто отвечал безмолвием.

Нулевой шаг: ST‑Link, UART и немного костылей

Прежде чем заниматься портированием FUZIX, нужно было получить два базовых инструмента: возможность загружать экспериментальные прошивки и хоть как‑то видеть, что происходит после запуска.

Подобный клон ST-Link я использовал для прошивки

Подобный клон ST‑Link я использовал для прошивки

Для прошивки я использовал ST‑Link V2, подключённый к отладочному интерфейсу SWD. На первых этапах без программатора было не обойтись: USB ещё не работал, собственного загрузчика у экспериментальной системы не было, а неудачная сборка могла вообще не подавать признаков жизни.

Последовательная консоль стала второй необходимой частью. Именно через UART ядро должно было выводить сообщения о загрузке, найденной памяти, SD‑карте и возникающих ошибках. Кроме того, после успешного запуска этот же интерфейс использовался для работы с командной оболочкой.

Подходящего USB‑UART‑переходника с удобным кабелем (не древний mini‑usb) под рукой сначала не оказалось, зато рядом лежала Arduino Uno. Поэтому на время она превратилась в мост между UART Flipper и USB‑портом ноутбука. Здесь обнаружился небольшой нюанс: Arduino Uno использует логику 5 В, а GPIO Flipper рассчитаны на 3,3 В. Поэтому напрямую соединять Arduino TX с Flipper RX было рискованно. Для первых тестов хватило одностороннего подключения: Flipper передавал загрузочный лог, а Arduino только принимала его.

В результате к флипперу оказались подключёны сразу несколько устройствам: ST‑Link для загрузки прошивки, UART‑переходник для реализации консоли. MicroSD карточка периодически переставлялась в картридер для записи образа файловой системы. Выглядело это скорее как тестовый стенд, чем как карманное устройство, но для первых экспериментов такой конфигурации было достаточно.

Собранная схема для прошивки и отладки на первых этапах

Собранная схема для прошивки и отладки на первых этапах

Даже с UART всё заработало не сразу. Последовательный порт мог оставаться занят предыдущим процессом, менять имя в /dev, в терминал периодически вылетало:

cu: /dev/cu.usbserial-140: Line in use

Иногда вместо загрузочного лога появлялся поток нечитаемых символов (это напоминало текст со сбитой кодировкой) — обычно из‑за неверной скорости, но после проверки проводов и минимальной настройки терминала стенд был готов.

Теперь можно было переходить к самой FUZIX: добавлять новую аппаратную платформу и пытаться получить от ядра первые осмысленные строки.

Первый шаг: знакомим FUZIX с Flipper Zero

Когда программатор и UART‑консоль были готовы, я перешёл к самой операционной системе. На этом этапе FUZIX ещё ничего не знала о существовании Flipper Zero, а Flipper, в свою очередь, совершенно не подозревал, что скоро станет Unix‑компьютером.

К счастью, начинать с абсолютно пустого проекта мне не пришлось. В исходниках FUZIX уже были порты для Raspberry Pi Pico и нескольких микроконтроллеров на Cortex‑M4. Их можно было использовать как примеры, но просто заменить название процессора в Makefile оказалось недостаточно: STM32WB55 имеет собственную карту памяти, таблицу прерываний, периферию и особенности запуска.

Первым делом я добавил в дерево исходников новую платформу platform-flipperzero. В неё вошли настройки сборки, описание памяти, стартовый код и минимальные драйверы. Компоновщику нужно было объяснить, что помещать во внутреннюю Flash, где размещать данные и стек, а какие области SRAM лучше не трогать, поскольку ими пользуется второе ядро STM32WB55.

Затем я добавил код, который выполняется сразу после сброса. Он подготавливал память, устанавливал таблицу векторов и передавал управление ядру FUZIX. Также потребовалось настроить системный таймер и обработку исключений — без них можно было выполнить несколько инструкций, но до процессов и системных вызовов дело бы не дошло.

Процесс отладки выглядел примерно так:

  1. Изменить несколько строк кода.

  2. Собрать ядро.

  3. Прошить его через ST‑Link.

  4. Посмотреть на пустой терминал.

  5. Предположить, где всё сломалось.

  6. Повторить. (очень много раз)

Пустой терминал оказался удивительно малоразговорчивым собеседником. Если ошибка происходила до инициализации UART, система не могла сообщить даже о том, что ей плохо (а плохо ей было регулярно). Поэтому код запуска я проверял постепенно: сначала выполнение после сброса, затем работу памяти, исключений и системного таймера и только после этого — переход к полноценному ядру.

Первым полезным устройством стал UART. Затем я зарегистрировал его в FUZIX как системный TTY и использовал для стандартного ввода и вывода. После этого ядро наконец получило возможность рассказывать о происходящем, а отладка стала чуть меньше напоминать гадание на кофейной гуще.

Следом в конфигурации платформы появились основные параметры системы: 32-битная архитектура, flat‑модель памяти, многозадачность, один терминал и одно блочное устройство. Системный таймер работал с частотой 10 Гц, а загрузочным диском был назначен hda — будущая microSD.

Картина, которую приходилось наблюдать регулярно

Картина, которую приходилось наблюдать регулярно

В какой‑то момент терминал наконец перестал молчать и начал выводить осмысленные сообщения. Ядро стартовало, определяло доступную память, включало прерывания и доходило до выбора загрузочного устройства.

Правда, самого загрузочного устройства у него пока ещё не было.

FUZIX уже была готова искать корневую файловую систему, но microSD Flipper оставалась для неё просто куском неизвестного железа пластика с кремнием. Поэтому следующим шагом стал первый драйвер SD‑карты — медленный, программный и написанный по принципу «сначала пусть хоть как‑нибудь заработает».

SPIсать не получится: учу FUZIX работать с microSD

К этому моменту ядро уже запускалось и выводило сообщения через UART, но загрузиться до командной оболочки всё ещё не могло. В отличие от многих микроконтроллерных прошивок, FUZIX недостаточно одного исполняемого файла во Flash. Ей нужна корневая файловая система с /init, командной оболочкой, системными каталогами и утилитами.

Хранить всё это логичнее всего было на microSD, которая уже установлена во Flipper Zero. Оставалось решить небольшую проблему: FUZIX пока не умела с ней разговаривать.

Во Flipper карта подключена по SPI. Теоретически нужно было настроить аппаратный контроллер STM32WB55, выбрать правильный режим, частоту и линии GPIO, а затем аккуратно встроить всё это в подсистему блочных устройств FUZIX. Но на первом этапе мне хотелось проверить более важную вещь: сможет ли система вообще прочитать карту и смонтировать с неё корневую файловую систему.

Поэтому первый драйвер использовал программный SPI, также известный как bit‑banging. Вместо аппаратного контроллера код вручную переключал линии тактирования и данных по схеме, подобной данной:

  1. Установить CLK на HIGH.

  2. Прочитать MISO.

  3. Установить CLK на LOW.

  4. Повторить восемь раз.

Изящным такой подход назвать крайне сложно. Передача каждого байта превращалась в набор низкоуровневых команд для GPIO, а чтение целого сектора требовало повторить его несколько тысяч раз. Зато программный SPI почти не зависел от настройки периферийного блока STM32 и позволял быстро проверить команды карты и общую логику взаимодействия с картой памяти.

После инициализации карта была зарегистрирована в FUZIX как блочное устройство hda. Ядро могло обращаться к ней секторами по 512 байт, а всё остальное брала на себя файловая подсистема.

Сам образ файловой системы собирался на ноутбуке и записывался на карту через dd. После записи я перечитывал его и сравнивал SHA-256, чтобы при очередной ошибке не гадать, сломан драйвер или просто некорректно записалась карта.

Когда карта впервые ответила, ядро смогло определить устройство и дошло до монтирования корневого раздела:

SD drive 0: type=00C0 hda:Mounting root fs (root_dev=0, rw): OKStarting /init#

Так появился первый полноценный shell. Можно было выполнять ls, переходить между каталогами, запускать программы и читать файлы с microSD.

Впрочем, радость периодически сменялась уже знакомым сообщением:

AAAAAAAAAAAA

AAAAAAAAAAAA

Причин у такого поведения могло быть множество: ошибка в таймингах, незавершённая SPI‑транзакция, неверное состояние линии CS или слишком раннее обращение к карте. Иногда после неудачной перезагрузки файловая система монтировалась только для чтения, поскольку считала себя некорректно отключённой.

Для проверки режима read‑write я использовал максимально простой тест:

echo RW_TEST > /rwtestcat /rwtestsync

Затем Flipper перезагружался, а файл проверялся повторно. Если содержимое оставалось на месте, значит, работало не только чтение, но и запись, сброс кэша и повторное монтирование файловой системы.

Программный SPI выполнил свою задачу: доказал, что FUZIX может использовать внутреннюю microSD Flipper как полноценный корневой диск. Но оставлять его в таком виде мне конечно не хотелось. Он был медленным, отъедал ресурсы процессора во время каждой передачи и слишком сильно зависел от точности программных задержек.

Поэтому следующим этапом стала замена этого временного решения аппаратным SPI2. После неё микроконтроллеру больше не приходилось косплеить тактовый генератор для каждого бита, а работа с файловой системой стала заметно быстрее и стабильнее.

Хватит дёргать GPIO: переходим на SPI2

Хоть программный SPI показал, что карта и файловая система работают, оставлять его в постоянной прошивке мне точно не хотелось. Процессор тратил время на практически ручное переключение GPIO для каждого бита, а скорость и стабильность сильно зависели от программных задержек.

Переход на аппаратный SPI2 оказался проще, чем я ожидал. Нужно только было настроить выводы STM32WB55, выбрать режим SPI, оставить программное управление линией CS и установить две скорости, вычисленные экспериментальным путем: около 250 кГц для инициализации карты и 8 МГц для обычной работы.

После этого передачей отдельных битов занялся периферийный контроллер, а драйверу осталось только отправлять и принимать готовые байты. Чтение карточки microSD заметно ускорилось, загрузка стала стабильнее, а процессор наконец перестал косплеить генератор тактового сигнала.

На всякий случай я добавил тайм‑ауты ожидания аппаратных флагов. Если вдруг карта или SPI‑контроллер зависнут, ядро получит ошибку вместо бесконечного ожидания.

Так временный bit‑banging окончательно уступил место нормальному аппаратному интерфейсу. Правда, позднее выяснилось, что SPI2 в Флиппере нужен не только SD‑карте, но и дисплею — однако это уже совсем другая история.

Записать мало — нужно ещё сохранить

После перехода на аппаратный SPI чтение карты работало стабильно, и я решил проверить запись:

echo RW_TEST > /rwtestcat /rwtest

Файл успешно создавался, но после регулярно (но не всегда) исчезал, а файловая система иногда монтировалась только для чтения:

Filesystem was not cleanly unmounted.

Причина оказалась вполне типичной для Unix‑систем: часть данных оставалась в кэше и не успевала попасть на карту до сброса микроконтроллера. Поэтому я добавил нормальную обработку synchalt и reboot. Перед перезагрузкой система сбрасывала данные на microSD и отмечала файловую систему как корректно отключённую.

Финальная проверка выглядела так:

echo FINAL_RW_TEST > /finaltestsynchalt

После следующей загрузки файл остался на месте. Так microSD наконец стала полноценной read‑write корневой файловой системой, а не игрой в русскую рулетку с сохранением данных.

Убираем ещё один провод: USB CDC

После того как загрузка с microSD стала стабильной, следующим кандидатом на то, чтобы быть убранным оказался внешний UART‑переходник. У Flipper уже есть USB‑C, поэтому логично было использовать его и для системной консоли.

Я добавил поддержку USB CDC, и после загрузки FLIPPIX стала определяться на компьютере как обычный последовательный порт:

sudo cu -l /dev/cu.usbmodemB6C031B91 -s 115200

Теперь для работы с командной оболочкой был нужен только стандартный USB‑кабель. UART при этом остался запасным способом отладки — на случай, если мое очередное вмешательство в прошивку снова положит работу USB CDC.

Разумеется, не обошлось без проблем: после перезагрузки устройство могло сменить имя, зависнуть на слове Connected или потребовать переподключения кабеля. Но после исправления повторной инициализации USB консоль стала достаточно стабильной, а один лишний переходник наконец исчез со стола.

Убираем ST‑Link: прошивка через обычный USB

После перехода консоли на USB‑C последней необходимой кучей проводов оставался ST‑Link. Использовать программатор для каждого обновления было крайне неудобно (да и перенос флиппер с ST‑Link явно удобства не добавлял). Особенно если прошивку должен установить еще кто‑то кроме её слегка сумасшедшего разработчика.

К счастью, в STM32WB55 есть встроенный системный загрузчик с поддержкой USB DFU. Я добавил команду reboot2dfu, которая корректно завершает работу с microSD и перезапускает контроллер прямо в ROM DFU.

После этого новую сборку можно было загрузить обычным USB‑кабелем:

dfu-util -d 0483:df11 -a 0 \  -s 0x08000000:leave \  -D flippix-fuzix.bin

Так ST‑Link перестал наконец быть обязательной частью установки обновлений и превратился в аварийный инструмент на случай, если очередная версия прошивки совсем перестанет загружаться. Для штатного обновления FLIPPIX теперь было достаточно одного USB‑C кабеля.

Теперь с экраном: от терминала к собственному интерфейсу

После появления USB CDC системой уже можно было пользоваться без внешнего UART, но сам Flipper при этом оставался почти полностью неактивным. Вся работа с консолью происходила только в терминале компьютера, а встроенный экран вообще ничего не показывал.

Первой задачей стало простое зеркалирование консоли. Всё, что FUZIX отправляла в USB CDC, одновременно передавалось драйверу дисплея. Так на экране начали появляться загрузочный лог, приглашение #, введённые команды и результаты их выполнения.

Дисплей Flipper имеет разрешение всего 128×64 пикселя, поэтому разместить на нём полноценный терминал непросто. В обычном режиме получилось 21 знакоместо по горизонтали и семь строк текста. Позже я добавил компактный шрифт 4×6, с которым консоль вмещает уже 32 символа и девять строк правда читаемость оставляет делать лучшего, но я его все равно оставил.

Драйвер также научился обрабатывать минимальный набор управляющих последовательностей: перевод строки, возврат каретки, backspace, tab и некоторые ANSI‑команды перемещения и очистки экрана. Полноценной эмуляцией терминала это назвать крайне сложно, но для shell и большинства простых утилит возможностей вполне достаточно.

Неожиданная проблема обнаружилась на уровне железа: дисплей и microSD используют общий SPI2. Стоило экрану начать передачу в середине операции с картой, как вместо содержимого каталога появлялось уже знакомое:

sd: failed, retrying.

Пришлось добавить совместное управление шиной: перед обращением к экрану драйвер проверяет, завершила ли SD‑карта свою транзакцию, переключает настройки SPI и аккуратно управляет линиями CS. После передачи конфигурация восстанавливается, и карта может продолжить работу.

Для консоли появилась история на 64 строки. Кнопками вверх и вниз можно прокручивать предыдущий вывод — как одиночными нажатиями, так и удержанием. Если в терминале появляется новый символ, экран автоматически возвращается к актуальной нижней строке. В верхней части отображается небольшой индикатор, показывающий, насколько далеко пользователь ушёл от текущего вывода.

Постепенно к консоли добавилась статусная строка с названием FLIPPIX, номером версии, процентом заряда и графическим значком батареи. Состояние аккумулятора читается из BQ27220, а отдельный контроллер BQ25896 сообщает, идёт ли зарядка. Если Flipper подключён к питанию, рядом с батареей появляется небольшой значок молнии, правда с неприятной задержкой в 10 секунд.

На этом можно было остановиться, но пользоваться всей системой через одно приглашение # всё же не очень удобно. Поэтому поверх текстового драйвера появился небольшой интерфейс с главным меню:

Меню на флиппере, правда уже на гораздо более поздней версии

Меню на флиппере, правда уже на гораздо более поздней версии

Это не отдельная графическая оболочка и не тяжёлый UI‑фреймворк. Меню реализовано как небольшая машина состояний внутри платформенного кода: кнопки меняют выбранный пункт, а драйвер перерисовывает только изменившиеся строки. Такой подход требует совсем немного памяти, что для FUZIX важнее красивых анимаций.

В разделе Settings можно выбрать яркость подсветки, обычный или компактный шрифт консоли, а также время её автоматического отключения: 30 секунд, одну минуту, две минуты или Никогда. Отключается только подсветка — сама система, экран, USB и microSD продолжают работать. Нажатие кнопки, ввод с BLE‑клавиатуры (об этом далее) или новый вывод в консоль снова включают подсветку.

Раздел Power содержит команды RebootHalt и USB flash (ROM DFU). Для каждой из них требуется отдельное подтверждение, чтобы случайное нажатие не отправило устройство в перезагрузку посреди записи на карту. Также я добавил отображение текущего состояния Flipper через встроенный светодиод: зеленый — работает CDC, синий — DFU, желтый все остальное время.

Здесь обнаружилась любопытная аппаратная особенность: кнопка OK одновременно связана с линией BOOT0 микроконтроллера. Если выполнить сброс, пока пользователь всё ещё держит кнопку, обычная перезагрузка может неожиданно закончиться входом в ROM DFU. Поэтому перед reset прошивка ждёт, пока кнопка не будет отпущена.

Для ввода команд без компьютера я добавил экранную QWERTY‑клавиатуру. На ней есть буквы, цифры, символы, пробел, Backspace и Enter, а выбранная клавиша обводится рамкой в стиле оригинального интерфейса Flipper. Печатать длинные команды кнопками не очень удобно, но для lscdcat или reboot этого вполне хватает.

Bluetooth‑клавиатура: здесь всё пошло немного не по плану

После появления экранной консоли и меню захотелось сделать Flipper полностью автономным. Вводить команды экранной клавиатурой можно, но после нескольких строк начинаешь особенно ценить существование физических клавиш. Поэтому следующим пунктом стала поддержка обычной Bluetooth‑клавиатуры.

На бумаге задача выглядела просто:

  1. Включить Bluetooth.

  2. Найти клавиатуру.

  3. Подключиться.

  4. Получать нажатия клавиш.

  5. Передавать символы в TTY.

На практике каждый из этих пунктов оказался отдельным приключением.

STM32WB55 содержит два процессорных ядра. На Cortex‑M4 работает FLIPPIX, а второе ядро Cortex‑M0+ выполняет закрытый беспроводной стек ST. Основная прошивка не управляет радио напрямую: она отправляет команды второму процессору через IPCC и получает обратно HCI‑события.

Иными словами, чтобы принять букву A, сначала нужно запустить второй процессор, договориться с ним через общую память, инициализировать BLE‑стек, найти устройство, подключиться, провести pairing, обнаружить HID‑сервис, найти нужную характеристику и подписаться на уведомления. И только после этого можно надеяться увидеть код клавиши.

Первым достижением стала надпись:

BLE: starting

Правда, она могла оставаться на экране бесконечно. После очередной серии изменений появился следующий уровень успеха:

BLE: CPU2 ready

Это означало, что второй процессор хотя бы ответил. До работающей клавиатуры по‑прежнему было далеко, но теперь система зависала уже немного позже.

Дальше началась коллекция кодов ошибок:

F0F2Scan error 01Connect error 0CConnect error 13Connect error 82Connect error 91

Некоторые сборки зависали на starting, другие доходили до CPU2 ready, а затем переставали создавать USB CDC. Иногда Flipper загружался только после переподключения кабеля. В особенно удачные дни после перезагрузки на экране снова появлялся F2.

Положение осложнялось тем, что для STM32WB существует несколько вариантов беспроводной прошивки CPU2. Нужно было не только правильно запустить сопроцессор, но и убедиться, что установлен подходящий BLE Full stack. После нескольких диагностических сборок FLIPPIX наконец смогла прочитать его версию:

Radio: 01.14.00 T03BLE: CPU2 ready

Следующим этапом стало сканирование. Поначалу оно стабильно завершалось сообщением:

Scan error 01

Когда сканирование всё же заработало, список выглядел как набор адресов и кодов. Технически устройства вокруг были найдены, но определить, где среди них находится клавиатура, было непросто. Пришлось разбирать advertising packets, искать имя устройства и признаки HID‑периферии. После этого в меню наконец начали появляться понятные названия.

Клавиатура нашлась. Оставалось подключиться, что, разумеется, тоже не получилось с первого раза.

Интерфейс проходил через несколько обнадёживающих состояний:

Connecting...Finding HID...Subscribing...

А затем выдавал очередной Connect error. Иногда клавиатура гасила светодиоды прямо на этапе Finding HID, словно тоже уставала ждать и уходила спать. Это была самая дешёвая Bluetooth‑клавиатура из ближайшего Fix‑Price, поэтому отличить ошибку моего стека от особенностей её прошивки было не просто.

В какой‑то момент соединение наконец установилось, но символы в консоли не появлялись. При этом диагностический счётчик принятых пакетов увеличивался при каждом нажатии. Значит, радио, подключение и подписка уже работали — проблема находилась где‑то между сырым HID‑отчётом и FUZIX TTY.

Я добавил отображение содержимого входящих пакетов. После нажатия клавиш экран показывал:

A:     D:0000040000000000Enter: D:0000280000000000Space: D:00002C0000000000Esc:   D:0080000000000000

Это был важный момент: клавиатура действительно передавала правильные HID usage codes. Оставалось преобразовать их в ASCII и управляющие события.

После ручного добавления таблицы раскладки буквы наконец начали появляться в shell. Работали lspwdcat и другие команды. Казалось, что на этом Bluetooth можно считать побеждённым.

Примерно через 40 секунд клавиатура отключилась.

Сначала я решил, что она просто уходит в режим энергосбережения. Но отключение происходило даже при активном вводе. Причина оказалась в параметрах BLE‑соединения и обработке запросов периферии на их обновление. Клавиатура предлагала новые connection parameters, а FLIPPIX отвечала не так, как она ожидала. Через некоторое время supervision timeout завершал соединение.

Клавиатура и fllipper

Клавиатура и fllipper

После исправления обработки connection update клавиатура перестала отключаться и смогла работать непрерывно. В этот момент обычный ввод текста уже казался слишком простой функцией, поэтому появились дополнительные возможности:

  • стрелки вверх и вниз пролистывают историю команд;

  • Enter отправляет команду в shell;

  • Esc работает как кнопка Back;

  • Shift+Up и Shift+Down прокручивают экранную консоль;

  • история хранит несколько последних команд.

В результате Bluetooth‑клавиатура стала одной из самых сложных частей проекта. Для UART достаточно было правильно соединить три провода (не запутаться в трех соснах). BLE потребовал второго процессора, отдельной прошивки радиостека, транспорта между ядрами, сканирования, pairing, обнаружения сервисов, подписки на характеристики, разбора HID и настройки устойчивого соединения.

Зато после всех F20C1382 и 91 Flipper наконец превратился в настоящий маленький терминал: выбираешь клавиатуру в меню, подключаешься и вводишь команды без компьютера и проводов. Именно в этот момент идея карманного Unix‑компьютера перестала быть просто красивым описанием.

Стоит отдельно добавить что после каждой перепрошивка флиппера второй сопроцессор в нем остается в стадии подготовки и для того чтобы он заработал устройство необходимо еще раз вручную перезагрузить. При этой перегрузке может показаться что Флиппер завис но где‑то через 40 секунд устройство нормально перезагрузится и покажет стартовый экран.

ls без терминала: файловый браузер и USB File Bridge

После появления меню и экранной клавиатуры FLIPPIX уже можно было использовать без компьютера, но работа с файлами по‑прежнему оставалась исключительно терминальной. Чтобы посмотреть содержимое какого‑либо файла с карты, нужно было открыть консоль, набрать ls, затем cd, а для чтения файла — cat.

Поэтому следующим пунктом в главном меню стал файловый браузер. Он показывает содержимое microSD в привычном для Flipper виде: сначала каталоги, затем обычные файлы. Кнопками можно перемещаться по списку, заходить в подкаталоги и возвращаться назад.

Для текстовых файлов появился простой просмотрщик. Он переносит длинные строки по ширине экрана, заменяет непечатные символы точками и позволяет прокручивать содержимое кнопками вверх и вниз. Полноценного редактора пока нет, но посмотреть конфигурацию, лог или небольшой исходный файл уже можно без команды cat.

При реализации обнаружился важный нюанс: читать каталог или файл непосредственно из обработчика системного таймера нельзя. Операция с microSD может занять заметное время, а вместе с ней остановятся USB, кнопки и остальные системные задачи. Поэтому интерфейс только создаёт запрос на чтение, а файловая операция выполняется позже в обычном контексте ядра. После её завершения меню обновляет содержимое экрана.

Однако для копирования файлов всё ещё приходилось извлекать microSD и переставлять её в картридер. В стоковой прошивке Flipper содержимое карты доступно через USB, поэтому хотелось получить похожую возможность и в FLIPPIX.

Так появился USB File Bridge, состоящий из двух частей:

  • /bin/filebridge работает внутри FUZIX;

  • flippix-files запускается на компьютере и общается с Flipper через USB CDC.

С его помощью можно просматривать каталоги и передавать файлы, не извлекая карту:

flippix-files list /flippix-files upload local.txt /tmp/local.txtflippix-files download /tmp/local.txt copy.txtflippix-files mkdir /new-directoryflippix-files delete /new-directory

Использовать для этого обычный терминальный режим оказалось нельзя. TTY обрабатывает управляющие символы, перевод строк и echo, что совершенно не подходит для произвольного бинарного файла. Поэтому в драйвере CDC появился специальный raw‑режим: на время передачи данные идут напрямую между USB и программой filebridge, минуя обычную терминальную обработку.

Файлы передаются небольшими блоками. Для каждого блока вычисляется CRC32, а после завершения проверяется контрольная сумма всего файла. Сначала данные записываются во временный файл, и только после успешной проверки он получает окончательное имя. Если кабель отключится посреди передачи, уже существующий файл не будет испорчен.

Разумеется, первая проверка сразу закончилась ошибкой:

ERR CRC mismatch ae2ec6cb 7fffffff

Выглядело так, будто USB повреждает данные. Но фактическая CRC принятого блока — ae2ec6cb — полностью совпадала с расчётом на компьютере. Ошибочным оказалось ожидаемое значение: библиотечный strtoul() внутри FUZIX ограничивал числа выше 0x7fffffff.

После замены его небольшим собственным парсером 32-битных шестнадцатеричных значений контрольные суммы совпали. Финальная проверка включала загрузку бинарного файла на Flipper, обратное скачивание, сравнение SHA-256 и побайтовый cmp:

uploaded 6768 bytesdownloaded 6768 bytesFULL_ROUNDTRIP_VERIFIED

Так файловый браузер решил задачу просмотра данных на самом Flipper, а USB File Bridge — их обмена с компьютером. Картридер, наконец, перестал быть обязательной частью каждого обновления файловой системы.

Что получилось и что дальше

FLIPPIX OS начиналась с довольно скромной цели: загрузить FUZIX на Flipper Zero и увидеть приглашение #через UART. Первая рабочая версия требовала ST‑Link, внешнего UART‑переходника, картридера и приличного количества проводов.

Постепенно временный стенд превратился в более самостоятельную систему. Сейчас FLIPPIX умеет:

  • загружать FUZIX с read‑write файловой системой на microSD;

  • работать с картой через аппаратный SPI2;

  • предоставлять консоль через USB CDC;

  • обновлять прошивку через обычный USB‑C без обязательного ST‑Link;

  • выводить терминал на встроенный экран;

  • показывать заряд аккумулятора и состояние зарядки;

  • управляться через меню, физические кнопки и экранную клавиатуру;

  • подключать BLE HID‑клавиатуру;

  • просматривать каталоги и текстовые файлы;

  • передавать файлы между компьютером и microSD через USB File Bridge;

  • безопасно выполнять synchaltreboot и переход в ROM DFU.

При этом FLIPPIX не превращает Flipper Zero в полноценный Linux‑компьютер. Это всё ещё микроконтроллер без MMU, с 256 КБ SRAM и ограничением примерно в 64 КБ для одного пользовательского процесса. Обычные Linux‑программы здесь не запустятся — их нужно отдельно собирать или адаптировать под FUZIX.

Мой арт с дельфином из Flipper и пингвином TUX, который я поставил как страницу загрузки

Мой арт с дельфином из Flipper и пингвином TUX, который я поставил как страницу загрузки

Не поддерживается и значительная часть фирменного железа Flipper: Sub‑GHz, NFC, RFID и инфракрасный порт пока остаются за бортом проекта. Поэтому сейчас FLIPPIX скорее представляет собой экспериментальный карманный Unix‑терминал и платформу для изучения embedded‑разработки, чем замену оригинальной прошивке.

Практическая необходимость запускать Unix‑подобную ОС на Flipper Zero, конечно, сомнительна — я понимаю что это напоминает троллейбус из буханки хлеба. Но именно в процессе такого эксперимента пришлось разобраться с запуском ARM‑ядра, системными вызовами, таблицей прерываний, SPI, файловой системой, USB, BLE, HID, дисплеем и контроллерами питания. В этом смысле символ # в терминале оказался только началом.

В дальнейших планах:

  • сохранять настройки интерфейса между перезагрузками;

  • улучшить файловый браузер и добавить простые операции с файлами;

  • реализовать текстовый редактор;

  • добавить поддержку RTC и системного времени;

  • продолжить работу с энергосбережением;

  • исследовать GPIO, инфракрасный порт и другие периферийные устройства;

  • попробовать использовать Wi‑Fi Devboard как сетевой сопроцессор;

  • портировать дополнительные программы и утилиты FUZIX;

  • упростить первоначальную установку и обновление системы.

Исходный код порта находится в моем репозитории:

Готовые образы, инструкции на русском и английском и релизные файлы находятся отдельно:

Оригинальный проект FUZIX:

Прошивка пока остаётся экспериментальной и не заменяет оригинальную прошивку Flipper Zero (это не форк оригинальной прошивки). Перед установкой стоит ознакомиться с инструкцией и иметь под рукой qFlipper для восстановления устройства.

А главный вывод получился довольно простым: если устройство содержит процессор, немного памяти и хотя бы один последовательный интерфейс, рано или поздно кто‑нибудь обязательно попробует запустить на нём что‑то хотя бы отдаленно напоминающее linux. На этот раз очередь дошла и до Flipper.

ссылка на оригинал статьи https://habr.com/ru/articles/1061462/