LedFx und Verwandte denken in RGB-Pixeln: Ihr e131-Geraet schreibt je Pixel genau drei Kanaele. Unsere Belegung beginnt mit einem Master-Dimmer, also landet deren erstes Pixel auf Dimmer, R, G - ein rotes Effektbild (255,0,0) hiesse bei uns "Dimmer 255, Rot 0, Gruen 0" und damit Dunkelheit. Von Hand eintragen liess sich eine Lampe schon, sie zeigte aber Unsinn. Der Dimmer bleibt fuer DMX-Pulte richtig, deshalb eine zweite Belegung statt einer anderen: layout = "dimmer" (Vorgabe, unveraendert) oder "rgb" (R,G,B, drei Slots auf jedem Board). Im rgb-Layout ist eine Lampe genau ein Pixel. Ein Board ohne Farbkanaele bekommt trotzdem ein Farbtripel. Genommen wird der groesste der drei Werte auf beide Weisskanaele: die Lampe folgt sichtbar der Helligkeit des Effekts, ohne dass ein Farbmodell in den DMX-Pfad kommt - genau das wird dort sonst bewusst vermieden. Die Obergrenze fuer start_slot haengt jetzt am Layout (512 - slots + 1). Ein Wechsel, der einen bisher zulaessigen start_slot unzulaessig macht, wird abgelehnt statt still verschoben: eine Lampe, die nach einer Einstellung woanders im Universe liegt als angegeben, findet niemand wieder. Das neue Settings-Feld beginnt bei Offset 216, dem sizeof davor, und ist durch eine eigene Pruefung zur Uebersetzungszeit und einen Migrationstest abgesichert - derselbe Fallstrick, der beim Schlussreview fuer sacn_enabled gefunden wurde.
131 lines
4.9 KiB
C
131 lines
4.9 KiB
C
#include <string.h>
|
|
#include "unity.h"
|
|
#include "board/nl_board.h"
|
|
|
|
void setUp(void) {}
|
|
void tearDown(void) {}
|
|
|
|
static const char *IDS[] = {"evk", "e27_rgb", "e14_rgb", "gu10_rgb", "e27_cw", "e14_cw", "gu10_cw", "g95_cw"};
|
|
|
|
static void test_all_devices_present(void)
|
|
{
|
|
TEST_ASSERT_EQUAL_UINT(8, nl_boards_count);
|
|
for (size_t i = 0; i < 8; i++) {
|
|
const nl_board_t *b = nl_board_find(IDS[i]);
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(b, IDS[i]);
|
|
TEST_ASSERT_EQUAL_STRING(IDS[i], b->id);
|
|
TEST_ASSERT_NOT_NULL(b->name);
|
|
}
|
|
}
|
|
|
|
static void test_unknown_is_null(void)
|
|
{
|
|
TEST_ASSERT_NULL(nl_board_find("e27"));
|
|
TEST_ASSERT_NULL(nl_board_find(""));
|
|
}
|
|
|
|
static void test_rgb_flags_and_ct_ranges(void)
|
|
{
|
|
for (size_t i = 0; i < nl_boards_count; i++) {
|
|
const nl_board_t *b = &nl_boards[i];
|
|
bool rgb = strstr(b->id, "rgb") != NULL || strcmp(b->id, "evk") == 0;
|
|
/* Measured on real bulbs, 2026-09-13: every lamp but the G95 covers 2700..6500 K.
|
|
The G95 Vintage is a warm bulb and reaches 2000 K at the bottom but stops at
|
|
5000 K - hard-coded per board rather than derived, because "warm variant" is a
|
|
property of that one product, not of a class of them. */
|
|
bool g95 = strcmp(b->id, "g95_cw") == 0;
|
|
TEST_ASSERT_EQUAL_MESSAGE(rgb, b->has_rgb, b->id);
|
|
TEST_ASSERT_EQUAL_UINT16_MESSAGE(g95 ? 2000 : 2700, b->ct_min, b->id);
|
|
TEST_ASSERT_EQUAL_UINT16_MESSAGE(g95 ? 5000 : 6500, b->ct_max, b->id);
|
|
}
|
|
}
|
|
|
|
static void test_pwm_boards_consistent(void)
|
|
{
|
|
for (size_t i = 0; i < nl_boards_count; i++) {
|
|
const nl_board_t *b = &nl_boards[i];
|
|
if (b->driver != NL_DRV_PWM)
|
|
continue;
|
|
TEST_ASSERT_TRUE(b->pwm_freq_hz > 0);
|
|
TEST_ASSERT_TRUE(b->pwm[NL_CH_CW].pwm_ch >= 0);
|
|
TEST_ASSERT_TRUE(b->pwm[NL_CH_WW].pwm_ch >= 0);
|
|
for (int c = NL_CH_R; c <= NL_CH_B; c++)
|
|
TEST_ASSERT_EQUAL_MESSAGE(b->has_rgb, b->pwm[c].pwm_ch >= 0, b->id);
|
|
for (int c = 0; c < NL_CH_COUNT; c++) {
|
|
if (b->pwm[c].pwm_ch < 0)
|
|
continue;
|
|
TEST_ASSERT_TRUE(b->pwm[c].pwm_ch <= 4);
|
|
for (int d = c + 1; d < NL_CH_COUNT; d++) {
|
|
if (b->pwm[d].pwm_ch < 0)
|
|
continue;
|
|
TEST_ASSERT_NOT_EQUAL(b->pwm[c].pwm_ch, b->pwm[d].pwm_ch);
|
|
TEST_ASSERT_NOT_EQUAL(b->pwm[c].pin, b->pwm[d].pin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void test_sm_boards_consistent(void)
|
|
{
|
|
for (size_t i = 0; i < nl_boards_count; i++) {
|
|
const nl_board_t *b = &nl_boards[i];
|
|
if (b->driver != NL_DRV_SM2135EH)
|
|
continue;
|
|
TEST_ASSERT_TRUE(b->has_rgb);
|
|
TEST_ASSERT_NOT_EQUAL(NL_PIN_NONE, b->sm_clk_pin);
|
|
TEST_ASSERT_NOT_EQUAL(NL_PIN_NONE, b->sm_dat_pin);
|
|
TEST_ASSERT_NOT_EQUAL(b->sm_clk_pin, b->sm_dat_pin);
|
|
TEST_ASSERT_NOT_EQUAL(0, b->sm_current);
|
|
}
|
|
}
|
|
|
|
static void test_known_values_from_old_firmware(void)
|
|
{
|
|
const nl_board_t *evk = nl_board_find("evk");
|
|
TEST_ASSERT_EQUAL(NL_DRV_PWM, evk->driver);
|
|
TEST_ASSERT_EQUAL_UINT32(5000, evk->pwm_freq_hz);
|
|
TEST_ASSERT_EQUAL_INT8(0, evk->pwm[NL_CH_R].pwm_ch);
|
|
TEST_ASSERT_EQUAL_UINT8(NL_PB(18), evk->pwm[NL_CH_R].pin);
|
|
TEST_ASSERT_TRUE(evk->pwm[NL_CH_R].active_low);
|
|
|
|
const nl_board_t *e27 = nl_board_find("e27_rgb");
|
|
TEST_ASSERT_EQUAL(NL_DRV_SM2135EH, e27->driver);
|
|
TEST_ASSERT_EQUAL_UINT8(NL_PB(13), e27->sm_clk_pin);
|
|
TEST_ASSERT_EQUAL_UINT8(NL_PB(8), e27->sm_dat_pin);
|
|
TEST_ASSERT_EQUAL_HEX8(0x17, e27->sm_current);
|
|
TEST_ASSERT_EQUAL_HEX8(0x73, nl_board_find("e14_rgb")->sm_current);
|
|
TEST_ASSERT_EQUAL_HEX8(0x63, nl_board_find("gu10_rgb")->sm_current);
|
|
|
|
const nl_board_t *e27cw = nl_board_find("e27_cw");
|
|
TEST_ASSERT_TRUE(e27cw->pwm[NL_CH_CW].antiphase);
|
|
TEST_ASSERT_EQUAL_UINT8(NL_PB(13), e27cw->pwm[NL_CH_CW].pin);
|
|
TEST_ASSERT_EQUAL_UINT32(600, e27cw->pwm_freq_hz);
|
|
|
|
const nl_board_t *g95 = nl_board_find("g95_cw");
|
|
TEST_ASSERT_EQUAL_UINT8(NL_PB(15), g95->pwm[NL_CH_WW].pin);
|
|
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"), false));
|
|
TEST_ASSERT_EQUAL_UINT8(3, nl_board_dmx_slots(nl_board_find("g95_cw"), false));
|
|
TEST_ASSERT_EQUAL_UINT8(6, nl_board_dmx_slots(nl_board_find("evk"), false));
|
|
/* Im rgb-Layout belegt JEDES Board drei Slots - eine Lampe ist ein Pixel. */
|
|
TEST_ASSERT_EQUAL_UINT8(3, nl_board_dmx_slots(nl_board_find("gu10_rgb"), true));
|
|
TEST_ASSERT_EQUAL_UINT8(3, nl_board_dmx_slots(nl_board_find("g95_cw"), true));
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_all_devices_present);
|
|
RUN_TEST(test_unknown_is_null);
|
|
RUN_TEST(test_rgb_flags_and_ct_ranges);
|
|
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();
|
|
}
|