### [ На русском ] ↔ [ English ]

- NVS в ESP32
- Отображение информации: cd и ls
- Создание, удаление и редактирование значений: new, rm и set
- Экспорт и импорт: import, export

## ⇮NVS в ESP32

Хотя ESP-IDF (а так же Arduino Core) содержат функции для работы с NVS (библиотеки EEPROM и Preferences в Arduino), как таковой EEPROM в SoC от Espressif не предусмотрено: вместо этого EEPROM эмулируется на FLASH памяти и для этого на флеше есть специальный раздел, который по умолчанию называется "nvs".

NVS в ESP устроена в виде линейной базы данных, с одним уровнем иерархии: помимо обычных "ключ=значение" в NVS так же сохраняется т.н. namespace. Ключи могут иметь одинаковые имена если он относятся к разым неймспейсам. Здесь и далее по тексту: термины пространство имен, неймспейс, namespace и каталог означают одно и то же.

ESPShell позволяет перемещаться по NVS образом, похожим на работу с фаловой системой: есть знакомые ls, cd, rm. Для создания новых и изменения существующих значений есть команды new и set, а для экспорта и импорта есть команды import и export, сохраняющие NVS в виде текстового файла, что позволяет переносить NVS между разными устройствами и версиями NVS. Текстовый файл содержит команды ESPShell которые можно выполнить командой "exec" или "import" (на самом деле коанда "import" - это алиас команды "exec")

Для запуска NVS редактора нужно выполнить команду nvs:

      esp32#>nvs
      esp32-nvs(/)>

Появившееся приглашение говорит нам о том, что редактор запустился и мы находимся в корневом каталоге. В этом каталоге находятся пространства имен или namespaces, которые можно пролистать командой "ls" или активировать командой "cd": лучше всего думать о пространствах имен как о каталогах в фаловой системе. Чтобы выйти из режима редактирования NVS нужно выполнить команду "exit" или нажать Ctrl+z

## ⇮Отображение информации: команды cd, ls и dump

Рис. 1: Пример работы и вывод команд cd и ls

Для просмотра информации из NVS используется три команды: cd, ls и dump. Последняя используется лишь в тех случаях, когда команды "ls" недостаточно: например, если данные это текстовая строка, то команда ls покажет лишь первые 42 символа, а если тип данных - бинарный blob, то ls покажет лишь длину блока данных и первые 16 байт. Команда ls имеет один необязательный параметр: пространство имен. Если параметр опущен, то используется то значение, которое было установлено командой cd. Команда ls, будучи выполненной в корневом каталоге пространства имен выведет список пространства имен:

     esp32-nvs(/)>ls
     % NVS has 3 namespaces:
     %  Namespace "espshell" : 2 keys
     %  Namespace "phy" : 4 keys
     %  Namespace "nvs.net80211" : 25 keys
     esp32-nvs(/)>

Если же зайти, скажем, в пространство имен phy (это WiFi драйвер) командой "cd /phy", то выполнив там команду ls без параметров, получим табличку-список ключей и их значений:

      esp32-nvs(/)>cd phy
      esp32-nvs(/phy)ls
      % # |     Key name     |  Type  | Value (strings may be truncated. use "dump")
      % --+------------------+--------+-----------------------------------------------
      %  1| cal_address      | char[] | <A binary blob, not displayed>, 1 bytes
      %  2| cal_data         | char[] | <A binary blob, not displayed>, 1904 bytes
      %  3| cal_mac          | char[] | <A binary blob, not displayed>, 6 bytes
      %  4| cal_version      | uint32 | 701
      % --+------------------+--------+-----------------------------------------------
      % Total: 4 records

А можно - указать пространство имен непосредственно команде, ее первым аргументом:

    esp32-nvs(/phy)>ls ../espshell
    % # |     Key name     |  Type  | Value (strings may be truncated. use "dump")
    % --+------------------+--------+-----------------------------------------------
    %  1| tz               | char*  | UTC-07:00
    %  2| hostid           | char*  | <empty>
    % --+------------------+--------+-----------------------------------------------
    % Total: 2 records
    esp32-nvs(/phy)>

ПРИМЕЧАНИЕ: Для команды "cd" существование пространства имен не обязательно: если выполнить "cd sdfgsdfg" то никакой ошибки не произойдет и namespace будет установлено именно таким. При создании первого ключа в данном пространстве имен, оно появится в списке команды "ls /". Если же namespace будет пустым, то после выхода из него (например, командой cd /) пространство имен будет удалено.

