Problem with KVStore returning wrong value

Having some issues on a Giga R1 Wifi and trying to create a KVStore to store the ssid and password and for some reasons when re-booting the board the password is being returned when I ask for the ssid. I have even so much as created just a small sample file for testing just this part of the functionality and it still has this issue. I am pulling my hair out on this one.

Sample Code

#include <KVStore.h>
#include <kvstore_global_api.h>
/**************************************/
// sample code from - https://forum.arduino.cc/t/persistent-storage/939250/9
const char* const KVS_ssid = "wifi_ssid";         /* wifi network Key*/
char wifi_ssid[] = "";                             /* wifi network Value */
const char* const KVS_pass = "wifi_pass";           /* wifi_pass Key */
char wifi_pass[] = "";                            /* wifi_pass Value */
/**************************************/
void serial_listen(){

  if (Serial.available()){
    String PCRCommand = "";
    PCRCommand = Serial.readStringUntil('\n');
    PCRCommand.trim(); // trim whitspace from end of string
    if( PCRCommand.startsWith("set_ssid:") ){
      Serial.print("set_ssid:");
      PCRCommand.remove(0,9); // remove first 8 chars of string
      //set_ssid:Netgear96
      Serial.println(PCRCommand);
      int str_len = PCRCommand.length();
      strcpy(wifi_ssid,PCRCommand.c_str());
      kv_set(KVS_ssid, wifi_ssid, str_len + 1,  0);
      
      Serial.print("kv_get key: ");
      Serial.println(KVS_ssid);
      Serial.print("kv_get Value: ");
      Serial.println(wifi_ssid);
      Serial.println();

    }else if( PCRCommand.startsWith("set_pass:") ){

      //set_pass:curlyjungle455
      Serial.print("set_pass:");
      PCRCommand.remove(0,9); // remove first 8 chars of string 'set_pass:'
      Serial.println(PCRCommand);

      int str_len = PCRCommand.length();
      strcpy(wifi_pass,PCRCommand.c_str());
      kv_set(KVS_pass, wifi_pass, str_len + 1,  0);

      Serial.print("kv_get key: ");
      Serial.println(KVS_pass);
      Serial.print("kv_get Value: ");
      Serial.println(wifi_pass);
      Serial.println();
      //Serial.print("Send Formated DPRINT String:");
    }else if( PCRCommand.equals("kvs") ){
      kv_reset("/kv/");
      Serial.println("KVStore Reset!!!");
    }else if( PCRCommand.equals("reset") ){
      load_settings();
    }
  }
}
/**************************************/
void load_settings(){
  Serial.println("loading settings!!!");
  
  kv_get(KVS_ssid, wifi_ssid, 32, 0);
  Serial.print("kv_get key: ");
  Serial.println(KVS_ssid);
  Serial.print("kv_get Value: ");
  Serial.println(wifi_ssid);

  kv_get(KVS_pass, wifi_pass, 32, 0);
  Serial.print("kv_get key: ");
  Serial.println(KVS_pass);
  Serial.print("kv_get key Value: ");
  Serial.println(wifi_pass);

}
/**************************************/
void setup() {
  Serial.begin(9600);
  Serial.println("booting!!!");
  while (!Serial); /* wait for serial port to be active */
  /* Load all settings */
  // onstartup load current settings
  kv_get(KVS_ssid, wifi_ssid, 32, 0);
  Serial.print("kv_get key: ");
  Serial.println(KVS_ssid);
  Serial.print("kv_get Value: ");
  Serial.println(wifi_ssid);

  kv_get(KVS_pass, wifi_pass, 32, 0);
  Serial.print("kv_get key: ");
  Serial.println(KVS_pass);
  Serial.print("kv_get key Value: ");
  Serial.println(wifi_pass);
  if (*wifi_pass == '\0' || *wifi_ssid == '\0'){
    Serial.println("Set WIFI Crendentials!!!!");

  }else{
    Serial.println("Connect to WIFI with these credentials");
    Serial.print("SSID:");
    Serial.println(wifi_ssid);
    Serial.print("PASS:");
    Serial.println(wifi_pass);

  }

}
/**************************************/
void loop() {
  // put your main code here, to run repeatedly:
  serial_listen(); // listen on serial port

}

As you can see the variable names are different so its not a small typo at least that I can see. I have this sample setup in a way I can pass commands via the serial monitor

to set the ssid I pass "set_ssid:myssid"

to set the password I pass "set_pass:mypassword"

