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 в строку
- Как преобразовать строку в массив байтов
- Невозможно преобразовать «String» в «uint8_t {aka unsigned char}» при инициализации
- Как отправить строку на мастер с помощью i2c
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