Как вызвать более 10 различных функций на основе последовательного ввода?

Я хочу в основном вызывать функции на основе серийного идентификатора, который я ему назначаю. Например, если я хочу вызвать функцию AI, я наберу на серийном номере A, а если я хочу вызвать функцию B, я наберу B.

Теперь я просмотрел некоторые arduino.cc ASCII и другие таблицы поиска, но я все еще не получил результатов. ДА, я действительно могу выполнять последовательные вызовы с 1 по 9, используя тип данных char, но, как вы можете видеть, я ограничен только 9 вызовами функций, и именно здесь я ищу советы. Как я могу расширить этот последовательный порт, когда я его называю?

char id;
int led = 6; // контакт ШИМ, к которому подключен светодиод

void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);
}

void loop() {
  id = Serial.read();
  /*
    will only read the 1st bit on the buffer so
    no 10s...90s because the whole char so we
    need to use Strings datatype somehow but
    the thing is that the Serial.Read func only
    reads the first byte. R&D?
  */
  if (id == '1') {
    digitalWrite(led, HIGH);
  } else if (id == '2') {
    digitalWrite(led, LOW);
  }
}

Итак, в приведенном выше примере я использую char для связи. Я знаю, что есть другие типы данных, но не уверен, какой из них лучше всего подходит для сотен функций, которые я хотел бы вызывать через терминал.

Я думал об использовании алфавита AZ, а также az, чтобы удвоить вызовы функций, но я хотел бы придумать что-то более формальное, а не отдельные цифры или отдельные буквы (char).

, 👍2

Обсуждение

https://hackingmajenblog.wordpress.com/2016/02/01/reading-serial-on-the-arduino/, @Majenko

char может иметь 256 различных значений. Однако данные считываются по одному байту за раз. Возможно, вы захотите разработать свой собственный протокол связи для отправки нескольких байтов. Это даже не должно быть сложно: просто прочитайте x байт, а затем остановитесь., @uhours

@Маженко, еще раз спасибо тебе за помощь, приятель! Перейду по ссылке и все прочитаю., @Frank

@uhours, ты предлагаешь использовать цикл for в условных выражениях?, @Frank

@Frank, цикл for перед условиями, считывающий x байт в массив. Выйдите из цикла, когда массив заполнится. Используйте strcmp() для оценки содержимого. Кроме того, сделайте отступ в своем коде., @uhours

@uhours спасибо за совет, я буду молча читать об этой функции, которую вы упомянули, поскольку никогда раньше ею не пользовался., @Frank

Похоже, вы хотите написать какой-то интерпретатор команд. Возможно, вы захотите поискать вдохновение в этом [простом интерпретаторе командной строки Arduino](https://gist.github.com/edgar-bonet/607b387260388be77e96)., @Edgar Bonet


2 ответа


2

Во-первых, вы можете использовать строку для команд. Вам просто нужно убедиться, что есть какой-то специальный символ, чтобы отметить конец команды, например, символ новой строки. Считайте ввод из порта по одному символу за раз, строя строку по мере продвижения:

char buffer[256];
uint16_t buffer_index = 0;
void loop() {
  while (Serial.available()) {
    int c = Serial.read();
    if ('\n' == c) {
      buffer[buffer_index] = 0;   // Добавляем EOS
      // Завершить сообщение - сделать что-то
      buffer_index = 0;   // Сброс, готовность к следующему сообщению
    }
    else {
      // Добавляем символ в конец буфера
      buffer[buffer_index++] = c;
    }
    // Проверка на переполнение
    if (256 == buffer_index) {
        buffer_index = 0;
    }
  }
}

На этом сайте есть множество подобных примеров.

Далее, я не рекомендую использовать оператор if с сотней или более условий. Вместо этого вы можете хранить указатели на каждую функцию в массиве:

// Используйте typedef для упрощения
typedef void (*function_type)(); // Функция без параметров и без возврата, измените при необходимости
// Фиксированный размер массива
const int max_functions = 200;
// Создаем массив из 200 указателей на функции
function_type all_functions[max_functions] = {0};

Чтобы вызвать функцию №11:

all_functions[10]();

Теперь вам просто нужен способ соединить указатели функций с именем. Здесь может работать хэш-таблица. Хэш-функция преобразует строку в число. В сети много примеров. Вот один:

uint16_t djb2_hash(const char *str)
{
    unsigned long hash = 5381;
    int c;

    while (0 != (c = *str++)) {
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
    }

    // Примечание: мы используем оператор mod, чтобы убедиться, что значение
    // меньше размера массива
    return hash % max_functions;
}

Создайте две функции, одну для добавления функции и одну для извлечения:

bool add_function(const char *name, function_type f)
{
  // Преобразование имени в число
  uint16_t index = djb2_hash(name);  // 0 <= индекс <= max_functions
  if (NULL != all_functions[index]) {
    // По этому индексу уже сохранена функция
    Serial.print("Adding ");
    Serial.print(name);
    Serial.print("failed. index = ");
    Serial.println(index);
    return false;
  }
  else {
    // Сохраняем функцию в массиве
    all_functions[index] = f;
    return true;
  }
}
bool run_function(const char *name)
{
  uint16_t index = djb2_hash(name);
  if (NULL == all_functions[index]) {
    // Имя не найдено
    return false;
  }
  else {
    // Вызов функции
    all_functions[index]();
    return true;
  }
}

Наконец, создайте несколько функций и добавьте их в хеш-таблицу:

void f1() {Serial.println("F1");}
void f2() {Serial.println("F2");}
void f3() {Serial.println("F3");}

void setup() {
  Serial.begin(9600);
  add_function("F1", f1);
  add_function("F2", f2);
  add_function("F3", f3);
}

Осталось только изменить loop() для вызова функций при получении полного сообщения.

void loop() {
  while (Serial.available()) {
    int c = Serial.read();
    if ('\n' == c) {
      buffer[buffer_index] = 0;
      run_function(buffer);
      buffer_index = 0;
    }
    else {
      buffer[buffer_index++] = c;
    }
    // Проверка на переполнение
    if (256 == buffer_index) {
        buffer_index = 0;
    }
  }
}

Если вы введете "F1", "F2" или "F3" в последовательном мониторе, эти функции должны выполняться. Просто убедитесь, что вы изменили последовательный монитор, чтобы добавить новую строку.

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

,

Ваш код работает отлично, но что я могу сделать, чтобы вызвать функцию с параметрами void funct_X(char LetterX, int NumberX); ...... ?, @Navas Ema

@NavasEma Вы можете изменить typedef на typedef void (*function_type)(char,int);. И функцию run для all_functions[index](arg1, arg2);, @Johnny Mopp


0

конечный автомат был бы здесь наиболее эффективным подходом.

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

сердцевина кода будет примерно такой:

current_input = valid_input();  //получить верный ввод
input = cmd_2dig(current_input); //2-команды копания
switch (cmd_2dig) {
  case 0: do_cmd0(); break;
  case 1: do_cmd1(); break;
  case ....
  default: reset_cmd(); //неверная команда, сброс буфера команд
}
,