I can optionally reset the KVStore by sending "kvs" this will run the kv_reset("/kv/");

and I can pass "reset" and this should output the current values of the KVStore for the two variables.

the weird part is if I program the user and pass and run the "reset" which fires off the "load_settings()" function it shows the correct values. if I reboot the load settings returns the password for both variables.

Here is the serial out put from setting the variables the first time and as you can see everything looks correct after running "set_ssid:myssid", "set_pass:mypassword" and then "reset"

20:11:19.633 -> KVStore Reset!!!
20:11:26.997 -> set_ssid:myssid
20:11:26.997 -> kv_get key: wifi_ssid
20:11:26.997 -> kv_get Value: myssid
20:11:26.997 ->
20:11:37.278 -> set_pass:mypassword
20:11:37.278 -> kv_get key: wifi_pass
20:11:37.278 -> kv_get Value: mypassword
20:11:37.278 ->
20:11:53.313 -> loading settings!!!
20:11:53.313 -> kv_get key: wifi_ssid
20:11:53.313 -> kv_get Value: myssid
20:11:53.313 -> kv_get key: wifi_pass
20:11:53.313 -> kv_get key Value: mypassword

/-------------------------------------/
after re-booting I get this

20:01:46.027 -> kv_get key: wifi_ssid
20:01:46.027 -> kv_get Value: :myssid
20:01:46.027 -> kv_get key: wifi_pass
20:01:46.027 -> kv_get key Value: :mypassword
20:01:46.027 -> Connect to WIFI with these credentials
20:01:46.027 -> SSID:mypassword
20:01:46.027 -> PASS::mypassword

any idea what I am missing on this super simple setup?...

please keep in mind I am still a Arduino Newb and this is my first major project.

and another question in regards to the KV_Get() function, why does it require I send in a length, if I don't know the length of the string my user has input? is there a better way of executing the kv_get command??? some sort of reference variable or pointer I need to use?

kv_get(KVS_pass, wifi_pass, 32, 0);

Hi, I use the mbed::TDBStore (an implementation of kv) so don't have direct experience of KV, but the APIs are very similar.

I'd start by checking the return codes from the kv calls and turning on compiler warnings if not already done.

As for your last point, you can use int kv_get_info(const char *full_name_key, kv_info_t *info); to check for the existence and length of a key/value.

typedef struct info {
    /**
     * The key size
     */
    size_t size;
    /*
     * The Key flags, possible flags combination:
     * WRITE_ONCE_FLAG,
     * REQUIRE_CONFIDENTIALITY_FLAG,
     * REQUIRE_REPLAY_PROTECTION_FLAG
     */
    uint32_t flags;
} kv_info_t;
1 Like

has a few mins this morning to take a look. This should improve things:

char wifi_ssid[32] = "";                             /* wifi network Value */
char wifi_pass[32] = "";                            /* wifi_pass Value */
char wifi_ssid[32] = "";                             /* wifi network Value */
char wifi_pass[32] = "";                            /* wifi_pass Value */

surprisingly that simple fix seemed to work, I had a few minutes this morning to try that out and after a few re-boots to confirm it hasn't done the variable swapping again. Ill try making that same change in my other scripts and see if it stays that way.

I also tried using the "kv_into()" function and that works as well, but if I am hard coding the variable sizes I don't see the need for it in the future, unless it is going to have some adverse affect to the boards memory.

for future reference to anyone coming across this same issue here is a working sample with the issue apparently fixed

