Need more clarity with character arrays

well, good morning campers-

i built a little pen plotter and am working on the program to control it. i have no formal training and this is to help build my skills in this area. the idea is to feed in a string containing all the move commands for a given sequence. a square, a circle, a letter. that type of thing. each command starts with a letter, xdirection, xstep count, on of these guys "/" and then the ydirection and step count, or just a letter in the case of extend and retract as those don't change.
so far i worked out how to strip an individual command from the incoming string. that uses the asterisk to mark this as pointer, i believe. that works fine. then i can use the index of the array for the current command to cycle through it to compare each character. that all works too. i can use the incomingstep[i] to evaluate the character, i can print it to the screen, i can copy it to an int type and print that and it'll show "49" which is correct. we are looking at the number one in the character array. perfect. exactly what i need.
it will NOT strcpy or strcat.
not even after copying to an int and then copying that int to a different character array entirely and trying to strcat that new array.
i know this stuff is considered rough learning, but i want to bounce my head off this desk after three days of doing this..
everything i try shows the correct values and i can do anything i wish with them aside from being able to actually use them.
please help if you can.

you can see what i am trying to do near the bottom.
right about where the names start to lose meaning..

char *currentstring[] = {"R","Q+200/-200","E","M+100/-100","moist","R","Q+200/-200","E","M+100/-100"};
int currentstringindex = 0;
char incomingstep[100];
int currentstringcomplete = 0;
char xgoal[100] = {'\0'};
char ygoal[100] = {'\0'};

void setup() {
  Serial.begin(9600);
  while (!Serial) {
  }
  delay(3000);
  Serial.println("funckles da clone");
  delay(1000);
}

void loop() {
  if (currentstringcomplete == 0) {
    currentstringindex = parsecommand(currentstring, currentstringindex);
    delay(500);
  }
}

void retract() {
  Serial.println("we have retracted");
}

void extend() {
  Serial.println("we have extended");
}

int parsecommand(char *incomingcommand[], int incomingcommandindex) {
  if (incomingcommand[incomingcommandindex] == '\0') {
    incomingcommandindex = 0;
    currentstringcomplete = 1;
    Serial.println("that's it, cap'm");
    return incomingcommandindex;
  }
  else if (incomingcommand[incomingcommandindex] != '\0') {
    strcpy(incomingstep, incomingcommand[incomingcommandindex]);
    for (int i = 0; i < strlen(incomingstep); i++) {
      if (incomingstep[i] == 'R') {
        retract();
        incomingcommandindex = incomingcommandindex + 1;
        return incomingcommandindex;
      }
      else if (incomingstep[i] == 'E') {
        extend();
        incomingcommandindex = incomingcommandindex + 1;
        return incomingcommandindex;
      }
      else if (incomingstep[i] == 'Q') {
        Serial.println(incomingcommand[incomingcommandindex]);
        incomingcommandindex = incomingcommandindex + 1;
        return incomingcommandindex;
      }
      else if (incomingstep[i] == 'M') {
        Serial.print(incomingcommand[incomingcommandindex]);
        i++;
        if (incomingstep[i] == '+') {
          Serial.println("  <---positive");
        }
        else if (incomingstep[i] == '-') {
          Serial.println("  <---negative");
        }
        i++;
        if (isDigit(incomingstep[i])) {
          Serial.println(xgoal);
          Serial.println(incomingstep[i]);
          int goathole = incomingstep[i];
          char thisfatthing = goathole;
          strcat(xgoal, thisfatthing);
          Serial.println(thisfatthing);
          Serial.println(xgoal);
        }
        incomingcommandindex = incomingcommandindex + 1;
        return incomingcommandindex;
      }
      else if (incomingcommand[incomingcommandindex] != '\0') {
        Serial.print("unknown--->  ");
        Serial.println(incomingcommand[incomingcommandindex]);
        incomingcommandindex = incomingcommandindex + 1;
        return incomingcommandindex;
      }
      delay(500);
    }
  }
}

it may not be clear but the goal in this section is to take the number portions of the command and send them out as a stepcount. the plan is to walk through the array and strcat each number to a the stepcount array and send that out once have then all.

