Split string received from Terminal

My question is very much inline with this post, however I have a more direct need to be able to split the string received from the Terminal control.

The Terminal control offers me a non-graphical means of controlling devices without the need of cluttering my display with various controls e.g. Buttons, Sliders, Menu’s, etc.

This is what I am trying to achieve:
Use the Terminal to send a string, e.g:

  • R1:ON // set Relay1 to 1
  • L3:5 // turn Light3 on for 5min (1000 * 60 * 5)

The Arduino Sketch will receive the following string through param.asStr() :
R1:ON

In the Sketch I need to split the received string on the delimiter “:”… and this is where I have difficulty getting things to work.
(Please note that my knowledge and experience with C/C++ is very limited - non-existing actually)

I have tried strtok() but have difficulty in getting the string passed into a char array. I also don’t know if the string passed conforms to a C-string ended with “/0”. I’ve tried to test this with SizeOf() but I get an error. I’ve also tried both param.asStr() and param.getBuffer(), all failed. Also tried .toCharArray which also failed with data type conversion.

Unfortunately, most (90%) of the research I’ve done is focussed on the Serial.Read(). I’ve tried to implement the same logic they use there, but things go askew the moment I try to convert the received string to a char array.

Any suggestion would greatly be appreciated.

Regards,
T

See my snippet of a working copy that does string manipulation etx…

 BLYNK_WRITE(V14)
  {
  /// take "commands" from the terminal and acting accordingly ///
  /// https://www.arduino.cc/en/Tutorial/StringSubstring ///
  /// (stringOne.equalsIgnoreCase(stringTwo)) ///
  /// stringOne.trim(); ///
  /// int strgLenght = param.getLength(); /// get the string lenght ///
  String getString = param.asStr(); /// get the string ///
  getString.trim(); /// cut off the blank spece(s) at the end of the string ///
    if (getString.equalsIgnoreCase("timer1")) {
      timer1PrintOut();
      return;
    } else if ( getString.startsWith("onrl1") || getString.startsWith("onrl2") ) { /// format onrl1,30 or onrl2,34 ...
    int firstCommaIndex = getString.indexOf(',');
    relaySelection = getString.substring(0, firstCommaIndex);
    String durationToOn = getString.substring(firstCommaIndex+1);
    duration = durationToOn.toInt();
     
     ///terminal.printf("duration(in seconds)=%d\n", duration);
     ///terminal.flush();
     /// make a now() start with now()+duration stop at Timer1 if RL1 or Timer2 if RL2 ... ///
     manualEndTime = now()+ (duration - 1); /// this is the manualEndTime that manualActiveRLTimer will check each 2 seconds for RLn-OFF
     /// this need other commant from time library ... /// int2Time(manualEndTime);
     /// activate rl1 or rl2 and then call manualActiveRLTimer to take care the RL-OFF state   
     ///terminal.printf("The Relay:%s, Will be ON for %s seconds\n",relaySelection, durationToOn);
     terminal.println(); terminal.print(relaySelection+"  Duration="); 
     int2Time(duration);
     terminal.printf("  end @ %02d:%02d:%02d \n",hour(manualEndTime), minute(manualEndTime), second(manualEndTime));
     terminal.flush();
        
          if(relaySelection == "onrl1") { /// RL1 activation ///
            digitalWrite(RL1,HIGH);
            Blynk.virtualWrite(V16, HIGH);
            Blynk.setProperty(V16, "color", RED);
          } else if(relaySelection == "onrl2") { /// RL2 activation ///
            digitalWrite(RL2,HIGH);
            Blynk.virtualWrite(V17, HIGH);
            Blynk.setProperty(V17, "color", RED);
            }
     timer.enable(manualActiveRLTimer);
     return;
    } else if (getString.equalsIgnoreCase("restart")) {
    terminal.println("OK I do restart !");
    terminal.flush();
    yield(); yield(); delay(500);
    ESP.restart();
    } else if (getString.equalsIgnoreCase("?") || getString.equalsIgnoreCase("help")) {
    terminal.println("The regognised commands are: ");
    terminal.println("onrl1,nn, onrl2,nn, timer1(-4), sync, BDC, BDC0, fhon, fhoff, clear/cls, tson, tsoff, ts?, startled, stopled, tz?, tz2, tz3, diags, restart, cnv nnnn");
    terminal.flush();
    return;
    } else {
    // Send it back
    terminal.print("You said:");
    terminal.write(param.getBuffer(), param.getLength());
    terminal.println();
    terminal.println("Type ? or help to get help...");
    terminal.println();
  }
  // Ensure everything is sent
  terminal.flush();
}
/// END of V14 terminal processing function ///

