feat(sacn): Slotzahl je Board und vier Settings-Felder

This commit is contained in:
Michael Sauer
2026-09-19 23:56:00 +02:00
parent f09a51088a
commit 33d085cbed
6 changed files with 90 additions and 0 deletions
+5
View File
@@ -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;
}
+4
View File
@@ -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
+10
View File
@@ -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)
+5
View File
@@ -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);
+8
View File
@@ -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();
}
+58
View File
@@ -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();
}