Arduino

индекс
193,58

Знакомство с Arduino, часть 3. Морзе-клавиатура: бета-версия

В начале февраля я попробовала собрать морзе-клавиатуру на базе «радиоконструктора» 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), заменить пищалку вибро-моторчиком и, возможно, дополнить клавиатуру жк-экранчиком, на который можно будет выводить подсказки.
+39
25 февраля 2010, 14:49
19

комментарии (32)

+3
aryeh #
а блютуз??! Вы-же собирались добавить блютуз…
+1
telegamochka #
Конечно попробую подключить — но не все же сразу :) Тут с этими RGB-диодом и вибромотором вечер промучилась — а блютуз и жк-дисплей подключать все-таки сложнее :)
+1
InfernoMonk #
Хороший полет мысли.
Может стать отличным дополнением к Wiimote Whiteboard, которым уже активно пользуюсь.
habrahabr.ru/blogs/popular_science/31347/
habrahabr.ru/blogs/free_speech/2877/
0
telegamochka #
Спасибо, интересные ссылки :) Возможно, WiiMote можно использовать в качестве большой, дорогой и красивой морзе-клавиатуры ;))
+1
GiBS0N #
а WiFi / WiMax куда же без него ???? О_о
0
telegamochka #
Данному девайсу больше, чем Bluetooth, и не нужно :) WiFi будет просто зверски есть аккумулятор.
+1
T_Ildar #
А можно видео, чтоб посмотреть как работает?
0
telegamochka #
Попробую заснять :)
+1
rule #
очень сильное решение с фламастером :-), удобно нажимать-то?
0
telegamochka #
Категорически неудобно :( Хотя и попыталась расположить кнопки под кончиками большого и указательного пальцев. Нужно подумать над корпусом… Может, быть, рукоятка джойстика?

Хотя есть мысли и насчет другого решения: всю электронику и батареи разместить в браслете. А кнопки клавиатуры и вибромоторчик — вывести из браслета на проводах так, чтобы было удобно тержать в руке. Или закрепить на чем-то вроде колец :) Такое решение позволит, один раз заморочившись над корпусом-браслетом, использовать его и для аккордной клавиатуры, и для датчика пульса, и для других носимых устройств.
+1
ew1abz #
Про телеграфный ключ, изображенный на картинке, хочеться добавить, что для работы на нем используют движение руки вправо-влево, а не как на обычном. И без специальной электронной схемы он неработоспособен :(
0
telegamochka #
Собственно, я это и иимела в виду — что точки и тире определяются не длительностью нажатия, а замыканием разных контактов.
+1
Voventus #


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

А вот драйвер L293, мне кажется, будет пушкой против мухи в вашем случае. Ведь вибромотор для обратной связи подойдёт самый хилый, управлять которым можно любым маломощным транзистором.
0
telegamochka #
Спасибо :) В предыдущей версии я перестраховалась, поставив 2 резистора :) Вы могли бы дать совет по поводу вибромотора? У меня он — из набора Seeeduino Catalist Pack (спецификации его элементов: www.seeedstudio.com/depot/datasheet/catalyst.pdf). И у меня большое подозрение, что для его подключения все должно быть в составе набора. Иначе какой же это набор! Вот, что говорится про вибромотор:

Rated voltage 3.0V DC
Operating voltage 2.2~3.6V DC
Rated current 90mA max
Stall current 120mA max At rated voltage.
Starting voltage 2.0V DC max At rated load (vibration weight) any position of rotor.
Insulation resistance 1MΩ min At DC 100V between the lead wires and motor body.
Terminal resistance 2Ω approx. At 20c

