Однаждый мне рассказали о самодельном девайсе под названием Yayagram (по какой-то причине описанном создателем в Твиттере, а вот тут по-русски). Я на него посмотрел и решил сделать свой, с преферансом и поэтессами аркадными кнопками и старым термопринтером.
Так родился БабаГрам, чёрный ящик, открывающий моей бабушке общение в Телеграме.
Что такое Бабаграм

Мне хотелось сделать что-то похожее на Yayagram - устройство, которое бы печатало входящие сообщения на встроенном принтере, и позволяло бы бабушке отправлять мне и другим контактам сообщения. При этом бабушка не умеет набирать на клавиатуре телефона, и очень неуверенно читает с экрана.
Yayagram использовал провода и джеки для выбора, кому отправится сообщение. Я же решил использовать аркадные кнопки - мне кажется, это более естественный выбор. Они удобны и производятся для аркадных машин, на которых играют довольно азартно, а значит, выдерживают большие нагрузки.
Итого, нам нужно устройство с принтером, микрофоном, кнопками, выходом в интернет и управляющей этим всем логикой.
Оглавление
Начнём с "железной" части - термопринтера и периферии, контроллеров, питания. Дальше немного про производство корпуса. И, наконец, про код.
Термопринтер
Когда-то давно мне отдали старую кассу. Кажется, это была Ока 102к, но это не так важно. Выпаяв из неё всё, что может пригодиться, я стал обладателем древнего термопринтера. Вот такого.

Документации по этому термопринтеру я не нашёл, зато нашёл обозначение собственно термопечатающей головки (Т1001Б) и, после продолжительного гугления, даташит на неё. Считаю себя квалифицированным интернет-детективом после такого и выкладываю даташит на гугл драйв. Где я его нашёл, уже не помню.
Дальше надо было разобраться в плате, которая управляла этим термопринтером. Не утомляя вас деталями, скажу лишь, что логика там примитивная: головка - это сдвиговый регистр на 128 бит, где каждый бит - это один резистор в термоголовке. Нужно отправить 128 бит последовательно, а потом коротко подать сигнал "огонь", который включает нагрев выбранных резисторов. Важно не перегреть, а то сгорят. И больше 32 включать за раз нельзя, поэтому более заполненные строки будут печататься медленнее.
Выжигать линию на бумаге - это ещё полдела. Надо уметь двигать ленту. Для этого в принтере есть шаговый двигатель. От него идёт 6 проводов, 2 из которых одного цвета и, после долгого тыканья мультиметром, определены как один провод в двух лицах (для увеличения сечения). Есть стандартный шаговый двигатель с 5 проводами с вот таким вот расположением пинов:

Управлять таким двигателем довольно легко - знай себе по очереди дёргай фазы (а красная всегда или на GND или на VCC). Определив экспериментально последовательность фаз, которая приводит к движению ленты вверх, я пошёл дальше.
Начался долгий процесс обнюхивания с термоголовкой. Я подбирал тайминги тактового сигнала, время включения геены огненной нагрева резисторов и порядок битов в регистре. В силу существования big-endian/little-endian я посмотрел на все возможные комбинации перевёрнутых отпечатков, включая отзеркаленный и четырежды-внутри-отзеркаленный (когда каждый байт в неправильном порядке). Я даже смог добиться оттенков серого. В какой-то мере.

Но вот беда: напечатанное было существенно сжато по вертикали. Что это? Прямоугольные пиксели? Или я слишком мало шагов двигателя делаю? А не посмотреть ли мне на двигатель? Знаете, что я увидел? Два двигателя на одном валу. У каждого по 3 провода, один из которых, по видимому, центральный между фазами. Эти центральные провода были соединены в один на плате. Поэтому тыканье мультиметром показало их замкнутыми, но на самом деле они в моторе (моторах!) не замкнуты. Один двигатель вращает ленту вперёд, другой назад.
Так что, когда я делал 4 шага фаз, двигатели включались поочерёдно, и двигатель "вперёд" двигал ленту по-честному (и даже чуть дальше), а двигатель "назад" мешал. Выкинув 2 фазы из 4х, я всё починил.
Ни разу не слышал об однонаправленных шаговых двигателях, а они, оказывается, есть?
Периферия
Кроме термопринтера, который у меня уже был, мне были нужны кнопки и микрофон. Я купил их на амазоне. Про микрофон особо нечего рассказывать - самый дешёвый USB-микрофон, по стечению обстоятельств точно такой же, как в Yayagram'е. При распаковке бабаграма бабушкой он был вырван из корпуса и порвал провод внутри, потому что бабушка решила, что это ручка. Пришлось оперативно чинить - впервые в жизни паял в новогоднюю ночь.
Ещё я добавил пищалку из набора ардуино, но это мелочи.
А вот кнопки интереснее. Ну так, чуть-чуть. Они со светодиодами с интегрированными резисторами, и на 3.3v светятся тускло. Светодиоды можно заменять, и в одной кнопке я поменял стоковый белый светодиод на красный. Нет, не в кнопке запись, а в кнопке SOS.
Кнопки прекрасно монтируются на корпусе - у них есть "юбка". И очень легко разбираются. Контакты там под клеммы, но мне было лениво обжимать 24 клеммы, поэтому я просто припаял провода.

