Pull to refresh

Управляем светодиодной лентой при помощи Raspberry Pi и Android

Reading time 8 min
Views 65K
Здравствуй, завсегдатай и гость Хабра.

Давно читаю Хабр, но всё никак не было поводов написать статью, пока меня не накрыло мне очередной раз не напомнили о существовании Raspberry Pi и о такой вещи как WS2801 LED Strip. Тут уже однажды писали о подобной, но для связи с Ардуиной, чтобы получить Эмбилайт, поэтому я решил рискнуть и написать свою статью, с Лего и Котиками.

В статье будет мало картинок, несколько видео, много текста, включая лирические отступления не по сабжу и совсем мало кода, но в самом конце будут ссылки на проект на Гитхабе. А теперь обо всём по-порядку:

Всё началось с того, что я узнал, что один коллега по работе регулярно возит Малиновые Пирожки и продаёт их с минимум наценок, особенно своим коллегам. Очередной раз услышав о пирожках, не смог устоять и приобрёл у него оный. Что это такое и что с помощью него можно сделать, рассказывать не буду, и так все знают, а кто не знает, могут поискать, статей полно. Сам я с ним поигрался недельку-другую, настроил под что мне надо было и успокоился.


Но тут тот же коллега подкинул интересную инфу: можно заказать с Ибея управляемую (адресную) "гирлянду" (+ блок питания, коннекторы и соединительные проводки на свой вкус и цвет) и простейшим способом получить DYI Ambilight как это рассказано, например, тут. Или же, можно просто играться с этой гирляндой как душе угодно: в теории она позволяла в любой момент времени зажечь любую из лампочек любым RGB цветом, а ведь об обладании такой штукой я мечтал уже несколько лет!

Не долго думая, заказал необходимые части на Ибее. Расчётный срок доставки был порядка 3х недель, так что было пока время отвлечься на подготовку к поездке в отпуск на неделю в Швецию. По возвращении ещё был неделю дома, пытался заранее найти как можно больше информации о том как гирлянду надо будет подключать, искал различные скрипты и библиотеки для написания собственных алгоритмов управления. Толкового нашлось не очень много, одна Хабра-статья, правда она про связку с Ардуиной и несколько ссылок привели на этот Python-проект. Так как задачи делать эмбилайт у меня как раз таки не было, а к Python душа не лежит (сам люблю Java + Android), полезной информации там для себя нашёл не много. Время свободное было, гирлянда никак не приезжала, решил накатать на Андроиде простенькое приложение-симулятор гирлянды и написать пару алгоритмов управления последней. Приложение было на столько простое и не менее кривое, что даже показывать никому не стал.

В последние выходные отпуска мне привезли из Новосибирска долгожданную коробку Lego 42009, которая временно затмила мои мысли о гирлянде.

Планы по сборке Лего были назначены на следующие выходные, когда, примерно, ожидался и приезд посылки из Китая. Долго думал, чего же больше я жду — торжественного открытия коробки Лего и сборка топового на данный момент набора Lego Technic или же игра с мечтой последних нескольких лет. Посылка пришла вечером в пятницу, а на утро субботы планировалось начать сборку конструктора и снятие Time-Lapse процесса сборки.
Разумеется, я не смог удержаться в пятницу вечером и попытался подключить гирлянду в Пирожку, но в виду отсутствия необходимых проводков, подключение к GPIO портам RPi'я было затруднительно. Найдя какие-то старые проводки, разобрав старую мышку, начитавшись статей в Интернетах пытался подключить гирлянду хоть как-нибудь и запустить пробные скрипты. Но всё было тщетно. При подключении питания к гирлянде зажигалась первая лампочка белым светом и всё, никаких реакций не было независимо от того, что я запускал на Пироге. Изрядно помучавшись, решил отложить дело до утра.

Утром пришёл друг-фотограф для помощи в сборке и съёмки Лего, вместе с ним также безуспешно попытались подсоединить гирлянду к Пирогу. Через час бросили это дело, пошли готовить environment квартиры к сборке Передвижного Крана.
[лирическое отступление]
Сборка прошла на ура, кран оправдал все ожидания, оказался даже больше, чем думал, сам процесс сборки был непростой и увлекательный. Time-lapse видео сборки можно посмотреть тут.
[/лирическое отступление]

