Arnyminer Z
Arnyminer Z

Reputation: 6073

ESP32 restarts when trying to write to NVS

I am trying to make a save system for my ESP32 project, and I have the following code:

void write_string_nvs(char *memorySlot, String key, String value)
{
    nvs_handle my_handle;
    esp_err_t err = nvs_open(memorySlot, NVS_READWRITE, &my_handle);
    if (err == ESP_OK)
    {
        int kL = key.length();
        int vL = value.length();
        char keyA[kL + 1];
        key.toCharArray(keyA, kL + 1);
        char valueA[vL + 1];
        value.toCharArray(valueA, vL + 1);

        Serial.println("Storing \"" + String(keyA) + "\"(" + String(kL) + ")>\"" + String(valueA) + "\"(" + String(vL) + ") in NVS.");
        esp_err_t err = nvs_set_blob(my_handle, keyA, &valueA, vL);
        if (err == ESP_OK)
        {
            err = nvs_commit(my_handle);
            if (err == ESP_OK)
                Serial.println("Correctly saved \"" + key + "\" in " + String(memorySlot));
            else
                Serial.println("write_string_nvs::commit -> Could not save \"" + key + "\" in " + String(memorySlot) + ": " + esp_err_toString(err, true));
        }
        else
            Serial.println("write_string_nvs::nvs_set_blob -> Could not save \"" + key + "\" in " + String(memorySlot) + ": " + esp_err_toString(err, true) + "");
        nvs_close(my_handle);
    }
    else
        Serial.println("Could not initialize " + String(memorySlot) + " NVS slot: " + esp_err_toString(err, true) + "");
}

I call it the following way, from a serial command:

...
String params[3];
split(serialRead, ' ', params);

String s = params[0];
String k = params[1];
String v = params[2];

bool error = false;
if (s.length() <= 0) {
  error = true;
  Serial.println("Please, specify an storage name");
}
if (k.length() <= 0) {
  error = true;
  Serial.println("Please, specify a key");
}
if (v.length() <= 0) {
  error = true;
  Serial.println("Please, specify a value");
}

if (!error) {
  String slotName = "";
  if (startsWithIgnoreCase(s, "main")) {
    slotName = "storage";
  }
  if (startsWithIgnoreCase(s, "wifi")) {
    slotName = "wifi";
  }

  if (slotName.length() > 1) {
    Serial.println("Writing \"" + v + "\"" + " at \"\"" + k + "\" in " + slotName);

    char slot[slotName.length()];
    slotName.toCharArray(slot, slotName.length());

    write_string_nvs(slot, k, v);
  } else
    Serial.println("Specified invalid slot");
}

By doing this I am trying to make a command parser to store values and read them afterwards, with the following commands: storage write <wifi/main> <key> <value> and storage read <wifi/main> <key>.

But the problem comes when I try to type the write command, and the code executes, the ESP32 Serial returns:

assertion "heap != NULL && "realloc() pointer is outside heap areas"" failed: file "/Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/heap/heap_caps.c", line 285, function: heap_caps_realloc
abort() was called at PC 0x40152903 on core 1

Backtrace: 0x40091ca4:0x3ffce0c0 0x40091ed5:0x3ffce0e0 0x40152903:0x3ffce100 0x400847a9:0x3ffce130 0x4008483d:0x3ffce150 0x4008b2e9:0x3ffce170 0x4000bedd:0x3ffce190 0x400dd4e2:0x3ffce1b0 0x400dd544:0x3ffce1d0 0x400dd6a6:0x3ffce1f0 0x400dd6d1:0x3ffce210 0x400d1b06:0x3ffce240 0x400d5939:0x3ffce260 0x400de489:0x3ffce7d0 0x40094135:0x3ffce7f0

Rebooting...

I don't know what to do, I have tried some different write and read codes, but I can't find any that stores the values correctly. The read command works, but obviously, it doesn't return anything, because the memory is empty. Here's the read command, in case you want to take a look at it:

String read_string_nvs(char *memorySlot, String key)
{
    nvs_handle my_handle;
    esp_err_t err = nvs_open(memorySlot, NVS_READWRITE, &my_handle);
    String espErrStr = esp_err_toString(err, true);
    char *value;
    if (err == ESP_OK || startsWithIgnoreCase(espErrStr, "ESP_OK"))
    {
        size_t string_size;
        int kL = key.length();
        char wifi_slot[kL + 1];
        key.toCharArray(wifi_slot, kL + 1);
        esp_err_t err = nvs_get_str(my_handle, wifi_slot, NULL, &string_size);
        value = (char *)malloc(string_size);
        err = nvs_get_str(my_handle, wifi_slot, value, &string_size);

        nvs_close(my_handle);

        return String(value);
    }
    else
        Serial.println("Could not open memory (\"" + espErrStr + "\")");
    return espErrStr;
}

I've been with this issue for some weeks, and I really don't know what to do, maybe the system is not good for what I want, or I may be doing something wrong.

For developing I am using VSCode with PlatformIO.

Please, take a look and it and if you could tell me what's wrong or what to do, I'd be really pleased.

Thanks in advance.

Upvotes: 0

Views: 1582

Answers (2)

Antdigapony
Antdigapony

Reputation: 11

I am busy with the same problem (I am going to use 4Mb of the flash as a nvs partition) and I have found a some clue: https://www.esp32.com/viewtopic.php?t=6815

It seems that problem is with the RAM-size - the system needs a RAM to create the nvs-pages-map and if it's not enough for this task - it calls the system abort.

P.S. I have decoded my firmware.elf into firmware.lst with the addresses and the assembler code and the backtrace is so:

app_main -> initArduino -> nvs_flash_init -> nvs_flash_init_partition -> nvs_flash_init_custom -> ZN3nvs_storage_init_Ejj ->ZN3nvs_Storage_populateBlobIndicesIntrusiveList -> _Znwj -> _cxa_allocate_exception -> terminatev -> cxabiv111_terminateEPFvvE - here the system aborts

To decode the .elf into .lst - just copy the firmware.elf into the folder with the xtensa-esp32-elf-objdump.exe (it is probably here .platformio\packages\toolchain-xtensa32\bin) and run in the command prompt - xtensa-esp32-elf-objdump.exe -S -l -d firmware.elf > [YourFileName].lst

Upvotes: 1

romkey
romkey

Reputation: 7109

These lines are problematic:

char slot[slotName.length()];
slotName.toCharArray(slot, slotName.length());

write_string_nvs(slot, k, v);

slotName.length() will return the number of characters in slotName. slot is a C string, which needs a null terminating character at the end (\0), so it needs to be declared with one byte more than the number of characters in the string. The declaration you have is too short.

You can side-step the problem by rewriting these lines as:

write_string_nvs(slotName.c_str(), k, v);

String already stores its contents as a C string internally, so the c_str() method just gives you a pointer to the buffer it manages. Be careful with this, that pointer won't be valid after the String object becomes invalid, so if the String is a variable in a function or code block, its c_str() will stop being valid when that you leave that function or code block.

Since this is some kind of heap or memory allocation issue it's possible the bug is outside of the code you shared. I would review all the code looking for instances of where you convert a String to a C character array and try using the c_str() idiom instead.

This is a pretty common problem that bites a lot of programmers.

It's also possible the problem is in your write_string_nvs() implementation.

Upvotes: 0

Related Questions