Урок 2.1. Кнопки. Тренажер азбуки Морзе



  • Цель урока

    Привет! Сегодня мы сделаем простой, но эффективный тренажер азбуки Морзе (рис. 1).

    Рисунок 1.

    При включении устройства на экране появляется логотип и указание нажать первую кнопку для того, чтобы начать. После на экране буду поочерёдно появляться буквы английского алфавита и цифры. Также применяется графическое и звуковое отображение. Для того, чтобы отстучать код используют вторую кнопку. Для того, чтобы повторить звуковой сигнал используют третью кнопку. Если пользователь верно отстучит код, то произойдет смена буквы. После завершения всего цикла - всё начинает заново с первой буквы алфавита.

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

    Перечень компонентов для урока

    • M5STACK;
    • кабель USB-C из стандартного набора.

    Немного теории

    Код Морзе является способом передачи текстовой информации в виде серии включения-выключения тонового сигнала, который может быть непосредственно расшифрован квалифицированным слушателем или наблюдателем без специального оборудования. Он назван в честь С. Ф. Б. Морса, изобретателя телеграфа. Международный код Морзе кодирует базовый латинский алфавит ISO, некоторые дополнительные латинские буквы, арабские цифры и небольшой набор пунктуационных и процедурных сигналов в виде стандартизированных последовательностей коротких и длинных сигналов, называемых "точками" и "тире", как в радиолюбительской практике. Поскольку многие неанглийские естественные языки используют более 26 латинских букв (рис. 2), для этих языков существуют расширения Азбуки Морзе.

    Рисунок 2. Графическая схема кодов Морзе

    В чрезвычайной ситуации, когда необходимо отправить сигнал бедствия используют также код Морзе. Наиболее распространенным сигналом бедствия является SOS - три точки, три тире и три точки – международно признанный договором.

    Рисунок 3. Телеграфный ключ

    Для того, чтобы отстучать код Морзе используется телеграфный ключ (рис.3), а устройство которое передает информацию с ключа называется телеграф.

    Начнём!

    Шаг 1. Запишем алфавитные названия букв

    String MorseTelephony[] =
    {
      "Alfa",
      "Bravo",
      "Charlie",
      "Delta",
      "Echo",
      "Foxtrot",
      "Golf",
      "Hotel",
      "India",
      "Juliett",
      "Kilo",
      "Lima",
      "Mike",
      "November",
      "Oscar",
      "Papa",
      "Quebec",
      "Romeo",
      "Sierra",
      "Tango",
      "Uniform",
      "Victor",
      "Whiskey",
      "Xray",
      "Yankee",
      "Zulu",
      "1",
      "2",
      "3",
      "4",
      "5",
      "6",
      "7",
      "8",
      "9",
      "0"
    };
    

    Шаг 2. Запишем напевы для букв и цифр

    String MorsePhonic[] =
    {
      "AL-FAH",
      "BRAH-VOH",
      "CHAR-LEE",
      "DELL-TAH",
      "ECK-OH",
      "FOKS-TROT",
      "GOLF",
      "HOH-TEL",
      "IN-DEE-AH",
      "JEW-LEE-ETT",
      "KEY-LOH",
      "LEE-MAH",
      "MIKE",
      "NO-VEM-BER",
      "OSS-CAH",
      "PAH-PAH",
      "KEH-BECK",
      "ROW-ME-OH",
      "SEE-AIR-RAH",
      "TANG-GO",
      "YOU-NEE-FORM",
      "VIK-TAH",
      "WISS-KEY",
      "ECKS-RAY",
      "YANG-KEY",
      "ZOO-LOO",
      "WUN",
      "TOO",
      "TREE",
      "FOW-ER",
      "FIFE",
      "SIX",
      "SEV-EN",
      "AIT",
      "NIN-ER",
      "ZEE-RO"
    };
    

    Шаг 4. Запишем коды Морзе для букв и цифр

    Пусть для "точка" используется 0, а для "тире" - 1. Если нет ничего, то -1. На каждый символ выделяется 5 чисел.

    int MorseCodes[] =
    {
      0,1,-1,-1,-1, // A
      1,0,0,0,-1, // B
      1,0,1,0,-1, // C
      1,0,0,-1,-1, // D
      0,-1,-1,-1,-1, // E
      0,0,1,0,-1,  //F
      1,1,0,-1,-1, // G
      0,0,0,0,-1,  // H
      0,0,-1,-1,-1,  // I
      0,1,1,1,-1, // J
      1,0,1,-1,-1, // K
      0,1,0,0,-1, // L
      1,1,-1,-1,-1, // M
      1,0,-1,-1,-1,  // N
      1,1,1,-1,-1, // O
      0,1,1,0,-1,  // P
      1,1,0,1,-1,  // Q
      0,1,0,-1,-1, // R
      0,0,0,-1,-1, // S
      1,-1,-1,-1,-1, // T
      0,0,1,-1,-1, // U
      0,0,0,1,-1,  // V
      0,1,1,-1,-1, // W
      1,0,0,1,-1,  // X
      1,0,1,1,-1, // Y
      1,1,0,0,-1, // Z
      0,1,1,1,1, // 1
      0,0,1,1,1, // 2
      0,0,0,1,1, // 3
      0,0,0,0,1, // 4
      0,0,0,0,0, // 5
      1,0,0,0,0, // 6
      1,1,0,0,0, // 7
      1,1,1,0,0, // 8
      1,1,1,1,0, // 9
      1,1,1,1,1 // 0
    };
    

    Шаг 5. Как это звучит и выглядит?

    Для воспроизведения напишем функцию void Play(int), которая в качестве аргумента принимает порядковый номер из массива int MorseCodes[].

    void Play(int i) {
      for (int j = i * 5; j < i * 5 + 5; j++)
      {
        int k = j - i * 5;
        int x = 40 + (k * (dash_width + padding));
        int y = 160;
        delay(padding_time);
        if (MorseCodes[j] == 0)
        {
          M5.Lcd.fillCircle(x + dash_width / 2, y + dot_height, dot_height, 0x0000);
          M5.Speaker.tone(630);
          delay(dot_time);
          M5.Speaker.mute();
        } 
        else if (MorseCodes[j] == 1)
        {
          M5.Lcd.fillRect(x, y, dash_width, dash_height, 0x0000);
          M5.Speaker.tone(630);
          delay(dash_time);
          M5.Speaker.mute();
        }  
        else;
      }
    }
    

    Для наглядности будем рисовать схемы из "точки" и "тире".

    Шаг 6. Всё понятно - кнопки давайте!

    Как только пользователь нажмет кнопку метод bool M5.BtnB.wasPressed() вернёт true и мы в этот момент заставим динамик издавать тоновый сигнал частотой 630 Гц, а также запомним текущее время millis() в переменную previous_time. - Зачем это надо? - Сейчас поймёте! После того, как пользователь отпустит кнопку метод bool M5.BtnB.wasReleased() вернёт true и мы уберем тон с динамика. Далее мы опять узнаем текущее время millis() и вычтем время сохраненное ранее (о котором говорили раньше), таким образом мы узнаем время нажатия клавиши пользователем.

    void loop() {
      for (int i = 0; i < sizeof(MorsePhonic) / sizeof(String); i++)
      {
        M5.Lcd.fillScreen(0xffff);
        M5.Lcd.drawRoundRect(40, 40, 64, 64, 10, 0x0000);
        M5.Lcd.setTextColor(0x0000);
        M5.Lcd.setTextSize(5);
        M5.Lcd.setCursor(60, 55);
        M5.Lcd.print(MorseTelephony[i][0]);
        M5.Lcd.setTextSize(2);
        M5.Lcd.setCursor(120, 50);
        M5.Lcd.print(MorseTelephony[i]);
        M5.Lcd.setCursor(120, 80);
        M5.Lcd.print(MorsePhonic[i]);
        Play(i);
        while (true)
        {
          int UserMorseCodeTime[5] = {};
          int actual_time = 0;
          for (int j = i * 5; j < i * 5 + 5; j++)
          {
            if (MorseCodes[j] > -1)
            {
              int k = j - i * 5;
              int previous_time = 0;         
              while (true)
              {
                if (M5.BtnB.wasPressed())
                {
                  previous_time = millis();
                  M5.Speaker.tone(630);
                }
                if (M5.BtnB.wasReleased())
                {
                  M5.Speaker.mute();
                  UserMorseCodeTime[k] = millis() - previous_time;
                  M5.update();
                  break;
                }
                if (M5.BtnC.wasReleased())
                {
                  Play(i);
                }
                M5.update();
              }
            }
          }
          int r = true;
          for (int j = i * 5; j < i * 5 + 5; j++)
          {
            int k = j - i * 5;
            if (MorseCodes[j] == 0)
              actual_time = dot_time;
            else if (MorseCodes[j] == 1)
              actual_time = dash_time;
            if (UserMorseCodeTime[k] == 0)
              break;
            int abs_ = abs(actual_time - UserMorseCodeTime[k]);
            int x = 50 + k * 50;
            int y = 190;
            M5.Lcd.fillRect(x, y, 305, 30, WHITE);
            M5.Lcd.setCursor(x, y);
            if (abs_ > actual_time / 4)
            {
              r = false;
              M5.Lcd.print(":(");
            }
            else
            {
              M5.Lcd.print(":)");
            }
          }
          if (r)
            break;
        }
      }
    }
    

    Далее запишем каждую точку или тире для символа в массив int UserMorseCodeTime[5] и произведём сравнение с эталонным временем для точки и тире с допущением 25%. На этом всё :)

    Шаг завершающий. Запуск!

    В разделе "Download" прилагаются видео с демонстрацией работы. На этом урок завершён.

    ДОМАШНЕЕ ЗАДАНИЕ

    • Задание 1 уровня сложности: добавьте поздравление в конце прохождения алфавита и цифр;
    • Задание 2 уровня сложности: добавьте систему оценок пользователя: с первой попытки - максимальная оценка и т.д.;
    • Задание 3 уровня сложности: добавьте временной индикатор удержания клавиши во время ввода.

    Download