feat: Ring-Log für den Lichtzustand

This commit is contained in:
Michael Sauer
2026-09-11 13:30:26 +02:00
parent 91721ac47d
commit b1b4ec214b
4 changed files with 313 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
#include "config/nl_state_log.h"
#include <string.h>
#include "light/nl_boot_state.h"
#include "util/nl_crc.h"
#define ENTRY_MAGIC 0xA5u
static uint32_t s_sector[2];
static bool s_have;
static nl_light_state_t s_latest;
static uint32_t s_seq;
static uint32_t s_active;
static uint32_t s_next; /* next free slot in the active sector */
static uint32_t slot_addr(uint32_t sec, uint32_t slot)
{
return s_sector[sec] + slot * NL_STATE_ENTRY_SIZE;
}
static void encode(const nl_light_state_t *s, uint32_t seq, uint8_t e[NL_STATE_ENTRY_SIZE])
{
memset(e, 0, NL_STATE_ENTRY_SIZE);
e[0] = ENTRY_MAGIC;
e[1] = (uint8_t)((s->on ? 1u : 0u) | (s->mode == NL_MODE_WHITE ? 2u : 0u));
e[2] = s->brightness;
e[3] = s->r;
e[4] = s->g;
e[5] = s->b;
e[6] = (uint8_t)(s->ct & 0xFFu);
e[7] = (uint8_t)(s->ct >> 8);
e[8] = (uint8_t)seq;
e[9] = (uint8_t)(seq >> 8);
e[10] = (uint8_t)(seq >> 16);
e[11] = (uint8_t)(seq >> 24);
e[15] = nl_crc8(e, NL_STATE_ENTRY_SIZE - 1);
}
static bool decode(const uint8_t e[NL_STATE_ENTRY_SIZE], nl_light_state_t *s, uint32_t *seq)
{
if (e[0] != ENTRY_MAGIC || nl_crc8(e, NL_STATE_ENTRY_SIZE - 1) != e[15])
return false;
s->on = (e[1] & 1u) != 0;
s->mode = (e[1] & 2u) ? NL_MODE_WHITE : NL_MODE_COLOR;
s->brightness = e[2];
s->r = e[3];
s->g = e[4];
s->b = e[5];
s->ct = (uint16_t)(e[6] | (e[7] << 8));
*seq = (uint32_t)e[8] | ((uint32_t)e[9] << 8) | ((uint32_t)e[10] << 16) | ((uint32_t)e[11] << 24);
return true;
}
static bool is_free(const uint8_t e[NL_STATE_ENTRY_SIZE])
{
for (uint32_t i = 0; i < NL_STATE_ENTRY_SIZE; i++)
if (e[i] != 0xFF)
return false;
return true;
}
static uint32_t next_free_slot(uint32_t sec)
{
uint8_t e[NL_STATE_ENTRY_SIZE];
uint32_t next = 0;
for (uint32_t slot = 0; slot < NL_STATE_LOG_SLOTS; slot++) {
if (nl_flash_read(slot_addr(sec, slot), e, sizeof e) != 0 || !is_free(e))
next = slot + 1;
}
return next;
}
void nl_state_log_init(uint32_t sector0, uint32_t sector1)
{
uint8_t e[NL_STATE_ENTRY_SIZE];
nl_light_state_t st;
uint32_t seq;
s_sector[0] = sector0;
s_sector[1] = sector1;
s_have = false;
s_seq = 0;
s_active = 0;
for (uint32_t sec = 0; sec < 2; sec++) {
for (uint32_t slot = 0; slot < NL_STATE_LOG_SLOTS; slot++) {
if (nl_flash_read(slot_addr(sec, slot), e, sizeof e) != 0)
continue;
if (decode(e, &st, &seq) && (!s_have || seq > s_seq)) {
s_have = true;
s_seq = seq;
s_latest = st;
s_active = sec;
}
}
}
s_next = next_free_slot(s_active);
}
bool nl_state_log_load(nl_light_state_t *out)
{
if (!s_have)
return false;
*out = s_latest;
return true;
}
int nl_state_log_append(const nl_light_state_t *s)
{
uint8_t e[NL_STATE_ENTRY_SIZE];
uint32_t sec = s_active;
int r;
if (s_have && nl_light_state_equal(&s_latest, s))
return 0;
if (!s_have) {
sec = 0;
if (s_active != 0 || s_next != 0) {
if (nl_flash_erase_sector(s_sector[0]) != 0)
return -1;
}
s_next = 0;
} else if (s_next >= NL_STATE_LOG_SLOTS) {
sec = 1u - s_active;
if (nl_flash_erase_sector(s_sector[sec]) != 0)
return -1;
s_next = 0;
}
encode(s, s_seq + 1, e);
r = nl_flash_write(slot_addr(sec, s_next), e, sizeof e);
s_active = sec;
s_next++; /* also skip a half written slot */
if (r != 0)
return -1;
s_seq++;
s_latest = *s;
s_have = true;
return 0;
}
int nl_state_log_erase(void)
{
int r0 = nl_flash_erase_sector(s_sector[0]);
int r1 = nl_flash_erase_sector(s_sector[1]);
s_have = false;
s_seq = 0;
s_active = 0;
s_next = 0;
return (r0 == 0 && r1 == 0) ? 0 : -1;
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef NL_STATE_LOG_H
#define NL_STATE_LOG_H
#include <stdbool.h>
#include <stdint.h>
#include "config/nl_flash.h"
#include "light/nl_light_types.h"
#define NL_STATE_ENTRY_SIZE 16u
#define NL_STATE_LOG_SLOTS (NL_FLASH_SECTOR_SIZE / NL_STATE_ENTRY_SIZE)
void nl_state_log_init(uint32_t sector0, uint32_t sector1);
bool nl_state_log_load(nl_light_state_t *out);
int nl_state_log_append(const nl_light_state_t *s);
int nl_state_log_erase(void);
#endif