Как уже было указано, команда ls выводит содержимое NVS в виде таблицы: в ней указаны названия и типы ключей, а так же их значения, если эти значения являются числами. Строки отображаются целиком,только если они короче 42 символов, а бинарные данные (blob) не отображаются вовсе. Для отображения строк целиком, а так же бинарных данных существует команда dump KEY.

    esp32-nvs(/phy)>dump cal_address
    10 11 22 33 44 55
    esp32-nvs(/phy)>

Для данных небольшого размера (т.е. до 16 байт. лимит можно изменить с помощью команды "var tbl_min_len") отображение осуществляется в виде строчки. Для данных больщего размера включается форматированный табличный вывод:

Рис. 2: Пример работы и вывод команды dump sta.apinfo

Команда "dump" так же позволяет выводить длинные строчки, которые могут быть обрезаны командой "ls", целиком. Фомат вывода, впрочем, отличается от формата вывода бинарных данных:

      mars@esp32-nvs(/espshell)>dump hostid
      % "hostid" = "mars"

      mars@esp32-nvs(/espshell)>

Команда cd позволяет заходить в пространства имен. Выбранное пространство имен будет отображаться в строке-приглашении:

    esp32-nvs(/)>cd espshell
    esp32-nvs(/espshell)>

Как и при работе с файловой системой, команда "cd" редактора NVS может работать с относительными путями:

    esp32-nvs(/espshell)>cd ../phy

Каталог / (корневой каталог NVS) не содержит ни ключей ни их значений - в корневом каталоге расположены пространства имен. Как уже было сказано выше, NVS имеет лишь один уровень иерархии, поэтому "каталог в каталоге" создать нельзя.

Команда | Описание и примеры
--- | ---
ls [PATH] | Получить листинг каталога (пространства имен) или список пространств имен
    esp32-nvs(/)>ls
    %% NVS namespaces:
    %  Namespace "espshell" : 2 keys
    %  Namespace "phy" : 4 keys
    esp32-nvs(/)>
    esp32-nvs(/)>ls espshell
    % # |     Key name     |  Type  | Value (strings may be truncated. use "dump")
    % --+------------------+--------+-----------------------------------------------
    %  1| hostid           | char*  |
    %  2| tz               | char*  | UTC-07:00
    % --+------------------+--------+-----------------------------------------------
    % Total: 2 records
    esp32-nvs(/)>
Если PATH не указан, то использется путь, который был задан до этого командой "cd"
cd PATH | Сменить текущий каталог (пространство имен) на PATH. В качестве PATH может быть указано существующее ролстранство имен (и тогда команда "ls" что-то покажет) а может - несуществующее. В случае, когда PATH не существует, он будет создан. Это поведение используется для создания новых записей: если вам захотелось создать неймспейс "my_prefs" а в нем - записи, то нужно сменить каталог командой cd а затем создать записи командой new. Если никаких записей не создать и выйти, то такой пустой неймспейс будет тут же удален.
      esp32-nvs(/)>cd my_prefs
      esp32-nvs(/my_prefs)>ls
      % Namespace "my_prefs" (partition: "nvs") is empty or does not exist

      esp32-nvs(/my_prefs)>new TestKey char
      % Key created. Use "set TestKey ..." to set its value
      esp32-nvs(/my_prefs)>
cd PATH | Сменить текущий каталог (пространство имен) на PATH. В качестве PATH может быть указано существующее ролстранство имен (и тогда команда "ls" что-то покажет) а может - несуществующее. В случае, когда PATH не существует, он будет создан. Это поведение используется для создания новых записей: если вам захотелось создать неймспейс "my_prefs" а в нем - записи, то нужно сменить каталог командой cd а затем создать записи командой new. Если никаких записей не создать и выйти, то такой пустой неймспейс будет тут же удален.
      esp32-nvs(/)>cd my_prefs
      esp32-nvs(/my_prefs)>ls
      % Namespace "my_prefs" (partition: "nvs") is empty or does not exist

      esp32-nvs(/my_prefs)>new TestKey char
      % Key created. Use "set TestKey ..." to set its value
      esp32-nvs(/my_prefs)>
