Однажды мне повезло выкроить время на посещение мероприятия Hard StartUp в рамках нашего провинциального hackspace. Сам я от выступления категорически отказался под предлогом бессистемного расстройства вокабулярия к концу рабочего дня, но послушал выступающих с интересом; явно бросалось в глаза, что докладчики испытывали недостаток в пульте дистанционного управления презентацией: приходилось просить листать слайды сидящего за ноутбуком человека.
Ясно, что радиокружок хакспейс выруливает в основном за счет энтузиазма его активистов и базы ИТМО, и у ребят просто могло отсутствовать нужное оборудование. Уверен, что это было не последнее их мероприятие с проведением презентаций, поэтому предлагаю свой Arduino-вариант аналогичного устройства - надеюсь, оно пригодится не только им.
Ингредиенты
Сначала создадим прототип - из того, что найдется под рукой. Мне всегда несколько проще, потому что у меня под рукой целый mk90.ru/store ;)
1. Плата Freeduino32u4 - содержит единственный чип ATmega32u4, который может изображать из себя клавиатуру, и поэтому именно ее мы будем подключать к компьютеру с презентацией;
| |
2. Плата Freeduino Nano - будет находиться в руке у докладчика, считывая нажатия на кнопки прокрутки и передавая их Freeduino 32u4;
| |
3. Кнопки, проводки и пара беспаечных макеток, чтобы не хвататься за паяльник по пустякам.
| |
4. Комплект модулей беспроводной связи - наверное, самые дешевые устройства для передачи низкоскоростного информационного потока через эфир для небольших расстояний.
|
Немного теории
Прежде всего, вот заранее мой ответ фанатикам IEEE 802.11, на вопрос "почему в списке нет wifi?!": не палите из пушки по микробам! В нашей задаче не нужна маршрутизация, одновременная коммуникация между несколькими устройствами и повышенная проникающая способность радиоизлучения. Нас устроят гораздо более простые устройства - т.н. RF-трансиверы. В основном, они отличаются мощностью, несущей частотой и способом модуляции. Возьмем недорогой маломощный модуль, используемый в самоходных радиоуправляемых игрушках и работающий на нелицензируемой в России частоте 433 МГц, с модуляцией ASK.
"ASK" означает, что частота и фаза сигнала остаются постоянными, а для кодирования единицы и нуля варьируется амплитуда. Подобные приемники и передатчики можно собрать и самостоятельно, подробнее об этом можно прочесть, например у Алексея Кравченко в книге "10 практических устройств на AVR, книга 2".
Однако, слабые места такого решения тоже выглядят внушительно:
- два передатчика, работающие на одной несущей частоте, будут конфликтовать, мешая друг другу;
- приемник не имеет возможности отличить посылку от одного передатчика от посылки другого - надо позаботиться об идентификации программным способом;
- сильная подверженность помехам, радиус действия зависит от питающего напряжения;
- практически нулевая безопасность, шифрование аппаратно не поддерживается.
Что-то решается программно, что-то - схемотехнически, но самое главное - такие приемопередатчики поддерживаются ArduinoIDE с помощью специальной библиотеки VirtualWire.
Схема
Рисуем схему, которая будет подключаться к USB:
И ту, которая в руке у лектора (с кнопочками):
Предполагаем пока, что обе платы питаются от компьютера, через USB.
Как бы не ругали беспаечную макетку за надежность контактов, на несложных схемах работать с ней весьма удобно. Я использую сочетание жестких и гибких проводов (вообще, это дело вкуса - гибкими быстрее, но менее наглядно). С платой 32u4:
С платой Freeduino Nano:
Кнопки устанавливать я поленился, выбрав в качестве имитации их нажатия втыкание проводка в дырочку рядом с соответствующим контактом Nano (естественно, другим концом провод воткнут в GND).
Скетчи
Как уже упоминалось, существует библиотека VirtualWire. Она будет делать за нас всю нудную работу - не только управлять приемником и передатчиком, но и кодировать наше сообщение помехоустойчивым кодом, а также считать/проверять контрольную сумму всего сообщения. Выбирая пины Arduino-совместимой платы для подключения приемника и передатчика, я вполне сознательно выбрал те, с которыми библиотека работает по умолчанию (помните об этом, если будете менять схему - не забудьте передать соответствующие параметры библиотеке).
Скетч передатчика:
#include <VirtualWire.h> #define buttonsCount 3 int bState[buttonsCount]; int bNum[buttonsCount] = {2,3,4}; void setup() { // buttons for (byte i=0;i<buttonsCount;i++) { bState[i] = HIGH; pinMode(bNum[i],INPUT); // для ясности, после сброса все пины и так уже входы digitalWrite(bNum[i],HIGH); // pullup } // инициализируем последовательный порт - только для отладки Serial.begin(9600); // инициализируем библиотеку VirtualWire vw_setup(2000); // скорость обмена в бит/сек } void loop() { for (byte i=0;i<buttonsCount;i++) { if (digitalRead(bNum[i])!=bState[i]) { if (bState[i] == HIGH) { char key[2] = {i+1,'\0'}; vw_send((uint8_t *)key, strlen(key)); vw_wait_tx(); bState[i] = LOW; } else bState[i] = HIGH; } } delay(100); }
Перед началом использования кнопок, в setup включаем подтягивающие резисторы (зачем нужны подтягивающие резисторы, я уже писал). Далее, сканируем изменение состояния кнопки и, если она перешла из состояния "свободна" в "нажато", генерируем и передаем через беспроводной модуль сообщение с номером кнопки (единственный байт со значением 1, 2 или 3). Задержка в конце - примитивная борьба с дребезгом.
Скетч приемника:
#include <VirtualWire.h> #define KEY_LEFT 80 #define KEY_RIGHT 79 #define KEY_ESC 41 #define KEY_F5 62 void setup() { pinMode(13, OUTPUT); // для индикации приема vw_setup(2000); // скорость обмена в бит/сек vw_rx_start(); // инициализация и запуск приемника } void loop() { uint8_t buf[VW_MAX_MESSAGE_LEN]; uint8_t buflen = VW_MAX_MESSAGE_LEN; if (vw_get_message(buf, &buflen)) // неблокирующее чтение { digitalWrite(13, HIGH); // Зажигаем светодиод L - принято сообщение byte key = buf[0]; if (key==1) Keyboard.print(' '); else { { KeyReport kbd = {0}; if (key==2) kbd.keys[0] = KEY_LEFT; else kbd.keys[0] = KEY_RIGHT; Keyboard.sendReport(&kbd); } { KeyReport kbd = {0}; Keyboard.sendReport(&kbd); } } digitalWrite(13, LOW); } delay(200); }
Этот скетч будет корректно работать (и вообще скомпилируется) только с последним пропатченным ядром ArduinoIDE для Freeduino 32u4 - там функция sendReport сделана публичной, чтобы скетч был способен генерировать нажатия не только на символьные клавиши (буква 'A', цифра '8' или пробел), но и управляющие коды - (стрелки, F-клавиши и т.п.). После вызова sendReport с кодом нажатой клавиши, нужно всегда делать еще один, чтобы сообщить о ее отпускании, в противном случае через некоторое время получим режим повтора.
В данном скетче кнопка №1 транслируется в пробел, кнопка №2 - стрелка влево, кнопка №3 - стрелка вправо. Функция vw_get_message вернет нам полученное сообщение только в том случае, если совпала подсчитанная контрольная сумма - но для нас это полностью прозрачно, просто учитывайте, что она избавляет нас от необходимости проверять достоверность принятой информации с точки зрения эфирных искажений нашего примитивного ASK-сигнала ;)
Roadmap
Итак, макет завелся. Что дальше?
Устройства для реальной жизни можно делать и на основе готовых плат Freeduino, и с помощью ЛУТ-а. Но имейте ввиду, что ATmega32u4 выпускается только в SMD (в отличие от ATmega328P).
По питанию приемника все понятно - экономить не надо. А вот с передатчиком придется повозиться - продумать батарейную схему питания, позаботиться об экономном расходовании ее ресурса.
Наконец, если будут донимать помехи, можно попробовать улучшить помехоустойчивость (повторять сообщение о нажатии в эфир несколько раз или припаять антеннки).
Все это вы уже можете начинать самостоятельно или же подождать моего продолжения, если когда оно появится ;)