Так как начали позже, чем планировали, закончили ещё позже, к гирлянде не возвращались больше. Зато днём следующего дня приехал другой знакомый, который уже специалист — железячник, заодно и привёз очень нужные проводки разной длины и разъёмов для подключения гирлянды к Пирогу. Сели с ним ковыряться. Потратили часов 5, наверное и, казалось бы, безуспешно. Поначалу гирлянда не реагировала на внешние раздражители, то есть нас, никак. Потом иногда начали мигать несколько первых огоньков при касании разъёмов подключения к пирогу. Спустя час выяснили, что если замыкать пальцами провода, которые должны идти к Пирогу, то зажигаются или тухнут несколько первых огоньков гирлянды. Значит, она всё же рабочая, оставалось понять, почему же не работают скрипты. Беглый анализ Пайтоновского скрипта показал, что используется устройство /dev/spidev0.0, собственно у меня в системе он присутствовал. Параллельно пытался гуглить проблему, что-то находил, применял что скажут, но безуспешно.

К концу дня совсем выбившись из сил, гость меня покинул, а пока он ехал домой я решил забрести в конфиг Пирога
raspi-config
и обнаружить главный epic fail последних пары дней, а именно, что SPI у меня был выключен…
Включил. Скрипт заработал, можно было включать-выключать все диоды белым цветом и запускать два бесконечных действия — fade (постепенное появление и угасание рандомными цветами), или же chase — последовательно бегающий огонёк из начала в конец.

Счастью не было предела. Сделал даже пару фотографий построенного Лего-крана при свете гирлянды:
Lego 42009

Lego 42009

Cкудные возможности скрипта быстро надоели, а написать что-то своё на Пайтоне мне было достаточно проблематично. Поэтому, было принято решение написать своё простенькое Java-приложение для Raspberry и начать это завтра. А ведь «завтра» — это был понедельник. Именно в этот понедельник я не хотел идти на работу больше, чем во все остальные понедельники ранее. Пережив понедельник, убежав с работы пораньше, повесив гирлянду под потолок, на верхний край ковра, что висит и закрывает большую дыру в обоях, в моей квартире, уселся за код, но тут меня ждал ещё один подвох, впрочем, ожидаемый.
Установив все необходимые java-пакеты решил написать Hello World, скомпилить и запустить прям на пироге. Компиляция даже такого простого приложения на Rapsberry Pi-е заняла несколько секунд, порядка 5, что уж говорить о дальнейшем коде? Это было достаточно неудобно, также как и отсутствие нормальной IDE. Поэтому было принято решение писать код на большом PC, на пирог заливать по ftp скомпилированные классы и там их уже запускать. Позже, когда проект разросся до большого количества файлов стал собирать сразу в jar-ы. Подобрались к самому интересному. Стоял главный вопрос: как заставить гирлянду зажигаться используя Джаву. Поиск в Гугле ничего не дал, находились исходники на Си с использованием библиотек и пара скриптов на Пайтоне. На последнем это выглядело так:

spidev = file("/dev/spidev0.0", "wb")
spidev.write(pixels)
time.sleep(refresh_rate/1000)

где pixels — массив байтов, размером в 3 раза большим, чем количество лампочек в гирлянде. Каждые три элемента в массиве — это значения для RGB-каналов для каждой лампочки. Допустим, у нас есть гирлянда из 50 лампочек, чтобы её всю зажечь, нужно писать в файл /dev/spidev0.0 массив вида {R0, G0, B0, R1, G1, B1, ... R49, G49, B49}. Никаких заголовков, просто массив байтов и flush после.
Главная идея заключалась в том, что в каждый момент времени нужно писать в файл весь массив, даже если хочешь зажечь одну лампочку. Задержка по времени нужна для того, чтобы видеть все изменения на ленте, впрочем, её можно варьировать как угодно.



Возвращаясь к Java, там код записи в файл выглядит почти также (try-catch опущен):

FileOutputStream spidev = new FileOutputStream(new File("/dev/spidev0.0"));
spidev.write(bytes);
spidev.flush();
Thread.sleep();

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



