1

I have a Firmata sketch, that accepts string messages, partitions them and sends its' parts back to a Firmata client program. My problem is certainly at the sketch's side. The problem is that Firmata.sendString(argument1) and Firmata.sendString(argument2) methods don't send anything. More information is found in the sketch's code. How can I get the sendString methods to work?

/*
 * 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(&quot;46 passed&quot;);    
for (int i = 6; i &lt; (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(&quot;61 passed&quot;);
  for (int i = 5; i &lt; ; 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(&quot;77 passed&quot;);
  //let's write what is before the comma:
  for (int i = 5; i &lt; commaLocation; i++) {
    argument1[i - 5] = received[i];
    Firmata.sendString(&quot;81 iterator&quot;);
  }

  //let's write what's after the comma;
  char argument2[5];
  for (int i = (commaLocation + 1); i &lt; (strlen(received)); i++) {
    argument2[(i - (commaLocation + 1))] = received[i];
    Firmata.sendString(&quot;87 iterator&quot;);
  }

  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(); } }

Expected output:

What I send What I receive (excluding the debug messages, ; stands for newline)
DHTt() DHTt
DHTt(1) DHTt; 1
DHTt(1,2) DHTt; 1; 2

Real output:

What I send What I receive (excluding the debug messages)
DHTt() DHTt as long as there are no arguments, there are no problems. But the arguments are necessary for my task
DHTt(1) DHTt the argument are not sent
DHTt(1,2) DHTt two arguments are not sent neither
Starter
  • 153
  • 1
  • 13

2 Answers2

1

Here's a simplified version of your command parser using cstrings and pointers:

void parseArgs(char * rec){
  char cmd[32];
  char arg1[32]= {0};
  char arg2[32]= {0};
  char * ptr; // this is a re-usable workhorse pointer
  char * ptrDelim; // another workhorse to split arguments

// first copy all of it to cmd strcpy(cmd, rec); // find end of command ptr = cmd; while( isalpha(ptr[0]) ) ptr++; ptr[0] = '\0'; // mark end of command string

// find first arg ptr = strchr(rec, '(')+1; // set ptr to char to right of open paren strcpy(arg1, ptr); // copy stuff after open paren to arg1: ptr = strchr(arg1, ')');// find max extent of arg1 if(ptrDelim=strchr(arg1, ',')){ ptrDelim[0]='\0'; // end arg1's string at delim ptrDelim++; // move right one char strcpy(arg2, ptrDelim); // copy stuff after comma arg2[strlen(arg2)-1] = '\0'; // chop off ")" }else{ ptr[0]='\0';
}//end if comma?

Serial.println("\nGiven:" + String(rec)+";");
Serial.println("Command:" + String( cmd )+";"); Serial.println("Arg1:" + String( arg1 )+";"); Serial.println("Arg2:" + String( arg2 )+";");

}

Examples:

parseArgs("CMDn()");
parseArgs("CMDn(arg1)");
parseArgs("CMDn(arg1,arg2)");
dandavis
  • 1,037
  • 9
  • 12
1

Firmata.sendString does not send anything to the client in two cases:

  • If the message is empty;
  • If the message does not contain the \0 terminator in it.

In this case, while you're sending methodName, you're manually assigning the terminator to the string; and the sender works:

methodName[4] = '\0';

The only thing necessary to do to solve this argument problem is making each char variable end with a null terminator. Here you can see the solved code:

#include <Firmata.h>

//setup() and loop() won't be here, there are no changes in them

unsigned long charIndex(const char * findIn, unsigned int symbol) { //Find 'symbol' in char array 'findIn'. Unlike //strchr(), returns unsigned long or 0 if 'symbol' not found

char * pointer = strchr(findIn, symbol); if (pointer != NULL) return (pointer - findIn); if (pointer == NULL) return 0; }

void stringCallback(char *received) { //Let's declare all the necessary variables char methodName[5]; char argument1[5]; char argument2[5]; int commaLocation = 0; int lasti;

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

//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'; //The compiler doesn't add terminators 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(&quot;46 passed&quot;);    
if (charIndex(received, ',') != 0) commaLocation = charIndex(received, ',');

//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(&quot;61 passed&quot;);
  for (int i = 5; i &lt; (nullTerminatorIndex - 1); i++) {
    argument1[(i - 5)] = received[i];
    lasti = (i - 5);
  }

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

  Firmata.sendString(argument1); 
}

//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(&quot;77 passed&quot;);
  //let's write what is before the comma:
  for (int i = 5; i &lt; commaLocation; i++) {
    argument1[i - 5] = received[i];
    lasti = (i - 5);
  }

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

  //let's write what's after the comma;
  char argument2[5];
  for (int i = (commaLocation + 1); i &lt; (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); 
}

} }

Starter
  • 153
  • 1
  • 13