Смена деятельности — это то, что весьма хорошо влияет на плодотворность работы. Так что, когда мне предложили сделать драйвер для купюроприемника и диспенсера, а затем и платежный терминал целиком — я согласился, предупредив, правда, что работа для меня новая и мне неведомая.
С тех пор было написано штук 30 драйверов: часть из них — абсолютно уникальные, другая часть — разные реализации одного и того же.
Иметь дома устройство каждого вида — нецелесообразно. Тем более, что стоимость устройства сравнима со стоимостью разработки драйвера под него.
Но ведь тестировать как-то надо. Чаще всего устройство тестируется удаленно через remote desktop.
Но это жутко неудобно и часто приходится ждать наличия того или иного устройства.
И вот я созрел сделать OpenSource эмулятор на arduino.
Однако в данном случае возможность запустить эмулятор на любой совместимой плате имела для меня приоритетное значение. Благо arduino есть почти у каждого гика, а даже если нет — продаются они повсеместно.
Это решение — для программистов, а не для электронщиков. Поэтому вся работа сводится к сборке простенькой схемы с тремя кнопками, и семью резисторами. Впрочем, и без этого можно обойтись, если взять соответствующий готовый LCD Shield. У меня его под рукой не оказалось, поэтому пришлось сделать свой.
С arduino я до того момента никогда не работал, и эта задача показалась мне неплохим поводом познакомиться с платформой.
Эмулятор призван помочь в написании как отдельно взятых «драйверов» устройств, так и полноценных приложений, взаимодействующих с устройствами.
Этой статьей я преследую две цели:
1) Ознакомить программистов, которые так же, как и я, занимаются написанием драйверов, не имея на руках устройств, с дешевым решением, пригодным для первоначальной разработки и отладки.
2) Рассказать о фреймворке, который используется в моем скетче, чтобы желающие могли легко и непринужденно написать реализации эмуляторов для других устройств.
Более того, эмулятор будет полезен не только для специалистов, которые лишены физического доступа к устройствам.
Даже если у вас есть устройство — вы можете проверить лишь несколько основных режимов работы. Добиться некоторых ошибок на реальном устройстве бывает сложно. Эмулятор, в свою очередь, позволяет выставить любое состояние или ошибку. Что значительно расширяет возможности тестирования.
Для начала небольшая инструкция — как запустить и пользоваться:
Инструкция для пользователя
Диспенсеры можно использовать, не имея ничего, кроме непосредственно arduino с прошитым скетчем.
С купюроприемниками чуть сложнее, т.к. нужно инициировать ввод купюры, что невозможно сделать без экрана и кнопок, подключенных к arduino.
Первое что нужно сделать — это отредактировать файл config.h
В нем раскомментируем одну строку с указанием того, какое устройство конкретно нужно эмулировать.
На момент написания статьи доступно два устройства:
CCNET BILL VALIDATOR — купюроприемник на основе протокола ccnet v2.4
PULOON LCDM 2000 — один из самых распространенных диспенсеров на два номинала
Также нужно выбрать используемую библиотеку для экрана. У меня в наличии только i2c OLED 128х64, поэтому выбор сводится к одной из двух библиотек:
OLED I2C — шикарная библиотека с обширным спектром возможностей. Но она также потребляет значительное количество памяти. Запустить эмулятор с этой библиотекой на Arduino nano не представляется возможным — просто не хватит памяти.
SSD1306Ascii — к сожалению, не так хороша. Отсутствует двойная буферизация. В итоге любая перерисовка экрана сопровождается неприятным мерцанием. Зато остается еще достаточно места для кода самого эмулятора даже на малыше типа Arduino nano. Библиотека отсутствует в менеджере библиотек arduino, поэтому качать её придется вручную с github.
Можно изменить пины, используемые для кнопок, если в этом есть необходимость.
После всех проведенных манипуляций — компилируем скетч и заливаем его на arduino. Можно начинать разработку драйвера.
Стоит отметить, что при открытии serial соединения с arduino эмулятор автоматически перезагружается. Поэтому если ваш драйвер попробует отправить сообщение сразу после установки соединения, то, скорее всего, у него ничего не получится. Это поведение неспецифическое для устройств. Например, puloon lcdm не перезагружается после подключения и доступен сразу для обработки команд. И стоит в своем драйвере сделать задержку, либо отключить auto-reset на arduino. На разных версиях arduino это делается по-разному, поэтому инструкцию сюда не привожу — гугл вам в помощь.
На ccnet купюроприемниках auto-reset проблемой не является, т.к. в этом случае эмулятор просто пропустит несколько первых poll запросов от драйверов. Драйвер в любом случае должен уметь это обрабатывать.
Я рекомендую спаять небольшой dev board (или купить LCD Shield. Правда, тогда вам придется самостоятельно дописать поддержку экрана — на это потребуется совсем немного времени):
В данном случае это схема редактора kicad для arduino nano, но переделать её под любую версию arduino не составит труда. Схема не предполагает отключения auto-reset — из соображений удобства. Если хотите отключить auto-reset конкретно для nano, то надо добавить в схему резистор на 120 Ом между 5v и reset ногами.
Теперь поговорим о разработке.
Инструкция для разработчиков эмуляторов
На самом деле эта инструкция не только для тех, кто хочет добавить новое устройство.
Здесь я также расскажу о концепции работы экрана.
С экрана и начнем.
Враппер экрана
Основная идея работы с экраном заключается в том, что разработчик эмулятора может только выводить строки и скроллировать их.
Это позволяет выводить любое количество строк текста на экране любого размера.
По сути всё будет видно, даже если подключить экран, имеющий только две строки для вывода текста.
Для разработчика эмулятора работа с экраном сводится всего лишь к двум специфичным командам:
startDraw — вызывается в начале кадра. Первый параметр — количество строк, которое будет выведено (можно указывать больше, чем будет реально). Второй параметр — текущее значение скролла (по сути — указание сколько первых строк игнорировать и с какой начинать вывод на экран).
finishDraw — завершает кадр.
Ну а сам вывод текста делается штатным print и println.
Таким образом, разработчик эмулятора не взаимодействует напрямую с библиотекой экрана и ничего об экране не знает. Он лишь выводит текст и указывает значение скролла (которое может и не использоваться, если экран позволяет вместить все строки).
Реализация враппера для конкретной библиотеки также особой проблемой не является — достаточно взглянуть на lcdwrapper.h и lcdwrapper.cpp, чтобы в этом убедиться.
Не забываем добавлять свой экран в config.h и в FakeCND.ino
Эмулятор
Эмулятор наследуется от базового класса cBaseDevice.
Ему доступны пять методов для перекрытия:
init() — вызывается один раз в setup()
onPrev() — вызывается при нажатии на первую кнопку.
onNext() — вызывается при нажатии на вторую кнопку.
onOk() — вызывается при нажатии на третью кнопку.
update(int dt) — вызывается постоянно из loop(). в качестве параметра — время, прошедшее с предыдущего вызова, в миллисекундах
Собственно, всё. На этом фреймворк заканчивается и начинается работа эмулятора. В этой области фреймворк уже никак помочь не может. Его основная задача — обработать нажатие кнопок и дать универсальный и простой доступ к экрану.
github.com/Allexin/FakeCnD
Автор: AllexIn