Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/spin_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ To work with Spin API, include the following file in your code:

## Detailed documentation on available API classes:
- [Data API](spin_dataAPI.md)
- [MetaData API](spin_metaDataAPI.md)

## Detailed documentation on Hardware Abstraction Layer classes:
- [Comparator HAL](https://owntech-foundation.github.io/Documentation/powerAPI/classCompHAL)
Expand Down
122 changes: 122 additions & 0 deletions docs/spin_metaDataAPI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
!!! note ""
MetaData API lets you persist board and shield identity data (serial numbers, hardware versions, passwords, and a handful of generic extra slots) to flash, so it survives a reset or a firmware reflash.

This is useful to identify a board at runtime, to check that a shield matches the board it is plugged into, or to store any other small piece of configuration that needs to outlive a power cycle.

!!! warning
This is **not** secure storage: data is written in plain form to the NVS partition, with no secure element or read-protection involved. The password fields are only meant to gate casual spin/shield mismatches, not to protect a real secret.

### Include

MetaData API is part of Spin API: it is made available by including the `SpinAPI.h` header. From there, a `spin.metaData` object is available to interact with the API.
!!! note
```
#include <SpinAPI.h>
```

## Fixed-length fields

Serial numbers, versions, and passwords are stored under a fixed-length field. Writing requires passing the exact expected size, and reading requires providing a buffer at least as large as the field.

| Field | Length constant | Length (bytes) |
| ---------------- | --------------------- | --------------- |
| Spin serial number | `SPIN_SERIAL_LEN` | 13 |
| Shield serial number | `SHIELD_SERIAL_LEN` | 13 |
| Spin password | `SPIN_PASSWORD_LEN` | 10 |
| Shield password | `SHIELD_PASSWORD_LEN` | 10 |

!!! example
=== "Serial numbers"
```cpp
const char spin_serial[] = "SPIN000000001"; // 13 chars
const char shield_serial[] = "SHLD000000001"; // 13 chars

spin.metaData.setSpinSerialNumber(spin_serial, sizeof(spin_serial) - 1);
spin.metaData.setShieldSerialNumber(shield_serial, sizeof(shield_serial) - 1);

char buffer[SPIN_SERIAL_LEN + 1];
int8_t bytes_read = spin.metaData.getSpinSerialNumber(buffer, sizeof(buffer));
if (bytes_read >= 0)
{
buffer[SPIN_SERIAL_LEN] = '\0'; // The stored value is not null-terminated
}
```

=== "Passwords"
```cpp
const char spin_password[] = "SPINPASS01"; // 10 chars
const char shield_password[] = "SHLDPASS01"; // 10 chars

spin.metaData.setSpinPassword(spin_password, sizeof(spin_password) - 1);
spin.metaData.setShieldPassword(shield_password, sizeof(shield_password) - 1);

char buffer[SHIELD_PASSWORD_LEN + 1];
int8_t bytes_read = spin.metaData.getShieldPassword(buffer, sizeof(buffer));
if (bytes_read >= 0)
{
buffer[SHIELD_PASSWORD_LEN] = '\0';
}
```

!!! note
Stored values are raw ASCII bytes, not null-terminated. If fewer meaningful characters are needed than the field's fixed length, the caller must pad the buffer itself (e.g. with spaces or zeros) up to the full length.

## Hardware versions

Spin and shield hardware versions are stored as three separate `major`/`minor`/`rev` numbers rather than a buffer.

!!! example
```cpp
spin.metaData.setSpinVersion(1, 2, 0); // v1.2.0
spin.metaData.setShieldVersion(2, 0, 1); // v2.0.1

uint8_t major, minor, rev;
if (spin.metaData.getSpinVersion(&major, &minor, &rev) == 0)
{
// Use major, minor, rev
}
```

## Generic extra slots

Beyond the predefined fields, `METADATA_EXTRA_COUNT` generic slots (indexed `0` to `METADATA_EXTRA_COUNT - 1`) are available to store any small, variable-length piece of data, up to `METADATA_EXTRA_MAX_LEN` bytes each.

!!! example
```cpp
const char label[] = "MyLabel";
spin.metaData.setExtraData(0, (const uint8_t*)label, strlen(label));

uint8_t buffer[METADATA_EXTRA_MAX_LEN];
int8_t bytes_read = spin.metaData.getExtraData(0, buffer, sizeof(buffer));
if (bytes_read >= 0)
{
// buffer[0 .. bytes_read - 1] contains the stored data
}
```

!!! note
Unlike the fixed-length fields, the actual stored size of an extra slot is not known ahead of the read. The buffer passed to `getExtraData()` must therefore always be at least `METADATA_EXTRA_MAX_LEN` bytes, regardless of how much data was actually written to that slot.

## Error codes

All `set***()`/`get***()` functions return `0` (for setters) or the number of bytes read (for getters) on success, and a negative value on error:

| Return value | Meaning |
| ------------ | ------- |
| `-1` | Underlying storage error (nothing stored yet, corrupted data, or version mismatch) |
| `-2` | Provided buffer/size does not match what the field requires |
| `-3` | Extra slot `index` is out of range (`setExtraData()`/`getExtraData()` only) |

## Clearing all metadata

`clearAllMetaData()` erases every field owned by this class (serial numbers, versions, passwords, and all extra slots) without touching any other module's data in the shared NVS partition (e.g. ADC calibration or safety thresholds).

!!! example
```cpp
spin.metaData.clearAllMetaData();
```

## API Reference

::: doxy.powerAPI.class
name: MetaDataAPI
32 changes: 30 additions & 2 deletions zephyr/modules/owntech_spin_api/zephyr/src/MetaDataAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ enum : uint16_t
META_SHIELD_SERIAL = 0x01,
META_SPIN_VERSION = 0x02,
META_SHIELD_VERSION = 0x03,
META_SHIELD_PASSWORD = 0x04,
META_EXTRA_0 = 0x05,
META_SPIN_PASSWORD = 0x04,
META_SHIELD_PASSWORD = 0x05,
META_EXTRA_0 = 0x06,
/* META_EXTRA_0 .. META_EXTRA_0 + METADATA_EXTRA_COUNT - 1 (0x06-0x0A)
* are reserved for the generic extra slots. */
};

