Skip to content

Instantly share code, notes, and snippets.

@tslater2006
Created April 25, 2020 13:28
Show Gist options
  • Select an option

  • Save tslater2006/b7b976a53b48bad3cdd4d357af8c2dd5 to your computer and use it in GitHub Desktop.

Select an option

Save tslater2006/b7b976a53b48bad3cdd4d357af8c2dd5 to your computer and use it in GitHub Desktop.
ESP32 NVS Parser
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace esp32_flash_to_elf
{
public class ESP32NVSKeyPair
{
public string Namespace;
public string Key;
public string Value;
public ESP32NVSType Type;
}
public class ESP32NVSPartition
{
List<ESP32NVSPage> Pages = new List<ESP32NVSPage>();
public ESP32NVSPartition(byte[] data)
{
MemoryStream ms = new MemoryStream(data);
while (ms.Position < ms.Length - 1)
{
byte[] pageData = new byte[4096];
ms.Read(pageData);
Pages.Add(new ESP32NVSPage(pageData));
}
}
public ESP32NVSPage GetPageBySequence(uint sequence)
{
return Pages.Where(p => p.Header.Sequence == sequence).FirstOrDefault();
}
public void SaveToCSV(string filepath)
{
var pairs = GetValuePairs();
StreamWriter sw = File.CreateText(filepath);
sw.WriteLine("key,type,encoding,value");
List<ESP32NVSKeyPair> test = pairs.Where(p => p.Namespace == "diagnostics" && p.Key == "usage").ToList();
List<string> distinctNamespaces = pairs.Select(p => p.Namespace).Distinct().ToList();
foreach (var space in distinctNamespaces)
{
sw.Write(space);
sw.Write(',');
sw.WriteLine("namespace,,");
foreach (var item in pairs.Where(p => p.Namespace == space))
{
sw.Write(item.Key);
sw.Write(",data,");
switch (item.Type)
{
case ESP32NVSType.BLOB:
sw.Write("base64,");
break;
case ESP32NVSType.U8:
sw.Write("u8,");
break;
case ESP32NVSType.I8:
sw.Write("i8,");
break;
case ESP32NVSType.U16:
sw.Write("u16,");
break;
case ESP32NVSType.I16:
sw.Write("i16,");
break;
case ESP32NVSType.U32:
sw.Write("u32,");
break;
case ESP32NVSType.I32:
sw.Write("i32,");
break;
case ESP32NVSType.SZ:
sw.Write("string,");
break;
}
sw.WriteLine(item.Value);
}
}
sw.Close();
}
public List<ESP32NVSKeyPair> GetValuePairs()
{
List<ESP32NVSKeyPair> pairs = new List<ESP32NVSKeyPair>();
var allWrittenEntries = Pages.SelectMany(p => p.Entries.Where(e => e.State == ESP32NVSEntryState.WRITTEN)).ToList();
Dictionary<byte, string> Namespaces = new Dictionary<byte, string>();
for (var x = 0; x < Pages.Count; x++)
{
for (var y = 0; y < Pages[x].Entries.Count; y++)
{
var e = Pages[x].Entries[y];
if (e.Namespace == 0 && e.Type != ESP32NVSType.ANY)
{
var namespaceIndex = e.Data[0];
var namespaceString = UTF8Encoding.UTF8.GetString(e.Key).Trim('\0');
Namespaces.Add(namespaceIndex, namespaceString);
}
}
}
for (var x = 0; x < Pages.Count; x++)
{
for (var y = 0; y < Pages[x].Entries.Count; y++)
{
var e = Pages[x].Entries[y];
if (e.State != ESP32NVSEntryState.WRITTEN)
{
continue;
}
if (e.Namespace != 0 && e.Type != ESP32NVSType.ANY)
{
ESP32NVSKeyPair kvp = new ESP32NVSKeyPair();
kvp.Key = UTF8Encoding.UTF8.GetString(e.Key).Trim('\0');
if (kvp.Key.Equals("usage"))
{
//Debugger.Break();
}
kvp.Type = e.Type;
kvp.Namespace = Namespaces[e.Namespace];
switch (e.Type)
{
case ESP32NVSType.U8:
kvp.Value = e.Data[0].ToString();
break;
case ESP32NVSType.I8:
kvp.Value = ((sbyte)(e.Data[0])).ToString();
break;
case ESP32NVSType.U16:
kvp.Value = BitConverter.ToUInt16(e.Data).ToString();
break;
case ESP32NVSType.I16:
kvp.Value = BitConverter.ToInt16(e.Data).ToString();
break;
case ESP32NVSType.U32:
kvp.Value = BitConverter.ToUInt32(e.Data).ToString();
break;
case ESP32NVSType.I32:
kvp.Value = BitConverter.ToInt32(e.Data).ToString();
break;
case ESP32NVSType.BLOB:
kvp.Value = Convert.ToBase64String(e.Data);
break;
case ESP32NVSType.SZ:
kvp.Value = UTF8Encoding.UTF8.GetString(e.Data).Trim('\0');
break;
default:
break;
}
pairs.Add(kvp);
}
}
}
return pairs;
}
}
public enum ESP32NVSPageState : uint
{
ACTIVE = 0xFFFFFFFE, FULL = 0xFFFFFFFC
}
public class ESP32NVSPageHeader
{
public ESP32NVSPageState State;
public uint Sequence;
public ESP32NVSVersion Version;
public byte[] Unused;
public uint CRC32;
public byte[] EntryStateBitmap;
public List<ESP32NVSEntryState> EntryStates = new List<ESP32NVSEntryState>();
}
public class ESP32NVSPageEntry
{
public ESP32NVSEntryState State;
public byte Namespace;
public ESP32NVSType Type;
public byte Span;
public byte ChunkIndex;
public uint CRC32;
public byte[] Key;
public byte[] Data;
}
public enum ESP32NVSEntryState : byte
{
ERASED = 0x0, WRITTEN = 0x2, EMPTY = 0x3
}
public enum ESP32NVSType : byte
{
U8 = 0x01,
I8 = 0x11,
U16 = 0x02,
I16 = 0x12,
U32 = 0x04,
I32 = 0x14,
U64 = 0x08,
I64 = 0x18,
SZ = 0x21,
BLOB = 0x41,
BLOB_DATA = 0x42,
BLOB_IDX = 0x48,
ANY = 0xFF
}
public enum ESP32NVSVersion : byte
{
VERSION1 = 0xFF,
VERSION2 = 0xFE
}
public class ESP32NVSPage
{
public ESP32NVSPageHeader Header = new ESP32NVSPageHeader();
public List<ESP32NVSPageEntry> Entries = new List<ESP32NVSPageEntry>();
public ESP32NVSPage(byte[] data)
{
MemoryStream ms = new MemoryStream(data);
BinaryReader br = new BinaryReader(ms);
Header.State = (ESP32NVSPageState)BitConverter.ToUInt32(br.ReadBytes(4));
Header.Sequence = BitConverter.ToUInt32(br.ReadBytes(4));
Header.Version = (ESP32NVSVersion)br.ReadByte();
Header.Unused = br.ReadBytes(19);
Header.CRC32 = BitConverter.ToUInt32(br.ReadBytes(4));
Header.EntryStateBitmap = br.ReadBytes(32);
for (var entry_num = 0; entry_num < 126; entry_num++)
{
var bitnum = entry_num * 2;
var byte_index = (int)(bitnum / 8);
var temp = Header.EntryStateBitmap[byte_index];
temp = (byte)(temp >> (6 - (bitnum % 8)));
temp = (byte)(temp & 3);
Header.EntryStates.Add((ESP32NVSEntryState)(temp));
}
for (var x = 0; x < 126; x++)
{
/* 126 entries per page... */
var entry = new ESP32NVSPageEntry();
entry.Namespace = br.ReadByte();
entry.Type = (ESP32NVSType)br.ReadByte();
entry.Span = br.ReadByte();
entry.ChunkIndex = br.ReadByte();
entry.CRC32 = BitConverter.ToUInt32(br.ReadBytes(4));
entry.Key = br.ReadBytes(16);
entry.Data = br.ReadBytes(8);
entry.State = Header.EntryStates[x];
if (entry.Type == ESP32NVSType.BLOB || entry.Type == ESP32NVSType.SZ)
{
var dataSize = BitConverter.ToUInt16(entry.Data, 0);
var dataCRC32 = BitConverter.ToUInt32(entry.Data, 4);
MemoryStream msd = new MemoryStream();
for (var y = 1; y < entry.Span; y++)
{
msd.Write(br.ReadBytes(32), 0, 32);
x++;
}
msd.Seek(0, SeekOrigin.Begin);
var br2 = new BinaryReader(msd);
entry.Data = br2.ReadBytes(dataSize);
br2.Close();
}
Entries.Add(entry);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment