войти зарегистрироваться

Arduino whois

индекс
0,00

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

Даже на базе простейшего — лампочка с кнопкой — скетча можно собрать вполне самостоятельное устройство. Думаете, речь пойдет о настольной лампе? Нет. Попробуем собрать простейший вариант аккордной клавиатуры.



Аккордная клавиатура — это устройство, позволяющее вводить текст, но в отличие от обычной полноразмерной qwerty-клавиатуры аккордная имеет небольшое число кнопок (обычно от 3-5 и до дюжины). При этом символ вводится одновременным нажатием не одной, а нескольких кнопок — аккордом. Также символ может вводиться не одним, а несколькими последовательными нажатиями. На клавиатуре компьютера мы тоже время от времени используем аккорды — например, вводя заглавную букву одновременным нажатием соответствующей клавиши и shift'а. Или используя разнообразные заклинания вроде Shift+Ctrl+S или Ctrl+Alt+Del. Какую же аккордную клавиатуру можно собрать, используя только одну кнопку? Вспомним старую добрую проверенную временем азбуку Морзе.

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

Начнем с разработки альфа-версии устройства. Для альфы нам потребуются:
— Arduino-совместимая плата
— кнопка
— светодиод
— резистор 330 Ом
— резистор 1 кОм
— резистор 10 кОм
— макетная плата
— 7 проводков для макетной платы
— кабель USB/miniUSB

Поведение устройства.

Логика распознавания точек и тире будет простейшая. Если нажатие кнопки длилось менее некоторого времени, устройство определит сигнал как точку. Если дольше — как тире.

Длительное нажатие — переключит раскладку клавиатуры с кириллицы на латынь и обратно. В качестве индикатора будем использовать светодиод. Пусть погасший светодиод означает кириллическую раскладку, горящий — латинскую. При нажатии кнопки во время печати светодиод будет загораться (при включенной кириллице, для латыни — наоборот, гаснуть).

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

Схема скетча:


Соберем схему, показанную на рисунке выше. Черные проводки на схеме идут к «земле», красные — к +5В. Не забудем, что подключать диод нужно так, чтобы к «земле» был подключен отрицательный, более короткий вывод диода.

Схему скетча можно логически разбить на 2 цепи — диодную и кнопочную.

Светодиод имеет весьма небольшое внутреннее сопротивление, и если мы подключим его напрямую, то возникший в цепи большой ток может сжечь порт микроконтроллера. Поэтому, подключив небольшое 330-омное сопротивление, мы ограничим ток. Величину тока легко оценить, воспользовавштись законом Ома, гласящему, что ток равен отношению напряжения к сопротивлению: I = V/R. В нашем случае примем сопротивление светодиода за 0 и получим по формуле ток, равный 5 Вольт / 330 Ом = 15.1515… миллиампер. Порты микроконтроллера выдерживают ток до 40 мА (сколько выдержат ваши диод и микроконтроллер — смотрите по спецификациям), так что 15-16 мА — вполне в пределах допустимого.

Кнопочная цепь несколько сложнее. Что касается резистора R2 — тут все понятно. Мы подключаем его чтобы ограничить текущий через нажатую кнопку ток. Зачем нужен резистор R3? Попробуем разобраться в «физике» работы цифрового порта. Если мы подключим кнопку без этого резистора, подключающего «землю» к порту7, то при ненажатой кнопке к 7-му порту будет подключен только синий проводок, по которому не идет никакого тока. Казалось бы, это значит, что к порту ничего не подключено. Вовсе нет! Этот проводок, играя роль антенны, начнет собирать наводки от электросети, компьютера и т.п. В результате на вход порта посыплется цифровой мусор, хаотическая последовательность логических нулей и единиц — как если бы мы постоянно случайным образом нажмали/отпускали кнопку. Сопротивление R3 подводит к порту «землю» для того, чтобы при ненажатой кнопке на порт поступал совершенно конкретный потенциал — потенциал «земли», логический «ноль». Когда мы замкнем кнопку, на порт пойдет +5В — логическая «единица».

Исходный текст скетча для морзе-клавиатуры:

#define BUTTON  7
#define LED  11