static const uint16_t VERSION_FIELD_LEN = 3; /* major, minor, rev */
Expand Down Expand Up @@ -149,6 +152,30 @@ int8_t MetaDataAPI::getShieldVersion(uint8_t* major, uint8_t* minor, uint8_t* re
return 0;
}

int8_t MetaDataAPI::setSpinPassword(const char* password, uint8_t password_size)
{
if (password_size != SPIN_PASSWORD_LEN)
{
return -2;
}

int ret = nvs_storage_store_data(BOARD_METADATA | META_SPIN_PASSWORD,
password, SPIN_PASSWORD_LEN);
return (ret < 0) ? -1 : 0;
}

int8_t MetaDataAPI::getSpinPassword(char* buffer, uint8_t buffer_size)
{
if (buffer_size < SPIN_PASSWORD_LEN)
{
return -2;
}

int ret = nvs_storage_retrieve_data(BOARD_METADATA | META_SPIN_PASSWORD,
buffer, buffer_size);
return (ret < 0) ? -1 : ret;
}

int8_t MetaDataAPI::setShieldPassword(const char* password, uint8_t password_size)
{
if (password_size != SHIELD_PASSWORD_LEN)
Expand Down Expand Up @@ -214,6 +241,7 @@ int8_t MetaDataAPI::clearAllMetaData()
META_SHIELD_SERIAL,
META_SPIN_VERSION,
META_SHIELD_VERSION,
META_SPIN_PASSWORD,
META_SHIELD_PASSWORD,
META_EXTRA_0 + 0,
META_EXTRA_0 + 1,
Expand Down
59 changes: 48 additions & 11 deletions zephyr/modules/owntech_spin_api/zephyr/src/MetaDataAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@
static const uint8_t SPIN_SERIAL_LEN = 13;
static const uint8_t SHIELD_SERIAL_LEN = 13;

/* Fixed length, in bytes, of the shield password field (3 raw ASCII
* characters, not null-terminated). */
static const uint8_t SHIELD_PASSWORD_LEN = 3;
/* Fixed length, in bytes, of the spin and shield password fields (10 raw
* ASCII characters, not null-terminated). */
static const uint8_t SPIN_PASSWORD_LEN = 10;
static const uint8_t SHIELD_PASSWORD_LEN = 10;

/* Number of generic extra metadata slots, and max size of each. */
static const uint8_t METADATA_EXTRA_COUNT = 5;
Expand All @@ -54,11 +55,11 @@ static const uint8_t METADATA_EXTRA_MAX_LEN = 16;

/**
* @brief Persist board/shield identity data (serial numbers, versions,
* shield password, and generic extra slots) to flash.
* spin/shield passwords, and generic extra slots) to flash.
*
* @note This is NOT secure storage: data is written in plain form to the
* NVS partition, with no secure element or read-protection involved.
* The shield password field is only meant to gate casual shield
* The password fields are only meant to gate casual spin/shield
* mismatches, not to protect a real secret.
*/
class MetaDataAPI
Expand Down Expand Up @@ -186,6 +187,42 @@ class MetaDataAPI
*/
int8_t getShieldVersion(uint8_t* major, uint8_t* minor, uint8_t* rev);

/**
* @brief Store the Spin board password in persistent memory.
*
* @note This is not a secure secret store: the password is written in
* plain form to flash, retrievable via `getSpinPassword()`.
* It is only meant to gate casual spin/shield mismatches.
*
* @param[in] password Pointer to a buffer of exactly
* `SPIN_PASSWORD_LEN` (10) raw ASCII bytes.
* @param[in] password_size Size of `password` in bytes, must be
* exactly `SPIN_PASSWORD_LEN` (10).
*
* @return `0` if the password was correctly stored, negative value
* on error:
*
* - `-1`: underlying storage error,
*
* - `-2`: `password_size` is not exactly `SPIN_PASSWORD_LEN`.
*/
int8_t setSpinPassword(const char* password, uint8_t password_size);

/**
* @brief Retrieve the Spin board password from persistent memory.
*
* @param[in] buffer Buffer to receive the 10 raw ASCII bytes.
* @param[in] buffer_size Size of `buffer`, must be at least
* `SPIN_PASSWORD_LEN` (10).
*
* @return Number of bytes read (10) on success, negative value on error:
*
* - `-1`: underlying storage error,
*
* - `-2`: provided buffer is smaller than `SPIN_PASSWORD_LEN`.
*/
int8_t getSpinPassword(char* buffer, uint8_t buffer_size);

/**
* @brief Store the shield password in persistent memory.
*
Expand All @@ -194,9 +231,9 @@ class MetaDataAPI
* It is only meant to gate casual shield/board mismatches.
*
* @param[in] password Pointer to a buffer of exactly
* `SHIELD_PASSWORD_LEN` (3) raw ASCII bytes.
* `SHIELD_PASSWORD_LEN` (10) raw ASCII bytes.
* @param[in] password_size Size of `password` in bytes, must be
* exactly `SHIELD_PASSWORD_LEN` (3).
* exactly `SHIELD_PASSWORD_LEN` (10).
*
* @return `0` if the password was correctly stored, negative value
* on error:
Expand All @@ -210,11 +247,11 @@ class MetaDataAPI
/**
* @brief Retrieve the shield password from persistent memory.
*
* @param[in] buffer Buffer to receive the 3 raw ASCII bytes.
* @param[in] buffer Buffer to receive the 10 raw ASCII bytes.
* @param[in] buffer_size Size of `buffer`, must be at least
* `SHIELD_PASSWORD_LEN` (3).
* `SHIELD_PASSWORD_LEN` (10).
*
* @return Number of bytes read (3) on success, negative value on error:
* @return Number of bytes read (10) on success, negative value on error:
*
* - `-1`: underlying storage error,
*
Expand Down Expand Up @@ -262,7 +299,7 @@ class MetaDataAPI

/**
* @brief Erase all board/shield metadata fields (serial numbers,
* versions, password, and all extra slots).
* versions, passwords, and all extra slots).
*
* @note This only erases the metadata fields owned by this class. It
* does not affect ADC calibration data or safety thresholds,
Expand Down
Loading