Firmata.SendString не работает с конкретными переменными `char`

У меня есть скетч Firmata, который принимает строковые сообщения, разделяет их и отправляет свои части обратно в клиентскую программу Firmata. Моя проблема, безусловно, на стороне автора. Проблема в том, что методы Firmata.sendString(аргумент1) и Firmata.sendString(аргумент2) ничего не отправляют. Более подробная информация содержится в коде скетча. Как я могу заставить методы sendString работать?

/*
 * This sketch accepts strings in the following form:
 * CMDn(arg1,arg2)
 * CMDn — command name,
 * arg1 и arg2 — unnecessary arguments.
 * Each argument's length can be from 1 to 4 characters,
 * They're saved to separate variables.
 *
 * This sketch sends back the parsed commands partially:
 * the command name, argument #1 (if present), argument #2 (if present)
 */


#include <Firmata.h>


void stringCallback(char *received) {
  //CMDn(arg1,arg2)
  //CMDn(arg1)
  //CMDn(1)
  //CMDn()
  //012345678901234
  //          11111

  //This scheme up there is just for 
  //understanding the indexes of the chars in the string
  //in its various possible variations

  //Let's declare all the necessary variables
  char methodName[5];
  char argument1[5];
  char argument2[5];
  byte commaLocation = 0;

  //If the received string doesn't match the strings' syntax,
  //send an error message
  if (!(received[4] == '(' && strlen(received) <= 15)) {
    Firmata.sendString("ERR;");
    return;
  }

  //Let's take the 0-3 chars in the string and send it back.
  //It's probably the command name
  for (int i = 0; i < 4; i++) {
    methodName[i] = received[i];
  }
  methodName[4] = '\0'; //does or doesn't the compiler add the terminator automatically?
  Firmata.sendString(methodName); //send it
  //This string is being sent without problems.


  //If there is some arguments in the string,
  //it is detectable if the string char 5 is not ')'/
  if (received[5] != ')') {
    //Lent's find out if the input string has a `comma`
    //inside the parentheses. If it does, it means that
    //there are two arguments in the string.

    Firmata.sendString("46 passed");    
    for (int i = 6; i < (strlen(received) - 1); i++) {
      //If there is a comma, save its' index and break the loop
      if (received[i] == ',') {
        commaLocation = i;
        break;
      }
    }

    //If a comma is not present, its location variable equals 0,
    //sincerely we just collect all the chars between the parentheses
    //to the 'argument1' variable and send it.
    if (commaLocation == 0) {
      Firmata.sendString("61 passed");
      for (int i = 5; i < ; i++) {
        argument1[(i - 5)] = received[i];
      }


      argument1[strlen(argument1)] = '\0'; //Either this is executed or not, the outcome doesn't change 

      Firmata.sendString(argument1); //This very command doesn't work. It just does not send anything. 
                                     //Looks like 'argument1' is empty, but I'm awfully sure that it must 
                                     //not be empty. Anyway, the 'argument1' does not show up in the client
    }

    //If there is a comma present, then write chars before the comma
    //to 'argument1', and after the comma - to 'argument2'
    if (commaLocation != 0) {
      Firmata.sendString("77 passed");
      //let's write what is before the comma:
      for (int i = 5; i < commaLocation; i++) {
        argument1[i - 5] = received[i];
        Firmata.sendString("81 iterator");
      }

      //let's write what's after the comma;
      char argument2[5];
      for (int i = (commaLocation + 1); i < (strlen(received)); i++) {
        argument2[(i - (commaLocation + 1))] = received[i];
        Firmata.sendString("87 iterator");
      }
      
      argument1[strlen(argument1)] = '\0'; //Either this is executed or not, the outcome doesn't change 
      argument2[strlen(argument2)] = '\0'; 

      Firmata.sendString(argument1); //Not working
      Firmata.sendString(argument2); //Not working
    }
  }
}


void sysexCallback(byte command, byte argc, byte *argv) {
  Firmata.sendSysex(command, argc, argv);
}


void setup() {
  Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
  Firmata.attach(STRING_DATA, stringCallback);
  Firmata.attach(START_SYSEX, sysexCallback);
  Firmata.begin(57600);
}

void loop() {
  while (Firmata.available()) {
    Firmata.processInput();
  }
}

Ожидаемый результат:

