Saving Arbitrary Data in PS Keys

Background

This code example has a related User's Guide, here: UG103.7: Non-Volatile Data Storage Fundamentals

Description

This document describes how to save different data types and data structures in Persistent Storage (PS Store). The PS Store allows you to save byte arrays in flash. Any variables (integers, floats, structures, and so on.) are stored in memory as byte arrays. For example, a float requires four bytes to store the value.

The following code snippets implement a pair of helper functions that allow you to store basically any data in a PS key.

static uint16_t ps_save_object(uint16_t key, void *pValue, uint8_t size)
{
    struct gecko_msg_flash_ps_save_rsp_t *pResp = gecko_cmd_flash_ps_save(key, size, pValue);

    return (pResp->result);
}
static uint16_t ps_load_object(uint16_t key, void *pValue, uint8_t expected_size)
{
  struct gecko_msg_flash_ps_load_rsp_t *pResp = gecko_cmd_flash_ps_load(key);

  if (pResp->result == 0) {
    memcpy(pValue, pResp->value.data, pResp->value.len);

    // Sanity check: Length of data stored in PS key must match the expected value
    if (expected_size != pResp->value.len) {
      return (bg_err_unspecified);
    }
  }
  return (pResp->result);
}

First parameter is the PS key (from range 0x4000...0x407F). The second parameter is a pointer to the data object. The pointer is of type void so that the same code can be applied to any type of variables easily. The last parameter defines the size of the object. You can use sizeof() when calling these and use this solution easily in your code.

Here's an example how to store and load a float variable in a PS key:

float F;

// Store the value:
ps_save_object(0x4000, &F, sizeof(F));

// Load the value of F from PS:

if (ps_load_object(0x4000, &F, sizeof(F)) == 0) {
   // Value was loaded OK
} else {
  // Value not OK (either not stored in PS or length of the data does not match the expected value)
}

The same functions can be used for storing and loading the other fixed size objects, too. Assume you have defined a structure that bundles the Bluetooth address and name of a remote device into one variable and you want to save the whole structure in a PS key.

typedef struct
{
  bd_addr addr;
  char name[16];
}remote_device;

...

remote_device last_dev;

// Save the whole struct in PS, using key 0x4001:
ps_save_object(0x4001, &last_dev, sizeof(last_dev));

Notes:

Setting up

  1. Create a new SoC-Empty for your device that supports PS Store (Series 1, e.g., BG13)

  2. Replace the existing app.c with the one attached to this article.

  3. Change DEBUG_LEVEL to 1 in app.h to enable printing logs.

  4. Build and flash the program to your device.

Usage

The attached code example uses the helper functions above to save and load 5 different types of variables to/from the PS Store: unsigned integers, integers, floats, arrays, and structures. It is built on top of the basic SoC - Empty project and prints out the loaded values. If a PS key doesn't exist, that key will be saved and then loaded for printing.

To trigger reading/writing to the PS store, follow the steps below:

  1. Open a Bluetooth connection to your device by any means, e.g., with EFR Connect App.

  2. When the connection_opened event is handled, the program loads/saves to PS Store and prints out the results to a terminal application, such as TeraTerm.

  3. Disconnect and connect again to see how already written values are read back. Note that you may also reset the device and the values will be preserved.

Terminal Output

Source