NVProperty generic key value store using the MCU flash area.

Dependents:   Turtle_RadioShuttle

Files at this revision

API Documentation at this revision

Comitter:
Helmut Tschemernjak
Date:
Thu Jan 24 14:28:11 2019 +0100
Parent:
0:1083b3c785dc
Child:
2:7ab0f5202860
Commit message:
Added new files

Changed in this revision

NVProperty.cpp Show annotated file Show diff for this revision Revisions of this file
NVProperty.h Show annotated file Show diff for this revision Revisions of this file
NVPropertyProviderInterface.h Show annotated file Show diff for this revision Revisions of this file
NVProperty_L4Flash.cpp Show annotated file Show diff for this revision Revisions of this file
NVProperty_L4Flash.h Show annotated file Show diff for this revision Revisions of this file
NVProperty_L4OTP.cpp Show annotated file Show diff for this revision Revisions of this file
NVProperty_L4OTP.h Show annotated file Show diff for this revision Revisions of this file
NVProperty_SRAM.cpp Show annotated file Show diff for this revision Revisions of this file
NVProperty_SRAM.h Show annotated file Show diff for this revision Revisions of this file
x.txt Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty.cpp	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,355 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifdef ARDUINO
+#include <Arduino.h>
+#elif __MBED__
+#include <mbed.h>
+#else
+#error "Unkown operating system"
+#endif
+
+
+#include <NVPropertyProviderInterface.h>
+#include <NVProperty_SRAM.h>
+#ifdef ARDUINO_ARCH_ESP32
+  #include <NVProperty_ESP32NVS.h>
+  #include <NVProperty_ESP32efuse.h>
+#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD)
+  #include <NVProperty_D21Flash.h>
+#elif defined(__MBED__) && defined(TARGET_STM32L4)
+  #include <mbed.h>
+  #include <NVProperty_L4OTP.h>
+  #include <NVProperty_L4Flash.h>
+#else
+#error "Unkown implementation"
+#endif
+#include <NVProperty.h>
+
+
+NVProperty::NVProperty(int propSizekB, bool erase)
+{
+    _flash = NULL;
+    _otp = NULL;
+    _ram = new NVProperty_SRAM();
+    
+#ifdef ARDUINO_ARCH_ESP32
+    _flash = new NVProperty_ESP32NVS();
+#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD)
+    _flash = new NVProperty_D21Flash(propSizekB, erase);
+#elif TARGET_STM32L4
+	// TODO _flash = new NVProperty_L4Flash(propSizekB, erase);
+#else
+ #error "unkown platform"
+#endif
+
+#ifdef ARDUINO_ARCH_ESP32
+    _otp = new NVProperty_ESP32efuse();
+#elif TARGET_STM32L4
+    _otp = new NVProperty_L4OTP();
+#endif
+    _allowWrite = false;
+    _didOpen = false;
+}
+
+NVProperty::~NVProperty()
+{
+    if (_ram) {
+    	delete _ram;
+	}
+	if (_flash)
+        delete _flash;
+    if (_otp)
+        delete _otp;
+}
+
+
+int
+NVProperty::GetProperty(int key, int defaultValue)
+{
+    int res;
+    
+    if (!_didOpen)
+        OpenPropertyStore();
+    
+    if (_ram) {
+        res = _ram->GetProperty(key);
+        if (res != NVP_ENOENT)
+            return res;
+    }
+    if (_flash) {
+        res = _flash->GetProperty(key);
+        if (res != NVP_ENOENT)
+            return res;
+    }
+    if (_otp) {
+        res = _otp->GetProperty(key);
+        if (res != NVP_ENOENT)
+            return res;
+    }
+    return defaultValue;
+}
+
+
+int64_t
+NVProperty::GetProperty64(int key, int64_t defaultValue)
+{
+    int64_t res;
+    
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    if (_ram) {
+        res = _ram->GetProperty64(key);
+        if (res != NVP_ENOENT)
+            return res;
+    }
+    if (_flash) {
+        res = _flash->GetProperty64(key);
+        if (res != NVP_ENOENT)
+            return res;
+    }
+    if (_otp) {
+        res = _otp->GetProperty64(key);
+        if (res != NVP_ENOENT)
+            return res;
+    }
+    return defaultValue;
+}
+
+const char *
+NVProperty::GetProperty(int key, const char *defaultValue)
+{
+    const char *res;
+    
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    if (_ram) {
+        res = _ram->GetPropertyStr(key);
+        if (res != NULL)
+            return res;
+    }
+    if (_flash) {
+        res = _flash->GetPropertyStr(key);
+        if (res != NULL)
+            return res;
+    }
+    if (_otp) {
+        res = _otp->GetPropertyStr(key);
+        if (res != NULL)
+            return res;
+    }
+    if (res != NULL)
+        return res;
+    
+    return defaultValue;
+}
+
+int
+NVProperty::GetProperty(int key, void *buffer, int *size)
+{
+    int res;
+    int maxsize = *size;
+    
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    if (_ram) {
+        res = _ram->GetPropertyBlob(key, buffer, &maxsize);
+        if (res == NVP_OK)
+            return res;
+    }
+    if (_flash) {
+        res = _flash->GetPropertyBlob(key, buffer, &maxsize);
+        if (res == NVP_OK)
+            return res;
+    }
+    if (_otp) {
+        res = _otp->GetPropertyBlob(key, buffer, &maxsize);
+        if (res == NVP_OK)
+            return res;
+    }
+ 
+    return NVP_ENOENT;
+}
+
+
+int
+NVProperty::SetProperty(int key, NVPType ptype, int64_t value, NVPStore store)
+{
+    int res = NVP_OK;
+    
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    if (!_allowWrite)
+        return NVP_NO_PERM;
+    
+    if (store == S_RAM && _ram) {
+        	res = _ram->SetProperty(key, value, ptype);
+    } else if (store == S_FLASH && _flash) {
+            res = _flash->SetProperty(key, value, ptype);
+    } else if (store == S_OTP && _otp) {
+            res = _otp->SetProperty(key, value, ptype);
+    } else {
+        return NVP_NO_STORE;
+    }
+    return res;
+}
+
+
+int
+NVProperty::SetProperty(int key, NVPType ptype, const char *value, NVPStore store)
+{
+    int res = NVP_OK;
+    
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    if (!_allowWrite)
+        return NVP_NO_PERM;
+
+    if (store == S_RAM && _ram) {
+        res = _ram->SetPropertyStr(key, value, ptype);
+    } else if (store == S_FLASH && _flash) {
+        res = _flash->SetPropertyStr(key, value, ptype);
+    } else if (store == S_OTP && _otp) {
+        res = _otp->SetPropertyStr(key, value, ptype);
+    } else {
+        return NVP_NO_STORE;
+    }
+    
+    return res;
+}
+
+// NVProperty_SRAM::SetPropertyBlob(int key, const void *blob, int size, int type)
+
+
+int
+NVProperty::SetProperty(int key, NVPType ptype,  const void *blob, int length, NVPStore store)
+{
+    int res = NVP_OK;
+
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    if (!_allowWrite) {
+        return NVP_NO_PERM;
+	}
+ 	
+  	if (store == S_RAM && _ram) {
+        res = _ram->SetPropertyBlob(key, blob, length, ptype);
+    } else if (store == S_FLASH && _flash) {
+        res = _flash->SetPropertyBlob(key, blob, length, ptype);
+    } else if (store == S_OTP && _otp) {
+        res = _otp->SetPropertyBlob(key, blob, length, ptype);
+    } else {
+        return NVP_NO_STORE;
+    }
+
+    return res;
+}
+
+int
+NVProperty::EraseProperty(int key, NVPStore store)
+{
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    int res = NVP_OK;
+    
+    if (!_allowWrite)
+        return NVP_NO_PERM;
+
+    if (store == S_RAM && _ram) {
+        res = _ram->EraseProperty(key);
+    } else if (store == S_FLASH && _flash) {
+        res = _flash->EraseProperty(key);
+    } else if (store == S_OTP && _otp) {
+        res = _otp->EraseProperty(key);
+    } else {
+        return NVP_NO_STORE;
+    }
+    
+    return res;
+}
+
+int
+NVProperty::ReorgProperties(NVPStore store)
+{
+    int res = NVP_OK;
+    
+    if (!_didOpen)
+        OpenPropertyStore();
+
+    if (!_allowWrite)
+        return NVP_NO_PERM;
+
+    if (store == S_RAM && _ram) {
+        res = _ram->ReorgProperties();
+    } else if (store == S_FLASH && _flash) {
+        res = _flash->ReorgProperties();
+    } else if (store == S_OTP && _otp) {
+        res = _otp->ReorgProperties();
+    } else {
+        return NVP_NO_STORE;
+    }
+    
+    return res;
+}
+
+int
+NVProperty::OpenPropertyStore(bool forWrite)
+{
+    int res = NVP_OK;
+
+    if (_didOpen) {
+        if (_ram)
+            _ram->ClosePropertyStore();
+        if (_flash)
+            _flash->ClosePropertyStore();
+        if (_otp)
+            _otp->ClosePropertyStore();
+    }
+    
+    if (_ram)
+        _ram->OpenPropertyStore(forWrite);
+    if (_flash)
+        res = _flash->OpenPropertyStore(forWrite);
+    if (_otp)
+        _otp->OpenPropertyStore(forWrite);
+    _didOpen = true;
+    if(forWrite)
+        _allowWrite = true;
+    
+
+    return res;
+}
+
+int
+NVProperty::ClosePropertyStore(bool flush)
+{
+    int res = NVP_OK;
+
+    if (_didOpen)
+        return NVP_NO_PERM;
+
+    if (_ram)
+        _ram->ClosePropertyStore(flush);
+    if (_flash)
+        res = _flash->ClosePropertyStore(flush);
+    if (_otp)
+        _otp->ClosePropertyStore(flush);
+    return res;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty.h	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,175 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifndef __NVPROPERTY_H__
+#define __NVPROPERTY_H__
+
+
+#ifndef UNUSED
+ #define UNUSED(x) (void)(x)
+#endif
+
+class NVProperty {
+public:
+	/*
+	 * The property store size depends on the implementation
+	 * The ESP32 uses the NVS partition therefore the size is being ignored.
+	 * The D21 uses the default 16kByte which is a good tradeoff between
+	 * space needed for the application versus space for properties.
+     * Larger D21 property space (e.g. 20kB) has the advantage that the
+     * flash blocks are less busy. For the D21 it is a good idea to
+	 * use increments of 16kB because this is the region locking area size
+ 	 */
+    NVProperty(int propSizekB = 16, bool erase = false);
+    ~NVProperty();
+public:
+    enum NVPType {
+        T_BIT	= 1,
+        T_8BIT	= 2,
+        T_16BIT	= 3,
+        T_32BIT	= 4,
+        T_64BIT	= 5,
+        T_STR	= 6,	// strings can be up to 256 bytes including zero
+        T_BLOB	= 7,	// blobs can be up to 256 bytes long
+		T_MAX	= 15	// we have only 4 bit space for the NVP types
+    };
+    
+    enum NVPStore {
+        S_OTP	= 0x01,
+        S_FLASH	= 0x02,
+        S_RAM	= 0x04,
+    };
+    
+    enum NVPErrCode {
+        NVP_OK = 0,
+        NVP_NO_FLASH 	= -1,
+        NVP_NO_RAM 		= -2,
+        NVP_NO_STORE 	= -3,
+        NVP_NO_PERM 	= -4,
+        NVP_ERR_NOSPACE	= -5,
+        NVP_ERR_FAIL	= -6,
+        NVP_INVALD_PARM	= -7,
+        NVP_ENOENT		= -0x12345687,
+    };
+
+    /*
+     * Get property protocol version to allow
+     * API differences between multiple versions
+     */
+    int GetVersion(void) { return 100; };
+
+    /*
+     * A simple GetProperty retuns its values as an int or int64
+     * The order should always be S_RAM,S_FLASH, S_OTP
+     */
+    int GetProperty(int key, int defaultValue = 0);
+    int64_t GetProperty64(int key, int64_t defaultValue = 0);
+	/*
+	 * const char *GetProperty: receives a copy of the propery, use free to release it.
+	 */
+    const char *GetProperty(int key, const char *defaultValue = NULL);
+    /*
+     * when a block gets returned the buffer is filled up to the property
+     * or max at the bsize length.
+	 * if the buffer is NULL only the size value will be set
+     */
+    int GetProperty(int key, void  *buffer, int *size);
+
+    /*
+     * SetProperty
+     * It requires to use OpenPropertyStore and finally ClosePropertyStore(true)
+     * to write out all properties.
+	 * Properties are being limited to 256 bytes. (e.g. 255 long strings or 256 bytes blobs)
+     *
+	 * Number properties e.g. 0 or 1, or 123 are highly optimized in storage sizes
+	 * therefore the value is automatically being compressed to a bit or a little
+	 * number to use less flash storage space.
+     */
+    int SetProperty(int key, NVPType ptype, int64_t value, NVPStore store = S_FLASH);
+    int SetProperty(int key, NVPType ptype, const char *value, NVPStore store = S_FLASH);
+    int SetProperty(int key, NVPType ptype, const void *blob, int length, NVPStore store = S_FLASH);
+    
+    int EraseProperty(int key, NVPStore store = S_FLASH);
+	
+	/*
+	 * ReorgProperties is usually not needed because when a property storage is
+	 * full it reorganizes itself to make space for new properties.
+	 */
+	int ReorgProperties(NVPStore store = S_FLASH);
+	/*
+	 * Opens a property store for reading or writing.
+	 */
+    int OpenPropertyStore(bool forWrite = false);
+	/*
+	 * Closes the property store and flushes the data, depending on the
+	 * implementation flush may be not needed, e.g. D21
+	 */
+    int ClosePropertyStore(bool flush = false);
+
+    enum Properties {
+		RTC_AGING_CAL	= 1, // int8_t the RTC aging calibration value
+		ADC_VREF		= 2, // the adc refernce volatge in millivolt
+		HARDWARE_REV	= 3, // the hardware revision of the board
+		CPUID			= 4, // the internal CPU ID
+        
+		LORA_DEVICE_ID	= 10, // uint32_t the LoRa device ID
+		LORA_CODE_ID	= 11, // uint32_t the Code for the RadioShuttle protocol
+		LORA_REMOTE_ID	= 12, // specifies the server address
+		LORA_REMOTE_ID_ALT = 13, // specifies the alternate server address
+		LORA_RADIO_TYPE	= 14,	// 1 = Offline, 3 = Online, 4 = RS_Station_Basic
+		LORA_FREQUENCY 	= 15,	// channel frequency in Hz, e.g. 868100000
+		LORA_BANDWIDTH	= 16,	// channel bandwidth in Hz, e.g. 125000
+		LORA_SPREADING_FACTOR = 17, // e.g. 7
+		LORA_TXPOWER	= 18,	// e.g. 14 for 15 dBm.
+		LORA_FREQUENCY_OFFSET = 19,
+		LORA_APP_PWD	= 20,	// passwords are per app, there are only two placeholders
+		LORA_APP_PWD_ALT = 21,
+
+		LOC_LONGITUDE	= 25,	// a string
+		LOC_LATITUDE 	= 26,	// a string
+		LOC_NAME 		= 27, 	// a string with the location name
+		HOSTNAME 		= 28,	// the device host name
+        
+		WIFI_SSID		= 30,
+		WIFI_PASSWORD	= 31,
+		WIFI_SSID_ALT	= 32,
+		WIFI_PASSWORD_ALT = 33,
+		USE_DHCP		= 34,
+		MAC_ADDR		= 35,
+		NET_IP_ADDR		= 36,
+		NET_IP_ROUTER	= 37,
+		NET_IP_DNS_SERVER = 38,
+        
+		MQTT_SERVER		= 40,	// url mqtt or mqtts://user.password@server:port
+		MQTT_SERVER_ALT	= 41,
+		MQTT_TOPIC_INFO	= 42,
+		MQTT_TOPIC_ALARM= 43,
+		MQTT_TOPIC_CONTROL = 44,
+		MQTT_TOPIC_4	= 45,
+		MQTT_TOPIC_5	= 46,
+		
+		ALARM_STATUS 	= 50,   // alarm on=0, off !0
+		
+		PRIVATE_RANGE_START = 128,
+		PRIVATE_RANGE_END 	= 254,
+		PROPERTIES_EOF		= 255,
+		MAX_PROPERTIES		= 256,
+    };
+
+private:
+    NVPropertyProviderInterface *_ram;
+    NVPropertyProviderInterface *_flash;
+    NVPropertyProviderInterface *_otp;
+    bool _allowWrite;
+    bool _didOpen;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVPropertyProviderInterface.h	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,32 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifndef __NVPROPERTYPROVIDERINTERFACE__
+#define __NVPROPERTYPROVIDERINTERFACE__
+
+class NVPropertyProviderInterface {
+public:
+   	virtual ~NVPropertyProviderInterface() { }
+    
+    virtual int GetProperty(int key) = 0;
+    virtual int64_t GetProperty64(int key) = 0;
+    virtual const char *GetPropertyStr(int key) = 0;
+    virtual int GetPropertyBlob(int key, const void *blob, int *size) = 0;
+    virtual int SetProperty(int key, int64_t value, int type) = 0;
+    virtual int SetPropertyStr(int key, const char *str, int type) = 0;
+    virtual int SetPropertyBlob(int key, const void *blob, int size, int type) = 0;
+    virtual int EraseProperty(int key) = 0;
+    virtual int ReorgProperties(void) = 0;
+    virtual int OpenPropertyStore(bool forWrite = false) = 0;
+    virtual int ClosePropertyStore(bool flush = false) = 0;
+};
+
+#endif // __NVPROPERTYPROVIDERINTERFACE__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_L4Flash.cpp	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,523 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifdef TARGET_STM32L4
+
+#include <mbed.h>
+#include "main.h"
+#include <NVPropertyProviderInterface.h>
+#include <NVProperty_L4Flash.h>
+#include <NVProperty.h>
+
+#if 0
+#define OTP_TEST_IN_RAM // test OTP in RAM
+
+
+NVProperty_L4OTP::NVProperty_L4OTP()
+{
+	_debug = false;
+	_propSize = 1 * 1024; 					// no define in HAL laye
+	_startAddress = (uint8_t *) 0x1FFF7000; // no define in HAL layer
+
+#ifdef OTP_TEST_IN_RAM
+	static uint8_t *savedStart;
+	if (!savedStart) {
+ 		_startAddress = (uint8_t *) malloc(512);
+		memset(_startAddress, 0xff, 512);
+		savedStart = _startAddress;
+	} else {
+		_startAddress = savedStart;
+	}
+#endif
+	
+	_endAddress = _startAddress + (_propSize);
+	_lastEntry = NULL;
+	
+	_FlashInititalize();
+}
+
+
+NVProperty_L4OTP::~NVProperty_L4OTP()
+{
+	_debug = true;
+	_DumpAllEntires();
+}
+
+
+void
+NVProperty_L4OTP::_FlashInititalize(void)
+{
+	_flash_header *fh = (_flash_header *)_startAddress;
+	if (fh->magic == FLASH_PROP_MAGIC && fh->version == FLASH_PROP_VERSION && fh->size == _propSize) {
+		return;
+	}
+	
+	uint8_t *p = _startAddress;
+	for (int i = 0; i < (int)sizeof(_flash_header); i++) {
+		if (*p++ != 0xff)
+			return; // invalid data
+	}
+	
+	_flash_header f;
+	memset(&f, 0, sizeof(f));
+	f.magic = FLASH_PROP_MAGIC;
+	f.version = FLASH_PROP_VERSION;
+	f.size = _propSize;
+	
+	_OTPWrite(_startAddress, &f, sizeof(f));
+}
+
+
+int
+NVProperty_L4OTP::GetProperty(int key)
+{
+    return GetProperty64(key);
+}
+
+
+int64_t
+NVProperty_L4OTP::GetProperty64(int key)
+{
+	_flashEntry *p = _GetFlashEntry(key);
+	if (!p)
+		return NVProperty::NVP_ENOENT;
+
+    int64_t value = 0;
+	
+    switch(p->t.type) {
+		case NVProperty::T_BIT:
+			if (p->t.t_bit)
+				value = 1;
+			else
+				value = 0;
+			break;
+		case NVProperty::T_8BIT:
+			value = p->u.v_8bit;
+			break;
+		case NVProperty::T_16BIT:
+			{
+				int16_t v;
+				memcpy(&v, &p->u.v_16bit, sizeof(p->u.v_16bit));
+				value = v;
+			}
+			break;
+		case NVProperty::T_32BIT:
+			{
+				int32_t v;
+				memcpy(&v, &p->data.v_32bit, sizeof(p->data.v_32bit));
+				value =v;
+			}
+			break;
+		case NVProperty::T_64BIT:
+			memcpy(&value, p->data.v_64bit, sizeof(p->data.v_64bit));
+			break;
+		case NVProperty::T_STR:
+		case NVProperty::T_BLOB:
+			value = p->u.option.d_len;
+			break;
+	}
+    return value;
+}
+
+const char *
+NVProperty_L4OTP::GetPropertyStr(int key)
+{
+	_flashEntry *p = _GetFlashEntry(key);
+	if (!p || p->t.type != NVProperty::T_STR)
+		return NULL;
+    return strdup(p->data.v_str);
+}
+
+int
+NVProperty_L4OTP::GetPropertyBlob(int key, const void *blob, int *size)
+{
+	_flashEntry *p = _GetFlashEntry(key);
+	if (!p || p->t.type != NVProperty::T_BLOB)
+		return NVProperty::NVP_ENOENT;
+	
+	int cplen = std::min(*size, (int)p->u.option.d_len);
+	if (blob)
+		memcpy((void *)blob, p->data.v_blob, cplen);
+	*size = cplen;
+	
+    return NVProperty::NVP_OK;
+}
+
+
+int
+NVProperty_L4OTP::SetProperty(int key, int64_t value, int type)
+{
+	UNUSED(type);
+	uint8_t valbuf[FLASH_ENTRY_MIN_SIZE + sizeof(int64_t)];
+	_flashEntry *p = (_flashEntry *) valbuf;
+	int storeType;
+	
+	if (GetProperty64(key) == value) // no need to save it again.
+	    return NVProperty::NVP_OK;
+	
+	
+	memset(valbuf, 0, sizeof(valbuf));
+	
+	if (value == 0 ||  value == 1)
+		storeType = NVProperty::T_BIT;
+	else if (value >= -128 && value < 128)
+		storeType = NVProperty::T_8BIT;
+	else if (value >= -32768 && value < 32768)
+		storeType = NVProperty::T_16BIT;
+	else if (value > -2147483647 && value < 2147483648)
+		storeType = NVProperty::T_32BIT;
+	else
+		storeType = NVProperty::T_64BIT;
+	
+	p->key = key;
+	p->t.type = storeType;
+
+
+	switch(storeType) {
+		case NVProperty::T_BIT:
+			p->t.t_bit = value;
+			break;
+		case NVProperty::T_8BIT:
+			p->u.v_8bit = value;
+			break;
+		case NVProperty::T_16BIT:
+			p->u.v_16bit = value;
+			break;
+		case NVProperty::T_32BIT:
+			p->u.option.d_len = sizeof(p->data.v_32bit);
+			{
+				int32_t v = value;
+				memcpy(&p->data.v_32bit, &v, sizeof(p->data.v_32bit));
+			}
+			break;
+		case NVProperty::T_64BIT:
+			p->u.option.d_len = sizeof(p->data.v_64bit);
+			memcpy(p->data.v_64bit, &value, sizeof(p->data.v_64bit));
+			break;
+	}
+	int len;
+	if (storeType == NVProperty::T_BIT || storeType == NVProperty::T_8BIT || storeType == NVProperty::T_16BIT || storeType == NVProperty::T_32BIT) {
+		len = FLASH_ENTRY_MIN_SIZE;
+	} else { // 64/STR/BLOB
+		len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+		len += _GetFlashPaddingSize(len);
+	}
+	if ((uint8_t *)_lastEntry + len >= _endAddress) {
+		if (!_FlashReorgEntries(len))
+			return NVProperty::NVP_ERR_NOSPACE;
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, len);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
+
+	// _DumpAllEntires();
+    return NVProperty::NVP_OK;
+}
+
+
+int
+NVProperty_L4OTP::SetPropertyStr(int key, const char *value, int type)
+{
+	if (type != NVProperty::T_STR)
+		return NVProperty::NVP_INVALD_PARM;
+	
+	_flashEntry *p = _GetFlashEntry(key);
+	if (p && p->t.type == NVProperty::T_STR && strcmp(p->data.v_str, value) == 0) {
+		return NVProperty::NVP_OK;
+	}
+
+	int err = NVProperty::NVP_OK;
+	
+	p = new _flashEntry();
+	if (!p)
+		return NVProperty::NVP_ERR_NOSPACE;
+	
+	p->key = key;
+	p->t.type = NVProperty::T_STR;
+	int cplen = std::min(strlen(value), sizeof(p->data.v_str)-1);
+	memcpy(p->data.v_str, value, cplen);
+	p->u.option.d_len = cplen + 1; // zero term
+	
+	int len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+	len += _GetFlashPaddingSize(len);
+
+	if ((uint8_t *)_lastEntry + len >= _endAddress) {
+		if (!_FlashReorgEntries(len)) {
+			err = NVProperty::NVP_ERR_NOSPACE;
+			goto done;
+		}
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, len);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
+
+done:
+	delete[] p;
+	// _DumpAllEntires();
+    return err;
+}
+
+int
+NVProperty_L4OTP::SetPropertyBlob(int key, const void *blob, int size, int type)
+{
+	if (type != NVProperty::T_BLOB)
+		return NVProperty::NVP_INVALD_PARM;
+	
+	_flashEntry *p = _GetFlashEntry(key);
+	if (p && p->t.type == NVProperty::T_BLOB && size == p->u.option.d_len) { // check for duplicate
+		if (memcmp(blob, p->data.v_blob, size) == 0)
+			return NVProperty::NVP_OK;
+	}
+	int err = NVProperty::NVP_OK;
+	
+	p = new _flashEntry();
+	if (!p)
+		return NVProperty::NVP_ERR_NOSPACE;
+	
+	p->key = key;
+	p->t.type = NVProperty::T_BLOB;
+	int cplen = std::min(size, (int)sizeof(p->data.v_blob));
+	p->u.option.d_len = cplen;
+	memcpy(p->data.v_blob, blob, cplen);
+	
+	int len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+	len += _GetFlashPaddingSize(len);
+
+	if ((uint8_t *)_lastEntry + len >= _endAddress) {
+		if (!_FlashReorgEntries(len)) {
+			err = NVProperty::NVP_ERR_NOSPACE;
+			goto done;
+		}
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, len);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
+
+done:
+	delete[] p;
+	// _DumpAllEntires();
+    return err;
+}
+
+int
+NVProperty_L4OTP::EraseProperty(int key)
+{
+	uint8_t valbuf[FLASH_ENTRY_MIN_SIZE];
+	_flashEntry *p = (_flashEntry *) valbuf;
+
+	_flashEntry *op = _GetFlashEntry(key);
+	if (!op)
+		return NVProperty::NVP_ENOENT;
+	if (op->t.deleted)
+		return NVProperty::NVP_OK;
+	
+	memset(valbuf, 0, sizeof(valbuf));
+	p->key = key;
+	p->t.type = op->t.type;
+	p->t.deleted = true;
+	
+	if ((uint8_t *)_lastEntry + FLASH_ENTRY_MIN_SIZE > _endAddress) {
+		if (!_FlashReorgEntries(FLASH_ENTRY_MIN_SIZE))
+			return NVProperty::NVP_ERR_NOSPACE;
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, FLASH_ENTRY_MIN_SIZE);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + FLASH_ENTRY_MIN_SIZE);
+
+	// _DumpAllEntires();
+	return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_L4OTP::ReorgProperties(void)
+{
+    return NVProperty::NVP_OK;
+}
+
+
+int
+NVProperty_L4OTP::OpenPropertyStore(bool forWrite)
+{
+	UNUSED(forWrite);
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_L4OTP::ClosePropertyStore(bool flush)
+{
+    return NVProperty::NVP_OK;
+}
+
+#if 1
+void
+NVProperty_L4OTP::_DumpAllEntires(void)
+{
+	if (!_debug)
+		return;
+	
+	dprintf("------------- DumpAllEntires -------- ");
+
+	int index = 0;
+	_flashEntry *p = (_flashEntry *)(_startAddress + sizeof(_flash_header));
+	while((uint8_t *)p < _endAddress && p->key != NVProperty::PROPERTIES_EOF) {
+
+		int64_t value = 0;
+    	switch(p->t.type) {
+		case NVProperty::T_BIT:
+			if (p->t.t_bit)
+				value = 1;
+			else
+				value = 0;
+			break;
+		case NVProperty::T_8BIT:
+			value = p->u.v_8bit;
+			break;
+		case NVProperty::T_16BIT:
+			{
+				int16_t v;
+				memcpy(&v, &p->u.v_16bit, sizeof(p->u.v_16bit));
+				value = v;
+			}
+			break;
+		case NVProperty::T_32BIT:
+			{
+				int32_t v;
+				memcpy(&v, &p->data.v_32bit, sizeof(p->data.v_32bit));
+				value = v;
+			}
+			break;
+		case NVProperty::T_64BIT:
+			memcpy(&value, p->data.v_64bit, sizeof(p->data.v_64bit));
+			break;
+		case NVProperty::T_STR:
+		case NVProperty::T_BLOB:
+			value = p->u.option.d_len;
+			break;
+		}
+		index++;
+		if (p->t.deleted) {
+			dprintf("Dump[%.2d] Key: %d Type: %d deleted(%d)", index, p->key, p->t.type, p->t.deleted);
+
+		} else if (p->t.type == NVProperty::T_STR) {
+			dprintf("Dump[%.2d] Key: %d Type: %d value: %s", index, p->key, p->t.type, p->data.v_str);
+		} else if (p->t.type == NVProperty::T_BLOB) {
+			dprintf("Dump[%.2d] Key: %d Type: %d len: %d", index, p->key, p->t.type, p->u.option.d_len);
+			dump("Blob",  p->data.v_str, p->u.option.d_len);
+		} else {
+			if (p->t.type == NVProperty::T_64BIT) {
+				dprintf("Dump[%.2d] Key: %d Type: %d value: %lld (0x%llx)", index, p->key, p->t.type, value, value);
+			} else {
+				dprintf("Dump[%.2d] Key: %d Type: %d value: %ld (0x%x)", index, p->key, p->t.type, (int32_t)value, (unsigned int)value);
+			}
+		}
+		
+		p = (_flashEntry *)((uint8_t *)p + _GetFlashEntryLen(p));
+	}
+	int freebytes = _endAddress -(uint8_t *)_lastEntry;
+	if (_lastEntry)
+		dprintf("------ %d bytes free -------", freebytes);
+}
+#endif
+
+NVProperty_L4OTP::_flashEntry *
+NVProperty_L4OTP::_GetFlashEntry(int key, uint8_t *start)
+{
+	_flashEntry *p;
+
+	if (start)
+		p = (_flashEntry *)start;
+	else
+		p = (_flashEntry *)(_startAddress + sizeof(_flash_header));
+	_flashEntry *lastP = NULL;
+	while(true) {
+		if ((uint8_t*)p >= _endAddress || p->key == NVProperty::PROPERTIES_EOF) {
+			if ((uint8_t*)p <= _endAddress)
+				_lastEntry = p;
+			if (!lastP || lastP->t.deleted)
+				return NULL;
+			break;
+		}
+		if (p->key == key)
+			lastP = p;
+
+		p = (_flashEntry *)((uint8_t *)p + _GetFlashEntryLen(p));
+	}
+	return lastP;
+}
+
+
+int
+NVProperty_L4OTP::_GetFlashEntryLen(_flashEntry *p)
+{
+	int len = 0;
+	
+	switch(p->t.type) {
+		case NVProperty::T_64BIT:
+		case NVProperty::T_STR:
+		case NVProperty::T_BLOB:
+			len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+			len += _GetFlashPaddingSize(len);
+			break;
+		default:
+			len = FLASH_ENTRY_MIN_SIZE;
+	}
+	return len;
+}
+
+int
+NVProperty_L4OTP::_GetFlashPaddingSize(int len)
+{
+	int remain = len % FLASH_PADDING_SIZE;
+	
+	if (remain == 0)
+		return 0;
+	
+	return (len + FLASH_PADDING_SIZE - remain) - len;
+}
+
+
+int
+NVProperty_L4OTP::_FlashReorgEntries(int minRequiredSpace)
+{
+	return 0; // no reorg on OTP
+}
+
+
+void
+NVProperty_L4OTP::_OTPWrite(uint8_t *address, const void *d, size_t length)
+{
+	//FlashIAP f;
+	//f.init();
+#ifdef OTP_TEST_IN_RAM
+	memcpy(address, d, length);
+#else
+	uint8_t *data = (uint8_t *)d;
+	uint32_t addr = (uint32_t)address;
+	volatile uint64_t data64;
+
+	HAL_FLASH_Unlock();
+	while (length > 0) {
+		for (uint8_t i = 0; i < 8; i++) {
+			*(((uint8_t *) &data64) + i) = *(data + i);
+        }
+	    int err = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, data64);
+		addr += 8;
+		data += 8;
+		length -= 8;
+	}
+    HAL_FLASH_Lock();
+#endif
+}
+
+#endif
+
+
+#endif // TARGET_STM32L4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_L4Flash.h	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,96 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifndef __NVPROPERTY_L4FLASH__
+#define __NVPROPERTY_L4FLASH__
+
+class NVProperty_L4Flash : public NVPropertyProviderInterface {
+public:
+	NVProperty_L4Flash(int propSizekB, bool erase);
+	~NVProperty_L4Flash();
+	
+    virtual int GetProperty(int key);
+    virtual int64_t GetProperty64(int key);
+    virtual int GetPropertyBlob(int key, const void *blob, int *size);
+    virtual const char *GetPropertyStr(int key);
+    virtual int SetProperty(int key, int64_t value, int type);
+    virtual int SetPropertyStr(int key, const char *value, int type);
+    virtual int SetPropertyBlob(int key, const void *blob, int size, int type);
+    virtual int EraseProperty(int key);
+    virtual int ReorgProperties(void);
+    virtual int OpenPropertyStore(bool forWrite = false);
+    virtual int ClosePropertyStore(bool flush = false);
+	
+private:
+	void _FlashInititalize(bool force = false);
+	void _FlashEraseRow(int startRow, int count = 1);
+	void _FlashWrite(uint8_t *address, const void *data, size_t length);
+	bool _FlashIsCleared(uint8_t *address, int len);
+	void _FlashWritePage(int page, int offset, uint8_t *data, int length);
+
+	struct _flash_header {
+		uint32_t magic;
+		uint16_t version;
+		uint16_t sizeKB;
+	};
+	
+	static const int FLASH_ENTRY_HEADER			= 4;
+	static const int FLASH_ENTRY_HEADER_SHORT	= 2;
+	static const int MAX_DATA_ENTRY				= 256;
+	
+	struct _flashEntry {
+		uint8_t key;	// Property key
+		struct {
+			uint8_t deleted	: 1; // this key has been deleted
+			uint8_t t_bit  	: 1; // contains the bool value
+			uint8_t reserv1	: 1; //
+			uint8_t reserv2	: 1; //
+			uint8_t type   	: 4; // NVPType
+		} t;
+		union {
+			int16_t v_16bit;
+			int8_t	v_8bit;
+			struct {
+				uint8_t d_len;				 // data length
+				uint8_t f_reserv1  		: 8;
+			} option;
+		} u;
+		union {
+			int32_t v_32bit;
+			int32_t v_64bit[2];	// use use 2 x 32-bit to avoid 64-bit struct padding
+			char v_str[MAX_DATA_ENTRY];
+			uint8_t v_blob[MAX_DATA_ENTRY];
+		} data;
+	};
+	
+	_flashEntry * _GetFlashEntry(int key, uint8_t *start = NULL);
+	int _GetFlashEntryLen(_flashEntry *k);
+	int _GetFlashPaddingSize(int len);
+	_flashEntry *_lastEntry;
+	void _DumpAllEntires(void);
+	int _FlashReorgEntries(int minRequiredSpace);
+	bool _debug;
+	int _propSizekB;
+	int _pageSize;
+	int _numPages;
+	int _rowSize;
+	uint8_t *_startAddress;
+	uint8_t *_endAddress;
+
+	static const int FLASH_PROP_MAGIC = 0x4c6f5261; // "LORA"
+	static const int FLASH_PROP_VERSION = 3;
+	static const int _bootlLoaderSize = 8192;
+	static const int _lockRegions = 16;	// d21 lock regions are always 16 for the entire ram
+
+
+};
+
+#endif // __NVPROPERTY_L4FLASH__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_L4OTP.cpp	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,529 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifdef TARGET_STM32L4
+
+#include <mbed.h>
+#include "main.h"
+#include <NVPropertyProviderInterface.h>
+#include <NVProperty_L4OTP.h>
+#include <NVProperty.h>
+
+
+#if 0	// sample test code for a man app.
+	{
+	NVProperty p;
+	
+	p.OpenPropertyStore(true);
+	dprintf("OTP--1: %d", p.GetProperty(p.CPUID, -1));
+	p.SetProperty(p.CPUID, p.T_32BIT, 123, p.S_OTP);
+	dprintf("OTP123: %d", p.GetProperty(p.CPUID, 0));
+	p.SetProperty(p.CPUID, p.T_32BIT, 0x12345678, p.S_OTP);
+	dprintf("OTP0x12345678: %x", p.GetProperty(p.CPUID, 0));
+	p.EraseProperty(p.CPUID, p.S_OTP);
+	dprintf("OTP:-2 %d", p.GetProperty(p.CPUID, -2));
+	dprintf("OTP: Host %s", p.GetProperty(p.HOSTNAME, "MyHost"));
+	p.SetProperty(p.HOSTNAME, p.T_STR, "Wunstorf", p.S_OTP);
+	dprintf("OTP: Host %s", p.GetProperty(p.HOSTNAME, "MyHost"));
+	p.SetProperty(p.CPUID, p.T_32BIT, 9876, p.S_OTP);
+	dprintf("OTP9876: %d", p.GetProperty(p.CPUID, 0));
+	dprintf("OTP: Host %s", p.GetProperty(p.HOSTNAME, "MyHost"));
+	
+	}
+#endif
+
+// #define OTP_TEST_IN_RAM // test OTP in RAM
+
+
+NVProperty_L4OTP::NVProperty_L4OTP()
+{
+	_debug = false;
+	_propSize = 1 * 1024; 					// no define in HAL laye
+	_startAddress = (uint8_t *) 0x1FFF7000; // no define in HAL layer
+
+#ifdef OTP_TEST_IN_RAM
+	static uint8_t *savedStart;
+	if (!savedStart) {
+ 		_startAddress = (uint8_t *) malloc(512);
+		memset(_startAddress, 0xff, 512);
+		savedStart = _startAddress;
+	} else {
+		_startAddress = savedStart;
+	}
+#endif
+	
+	_endAddress = _startAddress + (_propSize);
+	_lastEntry = NULL;
+	
+	_FlashInititalize();
+}
+
+
+NVProperty_L4OTP::~NVProperty_L4OTP()
+{
+#ifdef OTP_TEST_IN_RAM
+	_debug = true;
+	_DumpAllEntires();
+	wait_ms(100);
+	dump("_startAddress", _startAddress, 100);
+#endif
+}
+
+
+void
+NVProperty_L4OTP::_FlashInititalize(void)
+{
+	_flash_header *fh = (_flash_header *)_startAddress;
+	if (fh->magic == FLASH_PROP_MAGIC && fh->version == FLASH_PROP_VERSION && fh->size == _propSize) {
+		return;
+	}
+	
+	uint8_t *p = _startAddress;
+	for (int i = 0; i < (int)sizeof(_flash_header); i++) {
+		if (*p++ != 0xff)
+			return; // invalid data
+	}
+	
+	_flash_header f;
+	memset(&f, 0, sizeof(f));
+	f.magic = FLASH_PROP_MAGIC;
+	f.version = FLASH_PROP_VERSION;
+	f.size = _propSize;
+	
+	_OTPWrite(_startAddress, &f, sizeof(f));
+}
+
+
+int
+NVProperty_L4OTP::GetProperty(int key)
+{
+    return GetProperty64(key);
+}
+
+
+int64_t
+NVProperty_L4OTP::GetProperty64(int key)
+{
+	_flashEntry *p = _GetFlashEntry(key);
+	if (!p)
+		return NVProperty::NVP_ENOENT;
+
+    int64_t value = 0;
+	
+    switch(p->t.type) {
+		case NVProperty::T_BIT:
+			if (p->t.t_bit)
+				value = 1;
+			else
+				value = 0;
+			break;
+		case NVProperty::T_8BIT:
+			value = p->u.v_8bit;
+			break;
+		case NVProperty::T_16BIT:
+			{
+				int16_t v;
+				memcpy(&v, &p->u.v_16bit, sizeof(p->u.v_16bit));
+				value = v;
+			}
+			break;
+		case NVProperty::T_32BIT:
+			{
+				int32_t v;
+				memcpy(&v, &p->data.v_32bit, sizeof(p->data.v_32bit));
+				value = v;
+			}
+			break;
+		case NVProperty::T_64BIT:
+			memcpy(&value, p->data.v_64bit, sizeof(p->data.v_64bit));
+			break;
+		case NVProperty::T_STR:
+		case NVProperty::T_BLOB:
+			value = p->u.option.d_len;
+			break;
+	}
+    return value;
+}
+
+const char *
+NVProperty_L4OTP::GetPropertyStr(int key)
+{
+	_flashEntry *p = _GetFlashEntry(key);
+	if (!p || p->t.type != NVProperty::T_STR)
+		return NULL;
+    return strdup(p->data.v_str);
+}
+
+int
+NVProperty_L4OTP::GetPropertyBlob(int key, const void *blob, int *size)
+{
+	_flashEntry *p = _GetFlashEntry(key);
+	if (!p || p->t.type != NVProperty::T_BLOB)
+		return NVProperty::NVP_ENOENT;
+	
+	int cplen = std::min(*size, (int)p->u.option.d_len);
+	if (blob)
+		memcpy((void *)blob, p->data.v_blob, cplen);
+	*size = cplen;
+	
+    return NVProperty::NVP_OK;
+}
+
+
+int
+NVProperty_L4OTP::SetProperty(int key, int64_t value, int type)
+{
+	UNUSED(type);
+	uint8_t valbuf[FLASH_ENTRY_MIN_SIZE + sizeof(int64_t)];
+	_flashEntry *p = (_flashEntry *) valbuf;
+	int storeType;
+	
+	if (GetProperty64(key) == value) // no need to save it again.
+	    return NVProperty::NVP_OK;
+	
+	memset(valbuf, 0, sizeof(valbuf));
+	
+	if (value == 0 ||  value == 1)
+		storeType = NVProperty::T_BIT;
+	else if (value >= -128 && value < 128)
+		storeType = NVProperty::T_8BIT;
+	else if (value >= -32768 && value < 32768)
+		storeType = NVProperty::T_16BIT;
+	else if (value > -2147483647LL && value < 2147483648LL)
+		storeType = NVProperty::T_32BIT;
+	else
+		storeType = NVProperty::T_64BIT;
+	
+	p->key = key;
+	p->t.type = storeType;
+
+
+	switch(storeType) {
+		case NVProperty::T_BIT:
+			p->t.t_bit = value;
+			break;
+		case NVProperty::T_8BIT:
+			p->u.v_8bit = value;
+			break;
+		case NVProperty::T_16BIT:
+			p->u.v_16bit = value;
+			break;
+		case NVProperty::T_32BIT:
+			p->u.option.d_len = sizeof(p->data.v_32bit);
+			{
+				int32_t v = value;
+				memcpy(&p->data.v_32bit, &v, sizeof(p->data.v_32bit));
+			}
+			break;
+		case NVProperty::T_64BIT:
+			p->u.option.d_len = sizeof(p->data.v_64bit);
+			memcpy(p->data.v_64bit, &value, sizeof(p->data.v_64bit));
+			break;
+	}
+	int len;
+	if (storeType == NVProperty::T_BIT || storeType == NVProperty::T_8BIT || storeType == NVProperty::T_16BIT || storeType == NVProperty::T_32BIT) {
+		len = FLASH_ENTRY_MIN_SIZE;
+	} else { // 64/STR/BLOB
+		len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+		len += _GetFlashPaddingSize(len);
+	}
+	if ((uint8_t *)_lastEntry + len >= _endAddress) {
+		if (!_FlashReorgEntries(len))
+			return NVProperty::NVP_ERR_NOSPACE;
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, len);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
+
+	// _DumpAllEntires();
+    return NVProperty::NVP_OK;
+}
+
+
+int
+NVProperty_L4OTP::SetPropertyStr(int key, const char *value, int type)
+{
+	if (type != NVProperty::T_STR)
+		return NVProperty::NVP_INVALD_PARM;
+	
+	_flashEntry *p = _GetFlashEntry(key);
+	if (p && p->t.type == NVProperty::T_STR && strcmp(p->data.v_str, value) == 0) {
+		return NVProperty::NVP_OK;
+	}
+
+	int err = NVProperty::NVP_OK;
+	
+	p = new _flashEntry();
+	if (!p)
+		return NVProperty::NVP_ERR_NOSPACE;
+	
+	p->key = key;
+	p->t.type = NVProperty::T_STR;
+	int cplen = std::min(strlen(value), sizeof(p->data.v_str)-1);
+	memcpy(p->data.v_str, value, cplen);
+	p->u.option.d_len = cplen + 1; // zero term
+	
+	int len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+	len += _GetFlashPaddingSize(len);
+
+	if ((uint8_t *)_lastEntry + len >= _endAddress) {
+		if (!_FlashReorgEntries(len)) {
+			err = NVProperty::NVP_ERR_NOSPACE;
+			goto done;
+		}
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, len);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
+
+done:
+	delete[] p;
+	// _DumpAllEntires();
+    return err;
+}
+
+int
+NVProperty_L4OTP::SetPropertyBlob(int key, const void *blob, int size, int type)
+{
+	if (type != NVProperty::T_BLOB)
+		return NVProperty::NVP_INVALD_PARM;
+	
+	_flashEntry *p = _GetFlashEntry(key);
+	if (p && p->t.type == NVProperty::T_BLOB && size == p->u.option.d_len) { // check for duplicate
+		if (memcmp(blob, p->data.v_blob, size) == 0)
+			return NVProperty::NVP_OK;
+	}
+	int err = NVProperty::NVP_OK;
+	
+	p = new _flashEntry();
+	if (!p)
+		return NVProperty::NVP_ERR_NOSPACE;
+	
+	p->key = key;
+	p->t.type = NVProperty::T_BLOB;
+	int cplen = std::min(size, (int)sizeof(p->data.v_blob));
+	p->u.option.d_len = cplen;
+	memcpy(p->data.v_blob, blob, cplen);
+	
+	int len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+	len += _GetFlashPaddingSize(len);
+
+	if ((uint8_t *)_lastEntry + len >= _endAddress) {
+		if (!_FlashReorgEntries(len)) {
+			err = NVProperty::NVP_ERR_NOSPACE;
+			goto done;
+		}
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, len);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
+
+done:
+	delete[] p;
+	// _DumpAllEntires();
+    return err;
+}
+
+int
+NVProperty_L4OTP::EraseProperty(int key)
+{
+	uint8_t valbuf[FLASH_ENTRY_MIN_SIZE];
+	_flashEntry *p = (_flashEntry *) valbuf;
+
+	_flashEntry *op = _GetFlashEntry(key);
+	if (!op)
+		return NVProperty::NVP_ENOENT;
+	if (op->t.deleted)
+		return NVProperty::NVP_OK;
+	
+	memset(valbuf, 0, sizeof(valbuf));
+	p->key = key;
+	p->t.type = op->t.type;
+	p->t.deleted = true;
+	
+	if ((uint8_t *)_lastEntry + FLASH_ENTRY_MIN_SIZE > _endAddress) {
+		if (!_FlashReorgEntries(FLASH_ENTRY_MIN_SIZE))
+			return NVProperty::NVP_ERR_NOSPACE;
+	}
+
+	_OTPWrite((uint8_t *)_lastEntry, p, FLASH_ENTRY_MIN_SIZE);
+	_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + FLASH_ENTRY_MIN_SIZE);
+
+	// _DumpAllEntires();
+	return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_L4OTP::ReorgProperties(void)
+{
+    return NVProperty::NVP_OK;
+}
+
+
+int
+NVProperty_L4OTP::OpenPropertyStore(bool forWrite)
+{
+	UNUSED(forWrite);
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_L4OTP::ClosePropertyStore(bool flush)
+{
+    return NVProperty::NVP_OK;
+}
+
+#if 1
+void
+NVProperty_L4OTP::_DumpAllEntires(void)
+{
+	if (!_debug)
+		return;
+	
+	dprintf("------------- DumpAllEntires -------- ");
+
+	int index = 0;
+	_flashEntry *p = (_flashEntry *)(_startAddress + sizeof(_flash_header));
+	while((uint8_t *)p < _endAddress && p->key != NVProperty::PROPERTIES_EOF) {
+
+		int64_t value = 0;
+    	switch(p->t.type) {
+		case NVProperty::T_BIT:
+			if (p->t.t_bit)
+				value = 1;
+			else
+				value = 0;
+			break;
+		case NVProperty::T_8BIT:
+			value = p->u.v_8bit;
+			break;
+		case NVProperty::T_16BIT:
+			{
+				int16_t v;
+				memcpy(&v, &p->u.v_16bit, sizeof(p->u.v_16bit));
+				value = v;
+			}
+			break;
+		case NVProperty::T_32BIT:
+			{
+				int32_t v;
+				memcpy(&v, &p->data.v_32bit, sizeof(p->data.v_32bit));
+				value = v;
+			}
+			break;
+		case NVProperty::T_64BIT:
+			memcpy(&value, p->data.v_64bit, sizeof(p->data.v_64bit));
+			break;
+		case NVProperty::T_STR:
+		case NVProperty::T_BLOB:
+			value = p->u.option.d_len;
+			break;
+		}
+		index++;
+		if (p->t.deleted) {
+			dprintf("Dump[%.2d] Key: %d Type: %d deleted(%d)", index, p->key, p->t.type, p->t.deleted);
+
+		} else if (p->t.type == NVProperty::T_STR) {
+			dprintf("Dump[%.2d] Key: %d Type: %d value: %s", index, p->key, p->t.type, p->data.v_str);
+		} else if (p->t.type == NVProperty::T_BLOB) {
+			dprintf("Dump[%.2d] Key: %d Type: %d len: %d", index, p->key, p->t.type, p->u.option.d_len);
+			dump("Blob",  p->data.v_str, p->u.option.d_len);
+		} else {
+			if (p->t.type == NVProperty::T_64BIT) {
+				dprintf("Dump[%.2d] Key: %d Type: %d value: %lld (0x%llx)", index, p->key, p->t.type, value, value);
+			} else {
+				dprintf("Dump[%.2d] Key: %d Type: %d value: %ld (0x%x)", index, p->key, p->t.type, (int32_t)value, (unsigned int)value);
+			}
+		}
+		
+		p = (_flashEntry *)((uint8_t *)p + _GetFlashEntryLen(p));
+	}
+	int freebytes = _endAddress -(uint8_t *)_lastEntry;
+	if (_lastEntry)
+		dprintf("------ %d bytes free -------", freebytes);
+}
+#endif
+
+NVProperty_L4OTP::_flashEntry *
+NVProperty_L4OTP::_GetFlashEntry(int key, uint8_t *start)
+{
+	_flashEntry *p;
+
+	if (start)
+		p = (_flashEntry *)start;
+	else
+		p = (_flashEntry *)(_startAddress + sizeof(_flash_header));
+	_flashEntry *lastP = NULL;
+	while(true) {
+		if ((uint8_t*)p >= _endAddress || p->key == NVProperty::PROPERTIES_EOF) {
+			if ((uint8_t*)p <= _endAddress)
+				_lastEntry = p;
+			if (!lastP || lastP->t.deleted)
+				return NULL;
+			break;
+		}
+		if (p->key == key)
+			lastP = p;
+
+		p = (_flashEntry *)((uint8_t *)p + _GetFlashEntryLen(p));
+	}
+	return lastP;
+}
+
+
+int
+NVProperty_L4OTP::_GetFlashEntryLen(_flashEntry *p)
+{
+	int len = 0;
+	
+	switch(p->t.type) {
+		case NVProperty::T_64BIT:
+		case NVProperty::T_STR:
+		case NVProperty::T_BLOB:
+			len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
+			len += _GetFlashPaddingSize(len);
+			break;
+		default:
+			len = FLASH_ENTRY_MIN_SIZE;
+	}
+	return len;
+}
+
+int
+NVProperty_L4OTP::_GetFlashPaddingSize(int len)
+{
+	int remain = len % FLASH_PADDING_SIZE;
+	
+	if (remain == 0)
+		return 0;
+	
+	return (len + FLASH_PADDING_SIZE - remain) - len;
+}
+
+
+int
+NVProperty_L4OTP::_FlashReorgEntries(int minRequiredSpace)
+{
+	return 0; // no reorg on OTP
+}
+
+
+void
+NVProperty_L4OTP::_OTPWrite(uint8_t *address, const void *d, size_t length)
+{
+#ifdef OTP_TEST_IN_RAM
+	memcpy(address, d, length);
+#else
+	OTPWrite(address, d, length);
+#endif
+}
+
+#endif // TARGET_STM32L4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_L4OTP.h	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,87 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifndef __NVPROPERTY_L4OTP__
+#define __NVPROPERTY_L4OTP__
+
+class NVProperty_L4OTP : public NVPropertyProviderInterface {
+public:
+	NVProperty_L4OTP();
+	~NVProperty_L4OTP();
+
+    virtual int GetProperty(int key);
+    virtual int64_t GetProperty64(int key);
+    virtual int GetPropertyBlob(int key, const void *blob, int *size);
+    virtual const char *GetPropertyStr(int key);
+    virtual int SetProperty(int key, int64_t value, int type);
+    virtual int SetPropertyStr(int key, const char *value, int type);
+    virtual int SetPropertyBlob(int key, const void *blob, int size, int type);
+    virtual int EraseProperty(int key);
+    virtual int ReorgProperties(void);
+    virtual int OpenPropertyStore(bool forWrite = false);
+    virtual int ClosePropertyStore(bool flush = false);
+
+private:
+	void _FlashInititalize(void);
+	void _OTPWrite(uint8_t *address, const void *data, size_t length);
+
+	struct _flash_header {
+		uint32_t magic;
+		uint16_t version;
+		uint16_t size;
+	};
+
+	static const int FLASH_ENTRY_MIN_SIZE		= 8;
+	static const int MAX_DATA_ENTRY				= 256-FLASH_ENTRY_MIN_SIZE;
+	static const int FLASH_PADDING_SIZE			= 8; // writes sizes and alignment must be multiple of 64-bit,
+	
+	struct _flashEntry {
+		uint8_t key;	// Property key
+		struct {
+			uint8_t deleted	: 1; // this key has been deleted
+			uint8_t t_bit  	: 1; // contains the bool value
+			uint8_t reserv1	: 1; //
+			uint8_t reserv2	: 1; //
+			uint8_t type   	: 4; // NVPType
+		} t;
+		union {
+			int16_t v_16bit;
+			int8_t	v_8bit;
+			struct {
+				uint8_t d_len;				 // data length
+				uint8_t f_reserv1  		: 8;
+			} option;
+		} u;
+		union {
+			int32_t v_32bit;
+			int32_t v_64bit[2];	// use use 2 x 32-bit to avoid 64-bit struct padding
+			char v_str[MAX_DATA_ENTRY];
+			uint8_t v_blob[MAX_DATA_ENTRY];
+		} data;
+	};
+	
+	_flashEntry * _GetFlashEntry(int key, uint8_t *start = NULL);
+	void _DumpAllEntires(void);
+	int _GetFlashEntryLen(_flashEntry *k);
+	int _GetFlashPaddingSize(int len);
+	int _FlashReorgEntries(int minRequiredSpace);
+
+	_flashEntry *_lastEntry;
+	uint8_t *_startAddress;
+	uint8_t *_endAddress;
+	bool _debug;
+	int _propSize;
+
+	static const int FLASH_PROP_MAGIC = 0x4c4f5450; // "LOTP"
+	static const int FLASH_PROP_VERSION = 1;
+};
+
+#endif // __NVPROPERTY_L4OTP__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_SRAM.cpp	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,222 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <NVPropertyProviderInterface.h>
+#include <NVProperty.h>
+#include <NVProperty_SRAM.h>
+
+NVProperty_SRAM::NVProperty_SRAM()
+{
+    
+}
+
+NVProperty_SRAM::~NVProperty_SRAM()
+{
+    std::map<int, PropertyEntry>::iterator re;
+    for(re = _props.begin(); re != _props.end(); re++) {
+        if (re->second.type == NVProperty::T_STR)
+            free(re->second.str);
+        if (re->second.type == NVProperty::T_BLOB)
+            delete[] (char *)re->second.data;
+    }
+    
+    _props.clear();
+}
+
+int
+NVProperty_SRAM::GetProperty(int key)
+{
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it != _props.end()) {
+        switch (it->second.type) {
+            case NVProperty::T_STR:
+                return NVProperty::NVP_ENOENT;
+    	        break;
+            case NVProperty::T_BLOB:
+                return NVProperty::NVP_ENOENT;
+                break;
+            default:
+                return it->second.val32;
+        }
+    }
+    return NVProperty::NVP_ENOENT;
+};
+
+
+int64_t
+NVProperty_SRAM::GetProperty64(int key)
+{
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it != _props.end()) {
+        switch (it->second.type) {
+            case NVProperty::T_STR:
+                return NVProperty::NVP_ENOENT;
+                break;
+            case NVProperty::T_BLOB:
+                return NVProperty::NVP_ENOENT;
+                break;
+            default:
+                return it->second.val64;
+        }
+    }
+    return NVProperty::NVP_ENOENT;
+}
+
+const char *
+NVProperty_SRAM::GetPropertyStr(int key)
+{
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it != _props.end()) {
+        if (it->second.type == NVProperty::T_STR) {
+            return (const char *)it->second.str;
+        }
+    }
+    return NULL;
+}
+
+int
+
+NVProperty_SRAM::GetPropertyBlob(int key, const void *blob, int *size)
+{
+    if (!blob || *size <= 0)
+        return NVProperty::NVP_INVALD_PARM;
+    
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it != _props.end()) {
+        switch (it->second.type) {
+            case NVProperty::T_BLOB:
+                *size = std::min(*size, (int)it->second.size);
+				if (blob)
+                	memcpy((void *)blob, it->second.data, *size);
+                return *size;
+                break;
+            default:
+                break;
+        }
+    }
+    return NVProperty::NVP_ENOENT;
+}
+
+
+int
+NVProperty_SRAM::SetProperty(int key, int64_t value, int type)
+{
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it != _props.end()) {
+        it->second.val32 = value;
+        return 0;
+    }
+    
+    struct PropertyEntry r;
+    memset(&r, 0, sizeof(r));
+    r.key = key;
+    r.type = type;
+    if (type <= NVProperty::T_32BIT) {
+        r.size = sizeof(r.val32);
+        r.val32 = value;
+    } else if (type == NVProperty::T_64BIT) {
+        r.size = sizeof(r.val64);
+        r.val64 = value;
+    }
+    
+    _props.insert(std::pair<int,PropertyEntry> (key, r));
+    
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::SetPropertyStr(int key, const char *str, int type)
+{
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it != _props.end()) {
+        if (it->second.str)
+            free(it->second.str);
+        it->second.str = strdup(str);
+        it->second.size = strlen(str)+1;
+        return NVProperty::NVP_OK;
+    }
+    
+    struct PropertyEntry r;
+    memset(&r, 0, sizeof(r));
+    r.key = key;
+    r.type = type;
+    r.size = strlen(str)+1;
+    r.str = strdup(str);
+    
+    _props.insert(std::pair<int,PropertyEntry> (key, r));
+        
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::SetPropertyBlob(int key, const void *blob, int size, int type)
+{
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it != _props.end()) {
+        if (it->second.data)
+        	delete[] (char *)it->second.data;
+        it->second.size = size;
+        it->second.data = new char[size];
+        memcpy(it->second.data, blob, size);
+
+        return NVProperty::NVP_OK;
+    }
+    
+    struct PropertyEntry r;
+    memset(&r, 0, sizeof(r));
+    r.key = key;
+    r.type = type;
+    r.size = size;
+    r.data = new char[size];
+    memcpy(r.data, blob, size);
+    _props.insert(std::pair<int,PropertyEntry> (key, r));
+    
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::EraseProperty(int key)
+{
+    std::map<int, PropertyEntry>::iterator it = _props.find(key);
+    if(it == _props.end()) {
+        return NVProperty::NVP_ENOENT;
+    }
+    if (it->second.type == NVProperty::T_STR)
+        free((char *)it->second.data);
+    if (it->second.type == NVProperty::T_BLOB)
+        delete[] (char *)it->second.data;
+
+    _props.erase(it->first);
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::ReorgProperties(void)
+{
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::OpenPropertyStore(bool forWrite)
+{
+	UNUSED(forWrite);
+    return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::ClosePropertyStore(bool flush)
+{
+	UNUSED(flush);
+    return NVProperty::NVP_OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_SRAM.h	Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,55 @@
+/*
+ * This is an unpublished work copyright
+ * (c) 2019 Helmut Tschemernjak
+ * 30826 Garbsen (Hannover) Germany
+ *
+ *
+ * Use is granted to registered RadioShuttle licensees only.
+ * Licensees must own a valid serial number and product code.
+ * Details see: www.radioshuttle.de
+ */
+
+#ifndef __NVSProperty_SRAM__
+#define __NVSProperty_SRAM__
+
+
+#ifdef ARDUINO
+#undef min
+#undef max
+#undef map
+#endif
+#include <map>
+
+
+class NVProperty_SRAM : public NVPropertyProviderInterface {
+public:
+    NVProperty_SRAM();
+    ~NVProperty_SRAM();
+    virtual int GetProperty(int key);
+    virtual int64_t GetProperty64(int key);
+    virtual const char *GetPropertyStr(int key);
+    virtual int GetPropertyBlob(int key, const void *blob, int *size);
+    virtual int SetProperty(int key, int64_t value, int type);
+    virtual int SetPropertyStr(int key, const char *value, int type);
+    virtual int SetPropertyBlob(int key, const void *blob, int size, int type);
+    virtual int EraseProperty(int key);
+    virtual int ReorgProperties(void);
+    virtual int OpenPropertyStore(bool forWrite = false);
+    virtual int ClosePropertyStore(bool flush = false);
+
+private:
+    struct PropertyEntry {
+        int key;
+        uint8_t type;
+        uint8_t size;
+        union {
+            int val32;
+            int64_t val64;
+            void *data;
+            char *str;
+        };
+    };
+    std::map<int, PropertyEntry> _props;
+};
+
+#endif // __NVSProperty_SRAM__