Введение
Ikea — любопытный магазин. Даже если вы пришли в него с намерением купить одну конкретную вещь и не отвлекаться на всё остальное барахло, то выйдете, купив в три раза больше нужного. Для нас, хакеров, этот эффект в особенности проявляется в отделе Ikea с кабелями питания или батарейками.
Для меня последний случай такого помешательства произошёл в Нидерландах. Я находился в запланированной поездке, планируя провести в стране всего несколько недель. Однако это было в самом начале 2020 года и бедствия с COVID-19… Мне пришлось оставаться вдалеке от своей лаборатории гораздо дольше.
И когда я увидел в Ikea в линейку электронных товаров Frekvens, то не смог сдержаться и купил её всю.
Для тех, кто не знает: линейка Ikea Frekvens должна стать современным аналогом олдскульного набора «HiFi» плюс набора шестиугольной светомузыки, которая, похоже, была в домах половины подростков 90-х. В данном случае линейка соединяемых друг с другом устройств состоит из Bluetooth-динамика для вывода звука и светодиодного проектора (дополняемого различными насадками), мерцающего в такт музыке, а также светодиодного куба-дисплея, способного отображать анимации, тоже двигающиеся под музыку. Ikea хвастается, что эта линейка разработана совместно с Teenage Engineering, известной своей серией портативных «игрушечных» синтезаторов Pocket Operator.
Как показано в рекламном видео Ikea, готовый результат покупки нескольких десятков устройств должен выглядеть как невыносимый мерцающий карнавал, который мой внутренний 15-летний подросток был бы не против устроить в своей комнате.
Особенно меня заинтересовал светодиодный куб-дисплей: это качественное интегрированное устройство, работающая от электросети. На передней панели расположена матрица из 16 строк и 16 столбцов очень ярких белых светодиодов, то есть в сумме 256 светодиодов. Встроенные в устройство анимации были довольно простыми, но они привели меня к мысли, что железо на самом деле может управлять каждым светодиодом по отдельности. Однако из-за этого маленькая коробочка оказалась довольно дорогой: мой карман опустел примерно на 40 евро. За эту сумму вы получаете коробку со светодиодами и кабель питания. Так как предполагается, что вы должны купить этот товар вместе с другими продуктами линейки Frekvens, вы получаете и дополнительные соединительные элементы: удлинительный кабель питания для сквозной подачи питания от электросети к другим коробкам, а также набор винтов и пластмассовых накладок, которыми можно механически скрепить корпуса друг с другом.
Как только я добрался домой, я подключил устройство и… меня, если честно, немного разочаровало то, что они сделали с железом. Устройство имело несколько анимаций, которые можно было выбирать нажатием кнопки на задней части корпуса; в корпусе установлен небольшой микрофон — каждый раз, когда он распознаёт шум, кадр анимации меняется. Анимации не очень интересны: они состоят всего из четырёх-пяти кадров, а сами кадры только черно-белые, без оттенков серого. Если вам любопытно, то я записал небольшое видео со всеми анимациями.
Очевидно, меня это не устраивало. Давайте взломаем устройство — посмотрим, что же заставляет его срабатывать, и можно ли сделать его поведение чуть более интересным.
Вскрытие
Для разборки Frekvens достаточно будет отвёртки под крестообразный шлиц и, возможно, ножа. Также вам понадобится солидная доля упорства, ведь устройство разбирается непросто: конструкция усложнена и некоторые элементы залиты силиконом, вероятно, для избавления от вибрации и упрощения изготовления.
Разборка корпуса Frekvens начинается сзади. В корпусе есть ответстия под винты со всех сторон, за исключением передней; благодаря набору винтов и накладок из комплекта устройства его можно соединять с другими гаджетами Frekvens. Однако сзади отверстия под винты немного глубже и под ними есть винты, крепящие корпус.
Сняв заднюю крышку, мы видим, что все отверстия для винтов имеют металлические вставки с резьбой, в которые можно вкручивать винты. Здорово! Это должно обеспечить отверстиям достаточный запас прочности. Возможно, что это сильно усложняет производство или разборку; последняя осложнена тем, что нам сначала нужно разрезать капли затвердевшего силиконового компаунда.
Вставки на самом деле крепятся другими пластмассовыми вставками; их нужно снять в первую очередь.
Других фотографий процесса разборки у меня нет, но после этого этапа вы увидите четыре винта, которые нужно выкрутить, а затем процесс продолжается как и раньше. Разберитесь, какой пластмассовой штукой скреплено всё остальное, разрежьте силиконовый компаунд, снимите пластмассовый крепёж. Продолжайте, пока не сможете вытащить печатную плату.
Вот и она: обратная сторона печатной платы. Как оказалось, она собрана из двух печатных плат: белая имеет две стороны — на задней части находятся все светодиодные драйверы (и к ней прикреплена другая плата), а на передней — сами светодиоды.
Белая печатная плата спроектирована довольно прилично: вместо матрицы из светодиодов там находятся 16 LED-драйверов SCT2024, работающих от постоянного тока. Эти светодиодные драйверы имеют 16 каналов, то есть каждый светодиод управляется напрямую. Светодиодные драйверы способны только полностью включать или выключать светодиоды; в самих драйверах нет нативной поддержки оттенков серого. По сути, они выполняют сдвиг регистров при помощи регулируемых током выходов, и все они переключаются последовательно; интерфейс подключения зелёной печатной платы состоит из её шины синхронизации и данных, линии разрешения защёлкивания, линии включения вывода, заземления и питания.
На этой зелёной плате находится то, что является крошечным
На передней части белой платы расположены все светодиоды. Также к ней припаян крошечный плоский микрофон. Сначала я думал, что он припаян к зелёной плате и торчит через отверстие в белой плате, но на самом деле он просто плоский.
Вся остальная электроника расположена на задней части платы. Её там мало: только источник питания под брендом Ikea, выдающий 4 В. Под источником питания находится маленькая печатная плата, на которой есть два тактильных переключателя для кнопок на задней части корпуса.
Итак, если мы хотим «оптимизировать» дизайн, то у нас есть очевидное слабое место: маленькую зелёную плату с процессором лучше заменить на нечто более мощное.
Модифицируем железо
Я подумал, что возможно получится заменить оборудование на ESP-Cam. Это очень дешёвая (10 евро или около того) плата с чипом ESP32 WiFi/BT, несколькими мегабайтами PSRAM, а также модулем камеры. Мне не очень интересна светомузыка, я хотел, чтобы устройство реагировало на что-то визуальное. Так как в переднем торце уже было отверстие для приёмника, я думал, что смогу использовать его под камеру. Также мне понадобится просверлить отверстие в белой плате там, где находится микрофон; к счастью, кроме этих теперь бесполезных дорожек микрофона, это разрушит только одну дорожку светодиода. Я просверлил отверстие и восстановил дорожку. Временно вернув плату на место и включив куб, я убедился, что все светодиоды по-прежнему работают.
Теперь у меня была плата с просверленным отверстием, и мне оставалось только подключить и механически соединить ESP-Cam. Проектировщики любезно указали назначение всех контактных площадок на шелкографии зелёной платы, поэтому мне почти не пришлось ничего гадать. Так как источник питания подаёт всего 4 В, я подключил его напрямую к входу 3,3 В чипа ESP-CAM. Мне пришлось так сделать, потому что когда я подключил его к контакту Vin 5V, чип не захотел запускаться… Скорее всего, LDO-регулятор имеет немного высокое падение напряжения. Возвращаясь назад, я теперь думаю, что стоило попробовать подключить диод последовательно с источником 4 В, чтобы получить 3,3-3,4 В, но и моё решение сработало; ESP32 довольно неприхотлив.
Припаяв все разъёмы, я должен был всё равно разобраться с механикой. Это был «быстрый и грязный» проект, и я уже отпаял до этого разъёмы ESP-Cam, поэтому для выравнивания камеры с отверстием можно было обойтись несколькими прокладками и множеством эпоксидки.
После этой операции осталась последняя проблема: светодиоды рядом с камерой светили на её кадровое окно, вызывая всевозможные странные артефакты. Первым делом для их устранения я вырезал из алюминиевой ленты небольшой ободок для защиты камеры от прямого света.
Вторым шагом стало нанесение акриловой краски. Это пластмассовая вставка, на которой расположены все диффузоры, через которые светят светодиоды. Я уже увеличил отверстие, которое в нём имелось для микрофона, чтобы создать для камеры более широкое кадровое окно, но это привело к появлению другой проблемы — отверстие было слишком заметным и на камеру всё равно падало слишком много рассеянного света. С обеими проблемами довольно неплохо справилось пятно чёрной акриловой краски; камера по-прежнему получала довольно много света от светодиодов, но теперь он не заливал её полностью.
Итак, трансплантация
Программное обеспечение
Перейдём к ПО. Первым делом нам нужно добиться контроля над LED-драйверами. Это нетрудно: сигналы драйверов почти напрямую согласуются с периферийным интерфейсом SPI чипа ESP32: площадка CLK на плате соединяется с CLK периферийного интерфейса SPI, площадка DA на плате соединяется с MOSI сигнала SPI. Это позволит синхронизировать 256 бита с цепочкой LED-драйверов: каждый синхронизируемый бит управляет включением или отключением соответствующего светодиода. Кроме того, существует вход LAK. Если сигнал на нём низкий, то светодиод сохраняет своё предыдущее значение, вне зависимости от того, что было передано в регистры сдвига; если сигнал высокий, то все они одновременно переключаются на значения, находящиеся в данный момент в регистре сдвига. Наконец, есть вход EN, включающий или отключающий все светодиоды; я не подключил его к GPIO и просто всегда включаю светодиоды программно.
Таким образом, я могу теперь включать и отключать отдельные светодиоды, получив чёрно-белый дисплей. Однако мне нужно другое. С достаточной вычислительной мощью дисплей должен быть способен и на отображение оттенков серого — для этого просто нужно включать-отключать светодиоды быстрее, чем видит глаз.
Для реализации подобного с одним светодиодом я обычно использую PWM, но в случае 256 светодиодов это отнимет значительную часть мощностей процессора ESP32. Поэтому вместо этого я решил использовать Binary Code Modulation (также называемую Bit Angle Modulation). Это методика, позволяющая получить оттенки серого с меньшими вычислительными мощностями ЦП. Я уже успешно применял её в других проектах, поэтому был уверен, что она сработает.
И она сработала. Также я добавил таблицу поиска для преобразования светлоты CIE в PWM, потому что глаз по сути реагирует на яркость светодиодов нелинейно; таблица поиска устраняет эту проблему. В конечном итог мне удалось добиться достаточной величины оттенков серого и линейного масштабирования оптической яркости в соответствии с заданным мной значением пикселя.
Затею с камерой реализовать было совсем несложно: есть компонент ESP-IDF, который можно добавить в проект; он сам позаботится о настройке камеры и обмене данными с ней, достаточно лишь указать нужные вам параметры, а затем запросить битовую карту, которую видит камера. Единственное чем, я не смог воспользоваться в стандартной настройке, было то, что по умолчанию включены автоматическое выравнивание и автоматическая выдержка, и это довольно сильно мешало моему способу управления светодиодами: в зависимости от того, в какой части последовательности Binary Code Modulation было создано изображение, камера довольно хаотично увеличивала или понижала выравнивание и выдержку. Я устранил проблему, переключив камеру в режим ручного выравнивания и выдержки; код сам изучает изображение и определяет нужна ли этим параметрам компенсация. Благодаря такой ручной обработке мне также удалось исключить пиксели, которые смотрели прямиком на светодиоды, чтобы полностью убрать из уравнения. Это тоже сильно помогло в получении стабильного изображения.
Теперь у меня есть камера и холст 16x16 в оттенках серого. Что мне с ними сделать? Мне пришла идея: во времена самых первых компьютеров Macintosh существовало популярное расширение, которое только добавляло в панель меню пару мультяшных глаз. Эти глаза просто следили за курсором. (Вариация этой темы для Linux/Unix под названием «xeyes» по-прежнему существует.) Что если я попробую повторить это в реальной жизни?
Для начала мне нужно было несколько изображений глаз. Я не думал, что смогу нарисовать что-то стоящее, поэтому вместо этого решил в качестве базовых картинок использовать собственные глаза; посмотрите короткое видео, в котором я смотрю и моргаю во всех направлениях. Стоит учесть, что из-за сложившейся ситуации у меня было очень мало профессиональных инструментов: я лежу на полу, чтобы максимизировать освещение, падающее с потолочных флуоресцентных ламп, и получить разборчивое изображение; всё видео снято на смартфон, который я держу в руках.
Так как исходные данные были довольно отстойными, пришлось хорошенько поработать над постобработкой. Я начал с вырезания фрагмента, который приблизительно находился вокруг моего правого глаза. Затем я преобразовал каждый кадр видео в изображение и удалил все избыточные изображения; в конце концов у меня осталась хорошая выборка изображений меня, смотрящего в разных направлениях, а также несколько кадров с морганием. Однако, поскольку я использовал для записи мобильный телефон, видео получилось немного дрожащим и изображения скакали. Чтобы исправить это, я пропустил набор изображений через программу склейки изображений Hugin, которую обычно используют для панорам и HDR-изображений; на выходе я получил картинки, идеально центрированные на этой части моего лица. Теперь мне оставалось только пометить, в каком направлении я смотрю, и моргаю ли я. Это я сделал, сначала преобразовав все изображения в оттенки серого, а затем загрузив их в Gimp. На каждом изображении я использовал красную точку, чтобы обозначить местонахождение центра зрачка, плюс красную точку в левом или правом углу, чтобы обозначить, моргаю ли я, и если моргаю, то полуоткрыт ли глаз, или закрыт.
После разметки таким образом каждого изображения, стало очень просто написать скрипт для получения расположения зрачка, а также состояния моргания. Также скрипт масштабировал изображения до 16x16 и сохранял их как сырые двоичные данные, готовые для записи в прошивку модуля ESP-CAM. В конечном итоге я получил набор изображений и индекс того, где находится зрачок и в каком состоянии моргания находится глаз.
У ESP-Cam есть довольно простой в использовании компонент библиотеки камеры для ESP-IDF, поэтому получать изображения было совсем несложно. Я настроил захват изображений 120x160 в оттенках серого, потому что их проще всего обрабатывать, а мне не требовалось большое разрешение, ведь конечный результат должен отображаться на экране размером 16x16. Однако, у меня по-прежнему оставалась аппаратная проблема: камера всё ещё находится слишком близко к светодиодам.
Сначала я попытался решить её калибровкой: при запуске устройства оно делает два снимка: один со светодиодом близко к объективу камеры, и один только со светодиодом, находящимся далеко от камеры. Вычтя одно изображение из другого, можно понять, на какие пиксели влияют светодиоды. Эти пиксели сохраняются в маске; помеченные в маске пиксели в дальнейшем игнорируются. На рисунке выше показаны два снимка и маска.
Получив более чистое изображение, я мог перейти к распознаванию движения. Его я реализовал получением кадра с камеры и вычитанием из него предыдущего кадра. Затем я смог вычислить количество движения, сложив все получившиеся пиксели. Далее можно было вычислить местоположение концентрации движения, взяв среднее координат всех пикселей, взвешенных на разность кадров в этом пикселе. В конце выполняется магия с фильтрацией, чтобы устройство не смотрело дёргано вокруг, как джек-рассел-терьер с СДВГ: чтобы привлечь его внимание, объекты должны двигаться или более единообразно, или переместиться достаточно далеко.
Осталась единственная проблема: когда было очень темно, алгоритм распознавания движения иногда срабатывал на отражение светодиодов на находящихся в комнате блестящих объектах: если глаз моргал, то обращал внимание на собственное отражение. Это не совсем то, чего я хотел, поэтому я обошёл эту проблему, заставив алгоритм распознавания движений «слепнуть» при изменении изображения на светодиодах; таким образом я прекратил такое поведение. К тому же, это ещё и немного более реалистично: когда моргаете, вы не можете видеть.
Прошивка имеет несколько режимов отладки, демонстрирующих весь процесс. Вот небольшое видео с иллюстрацией этого:
Заключение
Итак, теперь у меня есть устройство, которое следит за мной… не особо полезно, согласен. Зато мне было интересно создавать его, и если у меня когда-нибудь найдётся что-то более полезное, что можно реализовать на светодиодном экране размером 16x16, то я смогу это сделать, ведь управляющий код у меня уже есть. Кстати о коде: можете свободно скачать мой сырой результат отсюда. Надеюсь, вам понравилось, и берегите себя.
Автор: PatientZero