Предистория
Организованность — не сильная сторона. Периодически на столе скапливается куча вещей: заказы с Aliexpress, компоненты для проектов, подарки и прочее, что не нашло своё место. Вот так и родился этот проект.
В один момент на столе лежали:
- Несколько 4G модемов, совместимых с openstick:
- MF800 — выбран для проекта
- UZ801 — неплохой размер, но нет батареи и достаточного количества доступных GPIO
- USB-модем — отложен из-за других проблем
- Клавиатура Clicks для iPhone 16 Pro Max (подарок от кузена — жаль, что нет подходящего iPhone 🥲)
- Дисплей Adafruit SHARP Memory Display
И разум, как в пещерные времена, соединил все три компонента вместе.
Конечно, вдохновение пришло не с пустого места — ранее доводилось узнать о:
Модем
По волшебству розницы и цепочек поставок можно купить 4G модем с WiFi, Bluetooth, дисплеем, собственной батареей и полностью разблокированный — менее чем за $20. Это — краеугольный камень проекта.
Существуют версии с дисплеем и без (вместо экрана — светодиоды), печатная плата одинаковая. Дисплей управляется чипом GC9107, но выглядит ужасно, поэтому отказались от него.
Linux устанавливается тривиально благодаря проекту openstick. Заводская прошивка — Android, но adb доступен сразу. Через adb можно перейти в режим EDL и перепрошить устройство. Важно сохранить все критичные разделы. На печатной плате есть контакты, замыкание которых выводит устройство прямо в EDL.
Дерево устройств, извлечённое из запущенной Android, оказалось кладезем информации.
Полезные ресурсы:
- GitHub OpenStick
- openstick.de
- Блог wvthoog.nl — использовался этот kernel для начала
- Блог extowerk.com
- GitHub проекта
Сложности с openstick начинаются на этапе драйверов и деревьев устройств, об этом — ниже.
Клавиатура
О клавиатуре Clicks сказать особо нечего. Очень приятна в использовании, единственная беда — цена (и придётся её резать 😱).
Протокол стандартный — обычная USB клавиатура с дополнительным proprietary endpoint от Apple, который iPhone может авторизовать перед использованием.
Для текущего проекта это значит, что это просто обычная клавиатура.
У Clicks есть мобильное приложение для конфигурации и обновлений. Сама клавиатура работает на CH32V203 или аналоге. Теоретически можно залить свой код, но причин для этого пока нет. В будущем хотелось бы утилиту для конфигурации.
Дисплей
SHARP дисплей выглядит отлично с высокой контрастностью, что прекрасно подходит для приложения — устройства только для сообщений. Кроме этого — тут нечего добавить.
Это просто дисплей. Отправляешь команды. Он отображает.
Адаптерная печатная плата
Быстро стало ясно, что нужна собственная плата. Неясно, что находится внутри MF800 под экраном, никогда не открывали, но уверенно можно сказать — внутри нет 5V повышающего преобразователя.
По причинам размера USB разъём пришлось бы обрезать, нужен способ это обработать.
Финальная плата обрабатывает: питание USB в режиме host, переключение режимов USB host/device, питание дисплея и преобразование уровней сигнала дисплея.
Плату заказали с поверхностной сборкой. Двусторонняя сборка дорогая, поэтому уместили все компоненты с одной стороны. Вторая сторона используется для контактных площадок межплатных соединений.
Все файлы на GitHub. Вкратце, плата содержит:
- TUSB320 — переключение режимов USB
- SN74LVC8T245 — преобразование уровней сигналов
- MCP1640 — 5V преобразователь
- TPS22917 (два экземпляра) — коммутация VBUS/VBAT
- USB разъём и FPC разъём для дисплея
- Тестовые площадки для подключения к MF800
Корпус. Часть 1
MF800 значительно больше других openstick модемов — из-за батареи и из-за свободной компоновки.
Чтобы втиснуть в корпус Clicks, либо ориентируем вертикально (получится чудовище), либо обрезаем плату для горизонтальной ориентации.
Правый разрез перерезает линию подключения батареи — это потребует дальнейшей пайки.
Резание платы
Резание прошло неожиданно гладко. Устройство загрузилось сразу, всё работало. Оказалось, под этими участками платы не проходили критичные линии.
Не тестировали USB соединение и 4G модем. Модем не вызывал опасений, а USB беспокоил — может быть случайно повредили via.
Пайка. Часть 1
Решили переиспользовать контактные площадки от старого дисплея. Рационально в этом не было, может быть, планировалось использовать жёсткий flex PCB с похожей схемой пайки (от идеи отказались сразу при виде цены).
Сейчас это выглядит очень наивно. Нужно было использовать помеченные площадки рядом с неустановленным разъёмом microSD. Никогда не проверяли, расшарены ли они с контактами SIM карты.
Когда устройство загрузилось и получили дерево устройств из Android, извлекли какие пины используются для SPI дисплея. Протестировали их gpioset, чтобы убедиться в правильности. То же самое для питания (отключали в дереве устройств и перезагружались) и земли.
Первым делом спаяли и проверили питание, потом USB. Это позволило тестировать отдельный функционал. USB вызывал больше опасений из-за схемотехники и того самого подозрительного via.
Сначала не работало. После проверки напряжений на площадках заметили в dmesg ноутбука, что USB ПЫТАЛСЯ перечислить устройства — что-то происходит.
Также заметили "high-speed". Удивило — не ожидали high-speed USB. Первая мысль — провода слишком длинные. Но перед тем, как их укоротить, попробовали быстрый хак — плотнее закрутили — и сработало 😲!
Сразу после попытались включить обратное направление. Потребовалось время. Оказалось, не все адаптеры с Aliexpress корректно проводят CC линии. Клавиатура сработала сразу, просто боялись тестировать первым, чтобы не сломать.
Пайка. Часть 2
Перед пайкой SPI линий к дисплею решили проверить, правильно ли переконфигурировали дерево устройств. Площадки проверили, но много может пойти не так.
spi@78b9000 {
compatible = "qcom,spi-qup-v2.2.1";
reg = <0x78b9000 0x500>;
interrupts = <0x00 0x63 0x04>;
clocks = <0x13 0x41 0x13 0x36>;
clock-names = "core", "iface";
dmas = <0x6d 0x0c 0x6d 0x0d>;
dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <0x84>;
pinctrl-1 = <0x85>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
spidev@0 {
//compatible = "linux,spidev";
compatible = "rohm,dh2228fv";
reg = <0>;
spi-max-frequency = <16000000>;
spi-cs-high;
};
};
Драйверы Qualcomm — нестабильны, и сокращённый compatible не экспортирует spidev, поэтому используем хак с dh2228fv.
С помощью утилиты spi-pipe в цикле смогли зафиксировать изменение напряжения на MOSI и CLK — достаточно чтобы понять, что что-то происходит. Предпочтительнее было бы использовать осциллограф или логический анализатор, но их не было под рукой.
Воодушевлённые успехами с питанием и USB, беспечно подключили дисплей к плате на включённом устройстве 🤦.
С дисплеем тут же что-то произошло, были уверены, что сломали. К счастью, ничего страшного не случилось. (Это происходит каждый раз при включении. Не уверены, нужно ли беспокоиться 😬.)
Убедившись, что ничего не дымит и не перегревается, начали разбираться с дисплеем.
После часа мучений с Python кодом (помощь AI), достигли нулевого прогресса. Слишком много точек отказа: преобразователь уровней, плохая маршрутизация, контакты и прочее.
Как обычно бывает (непонятно почему), драйвер Qualcomm плохо работает с CS (или не работает как надо). Повторили тот же тестовый скрипт, но на этот раз переключали CS пин вручную через libgpiod — и сработало!
Переделка проводки
Сразу разочаровались во всём. От тонкого провода для питания до хрупкого магнитного провода для сигналов и закрученной земли вокруг MOSI и CLK (что ничего не дало) — решили переделать всё.
На этот раз использовали магнитный провод потолще для сигналов и питания, он хорошо держит форму. USB data линии не переделывали — площадки выглядят очень подозрительно.
Драйвер дисплея в ядре
Все предыдущие тесты использовали простые Python скрипты, но правильный путь — драйвер ядра. Есть несколько вариантов драйверов, выбрали sharp-drm-driver от ardangelo. Причины выбора DRM драйвера:
- Позволяет использовать прямой вывод для видео (например,
mpv)- С framebuffer тоже можно, но это нестабильно в 2026
- Можно запустить X/Wayland
- Может быть, даже десктопное окружение для прикола
- Расширить драйвер для частичных обновлений
- Остаётся интерфейс framebuffer
Сработало почти сразу. Немного поэкспериментировали с CS и его активным высоким уровнем по умолчанию.
Вероятно впервые в жизни не было проблем с компиляцией модуля ядра. Использовавшееся ядро — 6.12.1-msm8916 с поддержкой модулей. Там был файл .config, который взяли.
Потом скачали mainline Linux 6.12.1 и надеялись, что серьёзных изменений нет. Оказалось достаточно, и после нескольких патчей драйвера всё просто заработало.
Вот как выглядит финальное дерево устройств. Обратите внимание, CS логика обрабатывается самим драйвером дисплея.
spi@78b9000 {
compatible = "qcom,spi-qup-v2.2.1";
reg = <0x78b9000 0x500>;
interrupts = <0x00 0x63 0x04>;
clocks = <0x13 0x41 0x13 0x36>;
clock-names = "core", "iface";
dmas = <0x6d 0x0c 0x6d 0x0d>;
dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <0x84>;
pinctrl-1 = <0x85>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
sharp_drm@0 {
compatible = "sharp-drm";
reg = <0>;
spi-max-frequency = <4000000>;
cs-gpios = <0x49 18 1>;
};
};
Проблема в том, что драйвер просто округляет цвета пикселей ниже какого-то значения в чёрный, выше — в белый (или наоборот, зависит от параметров). Для текста (или если готовить графику самостоятельно) это отлично, но для общего случая с видео и изображениями — выглядит ужасно. Решение — добавить dithering в драйвер.
Добавили пользовательское значение sysfs, которое позволяет включить dithering и выбрать алгоритм:
- Atkinson для видео
- Floyd-Steinberg для статичных изображений
Все исходные функции остались нетронутыми.
Подробнее о DRM драйвере и патчах можно найти на GitHub проекта.
Корпус. Часть 2
Когда всё работало и размеры стали известны, начали моделировать почти финальный корпус, который можно втиснуть в корпус клавиатуры без опасений сломать что-то.
Это было рискованно, потому что Apple не публикует расстояние USB-C разъёма внутри iPhone. Обошлись тем, что измерили стандартные кабели Apple (они плотно прилегают) и интерполировали.
Этот прототип оказался не особо полезен, но подтвердил правильность размеров USB разъёма и других элементов.
Две боковые стенки не напечатались, и при моделировании дисплей не был в наличии (кроме припаянного к девкит плате), поэтому крышка неполная.
Финальный корпус (пока)
Почти всё подошло идеально. Сначала быстро сбили прототип, скреплённый каптоновой лентой.
На этом этапе, из-за личных обстоятельств, потеряли доступ к куску инструментов (в основном 3D принтеру и остальному).
Волей обстоятельств решили склеить корпус горячим клеем вместо печати финальной версии с защёлками.
Также забыли о кнопке питания — она на плате и работает, но корпус не имеет выреза и кнопки. Решили маленькой дырой и булавкой. Очень некрасиво, но работает.
Большой момент — обрезание корпуса клавиатуры без инструментов. Результат предсказуемо некрасив. Старались обрезать меньше, чтобы потом обработать напильником.
Реалистично, не так уж плохо, но края нуждаются в обработке. Большая проблема — выступ горячего клея из корпуса.
Благодаря фотографии сверху вниз скрыли большую часть недостатков.
Забыли фотографировать сборку. Это то же самое, что раньше, плюс гибкая 4G антенна, припаянная к плате и приклеенная под дисплеем в верхней половине корпуса. Также установлена мини SIM.
Конфигурация батареи
Скопированные из Android дерево устройств содержали предустановленные конфигурации батареи и зарядки. Они более продвинутые, чем в mainline Linux.
Но всё равно ожидали, что будет просто. Главные вопросы — информация о батарее и энергопотребление адаптерной платы.
На уровне пользователя драйвер выдавал только напряжение батареи в микровольтах и флаг, заряжается ли. Логика батареи будет в приложении, так как не планируем модифицировать драйвер только ради процента батареи.
Зарядка работает хорошо. Также работает через USB порт passthrough клавиатуры, но только когда устройство загружено.
Что не сделано
Сон — главный нерешённый вопрос. Причина — недостаток тестирования в повседневной эксплуатации, особенно с включённым модемом, и отсутствие удобной кнопки питания.
Планируем решить это в ближайшем будущем, когда начнём использовать устройство для сообщений. Хотелось бы быстрый boot/shutdown.
Дополнительные методы ввода, например сенсорный экран или скролл-колесо, были бы отличным дополнением. Сенсор, в особенности, можно реализовать с минимальным пространством.
За ними идут звук или вибрация. Даже крошечный динамик на 8 кГц. На плате хватает места для усилителя и динамика в корпусе.
Косметические проблемы
В основном горячий клей и отсутствие кнопки питания — обе требуют новой печати, плюс заусенцы на корпусе клавиатуры, которые нужно опилить.
Батарея держится на куске скотча, иначе выпадает без корпуса. Пока это не приоритет.
Большой нижний зазор, определяющий высоту корпуса, тоже можно укоротить, но потребуется батарея с тем же разъёмом и другими параметрами.
Адаптерная плата проскальзывает в своём слоте, потому что гибкий кабель дисплея тянет его, и забыли добавить удерживающие язычки в крышку корпуса. Не критично, проблема только при вставке в корпус.
Наконец, крошечная часть дисплея закрыта корпусом. Как 1-2 пиксела по краям. Это исправится при следующей печати.
В целом очень доволены устройством, но небольшая доработка улучшила бы внешний вид. На фото выглядит нормально, в реальности хотелось бы получше.
Если интересно повторить или сделать что-то похожее, большинство материалов на GitHub проекта.
Будущее
Намеренно опустили софт в этом посте, потому что он совершенствуется по мере использования. И не люблю выпускать незаконченные проекты.
На момент написания уже нашли утечку памяти в исходном драйвере дисплея. Отправили патч, который это исправляет. Такое не легко найти без реального тестирования.
Текст полностью написан мною, человеком.
Связаться можно по адресу veggie_privacy_8y at icloud dot com