Но опять же, запускать эти «алгоритмы» — мало интереса и это тоже быстро надоело. Хотелось непосредственного управления. Было решено написать простенький сервер, опять же на Джаве, который принимал бы строковые данные, парсил их и взаимодействовал с гирляндой на основе присланных «команд». Для удобства было сделано так, что порт, количество лампочек в гирлянде и устройство (/dev/spidev0.0) задавались с помощью параметров при запуске сервера.
Клиент, в сою очередь, на Андроиде. В первой версии клиента был минимальный функционал — включить-выключить, установить цвет с помощью RGB-ползунков и «двигать» огонёк ещё одним ползунком. Не смотря на мою нелюбовь «с детства» к клиент-серверному коду, написанный код работал и даже правильно.
Довольно быстро понял, что 50 лампочек мне будет недостаточно и заказал ещё столько же в рассчёте на то, что позже их можно будет сложить в квадрат 10х10, а это новое поле для полёта фантазии, тут можно будет делать хоть классическую змейку.
Чуть позже мой знакомый студент помог переписать клиент-серверный код под более правильную архитектуру, теперь клиент общался с сервером с помощью заранее заданного набора команд, а данными для каждой команд служил параметр типа Object, следовательно, там могло быть всё, что угодно. Также, для защиты от «несанкцианированного доступа», на сервер добавил поле с паролем, а на клиенте посылал его первой командой. Если он не подходил, сервер отключал клиента. Это было сделано на тот случай, если кто-то знает по какому имени/айпишнику и порту доступен мой пирожок, чтобы не смог попытаться поиграться гирляндой.
Переделав код под новую архитектуру, начал наращивать функционал вширь: добавлял новые элементы интерфейса и новые алгоритмы управления лампочками.
Android Client
Добавилось и мерцание RGB-цветами и рандомными и радуга, в том числе двигающаяся. Был даже прикручен NFC для включения-выключения лампочек при чтении любой метки (удалено из финальной версии) Отдельно стоит упомянуть, что был прикручен Гугловый SpeechRecognizer, который был настроен на восприятие буквально 5ти команд: «красный», «зелёный», «синий», «жёлтый» и «радуга». Ну и самая, на мой взгляд, интересная функциональность — это использование всей длины гирлянды как… эквалайзера, наверное правильней сказать: количество последовательно зажжённых лампочек зависело от громкости звука, воспринимаемого микрофоном смартфона. Особенно эффектно это выглядит на примере трека Caspa — Sir Rock A Lot:


Общий обзор функциональности приложения:


После этого ещё немного побаловался с парой алгоритмов, правил найденные баги на сервере и клиенте, а также слегка рефакторил код. Сейчас главный управляющий экран выглядит так:
Весь код выложен на Гитхаб (библиотека, сервер и клиент) для неравнодушных. Буду рад, если он чем-то поможет. У самого планы сделать форк в версию 2.0, функциональность которой до конца я пока не определил для себя, но в планах примерно следующее:
  1. Выбор экрана управления: прямая лента, прямоугольник или квадрат.
  2. Расширение текущей функциональности для ленты, вытянутой в одну прямую.
  3. Написание пачки алгоритмов под вариант, когда лента сложена в квадрат и прямоугольник:
    • Управление зажжёной лампочкой
    • Эквалайзер из центра квадрата
    • Статичный текст/Бегущая строка
    • Змейка
    • что-нибудь ещё. Если будут идеи, пишите, с радостью обсужу и приму к сведению.

Из мелочей, что забыл вовремя написать:

1. Возможно надо корректировать цвет каждого диода перед записью в файл потому, что свет получается «холодный» и отдаёт в синеву при значениях 255-255-255. В Пайтоновском скрипте, что я находил, так и делается:

def correct_pixel_brightness(pixel):
corrected_pixel = bytearray(3)
corrected_pixel[0] = int(pixel[0] / 1.1)
corrected_pixel[1] = int(pixel[1] / 1.1)
corrected_pixel[2] = int(pixel[2] / 1.3)
return corrected_pixel

2. Иконку нарисовал мне тот же человек, что помогал записывать тайм-лапс постройки Лего. В основе неё лежит фотография моей кошки-упорошки
кошка-упорошка

3. Описанные действия с файлом /dev/spidev0.0 работают только для лент с контроллером WS2801. В прошлые выходные подарили на ДР знакомому (necrys) именно ленту, а не гирлянду (на этот раз как раз для эмбилайта), очень надеясь, что там тоже WS2801. Заказывать из Китая времени не было, покупали у нас в городе, продавец утрерждал, что контроллер такой же, хотя никаких надписей на упаковке и самой ленте не было. В самый разгар празднования Дня Рождения решили попробовать подключить. Безуспешно провозившись несколько часов, казалось бы, зная все подводные камни, включить так и не удалось, пока случайно не попробовали поменять протокол на SM16716, после чего она лента зажглась и даже работал chase, но гаснуть всё равно не хотела. Написание простой с-программы вопрос не решил, попытка записи массива по 3 байта зажигала такие участки ленты, что логику работы в тот день нам понять не удалось. После экспериментов, к середине следующего дня, новому обладателю ленты удалось таки раскурить-понять алгоритм работы с лентой: в файл нужно писать массив по 4 байта на светящуюся единицу, где первый байт должен быть всегда 0xFF, а остальные уже RGB. Изменение значения первого байта ведут к поведению ленты, смысл которого пока понять не удаётся. Именно в виду разницы контроллера, сборка и патчинг boblight, который с ним работал бы корректно и соединить его xbmc заняло несколько дней. Но после результат был достигнут:

За код просьба сильно не пинать, опыта написания больших проектов, к сожалению, пока не имею, да и когда начинал писать код, не думал, что вырастет до такого. А ещё это первый опыт работы с Гитом и выкладка своего кода в Open-Source.

Спасибо, надеюсь было интересно.
Tags:
Hubs:
+51
Comments 43
Comments Comments 43

Articles