Serial communication and atoi() trouble

Hello,
I've been having a lot of trouble getting my Arduino Mega 2560 to talk to my computer running a C++ program. I need to control a lot of motors with the Arduino and the direction and PWM duty cycle (0-255) are sent over by the computer using a message format where 's' is the start character, 'm' starts the sequence for each motor and 'x' delineates the two values sent for each motor. So for example 'sm1x255m0x150' would turn one motor in direction 1 with magnitude 255 and another one in direction 0 with magnitude 150.

The problem starts when converting this message to integers for writing to the pins. I think I'm using atoi() incorrectly because the output I get in the C++ console is just zeros rather than the values I've sent. What am I doing wrong?

This is the Arduino code (only uses one motor):

#include <stdlib.h>

byte dir_pin=52;
byte pwm_pin=10;

char charRead;
char buf1[2];
char buf2[4];

unsigned int dir, pwm;

void setup() {
  Serial.begin(9600);
  pinMode(dir_pin, OUTPUT); 
  pinMode(pwm_pin, OUTPUT); 
 // buf1[1]='\0'; 
 // buf2[3]='\0';
 // memset(buf1, '\0', 1);
 // memset(buf2, '\0', 3); 
   
}


void loop() {
 
    if(Serial.available()){
    
        charRead=Serial.read();
        if(charRead=='s'){
          charRead=Serial.read();
          if(charRead=='m'){
            buf1[0]=Serial.read();
          }
          charRead=Serial.read();
          if(charRead=='x'){
            buf2[0]=Serial.read();
            buf2[1]=Serial.read();
            buf2[2]=Serial.read();
          }     
       }
       dir=atoi(buf1);
       pwm=atoi(buf2);
       //Serial.println(buf1);
       //Serial.println(buf2);
       Serial.println(dir,DEC);
       Serial.println(pwm,DEC);
    }
       
    analogWrite(pwm_pin,pwm);
    digitalWrite(dir_pin,dir);  
}

This is the C++ code running on the computer:

#include <iostream>
#include "SerialClass.h"
#include <stdio.h>
#include <string.h>
using namespace std;

char buffer[20];
char test[20];
char to_send[1];

int main() {
	cout << "Hello" << endl;
	Serial arduino("COM4");
	sprintf(to_send,"sm%dx%d", 1, 150);
	arduino.WriteData( to_send , strlen(to_send));
	cout << "Data sent to Arduino: " << to_send<<endl;
	Sleep(100);
	arduino.ReadData(buffer,20);
	arduino.~Serial();
	cout << "Data received from Arduino:\n" << buffer << endl;
	return 0;
}

Usual problem; check to see if there is at least one character to read, then read all four of them.

So are you suggesting to add Serial.available() calls before reading each character?

Or just dumping it all in one buffer and then trying to pick things out? Because that's what I initially tried: using a String buffer and then using substrings, converting them to char arrays and then using atoi on them. That did not work either and seemed to a lot of complexity,

I don't like String, so if blocking doesn't matter (and it doesn't look like it should), just call available before each read.
A fairly simple function to wrap the two should suffice.

It seems like "I want to send strings to my Arduino over serial, and have the Arduino parse them" is such a common theme, and is a frustratingly complex task to achieve. Is there a convenient library or how-to or something somewhere?

To the O/P, this is the approach I tend to use:

#define TOKEN_LEN 15
char token[TOKEN_LEN];
uint8_t input_len = 0;

void handle_input(uint8_t c)
{

    /* c = 0x02, then a new read sequence is starting
     */
    if (c == 0x02) {
        input_len = 0;
        return;
    }

    /* c = 0x03, a read sequence is complete.
     * Check if this is a specific tag and if so, open the door.
     */
    if (c == 0x03) {
       if (input_len != 10) return;
       token[input_len] = 0;
       if (strcmp_p(token, PSTR("62E3086CED08"))) {
           open_latch();
       }
       return;
    }

    /* otherwise we have an input character
     */
    if (input_len <= TOKEN_LEN - 1)
       token[input_len++] = c;
}

void loop()
{  int c;
  while (Serial.available() > 0) {
      c = Serial.read();
      handle_input(c);
  }
}

The main loop just feeds characters to the parser, and it's the parser that accumulates and interprets them. In my sample interpreter, I am looking for RFID tag strings which my reader brackets with ctrl-b, ctrl-c. In your case you could look for the leading 's' and trailing newline.

How much control you have over how your strings are sent will determine what code you will use to capture the commands sent. The usual approach is to capture a string until a delimiter at the end of the data is reached (both start and stop delimiters can be used, like below). Using the serial monitor for testing the arduino code makes things easier. Using the string functions to parse the data usually work as long as the strings are fairly short and the strings are cleared after use.

// posted by another forum person
#define SOP '<'
#define EOP '>'

bool started = false;
bool ended = false;

char inData[80];
byte index;

void setup()
{
   Serial.begin(9600);
   // Other stuff...
}

void loop()
{
  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet
Serial.print(inData);
    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}

Thanks, guys! Both of these solutions work nicely

Also, if anyone else is curious this is the code I used to extract the actual integers from the received and parsed array of chars. So if the packet sent to the Arduino was ''sm1x150e" buffer contains "m1x150" (using zoomkat's code from above) and now two sub-arrays need to be created. These must end with the '\0' character for atoi() to work.

      char temp1[]={buffer[1],'\0'};
      char temp2[]={buffer[3],buffer[4],buffer[5],'\0'};
      dir=atoi(temp1);
      pwm=atoi(temp2);