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.
This commit is contained in:
Michael Sauer
2026-09-22 23:20:12 +02:00
parent 363aee12ef
commit 9ee1983dff
5 changed files with 24 additions and 20 deletions
+3 -3
View File
@@ -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.<universe_hi>.<universe_lo>` 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
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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. */
+12 -8
View File
@@ -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)
+7 -7
View File
@@ -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);