// Максимальная длительность точки, мс. Все, что нажато 1/4 секунды или быстрее - точка.
//  Остальное - тире.
#define MAX_DOT_PRESS_TIME  250
// Для переключения раскладки удерживаем кнопку 2 секунды. 
#define SWITCH_LAYOUT_TIME  2000
// Максимальная пауза между точками и тире в букве - 0.3 секунды.
// Если пауза больше - считаем ввод буквы завершенным и переходим к вводу следующей буквы.
#define DELIMITER_TIME  300
// Если кнопка не нажималась более минуты - переходим в спящий режим.
#define SLEEP_TIME  60000
// Выйти из спящего режима можно, удерживая нажатой кнопку в течение 2 секунд.
#define WAKEUP_TIME  2000

// Режимы работы
#define TYPING_MODE  0
#define SLEEP_MODE  1
int mode;

// Текущее и предыдущее состояния кнопки.
int btnState;
int btnPrevState;

// Время нажатия и отпускания кнопки.
unsigned long int timePress;
unsigned long int timeRelease;

// Этими символами мы будем обозначать точки и тире.
#define MORSE_DOT  '*'
#define MORSE_TIRE  '-'

// Максимальная длина символа азбуки Морзе (в точках и тире)
#define MAX_MORSE_SYMBOL_LENGTH  8
// Буфер для записи морзе-символа.
byte morseSymbol[MAX_MORSE_SYMBOL_LENGTH];
unsigned int morseSymbolLen;

// Таблица кодов Морзе. 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 MAX_SLEEP_BRIGHTNESS  16
int sleepBrightness;
// Яркость при выходе из спящего режима.
#define WAKEUP_BRIGHTNESS  255

void setup() {
  Serial.begin(9600);
  pinMode(LED, OUTPUT);
  pinMode(BUTTON, INPUT);
  mode = TYPING_MODE;
  morseSymbolLen = 0;
  btnPrevState = LOW;
  timePress = millis();
  timeRelease = millis();
  currentLayout = layoutLatin;
  newLayout = 0;
}