dump KEY_NAME | Выводит на экран значение ключа KEY_NAME в текущем пространстве имен. Эта команда работает только со строками и бинарными данными.
      esp32-nvs(/)>cd phy
      esp32-nvs(/phy)>dump cal_address
      % f0 9e 9e 22 7b 2c

## ⇮Создание, удаление и редактирование значений: new, rm и set

Чтобы создать новый элемент в NVS (т.е. создать новый ключ) следует выполнить команду "new", которая создаст новую запись и установит ее значение в 0 (для скалярных типов) или в однобайтовый буфер, внутри которого, опять же ноль.

      esp32-nvs(/)>cd phy
      esp32-nvs(/phy)>new Key unsigned char
      % Key created. Use "set Key ..." to set its value
      esp32-nvs(/phy)>new Key2 char* Hello, World!
      % Key created. Use "set Key2 ..." to set its value
      esp32-nvs(/phy)>

В данном примере мы создали запись с именем "Key", в пространстве имен "phy". Тип данных, который будет хранится в этой записи - unsigned char, т.е. беззнаковый байт. Второй ключ, который мы создали (Key2, строка) будет установлен в значение "Hello, World!" сразу после создания. Если значение не указано (т.е. команда new выглядит как-то так: "new unsigned char"), то будет использовано пустое значение: ноль для скалярных типов, или буфер с одним нулевым байтом для строк и блобов. Для указания типа данных используется синтаксис принятый в Си\Си++; поддерживаются следующие типы:

- signed, unsigned, (или без указания знаковости вовсе)
- char - 8 бит
- short - 16 бит
- int и long - 32 бита
- long long - 64 бита
- char* (или char *) - для строковых данных, оканчивающихся нулевым байтом
- char[] (или char []) - для бинарных данных

А так же их комбинации вроде "unsigned short int". Все типы "указатель" будут восприняты как "char *": т.е. можно написать "unsigned int *", но воспринято это будет именно как указатель на строку (т.е. char*). Аналогично в случае массивов, тип будет жестко преобразован к "char". Размер массива можно задавать, а можно и не задавать: "char [10]" или "char[]" сделают одно и тоже - массив переменной длины. Длина массива, как и длина строки будет определяться в момент установки нового значения командой "set"

Для удаления записей из NVS используется команда rm: она имеет один обязательный параметр. То, как работает эта команда станет понятнее из примеров. Предположим, что мы находимся в каталоге (namespace) "espshell":

    esp32-nvs(/)>cd espshell
    esp32-nvs(/espshell)>

Если мы выполним команду rm tz, то ключ tz из пространства имен espshell будет удален. Чтобы удалить ВСЕ ключи в данном пространстве имен, вместо имени ключа следует указать звездочку ("*")

    esp32-nvs(/)>cd espshell
    esp32-nvs(/espshell)>
    esp32-nvs(/espshell)>rm *
    % All keys in the namespace "espshell" were removed
    esp32-nvs(/espshell)>

Для удаления пространства имен целиком так же можно задать его первым аргументом команды rm:

    esp32-nvs(/phy)>rm ../espshell

Для удаления всех записей вообще, следует выполнить команду rm * непосредственно в корневом каталоге (в "/") или выполнить команду "rm /" в любом каталоге. Удаление записей NVS происходит без подтверждения (espshell не будет спрашивать у вас, уверены ли вы в том, что хотите удалить то или это)

Для установки значения существующего ключа (существующего или созданного командой "new") можно воспользоваться командой "set". У команды всего два параметра: название ключа и новое значение. Например, установим ключ "tz" в пространстве имен "espshell" в значение "Hello World!":

     esp32-nvs(/espshell)>set tz Hello World!
     esp32-nvs(/espshell)>ls
     % # |     Key name     |  Type  | Value (strings may be truncated. use "dump")
     % --+------------------+--------+-----------------------------------------------
     %  1| hostid           | char*  |
     %  2| tz               | char*  | Hello World!
     % --+------------------+--------+-----------------------------------------------
     % Total: 2 records
     esp32-nvs(/espshell)>

