From 9ee1983dff584f969d3cb48b07178d4aaa8688d9 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 22 Sep 2026 23:20:06 +0200 Subject: [PATCH] feat: sACN-Belegung rgb als Vorgabe Pixelorientierte Werkzeuge wie LedFx schreiben drei Kanaele je Pixel; mit dem Dimmer voran landet deren erstes Pixel auf Dimmer, R, G. rgb ist damit die Belegung, die out of the box das Richtige tut - der Dimmer bleibt fuer DMX-Pulte eine Umschaltung entfernt. --- README.md | 6 +++--- src/config/nl_settings.c | 2 +- src/config/nl_settings.h | 2 +- test/test_api.c | 20 ++++++++++++-------- test/test_settings.c | 14 +++++++------- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 28e57fe..8cc739a 100644 --- a/README.md +++ b/README.md @@ -75,14 +75,14 @@ Verfügbare Befehle in der Konsole: `help`. Die Lampe empfängt sACN (E1.31) auf UDP-Port 5568 — per Multicast auf `239.255..` und per Unicast direkt an ihre IP. Nach einem -Update ist der Empfang aktiv: Universe 1, Startslot 1. +Update ist der Empfang aktiv: Universe 1, Startslot 1, Belegung `rgb`. Es gibt zwei Slot-Belegungen, umschaltbar über `layout`: | `layout` | Slots ab `start_slot` | wofür | |---|---|---| -| `dimmer` (Vorgabe) | Dimmer, R, G, B, CW, WW — ohne RGB: Dimmer, CW, WW | DMX-Pulte, die einen Master-Dimmer erwarten | -| `rgb` | R, G, B (3 Slots auf jedem Board) | pixelorientierte Werkzeuge wie LedFx, die je Pixel drei Kanäle schreiben | +| `dimmer` | Dimmer, R, G, B, CW, WW — ohne RGB: Dimmer, CW, WW | DMX-Pulte, die einen Master-Dimmer erwarten | +| `rgb` (Vorgabe) | R, G, B (3 Slots auf jedem Board) | pixelorientierte Werkzeuge wie LedFx, die je Pixel drei Kanäle schreiben | Im `rgb`-Layout ist jede Lampe genau ein Pixel: Lampe 1 auf Offset 0, Lampe 2 auf Offset 3, und so weiter. Ein Board ohne Farbkanäle nimmt den größten der drei Werte auf beide diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c index 970ed4f..6e70e97 100644 --- a/src/config/nl_settings.c +++ b/src/config/nl_settings.c @@ -77,7 +77,7 @@ void nl_settings_defaults(nl_settings_t *s) s->sacn_universe = 1; s->sacn_start_slot = 1; s->sacn_hold = 0; - s->sacn_layout = 0; + s->sacn_layout = 1; } 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 476efc1..8ec3aa5 100644 --- a/src/config/nl_settings.h +++ b/src/config/nl_settings.h @@ -36,7 +36,7 @@ typedef struct { 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[1]; /* haelt sizeof auf einem Vielfachen von 4 */ - uint8_t sacn_layout; /* Offset 216 - 0 = Dimmer voran, 1 = nur R,G,B (nl_dmx_layout_t). + uint8_t sacn_layout; /* Offset 216 - 0 = Dimmer voran, 1 = nur R,G,B (Vorgabe 1). * Dieselbe Regel wie oben: neue Felder beginnen bei oder hinter * dem vorherigen sizeof (216), sonst ueberschreibt das Fuellbyte * eines aelteren Satzes sie. */ diff --git a/test/test_api.c b/test/test_api.c index b721e15..0acf523 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -361,7 +361,8 @@ static void test_settings_exposes_sacn_block(void) TEST_ASSERT_EQUAL_INT(1, (int)cJSON_GetNumberValue(sacn_field("universe"))); TEST_ASSERT_EQUAL_INT(1, (int)cJSON_GetNumberValue(sacn_field("start_slot"))); TEST_ASSERT_EQUAL_STRING("release", cJSON_GetStringValue(sacn_field("on_timeout"))); - TEST_ASSERT_EQUAL_INT(6, (int)cJSON_GetNumberValue(sacn_field("slots"))); + TEST_ASSERT_EQUAL_STRING("rgb", cJSON_GetStringValue(sacn_field("layout"))); + TEST_ASSERT_EQUAL_INT(3, (int)cJSON_GetNumberValue(sacn_field("slots"))); } static void test_settings_sacn_block_reflects_stored_values(void) @@ -442,6 +443,7 @@ static void test_put_sacn_rejects_fractional_numbers(void) /* Ein Board mit RGB braucht 6 Slots: ab 508 passt der Satz nicht mehr ins Universe. */ static void test_put_sacn_start_slot_must_leave_room_for_all_slots(void) { + fake_settings.sacn_layout = 0; /* die Vorgabe ist rgb mit 3 Slots - hier geht es um 6 */ call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"sacn\",\"start_slot\":507}"); TEST_ASSERT_EQUAL_INT(200, resp.status); call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"sacn\",\"start_slot\":508}"); @@ -451,7 +453,9 @@ static void test_put_sacn_start_slot_must_leave_room_for_all_slots(void) static void test_put_sacn_start_slot_limit_follows_the_board(void) { - fake_api_reset("g95_cw"); /* drei Slots: 510 passt noch */ + fake_api_reset("g95_cw"); + fake_settings.sacn_layout = 0; /* im dimmer-Layout haengt die Slotzahl am Board */ + /* Ein Board ohne Farbe hat auch mit Dimmer nur drei Slots: 510 passt noch. */ call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"sacn\",\"start_slot\":510}"); TEST_ASSERT_EQUAL_INT(200, resp.status); call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"sacn\",\"start_slot\":511}"); @@ -512,15 +516,15 @@ static void test_commands(void) static void test_settings_reports_the_layout_and_matching_slots(void) { + call(NL_HTTP_GET, "/api/v1/system/settings", TOKEN, NULL); + /* Vorgabe: rgb - und darin belegt auch ein Board mit Farbe nur drei Slots. */ + TEST_ASSERT_EQUAL_STRING("rgb", cJSON_GetStringValue(sacn_field("layout"))); + TEST_ASSERT_EQUAL_INT(3, (int)cJSON_GetNumberValue(sacn_field("slots"))); + + fake_settings.sacn_layout = 0; call(NL_HTTP_GET, "/api/v1/system/settings", TOKEN, NULL); TEST_ASSERT_EQUAL_STRING("dimmer", cJSON_GetStringValue(sacn_field("layout"))); TEST_ASSERT_EQUAL_INT(6, (int)cJSON_GetNumberValue(sacn_field("slots"))); - - fake_settings.sacn_layout = 1; - call(NL_HTTP_GET, "/api/v1/system/settings", TOKEN, NULL); - TEST_ASSERT_EQUAL_STRING("rgb", cJSON_GetStringValue(sacn_field("layout"))); - /* Im rgb-Layout belegt auch ein Board mit Farbe nur drei Slots. */ - TEST_ASSERT_EQUAL_INT(3, (int)cJSON_GetNumberValue(sacn_field("slots"))); } static void test_put_sacn_layout(void) diff --git a/test/test_settings.c b/test/test_settings.c index 4e9fc74..d013bf2 100644 --- a/test/test_settings.c +++ b/test/test_settings.c @@ -238,20 +238,20 @@ static void test_record_without_sacn_gets_defaults(void) /* 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_sacn_layout_defaults_to_dimmer(void) +static void test_sacn_layout_defaults_to_rgb(void) { nl_settings_t s; TEST_ASSERT_FALSE(nl_settings_load(&s)); - TEST_ASSERT_EQUAL_UINT8(0, s.sacn_layout); + TEST_ASSERT_EQUAL_UINT8(1, s.sacn_layout); } static void test_sacn_layout_roundtrip(void) { nl_settings_t in = sample("layout"), out; - in.sacn_layout = 1; + in.sacn_layout = 0; /* weg von der Vorgabe, sonst prueft der Test nur diese */ TEST_ASSERT_EQUAL_INT(0, nl_settings_save(&in)); TEST_ASSERT_TRUE(nl_settings_load(&out)); - TEST_ASSERT_EQUAL_UINT8(1, out.sacn_layout); + TEST_ASSERT_EQUAL_UINT8(0, out.sacn_layout); } /* Derselbe Fallstrick wie bei sacn_enabled, eine Feldgeneration weiter: ein Satz, der von @@ -261,7 +261,7 @@ static void test_record_without_layout_gets_the_default(void) { nl_settings_t in = sample("vor-layout"), out; uint32_t crc; - in.sacn_layout = 1; + in.sacn_layout = 0; /* der abgeschnittene Satz darf diese 0 nicht zurueckliefern */ nl_settings_save(&in); { uint16_t shorter = 216u; /* sizeof(nl_settings_t) vor dem Layout-Feld */ @@ -272,7 +272,7 @@ static void test_record_without_layout_gets_the_default(void) TEST_ASSERT_TRUE(nl_settings_load(&out)); TEST_ASSERT_EQUAL_STRING("vor-layout", out.name); TEST_ASSERT_EQUAL_UINT8(1, out.sacn_enabled); - TEST_ASSERT_EQUAL_UINT8(0, out.sacn_layout); + TEST_ASSERT_EQUAL_UINT8(1, out.sacn_layout); } static void test_record_size_stays_aligned(void) @@ -297,7 +297,7 @@ int main(void) RUN_TEST(test_sacn_defaults); RUN_TEST(test_sacn_roundtrip); RUN_TEST(test_record_without_sacn_gets_defaults); - RUN_TEST(test_sacn_layout_defaults_to_dimmer); + RUN_TEST(test_sacn_layout_defaults_to_rgb); RUN_TEST(test_sacn_layout_roundtrip); RUN_TEST(test_record_without_layout_gets_the_default); RUN_TEST(test_record_size_stays_aligned);