// Отправим на компьютер введенный символ.
void sendMorseSymbol() {
  int i, j;
  if (morseSymbolLen < 1) {
    return;
  }
  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 loop() {
  switch(mode) {
    case TYPING_MODE:
      typingLoop();
      break;
    case SLEEP_MODE:
      sleepLoop();
      break;
  }
}

void typingLoop() {
  int ledLevel;
  // Если включена кириллическая раскладка, при нажатии диод будет гореть.
  // Если включена латинская раскладка, при нажатии диод будет гаснуть.
  if (newLayout == 0) {
    ledLevel = (currentLayout == layoutCyrillic) ? 255 : 0;
  } else {
    ledLevel = (newLayout == layoutCyrillic) ? 255 : 0;
  }
  
  btnState = digitalRead(BUTTON);
  if (btnState == HIGH) {
    analogWrite(LED, ledLevel);
    if (btnPrevState == LOW) {
      // Пользователь нажал кнопку.
      timePress = millis();
    } else {
      if ((newLayout == 0) && ((millis() - timePress) > SWITCH_LAYOUT_TIME)) {
        // Если кнопка долго удерживалась в нажатом состоянии, сменим раскладку.
        if (currentLayout == layoutLatin) {
          newLayout = layoutCyrillic;
Serial.println("\nLayout: cyrillic\n");
        } else {
          newLayout = layoutLatin;
Serial.println("\nLayout: latin\n");
        }
      }
    }
  } else {
    analogWrite(LED, 255-ledLevel);
    if (btnPrevState == HIGH) {
      // Пользователь отпустил кнопку.
      timeRelease = millis();
      // Если мы только что поменяли раскладку - сменим ее.
      if (newLayout != 0) {
        sendMorseSymbol();
        currentLayout = newLayout;
        newLayout = 0;
      } else {
        if ((timeRelease - timePress) > MAX_DOT_PRESS_TIME) {
          morseSymbol[morseSymbolLen++] = MORSE_TIRE;
        } else {
          morseSymbol[morseSymbolLen++] = MORSE_DOT;
        }
        if (morseSymbolLen > MAX_MORSE_SYMBOL_LENGTH) {
          // Мы ввели максимальное число морзе-сигналов, которое может быть
          //  в морзе-символе. Анализируем символ и отправляем на компьютер.
          sendMorseSymbol();
        }
      }
    } else {
      unsigned long int timePause = millis() - timeRelease;
      if (timePause > SLEEP_TIME) {
        sleepBrightness = MAX_SLEEP_BRIGHTNESS;
        mode = SLEEP_MODE;
Serial.println("\nSLEEP_MODE\n");
      } else if ((timePause > DELIMITER_TIME) && (morseSymbolLen > 0)) {
        // Если пауза была слишком долгой, закончим ввод буквы.
        sendMorseSymbol();
      }
    }
  }
  btnPrevState = btnState;
  delay(10);
}

void sleepLoop() {
  // Мы же  спим - поэтому будем проверять статус кнопки редко - раз в 0.3 с.
  delay(300);

  // Определим, достаточно ли долго была нажата кнопка для выхода из спячки.
  boolean flagWakeUp = ((millis() - timePress) >= WAKEUP_TIME);

  // Помигаем светодиодом.
  analogWrite(LED, sleepBrightness);
  sleepBrightness = MAX_SLEEP_BRIGHTNESS - sleepBrightness;

  btnState = digitalRead(BUTTON);
  if (btnState == LOW) {
    if (btnPrevState == HIGH) {
      timeRelease = millis();
      if (flagWakeUp) {
        // Просыпаемся.
        mode = TYPING_MODE;
Serial.println("\nTYPING_MODE\n");
      }
    }
  } else {
    if (btnPrevState == LOW) {
      timePress = millis();
    } else {
      if (flagWakeUp) {
	// Просигнализируем пользователю, что кнопка удерживалась достаточно
	//  долго, чтобы устройство проснулось.
        analogWrite(LED, WAKEUP_BRIGHTNESS);
      }
    }
  }
  btnPrevState = btnState;
}


Скопируем текст скетча в Arduino IDE. Кнопкой Verify откомпилируем скетч.

Если компиляция прошла без проблем, загрузим код в микроконтроллер — подключим собранный макет устройства к компьютеру miniUSB-кабелем и нажмем кнопку Upload . Если IDE заявит, что не может соединиться с устройством, укажем, на каком USB-порту находится Arduino, перейдя в меню Tools -> Serial Port и выбрав в списке наше устройство.

Программа начнет работу сразу же после загрузки скетча на Arduino. Кнопкой запустим монитор COM-порта. Попробуем напечатать слово «привет». На азбуке Морзе оно будет выглядеть следующим образом:
*--*   *-*   **   *--   *   -
При этом непрерывно горящая лампочка (показывающая этим, что мы в латинской раскладке) будет гаснуть на время нажатий кнопки. Если мы подстроились под ритм программы и правильно «отстучали» все точки и тире (не забывая о паузах между буквами), на экране монитора COM-порта появится латинское «priwet»:


Переключимся на кириллицу, нажав и удерживая кнопку. Лампочка, отследив нажатие, погаснет. Как только нажатие будет достаточно долгим и раскладка переключится, лампочка светодиода загорится. При переключении с кириллицы на латынь лампочка будет вести себя с точностью до наоборот — зажжется, а при переключении раскладки — погаснет.

Снова отстучим «привет». В окошке COM-монитора вместо ожидаемых русских букв вылезут кракозябры. Естественно — скетч отправляет кириллицу в Unicode.

Оставим устройство на минуту в покое, не будем нажимать на нем кнопки. Через минуту включится спящий режим, о чем просигнализирует светодиод, мигая неярким светом.

Альфа-версия готова. Она делает именно то, что мы планировали. Но до релизного варианта устройству пока далеко. Стоило бы проработать следующие моменты:

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

— Фиксированная граница между длительностью точки и длительностью тире — неудобна. Такой подход привязывает пользователя к ритму, на который настроена наша морзе-клавиатура. Можно, конечно, поработать над логикой, заставив программу подстраиваться под скорость работы пользователя. Это было бы неплохое упражнение для начинающего программиста. Но с точки зрения коммерческого проекта — лишняя трата времени. Куда проще и удобнее заменить ввод одной кнопкой, к примеру, на ввод двумя кнопками: одна для точки, вторая для тире.

— Один из режимов использования аккордной клавиатуры — слепая печать. Поэтому стоило бы дополнить светодиодную индикацию вибрацией — чтобы пользователь чувствовал состояние клавиатуры, не глядя на нее.

Поработать над перечисленным мы попробуем в следующей статье.

Предыдущие статьи:
Arduino: первое знакомство

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

  • Хорошая статья!!! Спасибо!
    • Спасибо за оценку :) Старалась :)
  • Фиксированная граница между длительностью точки и длительностью тире — неудобна.
    А можно для определения воспользоваться переменным резистором, крутя его для подстройки длины нажатия.
    • Мне кажется, полностью цифровая подстройка более удобна. Кроме того, логично не подкручивать резистор, подбирая удобные для себя параметры, а заставить устройство анализировать точки-тире-паузы, подстраиваясь под скорость пользователя. Задача вполне решаемая, но для практического применения смысла не имеющая. Куда проще точки и тире вводить разными кнопками. И анализировать ничего не надо, и скорость печати повышается, и две кнопки дают больше вариантов управления. Например полсекунды удержания левой кнопки переключают на кириллицу, полсекунды правой — на латынь. А две одновременно нажатые кнопки позволяют сделать еще что-то :)
      • Азбука Морзе тем и хороша, что нужен всего 1 контакт для передачи символа. В противном случае снова перейдем к клавиатуре на весь набор символов ;).
        А автоматическую подстройку под скорость пользователя можно сделать, задав ему проигрывание заранее заданного куска (светодиод-то есть :) ).
        • У меня задача — создание удобной аккордной клавиатуры (если такая вообще возможна). Морзе-клавиатура — это предельный случай :) Всего одна кнопка. Я хочу поэкспериментировать с числом кнопок, принципами ввода. Возможно, вместо кнопок удобней использовать микро-джойстики… В общем, хочу попробовать найти вариант, с которым окажется действительно _удобно_ работать.
          • А как Вам такое устройство?
            • Спасибо за ссылку :) Девайс любопытный, но, как я понимаю, «заточен» не совсем под те же задачи. С его помощь можно позиционировать курсор, эмулировать нажатия правой/левой кнопок мыши — но не печатать. Если я правильно разобралась в конструкции, фраза «позволяет печатать на клавиатуре, приведя руку в горизонтальное положение» означает лишь то, что устройство не мешает печатать на обычной клавиатуре. Мне же интересна замена стандартной клаве, позволяющая вслепую печатать на ходу.
              • К сожалению, да. Оно всего лишь не мешает печатать. Однако, представьте, как было бы удобно использовать нечто подобное в совокупности с виртуальной клавиатурой!
  • Спасибо за статью. Давно хотел приобрести себе ардуино, но решился только после Вашей прошлой статьи. Теперь жду, когда уже стану счастливым обладателем и тоже смогу начать экспериментировать)
    • Удачи в экспериментах :) Игрушка действительно любопытная — и главное позволяет начать осваивать что-то параллельное программированию. Любопытно открывать для себя совершенно другой мир со в чем-то иной логикой :)
  • Расскажите, как подключать и управлять разными двигатели (шаговыми, обычными)
    • В процессе освоения — буду писать :) Пока на базе этого девайса планирую разобраться с RGB-диодом и вибро-моторчиком. Также на очереди bluetooth-модуль и жк-экран.
      • даешь разных схем интересных и полезных, а то надоели в этом блоге статьи про отпиливание куска пластмассы и приклеиваение различных частей, вместо инженерных поделок (пусть и простых :-) )
        • Тогда можно почитать и другие блоги, например могу порекомендовать mk90.blogspot.com
  • ИМХО, код морзе — не лучший вариант. С одной кнопкой лучше использовать какой-нибудь префиксный код (Шеннона-Фано или лучше даже Хаффмана). Таким образом можно и сократить количество нажатий и убрать паузу между вводом букв.
    • Мне кажется, основная проблема — не в минимизации нажатий. Если для ввода текста, к примеру, придется заучить наизусть таблицу ASCII-кодов, вряд ли подобный девайс станет популярным. Ввод прежде всего должен быть интуитивен, чтобы его можно было достаточно быстро и легко освоить. И, боюсь, для аккордных клавиатур эту задачу пока никто не решил…

      В принципе, если под каждым пальцем расположить трехпозиционную качельку, этого (3^5 = 243) хватит для ввода любого нужного печатного символа. Но необходимость запоминания кодов делает идею нереальной для практического использования :(
  • Где ассемблер? =)
    • Ассемблер где-то там, под слоем С++ ;)
    • А зачем он здесь? С ассемблером я имела дело на Z80 и на x86, но это было давно. В данном случае скорость разработки важнее оптимальности и скорости кода :)
    • а зачем?
  • И всё равно для меня сложновато(
  • R3 ненужен
    • R3 как раз точно нужен — он при отпущенной кнопке подтягивает сигнал на седьмом порту к «земле», логическому «нулю». Без R3 на 7 порт при отпущенной кнопке идет мусор. Может, Вы имели в виду, что не нужен R2? Его действительно нет в схеме подключения кнопки из книжки «Getting Started With Arduino». Собственно, подключить третий резистор (R2) мне посоветовали здесь: community.livejournal.com/ru_arduino/10922.html?thread=66218#t66218
      • порт в режим входа, при разомкнутой кнопке он резистором подтягивается к питанию, на входе лог.1
        при замкнутой кнопке ток стекает на землю и на вход не попадает — на входе лог.0
        так что R3 не нужен, автор выше прав
        • В книжке-учебнике по Ардуино (Getting Started With Arduino) приводится вариант подклчения кнопки через R3, без R2. При этом нажатая кнопка — 1, отпущенная — 0:
          • Чем плох вариант, когда нажатая кнопка — 0, отжатая — 1 от внутреннего пулапа АВРки? Вообще внешняя обвязка не нужна.

            И еще надо учитывать, что кнопка — штука механическая, при нажатии возникает «дребезг контактов». Поэтому не помешал бы программный debouncing.
            • Да в принципе ничем не плох. Просто я учусь работать с Ардуино по книжке — и беру оттуда варианты подключения. Очень уж не хочется пожечь девайс, который ждала месяц…

              Ну и параллельно пытаюсь понять — почему нужно подключать именно так, а не иначе.
            • Что касается «дребезга» — почему от него стоит избавляться программно, а не при помощи конденсатора?
              • Лишний кусок кода или дополнительная обвязка — это вопрос персональных предпочтений. А для программного debouncing'а у вас в коде уже и так есть задел с подсчетом интервалов. Еще немного проверок там, где if ((timeRelease — timePress) > MAX_DOT_PRESS_TIME), и — voila — программная защита от дребезга готова.
                • Идея в том, чтобы игнорировать нажатия и отпускания, длящиеся менее какого-то интервала? Действительно логично, вставлю :)
      • обычно делают так
        www.seattlerobotics.org/encoder/mar97/basics1.gif
        и обходятся одним резистором на кнопке

        вариант без R2 втречается реже, сейчас не могу найти почему,
        вроде это связано с рассеиваемой мощьностью на резисторах
        • Сейчас обычно включают подтягивающие резисторы в микроконтроллере а внешние выбрасывают совсем.
          • Ну, в почти каждом микроконтроллере есть так называемые подтягивающие резисторы, которые могут включатся программно(ардуиновцам сложно понять, привет ассемблер=)). Подтягивают они на плюс, т.е. управление осуществляется по отрицательной логике- 5В- лог «0», 0В- лог «1».
            Подробнее читаем тут easyelectronics.ru/avr-uchebnyj-kurs-ustrojstvo-i-rabota-portov-vvoda-vyvoda.html (Ди, респект за Упячку =)
  • А вообще дожились: никаких тебе принципиальных схем только картинки подключения проводочков. Прогресс…
    • Собственно, набор тем и хорош (с моей точки зрения), что позволяет даже чайнику сделать что-то работоспособное. Заодно заставляя потихоньку изучать предмет :) Так же в свое время воспринимался Macromedia Flash — возможность для чайников, не знающих и не желающих изучать программирование, написать что-то графическое, интерактивное.
    • Так оно ж все цифровое, здесь часто компактнее словами написать. Фраза типа «эта хрень подключена к MCU по SPI» достаточно точно опишет ситуацию, а даташиты позволят додумать соединения. А побочным эффектом весь этот процесс заставит понять, что происходит внутри силикона.
  • М-м-морзе =)
    А Вы не КВ-радиолюбительница случайно?
    • Нет :) Была бы я радиолюбительницей — задавала бы куда меньше глупых вопросов :) Что касается Морзе — в детстве в «Пионерской Правде» печатались материалы по обучению азбуке Морзе. Так что, приходя домой из школы (2 или 3 класс) я звонила в дверь «привет». Собственно, эти и еще несколько букв — все, что я помню из азбуки Морзе.

      Азбука Морзе в данном случае хороша тем, что она проста для реализации и проверена временем. Хотя, конечно, ей недостает многих символов, необходимых при работе с компьютером.
      • Как здорово!

        А в азбуку Морзе хоть @ недавно добавили, уже неплохо :)
  • НЛО прилетело и опубликовало эту надпись здесь.
Только авторизованные пользователи могут оставлять комментарии. Авторизуйтесь, пожалуйста.