From 33d085cbed80c83dd921adf8cefc61f0c744acf0 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 19 Sep 2026 23:56:00 +0200 Subject: [PATCH] feat(sacn): Slotzahl je Board und vier Settings-Felder --- src/board/nl_board.c | 5 ++++ src/board/nl_board.h | 4 +++ src/config/nl_settings.c | 10 +++++++ src/config/nl_settings.h | 5 ++++ test/test_board.c | 8 ++++++ test/test_settings.c | 58 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 90 insertions(+) diff --git a/src/board/nl_board.c b/src/board/nl_board.c index 526cd2a..70d4dee 100644 --- a/src/board/nl_board.c +++ b/src/board/nl_board.c @@ -49,3 +49,8 @@ const nl_board_t *nl_board_get(void) return nl_board_find(NINLIGHT_DEVICE_ID); } #endif + +uint8_t nl_board_dmx_slots(const nl_board_t *b) +{ + return (b != NULL && b->has_rgb) ? 6u : 3u; +} diff --git a/src/board/nl_board.h b/src/board/nl_board.h index 0a26785..7c02950 100644 --- a/src/board/nl_board.h +++ b/src/board/nl_board.h @@ -41,4 +41,8 @@ const nl_board_t *nl_board_find(const char *id); /* Board selected at build time via NINLIGHT_DEVICE_ID. */ const nl_board_t *nl_board_get(void); +/* Slots, die eine Lampe im DMX-Universe belegt: Dimmer + R,G,B,CW,WW bzw. Dimmer + CW,WW. + * Steht hier, damit API, Uebernahme-Schicht und Doku dieselbe Zahl benutzen. */ +uint8_t nl_board_dmx_slots(const nl_board_t *b); + #endif diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c index ec97167..d33d40b 100644 --- a/src/config/nl_settings.c +++ b/src/config/nl_settings.c @@ -38,6 +38,12 @@ typedef struct { * really has to start right behind the header (no padding). Compile-time, gnu99-compatible. */ typedef char nl_settings_layout_check[offsetof(rec_t, data) == sizeof(rec_hdr_t) ? 1 : -1]; +/* Die Satzlaenge muss ein Vielfaches von 4 bleiben - sonst zeigt NL_SETTINGS_CRC_OFF() woanders + * hin als offsetof(rec_t, crc), und jeder gespeicherte Datensatz faellt beim naechsten Boot + * durch die CRC-Pruefung. Wer hier anstoesst, fuegt ein Fuellbyte hinzu, statt die Pruefung + * zu entfernen. */ +typedef char nl_settings_size_check[(sizeof(nl_settings_t) % 4u == 0u) ? 1 : -1]; + static uint32_t s_addr[2]; void nl_settings_defaults(nl_settings_t *s) @@ -49,6 +55,10 @@ void nl_settings_defaults(nl_settings_t *s) s->fade_out_ms = 500; s->transition_ms = 300; s->wake_on_change = 0; + s->sacn_enabled = 1; + s->sacn_universe = 1; + s->sacn_start_slot = 1; + s->sacn_hold = 0; } 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 1941be8..b246a57 100644 --- a/src/config/nl_settings.h +++ b/src/config/nl_settings.h @@ -28,6 +28,11 @@ typedef struct { * switching on or off. */ uint8_t wake_on_change; /* if set, such a change also switches the lamp on when it was * off, unless the same call says "on": false. */ + uint8_t sacn_enabled; /* sACN/E1.31-Empfang aktiv (Vorgabe 1) */ + uint16_t sacn_universe; /* 1..63999 (Vorgabe 1) */ + uint16_t sacn_start_slot; /* 1..512 (Vorgabe 1) */ + uint8_t sacn_hold; /* 0 = nach dem Timeout zurueck zum HA-Zustand, 1 = halten */ + uint8_t _padding[2]; /* padding for 4-byte alignment of sizeof(nl_settings_t) */ } nl_settings_t; void nl_settings_defaults(nl_settings_t *s); diff --git a/test/test_board.c b/test/test_board.c index 939db9b..45e8d89 100644 --- a/test/test_board.c +++ b/test/test_board.c @@ -106,6 +106,13 @@ static void test_known_values_from_old_firmware(void) TEST_ASSERT_EQUAL_INT8(3, g95->pwm[NL_CH_WW].pwm_ch); } +static void test_dmx_slots(void) +{ + TEST_ASSERT_EQUAL_UINT8(6, nl_board_dmx_slots(nl_board_find("gu10_rgb"))); + TEST_ASSERT_EQUAL_UINT8(3, nl_board_dmx_slots(nl_board_find("g95_cw"))); + TEST_ASSERT_EQUAL_UINT8(6, nl_board_dmx_slots(nl_board_find("evk"))); +} + int main(void) { UNITY_BEGIN(); @@ -115,5 +122,6 @@ int main(void) RUN_TEST(test_pwm_boards_consistent); RUN_TEST(test_sm_boards_consistent); RUN_TEST(test_known_values_from_old_firmware); + RUN_TEST(test_dmx_slots); return UNITY_END(); } diff --git a/test/test_settings.c b/test/test_settings.c index 37d6cc6..43f4e83 100644 --- a/test/test_settings.c +++ b/test/test_settings.c @@ -182,6 +182,60 @@ static void test_erase(void) TEST_ASSERT_FALSE(nl_settings_load(&out)); } +static void test_sacn_defaults(void) +{ + nl_settings_t s; + TEST_ASSERT_FALSE(nl_settings_load(&s)); + TEST_ASSERT_EQUAL_UINT8(1, s.sacn_enabled); + TEST_ASSERT_EQUAL_UINT16(1, s.sacn_universe); + TEST_ASSERT_EQUAL_UINT16(1, s.sacn_start_slot); + TEST_ASSERT_EQUAL_UINT8(0, s.sacn_hold); +} + +static void test_sacn_roundtrip(void) +{ + nl_settings_t in = sample("dmx"), out; + in.sacn_enabled = 0; + in.sacn_universe = 4711; + in.sacn_start_slot = 300; + in.sacn_hold = 1; + TEST_ASSERT_EQUAL_INT(0, nl_settings_save(&in)); + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_UINT8(0, out.sacn_enabled); + TEST_ASSERT_EQUAL_UINT16(4711, out.sacn_universe); + TEST_ASSERT_EQUAL_UINT16(300, out.sacn_start_slot); + TEST_ASSERT_EQUAL_UINT8(1, out.sacn_hold); +} + +/* Ein Datensatz aus einer Firmware VOR sACN: die vier Felder muessen als echte Vorgaben + * zurueckkommen, nicht als Null - sonst waere sACN auf jeder aktualisierten Lampe aus. */ +static void test_record_without_sacn_gets_defaults(void) +{ + nl_settings_t in = sample("alt"), out; + uint32_t crc; + nl_settings_save(&in); + { + uint16_t shorter = (uint16_t)offsetof(nl_settings_t, sacn_enabled); + 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_STRING("ninHome", out.wifi_ssid); + TEST_ASSERT_EQUAL_UINT8(1, out.sacn_enabled); + TEST_ASSERT_EQUAL_UINT16(1, out.sacn_universe); + TEST_ASSERT_EQUAL_UINT16(1, out.sacn_start_slot); + TEST_ASSERT_EQUAL_UINT8(0, out.sacn_hold); +} + +/* Die Satzlaenge muss durch 4 teilbar bleiben, sonst laufen Schreiber und Leser beim CRC + * auseinander (siehe NL_SETTINGS_CRC_OFF in nl_settings.c). */ +static void test_record_size_stays_aligned(void) +{ + TEST_ASSERT_EQUAL_UINT(0, sizeof(nl_settings_t) % 4u); +} + int main(void) { UNITY_BEGIN(); @@ -196,5 +250,9 @@ int main(void) RUN_TEST(test_ota_pending_default_and_roundtrip); RUN_TEST(test_crc_offset_matches_reader); RUN_TEST(test_erase); + RUN_TEST(test_sacn_defaults); + RUN_TEST(test_sacn_roundtrip); + RUN_TEST(test_record_without_sacn_gets_defaults); + RUN_TEST(test_record_size_stays_aligned); return UNITY_END(); }