Что я посылаю То, что я получаю (за исключением отладочных сообщений,; означает перевод строки)
DHTt() DHTt
DHTt(1) DHTt; 1
ДГТт(1,2) DHTt; 1; 2

Реальный результат:

Что я посылаю Что я получаю (за исключением отладочных сообщений)
DHTt() DHTt до тех пор, пока нет аргументов, нет и проблем. Но аргументы необходимы для моей задачи
DHTt(1) DHTt аргумент не отправляется
ДГТт(1,2) DHTt два аргумента также не отправляются

, 👍1

Обсуждение

strlen(argument1) сообщает вам индекс первого символа \0", так что эта строка не имеет смысла. Это было бы намного проще, если бы вы использовали указатели на массив символов, чтобы вы могли легко установить/изменить начальную точку (например, с помощью recPtr = strchr(получено,',')`) вместо пропуска смещений. Поищите полезные инструменты в http://www.cplusplus.com/reference/cstring/, @dandavis

@DanDavis, разве строка cstring не эквивалентна структуре Arduino Строка, ни массиву символов?, @Starter

Кроме того, пожалуйста, укажите, какая конкретная строка кода?, @Starter

Они, вероятно, ссылаются на любую из строк, которые принимают форму "x[strlen(x)] = "\0"; "В лучшем случае" x " содержит допустимую строку, и эта строка является допустимой, если только она не помещает нулевой терминатор там, где он уже существует. Если " x " не является допустимой строкой c, бесполезность-это лучший результат, на который вы можете надеяться, но не единственный. Вы собираетесь сделать это с " аргументом 1 в двух местах. аргумент 2` в одном месте. Похоже, было бы неплохо просто потратить некоторое время на изучение строк вне контекста вашего проекта. Вы можете играть в среде рабочего стола и передавать то, что вы узнали., @timemage

Что ваш клиент (компьютер) делает со строкой? Вы используете там библиотеку или сами написали клиенту?, @PMF

@pmf, все, что делает моя клиентская программа, - это принимает строку, предоставленную пользователем, а затем отправляет ее в Arduino. Более подробную информацию можно найти на this вопрос., @Starter

@dandavis, "strchr" заставляет цикл " 81 линия` потерять разум. Является ли "указатель", который "strchr" возвращает, сохраняемым для переменной "байт"?, @Starter


2 ответа


Лучший ответ:

1

Firmata.sendString ничего не отправляет клиенту в двух случаях:

  • Если сообщение пустое;
  • Если сообщение не содержит в себе терминатора \0.

В этом случае при отправкеимени метода вы вручную назначаете терминатору строку; и отправитель работает:

methodName[4] = '\0';

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

#include <Firmata.h>

//setup() и loop() здесь не будет, в них нет изменений

unsigned long charIndex(const char * findIn, unsigned int symbol) {
  //Найти "символ" в массиве символов "Поиск". В отличие от
  //strchr(), возвращает значение unsigned long или 0, если "символ" не найден
  
  char * pointer = strchr(findIn, symbol);
  if (pointer != NULL) return (pointer - findIn);
  if (pointer == NULL) return 0;
}