Одна кнопка мне нужна была маленькая, поэтому я нарисовал и напечатал корпус для микропереключателя. Не буду публиковать модели - получилось не очень. По факту, она одноразовая и немножко залипает.
Контроллеры
Проблема всплыла, где не ждали. Я хотел собрать устройство на Raspberry Pi, а он 3.3 вольтовый. Но вся логика управления термопринтером 5-вольтовая, и кнопки на 3.3v светятся плохо.
В общем, я решил, что девайс я собираю сложный, а поэтому можно использовать два управляющих устройства, одно для 5v логики, другое для интернета и, в целом, для более сложных операций.
Я использовал Raspberry Pi Model 3B, но любой Pi (или аналог) должен сработать, если у него есть WiFi. Ну или можно сделать Babagram-over-Ethernet.

Для 5v логики я выбрал (по причине наличия дома) Attiny88. Я вообще очень люблю atmel'ы этой серии. Их сложно убить и очень просто программировать, если есть какая-нибудь ардуинка дома. И возможостей много за копейки. Единственный минус - у них очень мало памяти, как оперативной (512 байт) так и флеш (8Кб у 8x моделей). Но здесь тини будет очень прямолинейным исполнителем. Самое большое, что ей придётся хранить - одну строчку печати, 128 бит, 16 байт. Справится.
Для связи тини и Pi я использовал шину I2C: она требует мало проводов - SDA, SCL и земля. А ещё пины Pi не толерантны к 5 вольтам, поэтому любой протокол, который использует высокий уровень от ведомого устройства, потребует делителя напряжения. Лень подсказала мне, что I2C - мой выбор.
Пришлось, правда, подправить код I2C клиента в attiny - стоковый включает pull-up резистор, который выдаст 5v, а мне этого не надо. Высокий уровень будет задавать мастер, который у нас Pi. Все известные мне atmel'ы понимают 3.3v как логическую единицу. Attiny88 это гарантирует при питании от 5v (для него VIH >= 0.6VCC, то есть, 3v).

Неожиданно узнал, что гребёнка Raspberry Pi имеет количество пинов и шаг между ними в точности совпадающий со шлейфом IDE. Приятное открытие, которое позволило мне впервые в жизни сделать более-менее разборный девайс на шлейфах. Я уже давно выпаял IDE разъёмы с нескольких старых материнок, поэтому просто вырезал очень простую плату для разводки этого разъёма. И ещё одну для атмела, чтобы связать его с платой термопринтера (тоже шлейфом).
А вот так выглядят питание, Pi и плата термопринтера на стенке корпуса:

Питание
Для термопринтера было нужно 12 вольт, и у меня был блок питания 220 -> 12v (слева-сверху на фото выше).
Для 5v я сначала использовал копеечный асинхронный DC-DC преобразователь с алиэкспресса, но потом словил эпичные глюки из-за шумного питания (несмотря на развязывающие конденсаторы), и выкинул нафиг этот преобразователь. Вместо него использовал завалявшийся USB-C блок питания.

Для привыкшего к старому USB-A меня было открытием, что USB-C адаптеры требуют нетривиальной настройки, чтобы отдать даже стандартные 5 вольт. Нет, я понимаю, что сразу выдавать в линию 12v не стоит. Но 5 вольт можно бы... Ладно, там надо просто резистор добавить между линиями CC и GND. На 5.1кОм, которые есть только в серии E24 и далее, но ближайший "обычный" номинал в 4.7кОм у меня работает нормально. В итоге получается такая вот колбаса:

Корпус
С электроникой понятно, осталось разобраться с тем, как сделать коробку. Yayagram, судя по всему, использовал заводскую, но это не наш метод.
Я закупился 4мм фанерой и вырезал корпус на лазерном ЧПУ-станке (диодном!). Оказывается, если резать фанеру, которая для этого не предназначена, появляются забавные артефакты - некоторые волокна фанеры прорезаются плохо, и остаётся много непрореразнных точек. А в некоторых местах она горит внутри.
Лазер у меня на 40 китайских ватт. При питании от 12v он потребляет ~1.7A - такая вот китайская арифметика, но я отвлёкся. Худо-бедно фанеру он режет. Специальную "лазерную" фанеру он режет вообще отлично.

