Error in using indexOf not finding char in Arduino String - string

I have some code that I have no clue why it isn't working.
The code takes a serial input in the form of "xxx,yyy,zzz", where digits can range from 1 to 3 in each number. Because of an odd quirk in an app, it needs to be read as a char, then converted to a string to be handled. The intention is to split into 3 ints, red green and blue, from "RRR,GGG,BBB".
Now this works fine when I manually define String str (see commented code), but when I go and enter it from the serial console, it doesn't want to work. It seems to be coming from the indexOf(',') part, as while using Serial.print(c1);, I found that when I manually entered a string, it returned an index of the comma, but when I used the serial console, it returned -1 (not found).
And yes, the entered string into the console is in the correct format of "RRR,GGG,BBB", I've confirmed that by printing both phone and str independently.
while (Serial.available() > 0) {
char phone = Serial.read();
String str = String(phone);
//String str = "87,189,183";
int ln = str.length()-1;
int c1 = str.indexOf(','); //first place to cut string
int c2 = str.indexOf(',',c1+1); //second place
red = str.substring(0,c1).toInt();
green = str.substring(c1,c2).toInt();
blue = str.substring(c2,ln).toInt();
Serial.print(red);
Edit: With the Arduino String class, creating a string from a char is returning more than just one character, eleven in fact.

This:
char phone = Serial.read();
String str = String(phone);
will never create a string in str that has more than 1 character, since that's what you say you want.
This is the code for the Arduino's String(char) constructor:
String::String(char c)
{
init();
char buf[2];
buf[0] = c;
buf[1] = 0;
*this = buf;
}
So clearly your code will create a 1-character long string.
Also, beware of using indexes computed on the full string, on substrings later.

I'm try to guess that you are using these serial API http://playground.arduino.cc/Interfacing/CPPWindows.
while (Serial.available() > 0) {
char buf[12];
int len = Serial.ReadData(buf,11);
String str = String(buf);
//String str = "87,189,183";
int ln = str.length()-1;
int c1 = str.indexOf(','); //first place to cut string
int c2 = str.indexOf(',',c1+1); //second place
red = str.substring(0,c1).toInt();
green = str.substring(c1,c2).toInt();
blue = str.substring(c2,ln).toInt();
Serial.print(red);
If you are using other API like http://arduino.cc/en/Serial/Read you should follow these API where Serial is a Stream and read() return just the first available char.

Code was fixed by using a different function.
while (Serial.available() > 0) {
char phone = Serial.read();
str += phone;
//String str = "87,189,183";
int ln = str.length()-1;
int c1 = str.indexOf(','); //first place to cut string
int c2 = str.indexOf(',',c1+1); //second place
red = str.substring(0,c1).toInt();
green = str.substring(c1,c2).toInt();
blue = str.substring(c2,ln).toInt();
Serial.print(red);
I'm not sure why this works, and why before I was getting a string with more than one character. But it works!

Related

C++ How to parse math problem in string using TinyExpr?

First I need to detect if message from player contains math problem
like I get 2 messages from him
char* message1 = "Please solve this math problem";
char* message2 = "How much is 1 + 2 ?";
These messages are not static, player can say different messages.
I need to process message1 and message2
I need to detect math problem and cut it like see message2 and I need it to be "1+2"
char* mathProblemFromMessage = "1+2"; // <- I need to get this from message also detect if message contains any math problem before
double answer = te_interp(mathProblemFromMessage, 0);
printf("Answer is %f\n", answer);
my current code
std::string msg = Message;
if (func.contains_math_operators(msg.c_str()) && func.contains_number(msg.c_str()))
{
double answer = te_interp(Message, 0);
}
I think it fails and always results 0 because msg is "Player: 1+1" I need to cut math problem from it, I dont know how to do it...a
Find a position of the first digit in that string and get a substring from that point:
std::size_t found = msg.find_first_of("0123456789");
if(found != std::string::npos)
{
std::string sub = msg.substr(found);
double answer = te_interp(sub, 0);
}
If that also fails, try to trim invalid tail.
You may want to extend that find_first_of above to include open paren (, unary minus - or some supported function names (I am not familiar with TinyExpr)

Grabbing first number in string after some keyword occurrence in C++ (Arduino)

I have a string coming from PC through serial to a microcontroller (Arduino), e.g.:
"HDD: 55 - CPU: 12.6 - Weather: Cloudy [...] $";
by this function I found:
String inputStringPC = "";
boolean stringCompletePC = false;
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputStringPC += inChar;
if (inChar == '$') // end marker of the string
{
stringCompletePC = true;
}
}
}
I would like to extract the first number of it after the word HDD, CPU and also get the string after Weather (ie "cloudy"); my thinking is something like that:
int HDD = <function that does that>(Keyword HDD);
double CPU = <function that does that>(Keyword CPU);
char Weather[] = <function that does that>(Keyword Weather);
What is the right function to do that?
I looked into inputStringSerial.indexOf("HDD") but I am still a learner to properly understand what it does and don't know if theres a better function.
My approach yielded some syntax errors and confused me with the difference in usage between "String inputStringSerial" (class?) and "char inputStringSerial[]" (variable?). When I do 'string inputStringSerial = "";' PlatformIO complains that "string" is undefined. Any help to understand its usage here is greatly appreciated.
Thanks a bunch.
The String class provides member functions to search and copy the contents of the String. That class and all its member functions are documented in the Arduino Reference:
https://www.arduino.cc/reference/tr/language/variables/data-types/stringobject/
The other way a list of characters can be represented is a char array, confusingly also called a string or cstring. The functions to search and copy the contents of a char array are documented at
http://www.cplusplus.com/reference/cstring/
Here is a simple Sketch that copies and prints the value of the Weather field using a String object. Use this same pattern - with different head and terminator values - to copy the string values of the other fields.
Once you have the string values of HDD and CPU, you'll need to call functions to convert those string values into int and float values. See the String member functions toInt() and toFloat() at
https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/toint/
or the char array functions atoi() and atof() at
http://www.cplusplus.com/reference/cstdlib/atoi/?kw=atoi
String inputStringPC = "HDD: 55 - CPU: 12.6 - Weather: Cloudy [...] $";
const char headWeather[] = "Weather: "; // the prefix of the weather value
const char dashTerminator[] = " -"; // one possible suffix of a value
const char dollarTerminator[] = " $"; // the other possible suffix of a value
void setup() {
int firstIndex; // index into inputStringPC of the first char of the value
int lastIndex; // index just past the last character of the value
Serial.begin(9600);
// find the Weather field and copy its string value.
// Use similar code to copy the values of the other fields.
// NOTE: This code contains no error checking for unexpected input values.
firstIndex = inputStringPC.indexOf(headWeather);
firstIndex += strlen(headWeather); // firstIndex is now the index of the char just past the head.
lastIndex = inputStringPC.indexOf(dollarTerminator, firstIndex);
String value = inputStringPC.substring(firstIndex, lastIndex);
Serial.print("Weather value = '");
Serial.print(value);
Serial.println("'");
}
void loop() {
// put your main code here, to run repeatedly:
}
When run on an Arduio Uno, this Sketch produces:
Weather value = 'Cloudy [...]'

Replacing unknown substring?

public static String updatedStr(){
String [] ar= {"green","red","purple","black"};
String str="The colors are (blue), (blue), and (yellow). I prefer (orange)";
I would like a final output string of "The colors are green, red, and purple. I prefer black."
You can do it without using replace. Just iterate over the input String and add to a StringBuilder parts of the original String (that are not contained in parentheses) and the replacement words instead of the parts contained in parentheses.
public static String updatedStr()
{
String [] ar= {"green","red","purple","black"};
String str="The colors are (blue), (blue), and (yellow). I prefer (orange)";
StringBuilder out = new StringBuilder ();
int x = 0;
int pos = 0;
for(int i = str.indexOf('(', 0); i != -1; i = str.indexOf('(', i + 1)) {
out.append (str.substring(pos,i)); // add the part between the last ) and the next (
out.append (ar[x++]); // add replacement word
pos = str.indexOf(')', i) + 1;
}
out.append (str.substring(pos)); // add the part after the final )
return out.toString ();
}
This method returns :
The colors are green, red, and purple. I prefer black
This code makes some simplifying assumptions. For example, the number of elements in the replacements array should be at least as high as the number of words to be replaced. A more complete implementation should contain additional checks.
You can do this by calculating the position of where you are replacing and saving them in an array as follows:
public static String updatedStr(){
String [] ar= {"green","red","purple","black"};
String str="The colors are (blue), (blue), and (yellow). I prefer (orange)";
ArrayList<String> arr = new ArrayList<String>();
int pos [] = new int[ar.length]; // save locations here
for(int i = str.indexOf('(', 0); i != -1; i = str.indexOf('(', i + 1)) {
arr.add(str.substring(i + 1, str.indexOf(')', i)));
pos[arr.size()-1] = i; // save it!
}
// replace from right to left
for (int j=pos.length-1;j>=0;j--){
String newStr = str.substring(0, pos[j]+1) + ar[j] + str.substring(str.indexOf(')',pos[j]+1), str.length());
str = newStr;
}
return str;
}
The trick here is that I'm replacing from right to left so that the positions of where I need to replace do not move when I am replacing them.

scanner.nextInt() returns java.util.NoSuchElementException

This is my code, which is supposed to accept input from the user and set 2 int values. The exit function works correctly however when the input string is "5 2" for example, it sets x as 5 and throws the java.util.NoSuchElementException at the y = s2.nextInt(); line, even though there is a next int. In an example of nextInt() I saw, ints were seperated by a space and the scanner still picked up all the integers. Is mine missing something?
String exit = "-1";
Scanner s1 = new Scanner(System.in);
String input = s1.next();
Scanner s2 = new Scanner(input);
if (input.equals(exit))
Sequence.quit();
else {
x = s2.nextInt();
y = s2.nextInt();
}
If you print out your variable ìnput, you will see that it contains only "5" - since s1.next() wil stop at space.
Something like this:
String exit = "-1";
Scanner s1 = new Scanner(System.in);
int x = s1.nextInt();
if (x==-1)
Sequence.quit();
else {
y = s1.nextInt();
}

stick integer to string and char*

How can I add an integer variable to a string and char* variable? for example:
int a = 5;
string St1 = "Book", St2;
char *Ch1 = "Note", Ch2;
St2 = St1 + a --> Book5
Ch2 = Ch1 + a --> Note5
Thanks
The C++ way of doing this is:
std::stringstream temp;
temp << St1 << a;
std::string St2 = temp.str();
You can also do the same thing with Ch1:
std::stringstream temp;
temp << Ch1 << a;
char* Ch2 = new char[temp.str().length() + 1];
strcpy(Ch2, temp.str().c_str());
for char* you need to create another variable that is long enough for both, for instance. You can 'fix' the length of the output string to remove the chance of overrunning the end of the string. If you do that, be careful to make this large enough to hold the whole number, otherwise you might find that book+50 and book+502 both come out as book+50 (truncation).
Here's how to manually calculate the amount of memory required. This is most efficient but error-prone.
int a = 5;
char* ch1 = "Book";
int intVarSize = 11; // assumes 32-bit integer, in decimal, with possible leading -
int newStringLen = strlen(ch1) + intVarSize + 1; // 1 for the null terminator
char* ch2 = malloc(newStringLen);
if (ch2 == 0) { exit 1; }
snprintf(ch2, intVarSize, "%s%i", ch1, a);
ch2 now contains the combined text.
Alternatively, and slightly less tricky and also prettier (but less efficient) you can also do a 'trial run' of printf to get the required length:
int a = 5;
char* ch1 = "Book";
// do a trial run of snprintf with max length set to zero - this returns the number of bytes printed, but does not include the one byte null terminator (so add 1)
int newStringLen = 1 + snprintf(0, 0, "%s%i", ch1, a);
char* ch2 = malloc(newStringLen);
if (ch2 == 0) { exit 1; }
// do the actual printf with real parameters.
snprintf(ch2, newStringLen, "%s%i", ch1, a);
if your platform includes asprintf, then this is a lot easier, since asprintf automatically allocates the correct amount of memory for your new string.
int a = 5;
char* ch1 = "Book";
char* ch2;
asprintf(ch2, "%s%i", ch1, a);
ch2 now contains the combined text.
c++ is much less fiddly, but I'll leave that to others to describe.
You need to create another string large enough to hold the original string followed by the number (i.e. append the character corresponding to each digit of the number to this new string).
Try this out:
char *tmp = new char [ stelen(original) ];
itoa(integer,intString,10);
output = strcat(tmp,intString);
//use output string
delete [] tmp;

Resources