From d4b683de8faf2ebcf4a777e5606f9dcc8cb2c85c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Thu, 1 Oct 2026 23:01:02 +0200 Subject: [PATCH] feat(settings): Felder fuer den Standalone-AP-Modus --- src/config/nl_settings.c | 7 ++++++ src/config/nl_settings.h | 11 +++++++++ test/test_settings.c | 52 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c index 6e70e97..1d5e1ec 100644 --- a/src/config/nl_settings.c +++ b/src/config/nl_settings.c @@ -62,6 +62,12 @@ typedef char nl_settings_legacy_check[offsetof(nl_settings_t, sacn_enabled) == 2 * Vorgabe. */ typedef char nl_settings_layout_offset_check[offsetof(nl_settings_t, sacn_layout) == 216 ? 1 : -1]; +/* Dasselbe fuer die Standalone-Felder: standalone muss bei 220 beginnen, dem + * sizeof(nl_settings_t) der Firmware davor. Wer hier anstoesst, hat ein Feld zu frueh + * eingefuegt - dann kaeme standalone_pulse aus einem aelteren Satz als Fuellbyte (0) + * zurueck statt als Vorgabe (1), und der Startpuls waere still aus. */ +typedef char nl_settings_standalone_check[offsetof(nl_settings_t, standalone) == 220 ? 1 : -1]; + static uint32_t s_addr[2]; void nl_settings_defaults(nl_settings_t *s) @@ -78,6 +84,7 @@ void nl_settings_defaults(nl_settings_t *s) s->sacn_start_slot = 1; s->sacn_hold = 0; s->sacn_layout = 1; + s->standalone_pulse = 1; /* standalone, ap_channel, ap_ssid, ap_pass bleiben auf 0/leer */ } void nl_settings_init(uint32_t addr_a, uint32_t addr_b) diff --git a/src/config/nl_settings.h b/src/config/nl_settings.h index 8ec3aa5..d406ec6 100644 --- a/src/config/nl_settings.h +++ b/src/config/nl_settings.h @@ -41,6 +41,17 @@ typedef struct { * dem vorherigen sizeof (216), sonst ueberschreibt das Fuellbyte * eines aelteren Satzes sie. */ uint8_t _padding2[3]; /* haelt sizeof auf einem Vielfachen von 4 */ + uint8_t standalone; /* Offset 220 - 1 = eigener AP statt Station. Dieselbe Regel + * wie oben: neue Felder beginnen bei oder hinter dem + * vorherigen sizeof (220), sonst ueberschreibt das + * Fuellbyte eines aelteren Satzes sie. */ + uint8_t ap_channel; /* 0 = Vorgabe (6), sonst 1..13 */ + uint8_t standalone_pulse; /* 1 = beim Start kurz pulsen (Vorgabe) */ + uint8_t _padding3; + char ap_ssid[NL_SSID_LEN]; /* leer = "ninlight-" */ + char ap_pass[NL_PASS_LEN]; /* leer = einkompiliertes AP-Passwort. Taucht in keiner + * Antwort und in keiner Protokollzeile auf. */ + uint8_t _padding4[2]; /* haelt sizeof auf einem Vielfachen von 4 (324) */ } nl_settings_t; void nl_settings_defaults(nl_settings_t *s); diff --git a/test/test_settings.c b/test/test_settings.c index d013bf2..4ef38df 100644 --- a/test/test_settings.c +++ b/test/test_settings.c @@ -275,6 +275,55 @@ static void test_record_without_layout_gets_the_default(void) TEST_ASSERT_EQUAL_UINT8(1, out.sacn_layout); } +static void test_standalone_defaults(void) +{ + nl_settings_t s; + TEST_ASSERT_FALSE(nl_settings_load(&s)); + TEST_ASSERT_EQUAL_UINT8(0, s.standalone); + TEST_ASSERT_EQUAL_UINT8(0, s.ap_channel); /* 0 heisst "Vorgabe 6" */ + TEST_ASSERT_EQUAL_UINT8(1, s.standalone_pulse); + TEST_ASSERT_EQUAL_STRING("", s.ap_ssid); + TEST_ASSERT_EQUAL_STRING("", s.ap_pass); +} + +static void test_standalone_roundtrip(void) +{ + nl_settings_t in = sample("ap"), out; + in.standalone = 1; + in.ap_channel = 11; + in.standalone_pulse = 0; + strcpy(in.ap_ssid, "Wohnzimmerlampe"); + strcpy(in.ap_pass, "geheim123"); + TEST_ASSERT_EQUAL_INT(0, nl_settings_save(&in)); + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_UINT8(1, out.standalone); + TEST_ASSERT_EQUAL_UINT8(11, out.ap_channel); + TEST_ASSERT_EQUAL_UINT8(0, out.standalone_pulse); + TEST_ASSERT_EQUAL_STRING("Wohnzimmerlampe", out.ap_ssid); + TEST_ASSERT_EQUAL_STRING("geheim123", out.ap_pass); +} + +/* Ein Datensatz aus einer Firmware VOR dem Standalone-Modus: die neuen Felder muessen + * als echte Vorgaben zurueckkommen, nicht als Null. Sonst stuende standalone_pulse nach + * dem Update auf jeder Lampe auf 0 - der Startpuls waere still abgeschaltet. */ +static void test_record_without_standalone_gets_defaults(void) +{ + nl_settings_t in = sample("alt"), out; + uint32_t crc; + nl_settings_save(&in); + { + uint16_t shorter = 220u; /* sizeof(nl_settings_t) vor diesem Zweig */ + memset(&fake_flash_mem[NL_FL_SETTINGS_A + 12 + 217], 0, 3); /* Fuellbytes */ + memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 6], &shorter, 2); + crc = nl_crc32(0, &fake_flash_mem[NL_FL_SETTINGS_A], 12 + shorter); + memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 12 + shorter], &crc, 4); + } + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_STRING("alt", out.name); + TEST_ASSERT_EQUAL_UINT8(0, out.standalone); + TEST_ASSERT_EQUAL_UINT8(1, out.standalone_pulse); +} + static void test_record_size_stays_aligned(void) { TEST_ASSERT_EQUAL_UINT(0, sizeof(nl_settings_t) % 4u); @@ -301,5 +350,8 @@ int main(void) RUN_TEST(test_sacn_layout_roundtrip); RUN_TEST(test_record_without_layout_gets_the_default); RUN_TEST(test_record_size_stays_aligned); + RUN_TEST(test_standalone_defaults); + RUN_TEST(test_standalone_roundtrip); + RUN_TEST(test_record_without_standalone_gets_defaults); return UNITY_END(); }