Внезапно обнаружилось, что станок не совсем декартов. То есть, оси у него не перпендикулярны. Раньше я только платы вырезал, там перпендикулярность была не так важна. А тут я вырезал два прямоугольника на пределе размера стола и сложил их, провернув. И узрел расхождение в ~1.5мм. Пришлось подтюнить станок молотком, чтобы он стал перпендикулярнее.
Для сборки корпуса я напечатал на принтере уголки, часть обычных мебельных, а часть "трёхсторонних" - см. здесь. Очень удобно собирать по месту - часа два с шуруповёртом, и корпус примерно собрался. Правда, часть дырок оказались лишними, пришлось их залить эпоксидкой, и после покраски они стали глянцевыми.
После покраски встал вопрос, как наносить текст на корпус. Эмбоссированные пластиковые полоски как в Yayagram'е я сразу отверг - хотелось честных изображений на поверхности. Я попробовал гравировать фрезой поверх краски, предположив, что я срежу краску и оставлю более-менее яркую фанеру, но вышла фигня.
Фигня

Поэтому я перешёл к более тяжёлой артиллерии. Одну эмблему (для кнопки протяжки бумаги) я нарисовал более простым способом: вырезал лазером трафарет на клейкой этикетке, наклеил на дерево и закрасил акрилом из аэрографа. Это возможно, потому что эмблема без изолированных участков чёрного. Иначе говоря, область чёрного связная.
Фото покраски по трафарету



Для обычного текста такой режим не подходит. Либо придётся использовать трафаретный (стенсильный?) шрифт, который делает все буквы правильным образом связными, либо придумывать другой метод. Мне не хотелось ограничивать себя в том, что я могу нарисовать, поэтому я выбрал второй вариант.
Для рисования произвольного (одноцветного) изображения я заклеивал всю поверхность малярным скотчем, затем лазером гравировал участки, где должна быть краска (белые в конечном итоге). Это испаряло малярный скотч и чуть-чуть дерева, и я красил аэрографом по такой маске.
Фото более сложного процесса




На самом деле, не так уж сложно это оказалось делать. И качество на удивление неплохое. Единственно что плохо - контрастность ниже, потому что краска не очень хорошо ложится на шероховатую после лазера поверхность. Хотя казалось бы.
Фото собранного и покрашенного бабаграма:

Код
Код для аттини я написал как для ардуино и залил через ISP, которой была Arduino Uno.
Код для малины базируется на python-telegram-bot и работает как телеграм-бот. Там очень много чего сделано криво, в частности, многопоточность реализована плохо. Может быть, я допилю этот код до более красивого.
Запускать код как system service нельзя - это должна быть user service, иначе pyaudio не может использовать микрофон. Не знаю, почему, вероятно, из-за какой-то аутентификации в pulseaudio.
Я не базировался на коде Yayagram, потому что местами он ещё хуже. В частности, запись звука там производится запуском бинарника arecord, с последующим его убийством при отпускании кнопки записи.

Для печати текста на термопринтере нужен шрифт. Его нужно выбрать, и это было тяжело. Я пробовал векторные шрифты (потому что растровые были нечитаемыми), и выглядели они в разной степени криво. Как я понял потом, растровые не работали из-за описанного выше неправильного управления шаговым мотором. Но пока я этого не понял, я перепробовал десятки шрифтов и потратил кучу бумаги.
Наконец, починив управление мотором, я смог печатать один к одному и заменил шрифты на растровые. Какое же это было счастье!
Когда всё уже работало, я понял, что мне не всегда удобно слушать аудиосообщения, и я хочу читать текст. Для этого есть много разных сервисов, но я выбрал Google. Продравшись сквозь дебри версий компилятора Rust'а и glibc, я таки установил google-cloud-speech, и всё заработало. Если хотите мой совет, ставьте unstable версию raspbian'a - там версии пакетов более подходящие. Обновить свой Pi я уже не могу, потому что, оказывается, при обновлении отваливается WiFi.
Весь код лежит здесь: https://github.com/gurux13/babagram
Заключение
Бабушке очень понравился Бабаграм. Она его использует, и это очень удобно - мы посылаем сообщение, оно печатается на кассовой ленте, и, когда она видит вылезший чек, она отвечает нам аудиосообщением, которое тут же распознаётся в текст.

Спасибо, что дочитали до конца. Ну или долистали :) Надеюсь, было интересно.
Автор: gurux13