Кнопка Отправить работает только при нажатии сразу после этого

Я новичок в Arduino и пытаюсь создать "чат"на основе азбуки Морзе.

Я могу отправить сообщение азбукой Морзе, используя кнопку "Отправить" (ту, что справа), а когда я нажму кнопку слева, то на экране появится все декодированное сообщение. Однако этого не произойдет, если я не нажму кнопку отправки сразу же после введения азбуки Морзе. Кто-нибудь может мне помочь? Я попытался изменить период задержки, но, похоже, ничто не заставляет его посылать так, как должно.

Как я уже сказал, я новичок, так что, пожалуйста, полегче со мной.

Вот мой код:

/* String tone = "-... ..- -. .- ";
    

  Morse code - Receiver

  modified on 14 Apr 2019
  by Saeed Hosseini @ Electropeak
  https://electropeak.com/learn

*/
#include <LiquidCrystal.h>
// Create an LCD object. Parameters: (RS, E, D4, D5, D6, D7):
LiquidCrystal lcd = LiquidCrystal(2, 3, 4, 5, 6, 7);
String code = "";
int len = 0;
char ch;
int ok;
char new_char;
int i1=0;
int i2=1;
const int but_send=8;
const int but = 12;
const int led = 13;
String a="";
unsigned long pres_len = 0, rel_time, pres_time = 0, old_time_len = 0, old_pres = 0, space = 0;
int state = 0;
int unit_delay = 250;
int min_delay = 10;
int buttonState = 0;

char MakeString()
{
  if (pres_len < (unit_delay*3) && pres_len > 50)
  {
    return '.';                        //if button press less than 0.6sec, it is a dot
  }
  else if (pres_len > (unit_delay*3))
  {
    return '-';                        //if button press more than 0.6sec, it is a dash
  }
}