Как бы Вы посоветовали подключить такой вибромотор? Правильно ли я думаю, что его можно подключить через входящий в этот же набор «Transistor NPN»? Его параметры (описаны в упоминавшемся выше datasheet'е)^
Collector-Base Voltage: 40V
Collector-Emitter Voltage: 20V
Emitter-Base Voltage: 5V
Collector Current: 50 mA
Collector Power Dissipation: 625 mW

Видимо, базу подключаем к 12-му выводу Arduino, а ножку транзистора, что рисуется со стрелочкой (хоть убей не вспомню сейчас, эмиттер или коллектор) — к напряжению +3.3В (с Ардуино), третью ножку транзистора — к моторчику (и второй вывод моторчика — к земле)? Правильно?
+1
Voventus #
Всё правильно, но «наоборот». Эмиттер к земле, коллектор к одному выводу мотора, а второй вывод мотора к 3V3. Плюс ограничить ток базы резистором 10kOhm.

Единицей вращаем двигатель.

А фамилию стрелки можно запомнить так: указывает она направление движения положительных носителей заряда, т.е. «испускает» (emission, эмиттер) заряд.
0
telegamochka #
Спасибо! Я была уверена, что раз купила набор, то в нем должно быть все для подключения к ардуино любого элемента набора — без необходимости что-то докупать :) На выходных попробую подключить вибру.
+1
Voventus #
По науке мотор ещё диодом в обратку шунтируют, но вибромотор в вашем наборе хилый, поэтому транзистор не должно пробить.
0
telegamochka #
«мотор ещё диодом в обратку шунтируют» — хммм… Вы могли бы подсказать, как именно подключить диод?
+1
Voventus #
Да просто обнять мотор диодом. Катодом (где чёрточка) к выводу 3V3, а анодом к коллектору.
0
telegamochka #
Спасибо :)
0
haik_vm #
да вы, батенька, маньяк…
0
DpyuD #
тётенька маньяк, точнее будут)
0
maxic #
Вот интересно было бы увидеть реализацию «панели приборов (ЖК)» для автомобиля.
0
telegamochka #
Наверняка такие решения есть, можно погуглить «carduino».
Вот автомобильные проекты на ардуиньей базе: www.compcar.ru/forum/forumdisplay.php?s=&daysprune=&f=57
Я-то автомобильными девайсами вряд ли займусь — принципиально пользуюсь общественным транспортом :) А когда нужен автомобиль — мне проще остановить частника или вызвать такси. После того, как я начала использовать время в дороге для работы, я поняла, что если и куплю автомобиль только с персональным шофером. Или когда авто сможет само довезти меня по нужному маршруту :)
+1
ibnteo #
А как планируется подключать клавиатуру к компьютеру? Для этого нужен будет специальный софт, или она будет определяться как стандартная клавиатура?
0
telegamochka #
Хотелось бы, чтобы моя клавиатура без всяких специальнописаных драйверов опознавалась компьютером именно как клавиатура, так что придется разобраться с HID.
+1
ibnteo #
У меня аналогичная задача, только клавиатура будет не аккордная, так что тоже буду разбираться. Уже заказал Freeduino Nano.
+1
telegamochka #
Давайте держать связь и обмениваться опытом? :) Естественно, без взаимных претензий впоследствии относительно кода и т.п. — если впоследствии Вы или я решим сделать свои разработки коммерческими продуктами :))
0
ibnteo #
Обязательно, я в ЖЖ зафрендил тебя, буду следить за проектом. По своему проекту буду в ЖЖ публиковать что происходит, у меня будет открытый проект. Сегодня как раз купил макетную плату и набор перемычек, осталось дождаться когда приедет Freeduino Nano, и можно приступать к разработке.
0
agnostik #
Скажите пожалуйста, а вот эта вот белая плата с кучей дырочек — как она называется и где её купить?
0
telegamochka #
Это макетная плата, позволяющая без пайки собирать прототипы устройств. Купить можно, например, здесь: kiborgov.net/hardware/prototyping-boards-and-wires.html
Или заказать из-за рубежа, например, тут: www.sparkfun.com/categories/149
0
agnostik #
Спасибо. Только начинаю заниматься и появляется много глупых вопросов. А на официальном сайте ардуион ужасно плохо подана информация.

А не подскажите ещё, провода для этой платы специальные или можно брать любые которые «воткнутся»?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.