feat(sacn): Uebernahme-Schicht mit Timeout, Prioritaet und Sequenz

This commit is contained in:
Michael Sauer
2026-09-20 00:08:49 +02:00
parent 6be0473f18
commit fae22f8c64
5 changed files with 437 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
#include "light/nl_dmx.h"
#include <string.h>
#include "light/nl_engine.h"
static const nl_board_t *s_board;
static uint8_t s_slots; /* 3 oder 6 */
static nl_dmx_cfg_t s_cfg;
static bool s_streaming; /* es fliessen Pakete */
static bool s_override; /* die Engine haelt unsere Kanaele */
static uint8_t s_prio;
static uint8_t s_seq;
static uint32_t s_last_ms;
void nl_dmx_init(const nl_board_t *board)
{
s_board = board;
s_slots = nl_board_dmx_slots(board);
s_cfg.start_slot = 1;
s_cfg.hold = false;
s_cfg.release_fade_ms = 0;
s_streaming = false;
s_override = false;
s_prio = 0;
s_seq = 0;
s_last_ms = 0;
}
void nl_dmx_configure(const nl_dmx_cfg_t *cfg) { s_cfg = *cfg; }
bool nl_dmx_active(void) { return s_override; }
void nl_dmx_release(void)
{
if (s_override)
nl_engine_override_clear(s_cfg.release_fade_ms);
s_override = false;
s_streaming = false;
s_prio = 0;
}
/* Der Strom ist zu Ende - entweder durch das terminated-Bit oder durch Timeout. Im
* "release"-Modus geben wir den Override frei und blenden zurueck zum Zustand von
* nl_ctrl; im "hold"-Modus bleiben die zuletzt gesetzten Kanalwerte einfach stehen. */
static void stream_ended(void)
{
s_streaming = false;
s_prio = 0;
if (!s_cfg.hold && s_override) {
nl_engine_override_clear(s_cfg.release_fade_ms);
s_override = false;
}
}
static uint8_t scale(uint8_t v, uint8_t dim)
{
return (uint8_t)(((uint16_t)v * (uint16_t)dim + 127u) / 255u);
}
void nl_dmx_feed(const nl_e131_packet_t *p, uint32_t now_ms)
{
const uint8_t *s;
uint8_t ch[NL_CH_COUNT];
uint8_t dim;
if (s_board == NULL)
return;
/* Reicht das Paket bis zu unserem letzten Slot? Ein halb belegter Satz ergaebe auf den
* restlichen Kanaelen willkuerliche Werte - also gar nicht erst anwenden. */
if ((uint32_t)s_cfg.start_slot + s_slots - 1u > (uint32_t)p->slot_count)
return;
if (s_streaming) {
if (p->priority < s_prio)
return;
{
int8_t d = (int8_t)(p->sequence - s_seq);
if (d <= 0 && d > -NL_DMX_SEQ_WINDOW)
return; /* veraltet oder Wiederholung */
}
}
s_prio = p->priority;
s_seq = p->sequence;
s_last_ms = now_ms;
if (p->terminated) { /* die Quelle sagt selbst, dass sie fertig ist */
stream_ended();
return;
}
s_streaming = true;
s = p->slots + (s_cfg.start_slot - 1u);
dim = s[0];
memset(ch, 0, sizeof ch);
if (s_board->has_rgb) {
ch[NL_CH_R] = scale(s[1], dim);
ch[NL_CH_G] = scale(s[2], dim);
ch[NL_CH_B] = scale(s[3], dim);
ch[NL_CH_CW] = scale(s[4], dim);
ch[NL_CH_WW] = scale(s[5], dim);
} else {
ch[NL_CH_CW] = scale(s[1], dim);
ch[NL_CH_WW] = scale(s[2], dim);
}
nl_engine_override(ch);
s_override = true;
}
void nl_dmx_tick(uint32_t now_ms)
{
if (!s_streaming)
return;
if (now_ms - s_last_ms >= NL_DMX_TIMEOUT_MS)
stream_ended();
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef NL_DMX_H
#define NL_DMX_H
#include <stdbool.h>
#include <stdint.h>
#include "board/nl_board.h"
#include "net/nl_e131.h"
#define NL_DMX_TIMEOUT_MS 2500u /* E1.31, Abschnitt 6.7.1 */
#define NL_DMX_SEQ_WINDOW 20 /* E1.31, Abschnitt 6.7.2 */
typedef struct {
uint16_t start_slot; /* 1..512 */
bool hold; /* false = nach dem Timeout zurueck zum Zustand von nl_ctrl */
uint16_t release_fade_ms; /* Dauer der Rueckblende (aus settings.fade_in_ms) */
} nl_dmx_cfg_t;
/* Die Engine muss initialisiert sein. Nicht threadsicher: Aufrufer serialisieren. */
void nl_dmx_init(const nl_board_t *board);
void nl_dmx_configure(const nl_dmx_cfg_t *cfg);
/* Ein vom Parser angenommenes Paket. Prueft Sequenz, Prioritaet und Slotbereich und setzt
* bei Annahme den Override. */
void nl_dmx_feed(const nl_e131_packet_t *p, uint32_t now_ms);
/* Aus dem Licht-Task (alle ~10 ms): prueft den Timeout. */
void nl_dmx_tick(uint32_t now_ms);
/* Ein HA-/REST-Befehl holt die Kontrolle zurueck - in beiden Timeout-Modi. */
void nl_dmx_release(void);
/* true, solange die Lampe ferngesteuert ist, einschliesslich der Haltephase. */
bool nl_dmx_active(void);
#endif