2

I'm working with an arduino UNO. I would like to be able to send it set and get commands over serial and have it update class data class members accordingly by key value pair. I spoke to users in the cpp room, and they suggested I try using map to map every key to value.

command structure command:key:value

ex. set:key1:20 ex. get:key2

I created a class called Uno, assigned all my keys to private data members, and defined set and get functions. However, I am experiencing unintended behavior.

Unintended behavior The set function ends up setting key1 and key2 to the same value, and the get function returns a random integer value that wasn't assigned.

  #include <ArduinoSTL.h>
  #include <string.h>
  #include <map>

class Uno{
  //private
    int key1;
    int key2;
    std::map<String, int Uno::*> keys;
  public:
  //Constructor
    Uno(){
      key1 = 0;
      key2 = 0;
      keys["key1"] = &Uno::key1;
      keys["key2"] = &Uno::key2;
    }
    bool setKey(String key, int value){
      if (keys.count(key)) {
          this->*keys[key] = value;
          return true;
      }
      return false;
    }
    int getKey(String key){
      if (keys.count(key)) {
          return this->*keys[key];
      }
    }
};


String inputString = "";      // a String to hold incoming data
bool stringComplete = false;  // whether the string is complete
bool scan = false;            // whether a scan is in progress
Uno myMS;                      // global declaration of Mass Spec Class

void setup() {
  // initialize serial:
  Serial.begin(9600);
  // reserve 250 bytes for the inputString:
  inputString.reserve(250);
}

void loop() {
  if (stringComplete) {
    t(inputString); // tokenize and process string

    //RESET STRING
    inputString = "";
    stringComplete = false;
  }
}


void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}



void t(String inputString) {
    //get command
    String command = getValue(inputString, ':', 0);

    if(command == "set"){
      // get key, get value
       String key = getValue(inputString, ':', 1);
       int value = (getValue(inputString, ':', 2)).toInt();
       Serial.println(myMS.setKey(key,value));
    }
    else if(command == "get"){
       String key = getValue(inputString, ':', 1);
       int value = myMS.getKey(key);
       Serial.println(value);
    }
    else if(command == "stop"){
       scan = false; 
    }
    else if(command == "scan"){
       scan = true;
       while(scan){
//        Serial.println(myMS.scan());
       }
    }
    else{
       Serial.println("not recognized");
    }
}


String getValue(String data, char separator, int index)
{
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length()-1;

  for(int i=0; i<=maxIndex && found<=index; i++){
    if(data.charAt(i)==separator || i==maxIndex){
        found++;
        strIndex[0] = strIndex[1]+1;
        strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }
  return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}

EDIT: Here is a REPL IT working demo of concept provided by nwp. https://repl.it/repls/AntiqueProductiveMineral

Sample output

set:key1:10 ===> 1 (GOOD)
get:key1 ===> 32512
  • why do you think that this is an Arduino question? ... it appears to be a C/C++ question – jsotola Sep 26 '18 at 17:08
  • 1
    @jsotola It is an arduino question, because I am running the code on the arduino using the arduino String type. I am also using Serial on the arduino. I'm open to other arduino methods for storing key value pairs. – ex080 Sep 26 '18 at 17:10
  • the 32512 is the return value of getKey if it didn't found the key – Juraj Sep 26 '18 at 19:16
  • Ok, but that is confusing because I am using the same method to set. I know the set function is working correctly, because if I do Serial.println(key1); from inside the class it prints the correct value. – ex080 Sep 26 '18 at 19:30
3

You get random values because if the key is not found in getKey, then there is no return statement.

You attach the \n to the key in serialEvent(), then the key for getKey is "key1\n" and it is not found.

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    if (inChar == '\n') {
      stringComplete = true;
    } else {
      inputString += inChar;
    }
  }
}

If you have CR/LF set in Serial Monitor, then the \r character is send too and the parsed key is "key1\r\n".

There are obsolete things in your code, like the this->* or Uno::. Make the map std::map<String, int> keys;, because now you mix pointers and ints.

  • I have Newline only and it's not working anyways – KIIV Sep 26 '18 at 20:08
  • change the serialEvent to not to attach the \n to the string – Juraj Sep 26 '18 at 20:09
  • Right, he's not skipping it in that parser :D – KIIV Sep 26 '18 at 20:15
  • Ok, I like your approach, it works! – ex080 Sep 27 '18 at 16:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.