Программирование контроллера звуком с телефона — заметки на полпути

С настольного компьютера или ноутбука мы прошиваем микроконтроллеры обычно через USB, да часто и с каким-нибудь программатором. А что если мы хотим прошить или сконфигурировать поделку с телефона? Некоторые телефоны имеют USB-OTG. Некоторые контроллеры умеют BlueTooth. Существуют системы поддерживающие WiFi. К сожалению всё это не носит массового характера — нужен подходящий телефон, подходящий контроллер и т.п.

Стало интересно — можно ли «пропищать» данные через динамик телефона — и уловить их микрофоном присоединённым к микроконтроллеру? В этой статье я кратенько расскажу о своих жалких попытках в этом направлении. Пока «с полпути» — потому что, предположительно, демонстрация самого принципа для Ардуино может оказаться полезнее для большинства коллег, чем конечный, узконаправленный результат — загрузчик для конкретного проца.

Был «предшествующий этап» — передача через аудиоразъём телефона, по проводу — об этом тоже вкратце расскажу и покажу (можете попробовать).

Предупреждение!!!

Не нажимайте кнопки на прилагаемых веб-страничках не сняв наушники!

Идея не нова

Кто помнит ZX-Spectrum и некоторые другие компьютеры той эпохи — они загружались с помощью магнитофона, правда через линейный выход (а у себя на входе использовали фильтр и компаратор).

Телефоны с тональным набором — на них можно было набирать номер «пропищав» его в трубку — например часами с памятью на сколько-то номеров.

А ещё раньше была оперативная память для ранних ЭВМ, которая гоняла звуковую волну (обычно в плотной среде, не в воздухе) с одного конца на другой, а потом обратной связью подавала её вновь на начало. Динамическая, получается, память. На звуке:)

Предыдущая попытка

Я пока пропущу предысторию, зачем всё это было нужно — если кратко, для использования в образовательном процессе. Но поскольку идеального решения пока не получилось — использование пока тоже больше в тестовом режиме на добровольцах, которые не имели возможности отвертеться. Хо-хо-хо.

Прежняя версия позволяет программировать AVR чипы на ассемблере (мы использовали AtMega8) примерно следующим образом:

  • в контроллер прошивается кастомный загрузчик (вот код и прошивка)

  • кроме чипа нужен только разъём (на ADC3 и GND) и светодиод для наблюдения результата (а ещё кнопка для входа в режим программирования при старте)

  • подключаем аудиокабель с 3.5 джеком между контроллером и телефоном, нажатием кнопки при старте переводим контроллер в загрузочный режим

  • открываем веб-страничку с формой для ввода программы (вот она)

  • пишем код (по умолчанию там есть некий stub из пары команд)

  • жмём Compile и Burn (компилятор ассемблера там встроен в JS)

Если захотите попытаться прошить этот загрузчик — по-моему нужно фьюзами выставить частоту 1МГц внутреннего осциллятора. И запуск собственно загрузчика (в 1 кб памяти вроде). Он ограничен возможностью записать то ли 1 то ли 4 страницы.

В общем это работало более-менее -, но обнаружилась пара проблем, даже три:

  • придуманный протокол обмена был недостаточно надёжен — программа больше 50 команд уже через раз заливалась с ошибкой

  • и он был довольно медленным

  • а главное — аудиоразъём в наше время уже не на каждом телефоне присутствует!

Итак — над первыми двумя пунктами можно было голову поломать -, но по третьему пришло понимание что нужно делать следующий шаг — передавать звук прямо через динамик и ловить микрофоном.

На словах звучит понятно и даже банально. На деле всё немного сложнее оказывается.

Схема подключения