void Morse_decod()
{
  static String morse[] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
                             "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-",
                             ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", "!"
                            };
  int i = 0;
  while (morse[i] != "!")  
  {
    if (morse[i] == code)
    {
      Serial.print(char('A' + i));
      a=a+char('A' + i);
      //lcd.print(char('A' + i));
      Serial.print(" ");
      if(i2>=2)
      {
        i1++;
        i2=0;
      }
      else 
      {i2++;
      }
      break;
    }
    i++;
  }
  if (morse[i] == "!")
  {
    Serial.println("");
    Serial.println("This code is not exist!");
  }

  code = "";
}
void sendText()
{
  Serial.println(digitalRead(but_send));
  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  //if (digitalRead(but_send) == HIGH) {
    // turn LED on:
     //gitalWrite(led, HIGH);
  if(ok==1){
    Serial.println(a);}
  ok=0;
   
    //}
  //else {
    // turn LED off:
    //Serial.println("NIMIC");
  //}
 delay(200);
}
void setup() {
  Serial.begin(9600);
  pinMode(but, INPUT_PULLUP);
  pinMode(but_send, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  lcd.setCursor(i1, 12);
  lcd.begin(16, 2);
  

}
void loop() {  
label:  
  while (digitalRead(but) == HIGH) {}
  old_pres = rel_time;
  pres_time = millis();
  digitalWrite(led, HIGH);
  while (digitalRead(but) == LOW) {}
  rel_time = millis();
  digitalWrite(led, LOW);
  pres_len = rel_time - pres_time;
  space = pres_time - old_pres;
  if (pres_len > min_delay)
  {
    code += MakeString();
  }
  while ((millis() - rel_time) < (unit_delay * 3))
  {
    
    if (digitalRead(but) == LOW)
    {
      goto label;
      
    }
    
  }

  Morse_decod();
    delay(1000);
  if (digitalRead(but_send) == LOW)
  {
    ok=1;
    //Serial.println("A intrat aici");
    sendText();
    //delay(100);

  }
 
  
 //Serial.println(digitalRead(but_send));


 
 
  
}

Это то, что должно происходить все время, а не только после нажатия кнопки отправить очень быстро:

При необходимости я могу отправить ссылку из моего симулятора tinkercad.

, 👍2


3 ответа


2

Это выглядит так : while (digitalRead(but) == HIGH) {} будет захватывать процессор в цикле до тех пор, пока не будет нажата кнопка "но". Поэтому, если вы нажмете кнопку "отправить" сразу после использования "но", он может увидеть, что вы нажали "отправить", но если вы позволите циклу программы вернуться к while (digitalRead(but) == HIGH) {}, то ничего не произойдет, пока " но " не будет нажато снова.

,

3

ответ тависа прибил вашу проблему: ваша программа полностью не отвечает, когда она блокируется в первом цикле while функции loop (). Здесь я постараюсь дать вам несколько советов о том, как избежать этой ситуации, но сначала некоторые общие замечания:

if (pres_len < (unit_delay*3) && pres_len > 50)

Пороговое значение должно быть unit_delay*2, возможно, с более длинным значением. unit_delay. В азбуке Морзе точка-это одна единица, а тире-три. Если вам нужно установить порог, он должен быть где-то посередине. В противном случае вполне допустимое тире в 2,99 единицы было бы ошибочно принято за точку.

Кроме того, функция, возвращающая что угодно, кроме void, всегда должна возвращать значение. Что должна возвращать эта функция, если press_len меньше или равно 50? А если он точно равен unit_delay*3? Лучший способ убедиться, что вы не забыли ни одного случая, - это иметь else в конце, который возвращает значение по умолчанию в случае , если условие не совпадает:

else
    return '?';  // we could not understand

В данном конкретном случае мне кажется, что вы уже сделали дебаунс до вызова функции, так что его можно было бы упростить до следующего

char getSymbol()
{
    if (pres_len < 2 * unit_delay)
        return '.';
    else
        return '-';
}

Обратите внимание, что присвоение функциям осмысленных имен - это важный шаг к тому, чтобы сделать код легким для понимания и осмысления.

if(i2>=2)
{
  i1++;
  i2=0;
}
else
{i2++;
}

Все это не служит никакой полезной цели, так как вы не используете i1 или i2 после setup(). Удаление ненужных вещей также важно для ясности кода.

Serial.println("Этот код не существует!");

Он должен сказать: “Я не могу понять этот код!”, но он вполне может существовать.

А теперь перейдем к сути вопроса. Если вы хотите, чтобы программа была отзывчивой, вам следует использовать неблокирующее программирование. Это означает избавление от delay() (см. Blink Without Delay) и избавление от блокирующих циклов, таких как два цикла while в верхней части loop(). Очень полезным подходом является программирование вашей системы как конечного автомата. Пожалуйста, найдите время, чтобы прочитать и переварить эти два источника.

Далее, в качестве примера, я могу указать вам на мой декодер Морзе, основанный на концепции конечного автомата. Вам не обязательно смотреть на мой код, который может быть слишком низким уровнем на ваш вкус, но посмотрите на документацию внутренних частей программы. Она объясняет, как строятся и взаимодействуют друг с другом машины состояний. Ниже приведена диаграмма состояний, взятая из этой документации. Он показывает конечный автомат, который принимает дебютированные события “взлета” и “падения” в качестве входных данных и генерирует символы “точка”, “тире”, “конец символа” и “конец слова” в качестве выходных данных:

state diagram of Morse decoder

,

1

И Тэвис, и Эдгар Бонет попали в точку, и Эдгар предоставил вам отличный учебник.

Вы вряд ли заставите это работать должным образом в симуляторе браузера Tinkercad, потому что он работает с разной скоростью в зависимости от сложности кода, который он запускает. И сложность меняется, когда вы нажимаете кнопки, что приводит к отсчету времени при нажатии кнопки Морзе.

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

Tinkercad Morse code

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const byte MORSE_BUTTON_PIN = 6;
const byte SEND_BUTTON_PIN = 7;
const byte MORSE_LED_PIN = 8;

const unsigned long DEBOUNCE_DELAY_ms = 50;
const unsigned long MORSE_UNIT_1 = 500;  // milliseconds.
const unsigned long MORSE_UNIT_2 = 2 * MORSE_UNIT_1;  // milliseconds.
const unsigned long MORSE_UNIT_5 = 5 * MORSE_UNIT_1;  // milliseconds.

const String MORSE_CODES[] =
{
  ".-", "-...", "-.-.", "-..", ".",
  "..-.", "--.", "....", "..", ".---",
  "-.-", ".-..", "--", "-.", "---",
  ".--.", "--.-", ".-.", "...", "-",
  "..-", "...-", ".--", "-..-", "-.--",
  "--.."
};

String symbol;
String message;

Debouncer morse_button(MORSE_BUTTON_PIN, DEBOUNCE_DELAY_ms);
Debouncer send_button(SEND_BUTTON_PIN, DEBOUNCE_DELAY_ms);

void setup()
{
  Serial.begin(115200);
  pinMode(MORSE_BUTTON_PIN, INPUT);
  pinMode(SEND_BUTTON_PIN, INPUT);
  pinMode(MORSE_LED_PIN, OUTPUT);
  // Set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Hello, world!");
}

void loop()
{
  static unsigned long press_begin_time = 0;
  static unsigned long release_begin_time = 0;
  static bool measure_the_gap = false;

  morse_button.Update();
  send_button.Update();

  if (morse_button.Fall())
  {
    // Remember when the morse button was pressed and
    // switch on the LED.
    int timestamp = millis();
    digitalWrite(MORSE_LED_PIN, HIGH);
    press_begin_time = timestamp;
  }

  if (morse_button.Rise())
  {
    // Remember when the morse button was released and
    // switch off the LED.
    int timestamp = millis();
    digitalWrite(MORSE_LED_PIN, LOW);
    release_begin_time = timestamp;
    int pressed_time = timestamp - press_begin_time;
    if (pressed_time > MORSE_UNIT_2)
    {
      // Dash.
      //Serial.print("-");
      symbol += "-";
    }
    else
    {
      // Dot.
      //Serial.print(".");
      symbol += ".";
    }
    measure_the_gap = symbol.length() > 0;
  }

  if (measure_the_gap)
  {
    unsigned long timestamp = millis();
    //Serial.println("Measuring the gap.");
    if (((timestamp - release_begin_time) > MORSE_UNIT_2))
    {
      // A long gap between presses signifies the end of a symbol.
      //Serial.println("Long gap");
      //Serial.println(symbol);
      measure_the_gap = false;
      message += DecodeSymbol(symbol);
      symbol = "";
    }
  }

  if (send_button.Fall())
  {
    // Print the Morse string.
    //Serial.println(message);
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print(message);
    message = "";
  }

  // Delay for Tinkercad simulator.
  // Not needed for real hardware.
  delay(10);
}

char DecodeSymbol(String& symbol)
{
  //Serial.print(symbol);
  //Serial.print(": ");
  char ch = '?';
  for (int i = 0; i < 26; i++)
  {
    if (symbol == MORSE_CODES[i])
    {
      ch = 'A' + i;
      break;
    }
  }
  //Serial.println(ch);
  return ch;
}

Для разборки кнопок я использовал этот разборщик, который поставил на GitHub. Для Tinkercad вам нужно будет скопировать и вставить этот код debouncer (файл debouncer.h, за которым следует debouncer.cpp файл) в верхнюю часть файла кода Tnkercad, потому что Tinkercad не позволяет вам включать ваши собственные файлы кода.

,

+1, но обратите внимание, что: 1. "метка времени" и "pressed_time" должны быть "без знака долго", чтобы избежать проблем с опрокидыванием, но тогда эти переменные на самом деле не нужны. 2. символ.длина() > 0 всегда вычисляется сразу после добавления символа в "символ", и в этом контексте это всегда " истина`., @Edgar Bonet