В начале февраля я попробовала собрать морзе-клавиатуру на базе «радиоконструктора» Arduino. Получился вполне работоспособный прототип с единственной кнопкой, нажимая которую, можно «генерировать» точки и тире — из которых микроконтроллер будет собирать буквы и отправлять их на компьютер. Девайс (если можно назвать девайсом с полдюжины деталек на макетной плате) получился вполне работоспособный. Но для практического применения малопригодный, так что я собиралась усовершенствовать конструкцию. И вот, что у меня получилось.
Прежде всего, в новой версии морзе-клавиатуры стоило бы отказаться от ввода точек и тире одной кнопкой. Однокнопочный ввод — это необходимость подстраивать свой темп печати под временные интервалы, прописанные в программе, чтобы точки и тире пользователя «попадали» в точки и тире клавиатуры. Да и вводить точки-тире куда удобнее, не задумываясь над длительностью нажатия. К такому варианту давным-давно пришли радисты и телеграфисты, перейдя на подобные ключи:
Итак, мы будем использовать две кнопки — одну для точек, вторую для тире. Заодно заменим малоинформативный одноцветный светодиод на RGB-диод. Цветом можно будет информировать пользователя о текущей раскладке. Кроме того, стоило бы дополнить девайс обратной тактильной связью — например, через вибромотор. Правда, как выяснилось в процессе работы, вибромотор нужно подключать через специальную микросхему — драйвер управления моторами. Поэтому пока я заменила вибромотор пьезодинамиком-пищалкой, который можно подключить к Arduino напрямую, без дополнительных деталей.
Что мне потребовалось для работы:
1) Freeduino 2009 — полный аналог Arduino Duemilanove (впрочем, можно было обойтись куда более слабыми вариациями микроконтроллера)
2) Макетная плата
3) 2 кнопки
4) RGB-светодиод с общим анодом
5) 2 резистора 10 кОм
6) 3 резистора 330 Ом
7) Пьезо-динамик (на картинко по ссылке это Piezo Buzzer)
8) дюжина проводков для соединения деталей на макетной плате
Для создания альфа-версии мне дополнительно понадобились:
9) Полметра кабеля «витая пара». Как оказалось, проводки из «витой пары» вполне годятсяв качестве соединительных проводов для макетки.
10) Старый непишущий фломастер для надписей на компакт-дисках.
11) Паяльник, припой, изолента.
Как подключить RGB-светодиод.
В прошлой версии морзе-клавиатуры мы использовали простой одноцветный светодиод, с одним анодом и одним катодом. Его подключить было несложно: анод подключаем к выводу микроконтроллера, катод — через резистор к земле. Возможно, если бы у меня был RGB-диод с общим катодом и тремя анодами, я бы поступила аналогично. Но в набор Seeeduino Catalyst Pack входили RGB-диоды с общим анодом, так что пришлось немножко подумать над подключением (спасибо гуру Arduino, давшим мне правильные советы!). Суть решения оказалась в использовании инвертированной логики (возможно, термин не совсем правилен — буду благодарна за поправку). В прошлой версии светодиод горел, когда на вывод микроконтроллера (МК) подавалось напряжение, и гас, когда напряжение отсутствовало. В новой версии все будет наоборот. При отсутствии напряжения на выводе диод будет гореть, а при наличии — гаснуть. Для реализации этой хитроумной схемы подключим катоды диода через сопротивления к выводам процессора. А анод — подключим к +5В. Теперь при наличии +5В на выводе МК тока через диод не будет, а при отсутствии напряжения — ток потечет и зажжет светодиод. Естественно, при написании программы нам нужно помнить об инвертированной логике.
Мне посоветовали применить инвертированную же логику и при подключении кнопок — потому, что с учетом возможности короткого замыкания безопаснее тянуть провод с землей, чем с напряжением. Честно говоря, логика мне не совсем понятна — ведь электроны-то «текут» не с положительного, а с отрицательного вывода. Решив разобраться с этим позже, я подключила кнопки немного иначе, чем в предыдущей версии. Теперь при отпущенной кнопке с подключенного к ней вывода МК будет считывать HIGH, а при нажатой кнопке — LOW.
Зачем нам нужна пьезо-пищалка. При наборе точек и тире хотелось бы, не глядя на экран, получать подтверждение, что набранная буква принята и отправлена на компьтер. Иначе при быстром наборе, сделав недостаточную паузу между буквами, мы можем, например, вместо символов АЕ (*- *) отправить на компьютер бкуву Р (*-*). Если пользователь будет получать сигнал при отправке каждой буквы, он наделает меньше ошибок. Конечно, вместо «пищалки» стоило бы использовать вибро-моторчик, но пищалку подключить проще. А с виброй я разберусь в следующей части морзе-эпопеи. Так что пока вместо вибрации каждый отправленный клавиатурой символ будет сопровождаться коротким звуком.
Соберем схему:
Схему я рисовала в программе Fritzing, но мне нигде не удалось найти для нее RGB-светодиода. Поэтому я самостоятельно собрала эту «детальку», склеив красный, зеленый и синий светодиоды. При подключении диода стоит посмотреть на его спецификацию, чтобы понять, где какая ножка. Нелишним будет и протестировать диод, подключив общий анод к плюсу батарейки, а каждый из катодов — поочередно к его минусу. Для тестирования своего светодиода я использовала два подключенных последовательно полуторавольтовых (итого 3 вольта) AA-аккумулятора от фотоаппарата.
Откомпилируем и загрузим код (он довльно подробно откомментирован, так что просто приведу листинг):
Voila! Что умеет наш девайс? Левая кнопка выдает точку. Правая — тире. При этом диод вспыхивает в такт нажимаемым кнопкам (для тире — ярче, чем для точки). Если нажать левую и, не отпуская ее, правую — переключимся на кириллицу (зеленый цвет диода). А нажав правую и, не отпуская ее, левую — на латынь (желтый цвет). Если в течение минуты ни одна кнопка не нажималась, девайс передет в спящий режим, а диод начнет мигать фиолетовым.
Теоретически клавиатура готова. Но на практике не слишком-то удобно щелкать кнопками, расположенными на макетной плате. Попробуем придать нашей конструкции более функциональный вид.
В качестве корпуса я взяла старый высохший фломастер для надписей на компакт-дисках. В следующий раз я выберу более вместительный корпус — очень уж неудобно в такой узкой трубочке протягивать кучу проводов. А у нас их получится целых 10: 4 от светодиода и по 3 от каждой кнопки. Припаяем к каждой кнопке и диоду необходимые провода и резисторы:
(кажется, на этой фотографии я перепутала провода для напряжения и провода для земли — к счастью, ничего не сгорело, но пришлось все перепаять)
Можно, конечно, было соптимизировать, спаяв внутри вместе 3 «напряжения» и 2 «земли», но тогда смонтировать все это внутри фломастера стало бы сложнее. Кроме того, спаяв вместе кнопки и диод, я бы не смогла экспериментировать с различными вариантами корпусов и размещением кнопок и диода. Поэтому я припала к катодам светодиода по 330-омному резистору и проводок соответствующего цвета (R — оранжевый, G — зеленый, B — синий), а к аноду — оранжево-белый. Так же поступила и с кнопками, припаяв к каждой по 10-килоомному сопротивлению и по 3 провода — для земли (коричневый), питания (рыже-белый) и для подключения к выводам Arduino. Припаивая провода, стоит уделить внимание, какой цвет куда должен быть подсоединен, чтобы, глядя на выходящий из корпуса десяток проводов, не гадать, какой — к чему. Оголенные места проводов я обмотала скотчем. Хотя, видимо, грамотнее было бы изолировать их с помощью термоусадочной трубочки, входящей в набор «Seeeduino Catalyst Pack». Нужно будет расспросить об этом знающих людей :)
Перед тем, как поместить кнопки и диод в корпус, я убедилась, что спаянные детали работают:
Все в сборе:
Пожалуй, получившийся девайс можно назвать бета-версией. Функционал практически полностью реализован, осталось превратить поделку в законченное устройство.
Для следующей версии морзе-клавиатуры я собираюсь подобрать более удобный корпус, в котором уместится вся электроника (нужно будет перейти на более компактную версию Arduino), заменить пищалку вибро-моторчиком и, возможно, дополнить клавиатуру жк-экранчиком, на который можно будет выводить подсказки.
Прежде всего, в новой версии морзе-клавиатуры стоило бы отказаться от ввода точек и тире одной кнопкой. Однокнопочный ввод — это необходимость подстраивать свой темп печати под временные интервалы, прописанные в программе, чтобы точки и тире пользователя «попадали» в точки и тире клавиатуры. Да и вводить точки-тире куда удобнее, не задумываясь над длительностью нажатия. К такому варианту давным-давно пришли радисты и телеграфисты, перейдя на подобные ключи:
Итак, мы будем использовать две кнопки — одну для точек, вторую для тире. Заодно заменим малоинформативный одноцветный светодиод на RGB-диод. Цветом можно будет информировать пользователя о текущей раскладке. Кроме того, стоило бы дополнить девайс обратной тактильной связью — например, через вибромотор. Правда, как выяснилось в процессе работы, вибромотор нужно подключать через специальную микросхему — драйвер управления моторами. Поэтому пока я заменила вибромотор пьезодинамиком-пищалкой, который можно подключить к Arduino напрямую, без дополнительных деталей.
Что мне потребовалось для работы:
1) Freeduino 2009 — полный аналог Arduino Duemilanove (впрочем, можно было обойтись куда более слабыми вариациями микроконтроллера)
2) Макетная плата
3) 2 кнопки
4) RGB-светодиод с общим анодом
5) 2 резистора 10 кОм
6) 3 резистора 330 Ом
7) Пьезо-динамик (на картинко по ссылке это Piezo Buzzer)
8) дюжина проводков для соединения деталей на макетной плате
Для создания альфа-версии мне дополнительно понадобились:
9) Полметра кабеля «витая пара». Как оказалось, проводки из «витой пары» вполне годятсяв качестве соединительных проводов для макетки.
10) Старый непишущий фломастер для надписей на компакт-дисках.
11) Паяльник, припой, изолента.
Как подключить RGB-светодиод.
В прошлой версии морзе-клавиатуры мы использовали простой одноцветный светодиод, с одним анодом и одним катодом. Его подключить было несложно: анод подключаем к выводу микроконтроллера, катод — через резистор к земле. Возможно, если бы у меня был RGB-диод с общим катодом и тремя анодами, я бы поступила аналогично. Но в набор Seeeduino Catalyst Pack входили RGB-диоды с общим анодом, так что пришлось немножко подумать над подключением (спасибо гуру Arduino, давшим мне правильные советы!). Суть решения оказалась в использовании инвертированной логики (возможно, термин не совсем правилен — буду благодарна за поправку). В прошлой версии светодиод горел, когда на вывод микроконтроллера (МК) подавалось напряжение, и гас, когда напряжение отсутствовало. В новой версии все будет наоборот. При отсутствии напряжения на выводе диод будет гореть, а при наличии — гаснуть. Для реализации этой хитроумной схемы подключим катоды диода через сопротивления к выводам процессора. А анод — подключим к +5В. Теперь при наличии +5В на выводе МК тока через диод не будет, а при отсутствии напряжения — ток потечет и зажжет светодиод. Естественно, при написании программы нам нужно помнить об инвертированной логике.
Мне посоветовали применить инвертированную же логику и при подключении кнопок — потому, что с учетом возможности короткого замыкания безопаснее тянуть провод с землей, чем с напряжением. Честно говоря, логика мне не совсем понятна — ведь электроны-то «текут» не с положительного, а с отрицательного вывода. Решив разобраться с этим позже, я подключила кнопки немного иначе, чем в предыдущей версии. Теперь при отпущенной кнопке с подключенного к ней вывода МК будет считывать HIGH, а при нажатой кнопке — LOW.
Зачем нам нужна пьезо-пищалка. При наборе точек и тире хотелось бы, не глядя на экран, получать подтверждение, что набранная буква принята и отправлена на компьтер. Иначе при быстром наборе, сделав недостаточную паузу между буквами, мы можем, например, вместо символов АЕ (*- *) отправить на компьютер бкуву Р (*-*). Если пользователь будет получать сигнал при отправке каждой буквы, он наделает меньше ошибок. Конечно, вместо «пищалки» стоило бы использовать вибро-моторчик, но пищалку подключить проще. А с виброй я разберусь в следующей части морзе-эпопеи. Так что пока вместо вибрации каждый отправленный клавиатурой символ будет сопровождаться коротким звуком.
Соберем схему:
Схему я рисовала в программе Fritzing, но мне нигде не удалось найти для нее RGB-светодиода. Поэтому я самостоятельно собрала эту «детальку», склеив красный, зеленый и синий светодиоды. При подключении диода стоит посмотреть на его спецификацию, чтобы понять, где какая ножка. Нелишним будет и протестировать диод, подключив общий анод к плюсу батарейки, а каждый из катодов — поочередно к его минусу. Для тестирования своего светодиода я использовала два подключенных последовательно полуторавольтовых (итого 3 вольта) AA-аккумулятора от фотоаппарата.
Откомпилируем и загрузим код (он довльно подробно откомментирован, так что просто приведу листинг):
//================================================================================================================ // Настройки кнопок. //================================================================================================================ // Кнопка-точка будет слева. #define BUTTON_DOT 6 // Кнопка-тире - справа. #define BUTTON_TIRE 7 int buttons, buttonsPrev; // Здесь будем в виде битовых масок хранить нынешнее и предыдущее состояние клавиш. #define BUTTON_DOT_MASK 1 #define BUTTON_TIRE_MASK 2 // С помощью этих переменных мы засекаем, когда в последний раз была нажата и отпущена кнопка. Любая. По этому // времени мы определяем, завершен ли ввод текущего символа и не пора ли переходить в спящий режим. unsigned long int timeRelease, timePress; // Смену состояния кнопки на время менее 0.03с будем считать дребезгом и игнорировать. #define DEBOUNCING_TIME 30 // В эти переменные будем сохранять время для отфильтровывания дребезга контактов. unsigned long int timeDotDebouncing; unsigned long int timeTireDebouncing; // Максимальная пауза между точками и тире в букве - 0.4 секунды. // Если пауза больше - считаем ввод буквы завершенным и переходим к вводу следующей буквы. #define DELIMITER_TIME 500 // Если кнопка не нажималась более минуты - переходим в спящий режим. #define SLEEP_TIME 60000 // Выйти из спящего режима можно, удерживая нажатой любую кнопку в течение 1 секунды. #define WAKEUP_TIME 2000 // Для переключения раскладки на кириллицу нажимаем кнопку-точку и, не отпуская ее, нажимаем кнопку-тире. // Для переключения раскладки на латынь нажимаем правую кнопку-тире и, не отпуская ее, нажимаем кнопку-точку. //================================================================================================================ // Настройки RGB-светодиода. //================================================================================================================ // Для обратной связи будем использовать RGB-светодиод: #define LED_R 11 #define LED_G 10 #define LED_B 9 // Цвета диода будем задавать в виде числа-битовой маски: 00000RGB, вспомним цвета в старых добрых EGA и Yamaha MSX. // Семи цветов (черный не в счет) нам более, чем хватит. #define COLOR_BLACK 0 #define COLOR_BLUE 1 #define COLOR_GREEN 2 #define COLOR_CYAN 3 #define COLOR_RED 4 #define COLOR_MAGENTA 5 #define COLOR_YELLOW 6 #define COLOR_WHITE 7 // Кириллица - зеленый, латынь - желтый, спящий режм - мигание фиолетовым. #define COLOR_CYRILLIC_LAYOUT COLOR_GREEN #define COLOR_LATIN_LAYOUT COLOR_YELLOW #define COLOR_SLEEP_MODE COLOR_MAGENTA // Яркость мигания для режима печати и спящего режима. Не забывваем, что у нас логика инвертирована // и 0 означает максимальную яркость, а 255 - погашенный светодиод. #define BRIGHTNESS_TYPING_LOW (255-1) #define BRIGHTNESS_TYPING_DOT (255-7) #define BRIGHTNESS_TYPING_TIRE (255-15) #define BRIGHTNESS_SLEEP_LOW (255-0) #define BRIGHTNESS_SLEEP_HIGH (255-1) /* #define BRIGHTNESS_TYPING_LOW (255-7) #define BRIGHTNESS_TYPING_DOT (255-128) #define BRIGHTNESS_TYPING_TIRE (255-255) #define BRIGHTNESS_SLEEP_LOW (255-8) #define BRIGHTNESS_SLEEP_HIGH (255-128) */ //================================================================================================================ // Настройки пьезо-динамика. //================================================================================================================ #define PIEZO 12 byte piezoData; unsigned long int piezoShutUpTime; //================================================================================================================ // Азбука Морзе. //================================================================================================================ // Этими символами мы будем обозначать точки и тире. #define MORSE_DOT '*' #define MORSE_TIRE '-' // Точка или тире пока не введены. #define MORSE_EMPTY 0 // Это - для блокировки ввода точек/тире при смене раскладки или выходе из спящего режима. #define MORSE_LOCKED '!' // Максимальная длина символа азбуки Морзе (в точках и тире) #define MAX_MORSE_SYMBOL_LENGTH 8 // Буфер для записи морзе-символа. byte morseSymbol[MAX_MORSE_SYMBOL_LENGTH]; unsigned int morseSymbolLen; byte newMorseSignal; // Новый введенный сигнал - точка или тире. // Таблица кодов Морзе. N-ный элемент кода соответствует n-ному символу раскладки. char* code[] = { "*-","-***","*--","--*","-**","*","***-","--**","**","*---", "-*-","*-**","--","-*","---","*--*","*-*","***","-","**-", "**-*","****","-*-*","---*","----","--*-","-*--","-**-","**-**","**--", "*-*-", "*----","**---","***--","****-","*****","-****","--***","---**","----*","-----", "......","*-*-*-","---***","-*-*-","-*--*-","*----*","*-**-*","-****-","-**-*","**--**","--**--", "-***-","********","*--*-*","**-*-", "" }; // Кириллическая раскладка. char* layoutCyrillic[] = { "а","б","в","г","д","е","ж","з","и","й", "к","л","м","н","о","п","р","с","т","у", "ф","х","ц","ч","ш","щ","ы","ь","э","ю", "я", "1","2","3","4","5","6","7","8","9","0", ".",",",":",";","(","\'","\"","-","/","?","!", " *DELIMITER* "," *ERR* ","@"," *END* ", "" }; // Латинская раскладка. char* layoutLatin[] = { "a","b","w","g","d","e","v","z","i","j", "k","l","m","n","o","p","r","s","t","u", "f","h","c","ö","ch","q","y","x","é","ü", "ä", "1","2","3","4","5","6","7","8","9","0", ".",",",":",";","(","\'","\"","-","/","?","!", " *DELIMITER* "," *ERR* ","@"," *END* ", "" }; char** currentLayout; char** newLayout; //================================================================================================================ // Режимы работы. //================================================================================================================ #define TYPING_MODE 0 #define SLEEP_MODE 1 int mode; boolean flagWakeUp; // Этот флаг будем использовать для выхода из спящего режима. byte ledLevelSleepCounter; // Переключатель яркости для мигания диода в спящеь режиме. //================================================================================================================ void setup() { Serial.begin(9600); pinMode(LED_R, OUTPUT); pinMode(LED_G, OUTPUT); pinMode(LED_B, OUTPUT); // И кнопки, и светодиод у нас работают с инвертированной логикой: кнопка нажата = LOW, отпущена = HIGH, // светодиод горит на полную яркость = LOW, погашен = HIGH. Погасим светодиоды: analogWrite(LED_R, 255); analogWrite(LED_G, 255); analogWrite(LED_B, 255); pinMode(PIEZO, OUTPUT); digitalWrite(PIEZO, LOW); pinMode(BUTTON_DOT, INPUT); pinMode(BUTTON_TIRE, INPUT); buttons = 0; buttonsPrev = 0; mode = TYPING_MODE; flagWakeUp = false; morseSymbolLen = 0; currentLayout = layoutLatin; newLayout = 0; newMorseSignal = MORSE_EMPTY; ledLevelSleepCounter = 0; } //================================================================================================================ // Зажжем светодиод нужными цветом и яркостью. Не забываем, что у нас инвертирована логика и 0 - это самый яркий // свет, а 255 - погашенный светодиод. void setLed(int ledColor, int ledBrightness) { if (ledColor & COLOR_RED) { analogWrite(LED_R, ledBrightness); } else { analogWrite(LED_R, 255); } if (ledColor & COLOR_GREEN) { analogWrite(LED_G, ledBrightness); } else { analogWrite(LED_G, 255); } if (ledColor & COLOR_BLUE) { analogWrite(LED_B, ledBrightness); } else { analogWrite(LED_B, 255); } } //================================================================================================================ // Работа с пьезо-динамиком void doPiezo(unsigned long int currentTime) { if (currentTime >= piezoShutUpTime) { if (piezoShutUpTime > 0) { piezoShutUpTime = 0; digitalWrite(PIEZO, LOW); } return; } piezoData = (piezoData == LOW) ? HIGH : LOW; digitalWrite(PIEZO, piezoData); } void playPiezo(unsigned long int t, unsigned long int currentTime) { piezoShutUpTime = currentTime + t; } //================================================================================================================ // Считывание состояния кнопки с учетом возможного дребезга контактов. int getButtonState(int btnPrevState, int BUTTON_PIN, unsigned long int* timeDebouncing, unsigned long int currentTime) { int btnState = digitalRead(BUTTON_PIN); if (btnState == HIGH) { if (btnPrevState == LOW) { if (*timeDebouncing == 0) { // Засечем время, которое кнопка будет нажата - чтобы не спутать дребезг контактов с нажатием. *timeDebouncing = currentTime; // Пока не воспринимаем нажатие, считая его дребезгом контактов. btnState = LOW; } else { if ((currentTime - *timeDebouncing) < DEBOUNCING_TIME) { // Пока не воспринимаем нажатие, считая его дребезгом контактов. btnState = LOW; } else { // Это не дребезг контактов, это реальное нажатие кнопки. btnState = HIGH; *timeDebouncing = 0; } } } else { *timeDebouncing = 0; } } else { if (btnPrevState == HIGH) { if (*timeDebouncing == 0) { // Засечем время, которое кнопка будет нажата - чтобы не спутать дребезг контактов с нажатием. *timeDebouncing = currentTime; // Пока не воспринимаем отпускание, считая его дребезгом контактов. btnState = HIGH; } else { if ((currentTime - *timeDebouncing) < DEBOUNCING_TIME) { // Пока не воспринимаем отпускание, считая его дребезгом контактов. btnState = HIGH; } else { // Это не дребезг контактов, это реальное отпукание кнопки. btnState = LOW; *timeDebouncing = 0; } } } else { *timeDebouncing = 0; } } return btnState; } //================================================================================================================ // Отправим на компьютер введенный символ. void sendMorseSymbol() { int i, j; if (morseSymbolLen < 1) { return; } playPiezo(50, millis()); for (i = 0; code[i][0] != '\0'; i++) { // Сравним введенный символ с символами из таблицы кодов Морзе. for (j = 0; (j < morseSymbolLen) && (code[i][j] != '\0'); j++) { if (code[i][j] != morseSymbol[j]) { j = -1; break; } } if ((j != -1) && (j == morseSymbolLen) && (code[i][j]=='\0')) { // Символ из таблицы кодов Морзе соответствует введенному символу. // Отправим символ на компьютер. Serial.print(currentLayout[i]); morseSymbolLen = 0; return; } } // Символ в таблице не найден. Напечатаем нераспознанный символ. Serial.print(" ["); for (i = 0; i < morseSymbolLen; i++) { Serial.print(morseSymbol[i]); } Serial.print("] "); morseSymbolLen = 0; } //================================================================================================================ // Режим печати void typingLoop() { unsigned long int t, dt; // Эти пременные будем использовать для замеров времени. int btnDotState, btnTireState; // В эти переменные считаем состояния кнопок. В принципе, их можно было бы сразу // занести в переменную buttons, но так код будет понятнее. int ledLevel; // Яркость диода int ledColor; // Цвет диода, битовая маска - 00000RGB. // analogWrite(PIEZO, 0); t = millis(); // Не забываем, что у нас логика инвертирована, и нажатая кнопка - это LOW. btnDotState = getButtonState((buttonsPrev & BUTTON_DOT_MASK) ? LOW : HIGH, BUTTON_DOT, &timeDotDebouncing, t); btnTireState = getButtonState((buttonsPrev & BUTTON_TIRE_MASK) ? LOW : HIGH, BUTTON_TIRE, &timeTireDebouncing, t); buttons = ((btnDotState == LOW) ? BUTTON_DOT_MASK : 0) | ((btnTireState == LOW) ? BUTTON_TIRE_MASK : 0); if (buttons == 0) { // Обе кнопки отпущены, можно добавить введенную точку, тире или переключить раскладку. // Если пауза дольше SLEEP_TIME - перейдем в спящий режим. // Если пауза дольше DELIMITER_TIME - отправим символ. if (buttonsPrev != 0) { timeRelease = t; } if (newLayout) { currentLayout = newLayout; newLayout = 0; } else switch (newMorseSignal) { case MORSE_DOT: case MORSE_TIRE: morseSymbol[morseSymbolLen++] = newMorseSignal; break; // MORSE_DOT, MORSE_TIRE } newMorseSignal = MORSE_EMPTY; dt = t - timeRelease; if ((morseSymbolLen > 0) && (dt > DELIMITER_TIME)) { sendMorseSymbol(); } else if (dt > SLEEP_TIME) { mode = SLEEP_MODE; Serial.println("\nSleep mode\n"); } } else if (newMorseSignal != MORSE_LOCKED) { switch (buttons) { case BUTTON_DOT_MASK: if (newMorseSignal == MORSE_EMPTY) { // Нажата "точка". newMorseSignal = MORSE_DOT; timePress = t; } break; // BUTTON_DOT_MASK case BUTTON_TIRE_MASK: if (newMorseSignal == MORSE_EMPTY) { // Нажато "тире". newMorseSignal = MORSE_TIRE; timePress = t; } break; // BUTTON_DOT_MASK case BUTTON_DOT_MASK | BUTTON_TIRE_MASK: // Нажаты обе кнопки. Сменим раскладку. switch (buttonsPrev ) { case 0: // Маловероятно, что обе кнопки нажаты одновременно, но в этом случае переключимся на кириллицу. case BUTTON_DOT_MASK: if (newLayout == 0) { sendMorseSymbol(); newLayout = layoutCyrillic; Serial.println("\nLayout: cyrillic\n"); } break; // 0, BUTTON_DOT_MASK case BUTTON_TIRE_MASK: if (newLayout == 0) { sendMorseSymbol(); newLayout = layoutLatin; Serial.println("\nLayout: latin\n"); } break; // BUTTON_TIRE_MASK } timePress = t; newMorseSignal = MORSE_LOCKED; break; // BUTTON_DOT_MASK | BUTTON_TIRE_MASK } } // Займемся светодиодом. if (currentLayout == layoutCyrillic) { ledColor = COLOR_CYRILLIC_LAYOUT; } else { ledColor = COLOR_LATIN_LAYOUT; } setLed(ledColor, (buttons == 0) ? BRIGHTNESS_TYPING_LOW : ((buttons == BUTTON_DOT_MASK) ? BRIGHTNESS_TYPING_DOT : BRIGHTNESS_TYPING_TIRE)); doPiezo(t); buttonsPrev = buttons; delay(10); } //================================================================================================================ // Спящий режим void sleepLoop() { unsigned long int t, dt; // Эти пременные будем использовать для замеров времени. int btnDotState, btnTireState; // В эти переменные считаем состояния кнопок. В принципе, их можно было бы сразу // занести в переменную buttons, но так код будет понятнее. int ledLevel; // Яркость диода int ledColor; // Цвет диода, битовая маска - 00000RGB. // Мы же спим - поэтому будем проверять статус кнопок редко - раз в 0.3 с. delay(300); t = millis(); // Не забываем, что у нас логика инвертирована, и нажатая кнопка - это LOW. btnDotState = getButtonState((buttonsPrev & BUTTON_DOT_MASK) ? LOW : HIGH, BUTTON_DOT, &timeDotDebouncing, t); btnTireState = getButtonState((buttonsPrev & BUTTON_TIRE_MASK) ? LOW : HIGH, BUTTON_TIRE, &timeTireDebouncing, t); buttons = ((btnDotState == LOW) ? BUTTON_DOT_MASK : 0) | ((btnTireState == LOW) ? BUTTON_TIRE_MASK : 0); if (buttons != 0) { if (buttonsPrev == 0) { timePress = t; } // Определим, достаточно ли долго была нажата кнопка для выхода из спячки. if (!flagWakeUp && ((t - timePress) >= WAKEUP_TIME)) { flagWakeUp = true; } } else { if (buttonsPrev != 0) { timeRelease = t; } if (flagWakeUp) { // Просыпаемся. flagWakeUp = false; mode = TYPING_MODE; Serial.println("\nTYPING_MODE\n"); return; } } // Помигаем светодиодом. if (flagWakeUp) { // Зажжем цвет, соответствующий текущей раскладке. if (currentLayout == layoutCyrillic) { ledColor = COLOR_CYRILLIC_LAYOUT; } else { ledColor = COLOR_LATIN_LAYOUT; } ledLevel = BRIGHTNESS_TYPING_TIRE; } else { ledColor = COLOR_SLEEP_MODE; ledLevel = (ledLevelSleepCounter == 0) ? BRIGHTNESS_SLEEP_LOW : BRIGHTNESS_SLEEP_HIGH; ledLevelSleepCounter = 1-ledLevelSleepCounter; } setLed(ledColor, ledLevel); buttonsPrev = buttons; } //================================================================================================================ // Главный цикл. void loop() { switch(mode) { case TYPING_MODE: typingLoop(); break; case SLEEP_MODE: sleepLoop(); break; } }
Voila! Что умеет наш девайс? Левая кнопка выдает точку. Правая — тире. При этом диод вспыхивает в такт нажимаемым кнопкам (для тире — ярче, чем для точки). Если нажать левую и, не отпуская ее, правую — переключимся на кириллицу (зеленый цвет диода). А нажав правую и, не отпуская ее, левую — на латынь (желтый цвет). Если в течение минуты ни одна кнопка не нажималась, девайс передет в спящий режим, а диод начнет мигать фиолетовым.
Теоретически клавиатура готова. Но на практике не слишком-то удобно щелкать кнопками, расположенными на макетной плате. Попробуем придать нашей конструкции более функциональный вид.
В качестве корпуса я взяла старый высохший фломастер для надписей на компакт-дисках. В следующий раз я выберу более вместительный корпус — очень уж неудобно в такой узкой трубочке протягивать кучу проводов. А у нас их получится целых 10: 4 от светодиода и по 3 от каждой кнопки. Припаяем к каждой кнопке и диоду необходимые провода и резисторы:
(кажется, на этой фотографии я перепутала провода для напряжения и провода для земли — к счастью, ничего не сгорело, но пришлось все перепаять)
Можно, конечно, было соптимизировать, спаяв внутри вместе 3 «напряжения» и 2 «земли», но тогда смонтировать все это внутри фломастера стало бы сложнее. Кроме того, спаяв вместе кнопки и диод, я бы не смогла экспериментировать с различными вариантами корпусов и размещением кнопок и диода. Поэтому я припала к катодам светодиода по 330-омному резистору и проводок соответствующего цвета (R — оранжевый, G — зеленый, B — синий), а к аноду — оранжево-белый. Так же поступила и с кнопками, припаяв к каждой по 10-килоомному сопротивлению и по 3 провода — для земли (коричневый), питания (рыже-белый) и для подключения к выводам Arduino. Припаивая провода, стоит уделить внимание, какой цвет куда должен быть подсоединен, чтобы, глядя на выходящий из корпуса десяток проводов, не гадать, какой — к чему. Оголенные места проводов я обмотала скотчем. Хотя, видимо, грамотнее было бы изолировать их с помощью термоусадочной трубочки, входящей в набор «Seeeduino Catalyst Pack». Нужно будет расспросить об этом знающих людей :)
Перед тем, как поместить кнопки и диод в корпус, я убедилась, что спаянные детали работают:
Все в сборе:
Пожалуй, получившийся девайс можно назвать бета-версией. Функционал практически полностью реализован, осталось превратить поделку в законченное устройство.
Для следующей версии морзе-клавиатуры я собираюсь подобрать более удобный корпус, в котором уместится вся электроника (нужно будет перейти на более компактную версию Arduino), заменить пищалку вибро-моторчиком и, возможно, дополнить клавиатуру жк-экранчиком, на который можно будет выводить подсказки.