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 два аргумента также не отправляются |
@Starter, 👍1
Обсуждение2 ответа
Лучший ответ:
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);
}
}
}
Вот упрощенная версия вашего синтаксического анализатора команд, использующего строки 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
- Как разделить входящую строку?
- Как вывести несколько переменных в строке?
- форматирование строк в Arduino для вывода
- Очень простая операция Arduino Uno Serial.readString()
- DateTime в строку
- Как преобразовать строку в массив байтов
- Как отправить строку на мастер с помощью i2c
- Создание форматированной строки (включая числа с плавающей запятой) в Arduino-совместимом C++
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