Для установки бинарных данных (char[]) используется Си-подобный синтаксис: символы, за исключением специальных (начинающихся с "\", например "\n" или "\22") сохраняются "как есть". Символы \r\n\t\v преобразуются в соответствии с правилами Си\Си++, символы вида \AB (где A и B - шеснадцатиричные цифры) преобразуются в байт со значением 0xAB. Пример: установить значение cal_data равным 0x10, 0x20, Hello, 0x20, 0x10

      esp32->nvs(/phy)set cal_data \10\20Hello\20\10

Команда | Описание и примеры
--- | ---
new KEY_NAME KEY_TYPE VALUE | Создать ключ типа KEY_TYPE с именем KEY_NAME. В качестве KEY_TYPE может быть указан один из скалярных типов языка Си (т.е. char, short, int, long, unsigned/signed, long long, char *, char []) Пример: создать запись cal_address, задать значение cal_address таким - 0011:2233:4455
      esp32-nvs(/)>cd phy
      esp32-nvs(/phy)>new cal_address char[] \00\11\22\33\44\55
      % Key created. Use "set cal_address ..." to set its value
Обратите внимание, что если команде new не передать VALUE, то будет использован или ноль или буффер с одним байтом равным нулю. Поэтому, если ключ уже существует, а VALUE не указано, то ключ будет обнулен (строки превратятся в строчки нулевой длины а массивы - в массивы состоящие из одного байта, нуля)
set KEY_NAME KEY_VALUE | Задать новое значение для ключа KEY_NAME. Аргумент KEY_VALUE это либо число (в случае со скалярными типами) либо строка\массив. Массив и строка задаются одним и тем же способом. Пример: Изменить запись cal_address, задать значение 0011:2233:4455
      esp32-nvs(/phy)>set cal_address \00\11\22\33\44\55
rm PATH | Удалить ключи\пространства имен. В качестве аргумента указывается или название ключа или пространство имен. Если в качестве PATH указать "/" (корневой каталог) до будут удалены вообще все данные NVS Если в качестве PATH указать название пространства имен, то будут удалены все ключи в данном пространстве имен. Ну а если команде передать название ключа, то команда удалит только указанный ключ.

## ⇮Экспорт и импорт: import, export

Для того, чтобы сохранить содержимое NVS в файл существует команда "export":

    esp32-nvs(/nvs.net80211)>export nvs_backup.txt
    % Exporting namespace "nvs.net80211"..
    esp32-nvs(/nvs.net80211)>

Команда может использоваться для экспорта отдельных каталогов (пространств имен) а может - для экспорта всего содержиого NVS целиком. Последний аргумент команды это всегда имя файла, в который требуется сохранить данные из NVS. Пространство имен можно указать, а можно - не указывать. В таком случае в качестве пространства имен для экспорта будет использовано текущее пространство имен. Синтаксис команды станет понятнее из примеров:

    esp32-nvs(/phy)#>export /ffat/data.txt  ← экспортировать текущее пространство имен (т.е. "phy")
    esp32-nvs(/phy)#>export ../espshell /ffat/data.txt  ← экспортировать настройки espshell
    esp32-nvs(/phy)#>export  / /ffat/data.txt  ← экспортировать все содержимое NVS
    esp32#>

Сохраненный файл с данными NVS можно будет загрузить командой "import":

    esp32-nvs(/phy)#>import /ffat/data.txt  ← импортировать данные из файла в NVS.

При импорте совершенно не важно, в каком каталоге вы находитесь - можно выполнять импорт находясь в корневом каталоге или в каталоге, скажем, "phy", на результат это не повлияет никак. При экспорте то, в каком каталоге вы находитесь имеет значение лишь в случае, когда команда "export" запускается с одним аргументом - именем файла.

Команда | Описание и примеры
--- | ---
export [NVS_PATH] FILE_PATH | Экспорт содержимого NVS в текстовый файл (файловая система должна быть смонтирована, см. "mount") Если в качестве NVS_PATH указать "/" (корневой каталог) до будут экспортированы все данные NVS (все пространства имен).
           esp32-nvs(/espshell)>export / file.txt ← экспортировать все содержимое NVS в файл
Если в качестве NVS_PATH указать название пространства имен, то будет экспортировано указанное пространство имен.
           esp32-nvs(/espshell)>export /phy file.txt ← экспортировать содержимое пространства имен "phy"
Если же параметр NVS_PATH опустить, то будет экспортировано текущее пространство имен:
           esp32-nvs(/espshell)>export file.txt ← экспортировать пространство имен "espshell"
import FILE_PATH | Импорт содержимого текстового файла (файловая система должна быть смонтирована, см. "mount")
           esp32-nvs(/espshell)>import file.txt
Данная команда выполняет файл file.txt (является алиасом команды "exec").