Изначально я хотел чтобы электронная часть была максимально простой — подключить микрофон прямо к АЦП и всё. Та старая версия так и работает (видно по коду) — к сожалению при передаче «через воздух», а не по проводу сигнал получается очень слабый. Кроме того нелинейности такой передачи добавляют проблем — приходится пробовать цифровую фильтрацию, сглаживание и т.п. Старая версия использовала небольшие «пучки» — что-то около 10 периодов довольно пронзительной частоты — и расстояния между ними кодировали нули и единицы. Скорость передачи получалась просто божественная (вы сами можете проверить на указанной выше странице — добавьте десяток-другой NOP-ов в программу. Что-то порядка 10 команд в секунду.

Я решил сдаться и незначительно усложнить хардварную часть ради более надёжной и быстрой передачи.

c0060de1d394a52cd9c710cc2ca6682b.png

Подключение микрофона стандартное — через небольшой резистор к плюсу (не перепутаем полярность микрофона!) -, а с середины этой «стойки» через конденсатор колебания уходят на базу транзистора. Транзистор без подтяжки поэтому он обычно закрыт и напряжение на коллекторе близко к VCC — когда на микрофон приходит какой-то «клик» — импульс проходит через конденсатор и ненадолго его приоткрывает — на выходе PIN появляется коротенький по длительности «ноль». Этот выход мы и подключаем к выбранной ноге микроконтроллера.

В данном виде схема достаточно чувствительна к номиналам компонент (конденсатора и резистора в коллекторной цепи в первую очередь) — которые в свой черед зависят от коэффициента передачи транзистора. Предстоит немного поэкспериментировать насколько её можно «загрубить» с учетом что, например, многократные срабатывания можно отсеивать в коде (уже).

Программная часть

На текущий момент то что есть — это «демонстрашка» — веб-страничка на которой можно вбить какой-то текст в поле ввода и нажать «Beep». С помощью незамысловатого кода на JS всё это преобразуется в последовательность данных для вывода в звук.

Вот эта страничка https://rodiongork.github.io/avr-mic-loader/extra/sound2.html

Принцип кодирования пока такой (сильно в подробности не погружаюсь т.к. может ещё немного изменится):

  • в цепочке данных 0–0–0–0-…-0–0 местами появляются 1 — будем называть их «пиками», это значения для вывода в звук, а не логические значения, но не столь важно

  • бит 0 кодируется промежутком между двумя пиками в 13 нулей, а бит 1 более коротким, в 7 нулей

  • звук воспроизводится с дискретизацией 16кГц

  • байты кодируются по аналогии с UART — пока данных нет передаются единицы, а первый же «провал» в 0 является «стартовым битом» за которым следуют восемь бит данных

Что касается прошивки — это код для Arduino, так что вы несложно можете попробовать запустить такую установку сами (ну несколько упомянутых выше компонент понадобятся).

Код незамысловатый — он в цикле проверяет сигнал на входном пине, и когда обнаруживает провал в LOW (то есть «пик» в аудио-канале) — засекает время до следующих пиков. После каждого следующего пика мы определяем пришёл ли таким образом бит 0 или 1 — и если обнаруживаем 0, то начинаем отсчет нового байта. Когда байт готов — просто печатаем его.

Вот эта прошивка https://github.com/RodionGork/avr-mic-loader/blob/master/idea-proof/receiver-air/receiver-air/receiver-air.ino (что-то тут лишний уровень вложенности, ну да ладно)

Текст введённый на веб-страничке «пропискивается» через динамик, улавливается микрофоном и выводится в последовательный порт микроконтроллером, то есть его можно видеть в Serial Monitor:

Последние строчки - испытание появления ошибок

Последние строчки — испытание появления ошибок

К коду не стоит присматриваться внимательно или относиться слишком серьёзно — это сугубо черновик чтобы протестировать идею, оценить задержки и примериться как сделать загрузчик. Всё же в него включен макрос DEBUG — если его активировать, то вместо символов отрисовываются биты (как показано в середине картинки — из долларов и дефисов).

Оценка результата

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

Скорость передачи гораздо выше — нетрудно прикинуть что в среднем 1 бит занимает около 10 «точек» звуковой волны, а 11-битный байт, значит, 110 точек. При частоте 16кГц это больше 140 байт в секунду (то есть 70 AVR-ных команд).

Код «приёмника» стал гораздо проще т.к. не нужно обрабатывать «корявые» данные с АЦП. Это немаловажно для создания бутлоадера — чем меньше ассемблера, тем проще жить! Кроме того он подходит теперь и для чипов без АЦП вообще.

К ошибкам (на первый взгляд) система стала устойчивее. В принципе возникновение ошибок сейчас просто зависит от расстояния между микрофоном и динамиком.

Понятно что для передачи больших объёмов данных этот способ не очень-то подходит (хотя мы и проверим насколько его можно ускорить). Для прошивок в несколько килобайт сойдёт. Или для загрузки конфигурации.

Из достоинств — его можно использовать с любым в общем-то контроллером, лишь бы он умел записывать свою флэш-память (младшие AVR и некоторые другие чипы не умеют).

Но про контроллеры говорить пока рано — немножко поковыряюсь с тем чтобы сделать протокол надёжнее и тогда уже сотворю новую версию загрузчика.

Если кому-то будет не лень потестировать и возникнут идеи по улучшению или какие-то важные наблюдения — пожалуйста, делитесь! Если захотите как-то переиспользовать в своих поделках — милости прошу — на вопросы постараюсь ответить!

До скорого :)

© Habrahabr.ru