void stringCallback(char *received) {
  //Объявим все необходимые переменные
  char methodName[5];
  char argument1[5];
  char argument2[5];
  int commaLocation = 0;
  int lasti;

  byte nullTerminatorIndex = strlen(received); //For understanding

  //Если полученная строка не соответствует синтаксису строк,
  //отправить сообщение об ошибке
  if (!(received[4] == '(' && strlen(received) <= 15)) {
    Firmata.sendString("ERR;");
    return;
  }

  //Давайте возьмем 0-3 символа в строке и отправим их обратно.
  //Это, вероятно, название команды
  for (int i = 0; i < 4; i++) {
    methodName[i] = received[i];
  }
  methodName[4] = '\0'; //**Компилятор не добавляет терминаторы автоматически**.
  Firmata.sendString(methodName); //отправьте его
  //Эта строка отправляется без проблем.


  //Если в строке есть какие-то аргументы,
  //ее можно обнаружить, если символ строки 5 не является ')'/
  if (received[5] != ')') {
    //Давайте выясним, есть ли во входной строке "запятая"
    //в круглых скобках. Если это так, это означает, что в строке есть два аргумента.

    Firmata.sendString("46 passed");    
    if (charIndex(received, ',') != 0) commaLocation = charIndex(received, ',');

    //Если запятой нет, то ее переменная местоположения равна 0,
    //поэтому мы просто собираем все символы между круглыми скобками
    //в переменную 'argument1' и отправьте ее.
    if (commaLocation == 0) {
      Firmata.sendString("61 passed");
      for (int i = 5; i < (nullTerminatorIndex - 1); i++) {
        argument1[(i - 5)] = received[i];
        lasti = (i - 5);
      }

      argument1[(lasti + 1)] = '\0';
      lasti = 0;

      Firmata.sendString(argument1); 
    }

    //Если присутствует запятая, то напишите символы перед запятой
    //в "аргумент1", а после запятой - в "аргумент2"
    if (commaLocation != 0) {
      Firmata.sendString("77 passed");
      //давайте напишем, что перед запятой:
      for (int i = 5; i < commaLocation; i++) {
        argument1[i - 5] = received[i];
        lasti = (i - 5);
      }

      argument1[(lasti + 1)] = '\0';
      lasti = 0;

      //давайте напишем, что после запятой;
      char argument2[5];
      for (int i = (commaLocation + 1); i < (nullTerminatorIndex - 1); i++) {
        argument2[(i - (commaLocation + 1))] = received[i];
        lasti = (i - (commaLocation + 1));
      }

      argument2[(lasti + 1)] = '\0';
      lasti = 0;

      Firmata.sendString(argument1);
      Firmata.sendString(argument2); 
    }
  }
}
,

1

Вот упрощенная версия вашего синтаксического анализатора команд, использующего строки c и указатели:

void parseArgs(char * rec){
  char cmd[32];
  char arg1[32]= {0};
  char arg2[32]= {0};
  char * ptr; // это многоразовый указатель рабочей лошадки
  char * ptrDelim; // еще одна рабочая лошадка для разделения аргументов
  
  // сначала скопируйте все это в cmd
  strcpy(cmd, rec);
  // найти конец команды
  ptr = cmd;
  while( isalpha(ptr[0]) ) ptr++;
  ptr[0] = '\0'; // отметьте конец командной строки
  
  
  // find first arg
  ptr = strchr(rec, '(')+1; // установите ptr в символ справа от открытого родительского
  strcpy(arg1, ptr); // скопируйте содержимое после открытого родительского в arg1:
  ptr = strchr(arg1, ')');// найти максимальную протяженность arg1
  if(ptrDelim=strchr(arg1, ',')){
    ptrDelim[0]='\0'; // завершите строку arg1 в делиме
    ptrDelim++; // переместите вправо один символ
    strcpy(arg2, ptrDelim); // скопируйте материал после запятой
    arg2[strlen(arg2)-1] = '\0'; // отрубить ")"
  }else{
    ptr[0]='\0';    
  }//конец, если запятая?
  
    
 Serial.println("\nGiven:" + String(rec)+";");   
 Serial.println("Command:" + String( cmd )+";"); 
 Serial.println("Arg1:" + String( arg1 )+";"); 
 Serial.println("Arg2:" + String( arg2 )+";"); 
  
}

Примеры:

parseArgs("CMDn()");
parseArgs("CMDn(arg1)");
parseArgs("CMDn(arg1,arg2)");
,

Удалите строку ()-наличие строк после завершения отладки., @dandavis

В заявлении " if(ptrDelim=strchr(arg1, ',')) {"вы имели в виду == вместо"="?, @Starter

Кроме того, что делает ваш код, если во входящей строке нет аргументов?, @Starter

@Starter: я должен был завернуть выражение в скобки, как предложил компилятор, но это намеренное двоякое: если символ отсутствует в строке, он возвращает ложный нулевой указатель, и если он его находит, он возвращает истинную оценку _whilst_, указывающую на положение этого символа., @dandavis

@Starter: тогда они будут пустыми, что вы можете определить по "if(strlen(arg1))" или " if(arg1[0])` по мере необходимости., @dandavis

это работает неправильно. Когда я ввожу"CMDn(arg1,arg2)", я получаю [это](https://skr.sh/i/140221/UeOzNZHm.jpg); если никаких аргументов нет, то в "arg1 " есть")"; если есть только один аргумент, он записывается в "arg1" вместе с запятой., @Starter