strcat needs a null terminated C string, not a single char. Make a buffer two chars long and set it to nulls.

copy goathole into character zero of that buffer, then you can strcat that.

Turn up the warning level on the compiler - it'll give you a large number of errors to fix, one of which was the strcat problem.

1 Like

i owe you one big *ss hug, my man.

this won't match exactly your command language, but I wrote a demo class that helps with parsing multiple commands received on a Stream (like Serial). May be that will inspire you for your code or you can just adopt the class and use it if you can tailor your command language.

You can see the code in the simulator here

the command language recognises 4 types of commands

  • just a keyword, like ENGINE
  • a keyword, followed by a colon or equal sign (: or =) and any word, like ENGINE:ON or ENGINE=ON
  • a keyword, followed by a sharp sign (#) and any word, like ENGINE#12
  • a keyword, followed by a sharp sign (#) and any word, followed by a colon or equal sign and any word, like ENGINE#23:ON or ENGINE#23=128

of course when I say any word it must be something not containing one of the separators

The command can be terminated by a newline, semicolon, or tab.

so basically the commands look like this

KEYWORD
KEYWORD[=:]VALUE
KEYWORD#QUALIFIER
KEYWORD#QUALIFIER [=:]VALUE
End command by newline, semicolon, or tab.

where KEYWORD, VALUE and QUALIFIER can't hold any character from =:;\t\n

The way you use the class is that you define all the commands and the way they look like in an array of type CommandDescriptor where you set an ID of the command, the KEYWORD, and then the type (BARE_COMMAND will be just the keyword, VALUE if you have a value after the keyword (with = or :), QUALIFIER if you just have the qualifier after the keyword (with #), or QUALIFIER_VALUE if you have both.

const CommandDescriptor orders[] = {
  {0, "ENGINE", BARE_COMMAND},      // just a keyword 'ENGINE'
  {1, "ENGINE", VALUE},             // 'ENGINE:ON'
  {2, "ENGINE", QUALIFIER},         // 'ENGINE#12'
  {3, "ENGINE", QUALIFIER_VALUE},   // 'ENGINE#23:ON' or 'ENGINE#23=128'
};
const size_t numberOfOrders = sizeof orders / sizeof * orders;

Then you instantiate an Analyzer object that is templated with the max count of characters you accept in a command line, for example if I want a buffer of 100 characters I would write , you pass the orders and how many you have and the Stream to listen to.

Analyzer<100> analyzer(orders, numberOfOrders, Serial);// 100 characters is the maximum length of a received command

This works by polling, so the loop can't block, and you call

  CommandDetail * theCommand = analyzer.receivedCommand();

which will poll the Stream and wait for a full command line (terminated by a new line). When a command is recognised, theCommand will be a pointer to a CommandDetail structure with the information about the recognized data.

You can see in the example how I used that.

try typing in the Serial Monitor things like:

ENGINE
ENGINE:ON
ENGINE#2:STOP
ENGINE#3:FULL SPEED
ENGINE#4=128

or a few orders in one go (remember we set the max at 100 chars)
ENGINE;ENGINE:ON;ENGINE#2:STOP;ENGINE#3:FULL SPEED;ENGINE#4=128

if you try that last one, you'll see in the monitor

Recognized Command:
	Command: "ENGINE"	(ID: 0)
Recognized Command:
	Command: "ENGINE"	(ID: 1)
	Value: "ON"
Recognized Command:
	Command: "ENGINE"	(ID: 3)
	Qualifier: "2"
	Value: "STOP"
Recognized Command:
	Command: "ENGINE"	(ID: 3)
	Qualifier: "3"
	Value: "FULL SPEED"
Recognized Command:
	Command: "ENGINE"	(ID: 3)
	Qualifier: "4"
	Value: "128"
1 Like

i think something like what you have done will help me once i have some functional code and can start to alter and optimize my approach to different aspects of it. even now i can see how to do things better. i appreciate the advice.
i really, really want this stupid thing to draw a wang without me starting over every two days, so i am going to push ahead with my current course. :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.