If you need further help, don’t hesitate to ask me.

Thanks and Best Regards,
Mike Kranidis

Hi Mike,

Thanks for the reply and your sketch. Interestingly enough, I looked at one of your posts, posted back in 2016 I think it was. Although, this sketch looks much better than your original sketch ;).

I will definitely look jump into your sketch and follow your logic.
Is there any additional references to libraries I need to make in my sketch ?

Regards,
Tino

no, it is using the very standard build-in infrastructure of the ESP8266 Arduino Core 2.4.0-rc2 ( I strongly suggest to get the latest one, that is 2.4.0-rc2 )

Tell me your opinion after implemented this to your sketch.

Best Regards,
Mike Kranidis

Thanks for the reply, Mike.

I scanned through your sketch (not implemented yet) and just love what you did with the various number of commands you are pushing through. That is exactly what I have in mind and I am glad I decided to post my request for help… but not as glad as I am that you posted. Your post is the cherry on top.

Once I have converted your sketch and implemented on my nodeMCU, I will let you know what I think and how well it functions :wink:

Regards,
Tino

1 Like

But it’s still the same principle :wink: Looking at my own functions for parsing data (check my post about RF 433…) I think something like this will split the string for you:

    char tempChars[] = param.asStr(); // R1:ON 

	char * strtokIndx;  // This is used by strtok() as an index

	strtokIndx = strtok(tempChars,":");
	strcpy(myDevice, strtokIndx); // myDevice = R1

	strtokIndx = strtok(NULL, ":");
	strcpy(myCommand, strtokIndx);  // myCommand = ON

My code relies heavily on this excellent write up which explains the parse/splitting very well: Serial Input Basics - updated

See also this fine example:

Thank you for your reply and attached information. I also looked at both the Serial Input Basics and Serial Input Basics - updated links. However, I could not get anything to work. I will definitely play around with your code as to allow me versatility in logic methods.

Perfecto!! It seems to work now with your sketch example. Thank you for chpping in help here Mike, much appreciated.

I am not entirely sure but I think I know what happened:
What I did differently, and I am not sure if this is the reason, is I used a function to process the data received from the Terminal control, e.g.:

// Wait for input from Terminal
BLYNK_WRITE(V14) {

getTermData( param.asStr());
}

void getTermData(String termData) {
// process data here
}

In my mind there should not be any reason that the data type changed from the BLINK_WRITE function to the getTermData() function, however, I was getting all kinds of weird data type conversion errors.

Regards,
Tino

I’m not sure if I understand you… But variables declared in a function doesn’t get passed to another function unless you declare them as global.

https://www.arduino.cc/en/Reference/Scope

My apologies, wrong terminology. I should rather have said a “value” instead of a variable. I was referring to passing a value (derived from the variable param.asStr) to a function.

Regards,
T

Yes, but you assign values to a variable, which is then passed.

EDIT : It seems like I’ve been contributing to the confusion with a beginner’s mistake - Mixing up String with string. :roll_eyes: For that I’m sorry!

So I had to check how String works and this example works “as is”. Hopefully it will also work with param.asStr() in a BLYNK-function.

String myDevice; // Global
String myCommand;

void setup() {
  Serial.begin(115200);
}

void stringTest() {

  byte part1;
  byte part2;

  String getString = ("R1:ON");
  //String getString = param.asStr();

  part1 = getString.indexOf(':');
  myDevice = getString.substring(0, part1);
  part2 = getString.indexOf(':', part1 + 1 );
  myCommand = getString.substring(part2);

  Serial.print("The String is: ");
  Serial.println(getString);

  termData();
}

void termData() {

  if (myDevice == "R1") {
    if (myCommand ="ON") {
      Serial.println("Turning ON relay 1");
    }
    else {
      Serial.println("Turning OFF relay 1");
    }
  }
  
  Serial.print("Device is: ");
  Serial.println(myDevice);  
  Serial.print("Command is: ");
  Serial.println(myCommand);

}

void loop() {
  stringTest();
  delay(5000);
}

No worries :slight_smile: I still don’t know the difference between ‘string’ and ‘String’, except that most people advise not to use ‘String’ in a microcontroller environment… something to do with memory usage.

I’d like to look at your code as well. Less cluttered the way you presented it. Thanks for that.

Regards,
T