#include <KVStore.h>
#include <kvstore_global_api.h>
#include <mbed_error.h>
/**************************************/
// sample code from - https://forum.arduino.cc/t/persistent-storage/939250/9
const char* const KVS_ssid = "ssid";                /* wifi network Key*/
char wifi_ssid[32] = "";                              /* wifi network Value */
const char* const KVS_pass = "password";            /* wifi_pass Key */
char wifi_pass[32] = "";                              /* wifi_pass Value */
/**************************************/
void serial_listen(){

  if (Serial.available()){
    String PCRCommand = "";
    PCRCommand = Serial.readStringUntil('\n');
    PCRCommand.trim(); // trim whitspace from end of string
    if( PCRCommand.startsWith("set_ssid:") ){
      Serial.print("set_ssid:");
      PCRCommand.remove(0,9); // remove first 8 chars of string
      //set_ssid:Netgear96
      Serial.println(PCRCommand);
      int str_len = PCRCommand.length();
      strcpy(wifi_ssid,PCRCommand.c_str());
      kv_set(KVS_ssid, wifi_ssid, str_len,  0);
      
      Serial.print("kv_get key: ");
      Serial.println(KVS_ssid);
      Serial.print("kv_get Value: ");
      Serial.println(wifi_ssid);
      Serial.println();

    }else if( PCRCommand.startsWith("set_pass:") ){

      //set_pass:curlyjungle455
      Serial.print("set_pass:");
      PCRCommand.remove(0,9); // remove first 8 chars of string 'set_pass:'
      Serial.println(PCRCommand);

      int str_len = PCRCommand.length();
      strcpy(wifi_pass,PCRCommand.c_str());
      kv_set(KVS_pass, wifi_pass, str_len,  0);

      Serial.print("kv_get key: ");
      Serial.println(KVS_pass);
      Serial.print("kv_get Value: ");
      Serial.println(wifi_pass);
      Serial.println();
      //Serial.print("Send Formated DPRINT String:");
    }else if( PCRCommand.equals("kvs") ){
      kv_reset("/kv/");
      Serial.println("KVStore Reset!!!");
    }else if( PCRCommand.equals("reset") ){
      load_settings();
    }
  }
}
/**************************************/
void load_settings(){
  Serial.println("loading settings!!!");
  
  kv_info_t infoBuffer;
  //kv_get_info(KVS_ssid, &infoBuffer);  // Information for the key will be written to infoBuffer.
  kv_get(KVS_ssid, wifi_ssid, 32, nullptr);

  Serial.print("kv_get key: ");
  Serial.println(KVS_ssid);
  Serial.print("kv_get Value: ");
  Serial.println(wifi_ssid);

  //kv_get_info(KVS_pass, &infoBuffer);  // Information for the key will be written to infoBuffer.
  kv_get(KVS_pass, wifi_pass, 32, nullptr);

  Serial.print("kv_get key: ");
  Serial.println(KVS_pass);
  Serial.print("kv_get key Value: ");
  Serial.println(wifi_pass);

}
/**************************************/
void setup() {
  Serial.begin(9600);
  Serial.println("booting!!!");
  while (!Serial); /* wait for serial port to be active */
  /* Load all settings */
  // onstartup load current settings
  kv_info_t infoBuffer;
  int result = kv_get_info(KVS_ssid, &infoBuffer);  // Information for the key will be written to infoBuffer.
  if (result != MBED_SUCCESS) {
    Serial.print("kv_get_info wifi_ssid failed with error code ");
    Serial.println(result);
    //while (true) {}  // Do not continue.
  }
  result = kv_get(KVS_ssid, wifi_ssid, infoBuffer.size, nullptr);
  if (result != MBED_SUCCESS) {
    Serial.print("kv_get wifi_ssid failed with error code ");
    Serial.println(result);
    //while (true) {}  // Do not continue.
  }

  result = kv_get_info(KVS_pass, &infoBuffer);  // Information for the key will be written to infoBuffer.
  if (result != MBED_SUCCESS) {
    Serial.print("kv_get_info wifi_pass failed with error code ");
    Serial.println(result);
    //while (true) {}  // Do not continue.
  }
  kv_get(KVS_pass, wifi_pass, infoBuffer.size, nullptr);
  if (result != MBED_SUCCESS) {
    Serial.print("kv_get wifi_pass failed with error code ");
    Serial.println(result);
    //while (true) {}  // Do not continue.
  }
  Serial.print("kv_get key: ");
  Serial.println(KVS_pass);
  Serial.print("kv_get key Value: ");
  Serial.println(wifi_pass);



  if (*wifi_pass == '\0' || *wifi_ssid == '\0'){
    Serial.println("Set WIFI Crendentials!!!!");

  }else{
    Serial.println("Connect to WIFI with these credentials");
    Serial.print("SSID:");
    Serial.println(wifi_ssid);
    Serial.print("PASS:");
    Serial.println(wifi_pass);

  }

}
/**************************************/
void loop() {
  // put your main code here, to run repeatedly:
  serial_listen(); // listen on serial port
}

FYI, I believe (Arduino Team or others please correct if I'm wrong) that KVStore is using the internal flash of the MCU. As this flash is also where the sketch is stored there is the potential that your keys could be erased when uploading a new sketch.

It is for this reason that I'm using TDBStore in a dedicated MBRpartition of the external QSPI flash.

1 Like

I don't care if the values get overwritten on sketch upload, once i finish the sketch i wont be uploading to it anymore unless its to update to a future version and then i will just require the user the re-input the settings