From 8f260226b23bb062267d1c1d2cdbc25fed9af252 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 11:26:35 +0300 Subject: [PATCH 1/8] artnet: use EventGroup instead of task notify for input events --- components/artnet/artnet.c | 5 +++++ components/artnet/artnet.h | 2 +- components/artnet/include/artnet.h | 2 +- components/artnet/input.c | 18 +++++++----------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/components/artnet/artnet.c b/components/artnet/artnet.c index d45d4ac1..20e25dd6 100644 --- a/components/artnet/artnet.c +++ b/components/artnet/artnet.c @@ -52,6 +52,11 @@ int artnet_init(struct artnet *artnet, struct artnet_options options) LOG_ERROR("calloc(input_dmx)"); return -1; } + + if (!(artnet->input_events = xEventGroupCreate())) { + LOG_ERROR("xEventGroupCreate"); + return -1; + } } if (options.outputs) { diff --git a/components/artnet/artnet.h b/components/artnet/artnet.h index 5e397147..714f256b 100644 --- a/components/artnet/artnet.h +++ b/components/artnet/artnet.h @@ -79,7 +79,7 @@ struct artnet { struct artnet_dmx dmx; /* inputs */ - xTaskHandle input_task; + EventGroupHandle_t input_events; struct artnet_dmx *input_dmx; // last sync received at diff --git a/components/artnet/include/artnet.h b/components/artnet/include/artnet.h index 2a4c736b..bb391918 100644 --- a/components/artnet/include/artnet.h +++ b/components/artnet/include/artnet.h @@ -32,7 +32,7 @@ // limited by ARTNET_INPUT_TASK_INDEX_BITS #define ARTNET_INPUTS_MAX 16 -#define ARTNET_INPUT_TASK_INDEX_BITS 0xffff +#define ARTNET_INPUT_EVENT_BITS 0x00ffffff struct artnet; struct artnet_input; diff --git a/components/artnet/input.c b/components/artnet/input.c index b1f348ec..17be847d 100644 --- a/components/artnet/input.c +++ b/components/artnet/input.c @@ -64,27 +64,23 @@ void artnet_input_dmx(struct artnet_input *input, const struct artnet_dmx *dmx) xQueueOverwrite(input->queue, dmx); } - if (input->artnet->input_task) { - xTaskNotify(input->artnet->input_task, (1 << input->index) & ARTNET_INPUT_TASK_INDEX_BITS, eSetBits); + if (input->artnet->input_events) { + xEventGroupSetBits(input->artnet->input_events, (1 << input->index) & ARTNET_INPUT_EVENT_BITS); } } int artnet_inputs_main(struct artnet *artnet) { - uint32_t notify_bits; - - artnet->input_task = xTaskGetCurrentTaskHandle(); - for (;;) { - if (!xTaskNotifyWait(0, ARTNET_INPUT_TASK_INDEX_BITS, ¬ify_bits, portMAX_DELAY)) { - LOG_ERROR("xTaskNotifyWait"); - continue; - } + BaseType_t xClearOnExit = true; + BaseType_t xWaitForAllBits = false; + + EventBits_t event_bits = xEventGroupWaitBits(artnet->input_events, ARTNET_INPUT_EVENT_BITS, xClearOnExit, xWaitForAllBits, portMAX_DELAY); for (unsigned index = 0; index < artnet->input_count; index++) { struct artnet_input *input = &artnet->input_ports[index]; - if (!(notify_bits & (1 << index))) { + if (!(event_bits & (1 << index))) { continue; } From b9b19efbce326d13c98257803a8857af41fa4df5 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 13:22:58 +0300 Subject: [PATCH 2/8] artnet: replace artnet_dmx sync_mode with separate dmx/sync event bits, separate event_group for 24 output bits --- components/artnet/Kconfig | 2 +- components/artnet/artnet.c | 10 +++++++++ components/artnet/artnet.h | 4 ++++ components/artnet/include/artnet.h | 28 +++++++++++++------------ components/artnet/output.c | 33 +++++++++++++++++------------- components/artnet/protocol.c | 11 ---------- 6 files changed, 49 insertions(+), 39 deletions(-) diff --git a/components/artnet/Kconfig b/components/artnet/Kconfig index 796c25b8..83603d9d 100644 --- a/components/artnet/Kconfig +++ b/components/artnet/Kconfig @@ -2,7 +2,7 @@ menu "qmsk-artnet" config ARTNET_OUTPUTS_MAX int "Maximum Art-NET output universes" - range 0 20 + range 0 24 default 16 endmenu diff --git a/components/artnet/artnet.c b/components/artnet/artnet.c index 20e25dd6..dadab24a 100644 --- a/components/artnet/artnet.c +++ b/components/artnet/artnet.c @@ -160,3 +160,13 @@ void artnet_get_stats(struct artnet *artnet, struct artnet_stats *stats) stats->errors = stats_counter_copy(&artnet->stats.errors); stats->dmx_discard = stats_counter_copy(&artnet->stats.dmx_discard); } + +// node in synchronous DMX mode? +bool artnet_sync_state (struct artnet *artnet) +{ + if (artnet->sync_tick) { + return xTaskGetTickCount() - artnet->sync_tick < ARTNET_SYNC_TICKS; + } else { + return false; + } +} diff --git a/components/artnet/artnet.h b/components/artnet/artnet.h index 714f256b..3a687d44 100644 --- a/components/artnet/artnet.h +++ b/components/artnet/artnet.h @@ -46,6 +46,8 @@ void artnet_reset_inputs_stats(struct artnet *artnet); /* output.c */ struct artnet_output { + struct artnet *artnet; + enum artnet_port_type type; struct artnet_output_options options; struct artnet_output_state state; @@ -87,3 +89,5 @@ struct artnet { struct artnet_stats stats; }; + +bool artnet_sync_state (struct artnet *artnet); diff --git a/components/artnet/include/artnet.h b/components/artnet/include/artnet.h index bb391918..b0ea6b32 100644 --- a/components/artnet/include/artnet.h +++ b/components/artnet/include/artnet.h @@ -19,16 +19,12 @@ #define ARTNET_INPUT_NAME_MAX 16 #define ARTNET_OUTPUT_NAME_MAX 16 -// limited to 20 by the available FreeRTOS event group bits +// hard-limited to 24 by the available FreeRTOS event group bits #define ARTNET_OUTPUTS_MAX (CONFIG_ARTNET_OUTPUTS_MAX) -// up to 20 task notification bits for indexed outputs -// NOTE: FreeRTOS event groups only support 24 bits... -#define ARTNET_OUTPUT_EVENT_INDEX_BITS 0x000fffff -#define ARTNET_OUTPUT_EVENT_FLAG_BITS 0x00f00000 - -// flag bit to force output sync -#define ARTNET_OUTPUT_EVENT_SYNC_BIT 20 +// up to 24 task notification bits for indexed outputs +// NOTE: FreeRTOS event groups only support 24 bits +#define ARTNET_OUTPUT_EVENT_BITS 0x00ffffff // limited by ARTNET_INPUT_TASK_INDEX_BITS #define ARTNET_INPUTS_MAX 16 @@ -59,9 +55,6 @@ struct artnet_options { }; struct artnet_dmx { - // flags - uint8_t sync_mode : 1; // receiver is in sync mode, wait for sync event before refreshing output - // received sequence number uint8_t seq : 8; @@ -90,8 +83,17 @@ struct artnet_output_options { /* Set event group bits on DMX updates */ EventGroupHandle_t event_group; - /* Only bits matching ARTNET_OUTPUT_EVENT_INDEX_BITS are supported */ - EventBits_t event_bits; + /* Only bits matching ARTNET_OUTPUTS_EVENT_BITS are supported */ + EventBits_t dmx_event_bit; + + /* Only bits matching ARTNET_OUTPUTS_EVENT_BITS are supported */ + EventBits_t sync_event_bit; + + /* Set event group bits on DMX sync/update */ + EventGroupHandle_t output_events; + + /* Only bits matching ARTNET_OUTPUTS_EVENT_BITS are supported */ + EventBits_t output_event_bit; }; struct artnet_input_state { diff --git a/components/artnet/output.c b/components/artnet/output.c index 4ba959f9..26f38672 100644 --- a/components/artnet/output.c +++ b/components/artnet/output.c @@ -22,16 +22,16 @@ int artnet_add_output(struct artnet *artnet, struct artnet_output **outputp, str return -1; } - if (!options.event_group) { - LOG_DEBUG("event_bits unused"); - } else if (!options.event_bits) { - LOG_ERROR("event_bits=%08x empty", options.event_bits); + if (!options.output_events) { + LOG_DEBUG("output_events unused"); + } else if (!options.output_event_bit) { + LOG_ERROR("output_event_bit=%08x empty", options.output_event_bit); return -1; - } else if ((options.event_bits & ~ARTNET_OUTPUT_EVENT_INDEX_BITS)) { - LOG_ERROR("event_bits=%08x overflow", options.event_bits); + } else if ((options.output_event_bit & ~ARTNET_OUTPUT_EVENT_BITS)) { + LOG_ERROR("output_event_bit=%08x overflow", options.output_event_bit); return -1; } else { - LOG_DEBUG("event_bits=%08x", options.event_bits); + LOG_DEBUG("output_event_bit=%08x", options.output_event_bit); } LOG_DEBUG("output=%d address=%04x", artnet->output_count, options.address); @@ -43,6 +43,7 @@ int artnet_add_output(struct artnet *artnet, struct artnet_output **outputp, str struct artnet_output *output = &artnet->output_ports[artnet->output_count++]; + output->artnet = artnet; output->type = ARTNET_PORT_TYPE_DMX; output->options = options; output->queue = queue; @@ -208,10 +209,6 @@ void artnet_output_dmx(struct artnet_output *output, struct artnet_dmx *dmx) output->state.tick = tick; - if (dmx->sync_mode) { - stats_counter_increment(&output->stats.dmx_sync); - } - // attempt normal send first, before overwriting for overflow stats if (xQueueSend(output->queue, dmx, 0) == errQUEUE_FULL) { stats_counter_increment(&output->stats.queue_overwrite); @@ -219,8 +216,16 @@ void artnet_output_dmx(struct artnet_output *output, struct artnet_dmx *dmx) xQueueOverwrite(output->queue, dmx); } - if (output->options.event_group) { - xEventGroupSetBits(output->options.event_group, output->options.event_bits); + if (output->options.output_events) { + xEventGroupSetBits(output->options.output_events, output->options.output_event_bit); + } + + if (artnet_sync_state(output->artnet)) { + // wait for hard sync + stats_counter_increment(&output->stats.dmx_sync); + } else if (output->options.event_group && output->options.dmx_event_bit) { + // sync each update + xEventGroupSetBits(output->options.event_group, output->options.dmx_event_bit); } } @@ -262,7 +267,7 @@ int artnet_sync_outputs(struct artnet *artnet) stats_counter_increment(&output->stats.sync_recv); if (output->options.event_group != event_group) { - xEventGroupSetBits(output->options.event_group, (1 << ARTNET_OUTPUT_EVENT_SYNC_BIT)); + xEventGroupSetBits(output->options.event_group, output->options.sync_event_bit); // only notify each event_group once event_group = output->options.event_group; diff --git a/components/artnet/protocol.c b/components/artnet/protocol.c index 4ef986eb..d59c6928 100644 --- a/components/artnet/protocol.c +++ b/components/artnet/protocol.c @@ -151,16 +151,6 @@ int artnet_sendrecv_poll(struct artnet *artnet, struct artnet_sendrecv *sendrecv return artnet_send_poll_reply(artnet, sendrecv); } -// node in synchronous DMX mode? -static bool artnet_sync_state (struct artnet *artnet) -{ - if (artnet->sync_tick) { - return xTaskGetTickCount() - artnet->sync_tick < ARTNET_SYNC_TICKS; - } else { - return false; - } -} - int artnet_recv_dmx(struct artnet *artnet, const struct artnet_sendrecv *recv) { struct artnet_packet_dmx *dmx = &recv->packet->dmx; @@ -198,7 +188,6 @@ int artnet_recv_dmx(struct artnet *artnet, const struct artnet_sendrecv *recv) #endif // copy - artnet->dmx.sync_mode = artnet_sync_state(artnet); artnet->dmx.seq = seq; artnet->dmx.len = len; From c85793553558d43d3c37c51fcb6ff95050b3512f Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 14:20:10 +0300 Subject: [PATCH 3/8] main: leds sync_none stats counter --- main/leds_artnet.c | 2 ++ main/leds_cmd.c | 1 + main/leds_stats.c | 1 + main/leds_stats.h | 1 + 4 files changed, 5 insertions(+) diff --git a/main/leds_artnet.c b/main/leds_artnet.c index abfbea02..739313f1 100644 --- a/main/leds_artnet.c +++ b/main/leds_artnet.c @@ -107,6 +107,8 @@ static bool leds_artnet_sync_check(struct leds_state *state) if (!config->artnet_sync_timeout && state->artnet->sync_bits) { // any output set, free-running + stats_counter_increment(&stats->sync_none); + return true; } diff --git a/main/leds_cmd.c b/main/leds_cmd.c index 3cbaa6b8..0d2f6fe7 100644 --- a/main/leds_cmd.c +++ b/main/leds_cmd.c @@ -399,6 +399,7 @@ int leds_cmd_stats(int argc, char **argv, void *ctx) printf("\n"); print_stats_counter("artnet", "timeout", &stats->artnet_timeout); print_stats_counter("artnet", "sync", &stats->artnet_sync); + print_stats_counter("sync", "none", &stats->sync_none); print_stats_counter("sync", "timeout", &stats->sync_timeout); print_stats_counter("sync", "missed", &stats->sync_missed); print_stats_counter("sync", "full", &stats->sync_full); diff --git a/main/leds_stats.c b/main/leds_stats.c index 0865d22c..be472ca5 100644 --- a/main/leds_stats.c +++ b/main/leds_stats.c @@ -24,6 +24,7 @@ void init_leds_stats() stats_counter_init(&stats->artnet_timeout); stats_counter_init(&stats->artnet_sync); + stats_counter_init(&stats->sync_none); stats_counter_init(&stats->sync_timeout); stats_counter_init(&stats->sync_missed); stats_counter_init(&stats->sync_full); diff --git a/main/leds_stats.h b/main/leds_stats.h index c7c7a4b1..79a6038b 100644 --- a/main/leds_stats.h +++ b/main/leds_stats.h @@ -18,6 +18,7 @@ struct leds_stats { struct stats_counter artnet_timeout; struct stats_counter artnet_sync; + struct stats_counter sync_none; struct stats_counter sync_timeout; struct stats_counter sync_missed; struct stats_counter sync_full; From 920b631165e01bf3fed631abbd4e1d0e1bab599d Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 14:24:27 +0300 Subject: [PATCH 4/8] main: leds artnet with separate dmx/sync and output event bits --- main/leds_artnet.c | 57 +++++++++++++++++++++++++++------------------- main/leds_artnet.h | 1 + main/leds_task.c | 11 +++++---- main/leds_task.h | 12 ++++++---- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/main/leds_artnet.c b/main/leds_artnet.c index 739313f1..f8165662 100644 --- a/main/leds_artnet.c +++ b/main/leds_artnet.c @@ -3,6 +3,7 @@ #include "leds_config.h" #include "leds_state.h" #include "leds_stats.h" +#include "leds_task.h" #include "leds_test.h" #include "artnet_state.h" @@ -242,11 +243,11 @@ static int leds_artnet_timeout(struct leds_state *state) bool leds_artnet_active(struct leds_state *state, EventBits_t event_bits) { - if ((event_bits & ARTNET_OUTPUT_EVENT_INDEX_BITS)) { + if (event_bits & (1 << LEDS_EVENT_ARTNET_DMX_BIT)) { return true; } - if (event_bits & (1 << ARTNET_OUTPUT_EVENT_SYNC_BIT)) { + if (event_bits & (1 << LEDS_EVENT_ARTNET_SYNC_BIT)) { return true; } @@ -273,47 +274,50 @@ int leds_artnet_update(struct leds_state *state, EventBits_t event_bits) { struct leds_stats *stats = &leds_stats[state->index]; - bool data = event_bits & ARTNET_OUTPUT_EVENT_INDEX_BITS; - bool sync = event_bits & (1 << ARTNET_OUTPUT_EVENT_SYNC_BIT); + EventBits_t data_bits = xEventGroupClearBits(state->artnet->event_group, ARTNET_OUTPUT_EVENT_BITS); + + bool dmx = event_bits & (1 << LEDS_EVENT_ARTNET_DMX_BIT); + bool sync = event_bits & (1 << LEDS_EVENT_ARTNET_SYNC_BIT); bool miss = false; - bool sync_mode = false; bool update = false; bool timeout = false; if (state->test) { - if (data || sync) { + if (dmx || sync) { // clear any test mode output leds_test_clear(state); } } if (state->artnet->sync_missed) { - miss = true; - LOG_DEBUG("event_bits=%08x + sync_missed=%08x", event_bits, state->artnet->sync_missed); // handle updates with missed sync - event_bits |= state->artnet->sync_missed; + data_bits |= state->artnet->sync_missed; + miss = true; state->artnet->sync_missed = 0; } - if (data || miss) { + if (data_bits) { // set output from artnet universe for (unsigned index = 0; index < state->artnet->universe_count; index++) { - if (!(event_bits & (1 << index))) { + if (!(data_bits & (1 << index))) { continue; } - if (leds_artnet_sync_set(state, index)) { - // normal update + if (sync) { + // skip soft-sync + } else if (leds_artnet_sync_set(state, index)) { + // normal soft-sync update } else if (miss) { + // catch up to missed soft-sync update LOG_DEBUG("hit index=%u", index); } else { LOG_DEBUG("skip index=%u", index); - // skip for update of previous missed sync + // update with previous value for missed sync, will be read for next update continue; } @@ -327,28 +331,21 @@ int leds_artnet_update(struct leds_state *state, EventBits_t event_bits) LOG_WARN("leds%d: leds_artnet_set", state->index + 1); continue; } - - if (state->artnet->dmx.sync_mode) { - sync_mode = true; - } } } if (sync) { - // hard sync + // hard art-net sync stats_counter_increment(&stats->artnet_sync); update = true; - } else if (sync_mode) { - // wait for hard sync - update = false; } else if (leds_artnet_sync_check(state)) { // soft sync update = true; } // timeouts - if (data || sync) { + if (dmx || sync) { state->artnet->dmx_tick = xTaskGetTickCount(); leds_artnet_timeout_reset(state); } else if (state->artnet->timeout_tick) { @@ -412,6 +409,11 @@ int init_leds_artnet(struct leds_state *state, int index, const struct leds_conf return -1; } + if (!(state->artnet->event_group = xEventGroupCreate())) { + LOG_ERROR("xEventGroupCreate"); + return -1; + } + leds_artnet_timeout_reset(state); return 0; @@ -429,8 +431,15 @@ int start_leds_artnet(struct leds_state *state, const struct leds_config *config struct artnet_output_options options = { .address = artnet_address(config->artnet_net, config->artnet_subnet, artnet_universe), + + // wake up task on each art-net sync/update .event_group = state->event_group, - .event_bits = (1 << i), + .dmx_event_bit = (1 << LEDS_EVENT_ARTNET_DMX_BIT), + .sync_event_bit = (1 << LEDS_EVENT_ARTNET_SYNC_BIT), + + // mark updated outputs via a separate event group + .output_events = state->artnet->event_group, + .output_event_bit = (1 << i), }; snprintf(options.name, sizeof(options.name), "leds%u", state->index + 1); diff --git a/main/leds_artnet.h b/main/leds_artnet.h index 78f5ea09..c95e3660 100644 --- a/main/leds_artnet.h +++ b/main/leds_artnet.h @@ -15,6 +15,7 @@ struct leds_artnet_state { TickType_t dmx_tick; // last dmx frame struct artnet_dmx dmx; struct artnet_output **outputs; + EventGroupHandle_t event_group; unsigned sync_bits; // bitmask of outputs waiting for sync unsigned sync_missed; // bitmask of outputs with missed sync diff --git a/main/leds_task.c b/main/leds_task.c index 2f0bca2e..5b2eb2a7 100644 --- a/main/leds_task.c +++ b/main/leds_task.c @@ -85,12 +85,13 @@ static EventBits_t leds_task_wait(struct leds_state *state) const bool clear_on_exit = true; const bool wait_for_all_bits = false; - EventBits_t event_bits = xEventGroupWaitBits(state->event_group, ARTNET_OUTPUT_EVENT_INDEX_BITS | ARTNET_OUTPUT_EVENT_FLAG_BITS, clear_on_exit, wait_for_all_bits, wait_ticks); + EventBits_t event_bits = xEventGroupWaitBits(state->event_group, LEDS_EVENT_BITS, clear_on_exit, wait_for_all_bits, wait_ticks); - LOG_DEBUG("leds%d: artnet=%04x artnet_sync=%d test=%d", state->index + 1, - (event_bits & ARTNET_OUTPUT_EVENT_INDEX_BITS), - !!(event_bits & (1 << ARTNET_OUTPUT_EVENT_SYNC_BIT)), - !!(event_bits & (1 << LEDS_EVENT_TEST_BIT)) + LOG_DEBUG("leds%d: test=%d artnet_dmx=%d artnet_sync=%d sequence=%d", state->index + 1, + !!(event_bits & (1 << LEDS_EVENT_TEST_BIT)), + !!(event_bits & (1 << LEDS_EVENT_ARTNET_DMX_BIT)), + !!(event_bits & (1 << LEDS_EVENT_ARTNET_SYNC_BIT)), + !!(event_bits & (1 << LEDS_EVENT_SEQUENCE_BIT)) ); return event_bits; diff --git a/main/leds_task.h b/main/leds_task.h index 65c51efa..681fd90d 100644 --- a/main/leds_task.h +++ b/main/leds_task.h @@ -5,10 +5,14 @@ #include -// compatible with ARTNET_OUTPUT_EVENT_*_BIT/BITS -#define LEDS_EVENT_SYNC_BIT (ARTNET_OUTPUT_EVENT_SYNC_BIT + 0) -#define LEDS_EVENT_TEST_BIT (ARTNET_OUTPUT_EVENT_SYNC_BIT + 1) -#define LEDS_EVENT_SEQUENCE_BIT (ARTNET_OUTPUT_EVENT_SYNC_BIT + 2) +#define LEDS_EVENT_BITS 0x0f + +enum leds_event_bit { + LEDS_EVENT_TEST_BIT = 0, + LEDS_EVENT_ARTNET_DMX_BIT = 1, + LEDS_EVENT_ARTNET_SYNC_BIT = 2, + LEDS_EVENT_SEQUENCE_BIT = 3, +}; int init_leds_task(struct leds_state *state, const struct leds_config *config); int start_leds_task(struct leds_state *state, const struct leds_config *config); From 757d95a54fd4b8829e063d69a5a7b24847fa338f Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 15:37:15 +0300 Subject: [PATCH 5/8] main: artnet stats max inputs/outputs --- main/artnet_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/artnet_cmd.c b/main/artnet_cmd.c index cb58a3a3..073eedbe 100644 --- a/main/artnet_cmd.c +++ b/main/artnet_cmd.c @@ -55,7 +55,7 @@ int artnet_cmd_info(int argc, char **argv, void *ctx) } printf("\n"); - printf("Inputs: count=%u\n", input_count); + printf("Inputs: count=%u / max=%d\n", input_count, ARTNET_INPUTS_MAX); for (int i = 0; i < inputs_size && i < ARTNET_INPUTS_MAX; i++) { struct artnet_input_options *options = &artnet_input_options[i]; @@ -76,7 +76,7 @@ int artnet_cmd_info(int argc, char **argv, void *ctx) } printf("\n"); - printf("Outputs: count=%u\n", output_count); + printf("Outputs: count=%u / max=%d\n", output_count, ARTNET_OUTPUTS_MAX); for (int i = 0; i < outputs_size && i < ARTNET_OUTPUTS_MAX; i++) { struct artnet_output_options *options = &artnet_output_options[i]; From 2fc2eb57678d9749170324e9d45ecde9e8fc5806 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 15:37:58 +0300 Subject: [PATCH 6/8] projects/esp32: tune eth DMA memory usage, increase lwip mailbox size for 24 art-net universes --- components/artnet/Kconfig | 4 +++- projects/esp32/sdkconfig.defaults | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/components/artnet/Kconfig b/components/artnet/Kconfig index 83603d9d..cf4aaeef 100644 --- a/components/artnet/Kconfig +++ b/components/artnet/Kconfig @@ -3,6 +3,8 @@ menu "qmsk-artnet" int "Maximum Art-NET output universes" range 0 24 - default 16 + default 4 + help + Must be less than CONFIG_LWIP_UDP_RECVMBOX_SIZE and CONFIG_LWIP_TCPIP_RECVMBOX_SIZE to avoid dropped packets. endmenu diff --git a/projects/esp32/sdkconfig.defaults b/projects/esp32/sdkconfig.defaults index 90ed6017..0d3bec43 100644 --- a/projects/esp32/sdkconfig.defaults +++ b/projects/esp32/sdkconfig.defaults @@ -38,15 +38,21 @@ CONFIG_ETH_USE_SPI_ETHERNET=n CONFIG_ETH_USE_OPENETH=n ## Art-Net -CONFIG_ARTNET_OUTPUTS_MAX=20 +CONFIG_ARTNET_OUTPUTS_MAX=24 -# Tune ethernet -> lwip receive stack to handle UDP bursts for 20 artnet universes -CONFIG_ETH_DMA_BUFFER_SIZE=1600 -CONFIG_ETH_DMA_RX_BUFFER_NUM=24 +# Art-Net packet size will be 20-60 IP + 8 UDP + 12 + 6 Art-Net + 0-512 payload = 598 max +# Ethernet frame size is 14 + 4 + 1500 + 4 = 1522 max +# An Ethernet frame can be split across multiple DMA buffers, but a DMA buffer cannot contain muliple frames +# Choose a DMA buffer size to fit either one Art-Net frame (598) or half a maximum-sized frame (1522/2 = 761) +CONFIG_ETH_DMA_BUFFER_SIZE=768 + +# Tune ethernet -> lwip receive stack to handle UDP bursts for 24-32 artnet universes +CONFIG_ETH_DMA_RX_BUFFER_NUM=30 # Tune TX for ArtPollReply bursts (1/4 of universe count) CONFIG_ETH_DMA_TX_BUFFER_NUM=10 -# critically important for handling UDP bursts +# critically important for handling UDP bursts # the default =6 causes the majority of packets to be dropped for artnet universes >6 -CONFIG_LWIP_UDP_RECVMBOX_SIZE=24 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=32 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 From a404e27dc32e693565e5afd19eb7cec0aa1e81f6 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 15:47:29 +0300 Subject: [PATCH 7/8] leds artnet: do not soft-sync on data in art-net sync mode --- main/leds_artnet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/leds_artnet.c b/main/leds_artnet.c index f8165662..43615880 100644 --- a/main/leds_artnet.c +++ b/main/leds_artnet.c @@ -300,7 +300,8 @@ int leds_artnet_update(struct leds_state *state, EventBits_t event_bits) state->artnet->sync_missed = 0; } - if (data_bits) { + // wait until either artnet-sync or (non-sync) dmx to not trigger soft-sync on partial data in artnet sync mode + if (dmx || sync || miss) { // set output from artnet universe for (unsigned index = 0; index < state->artnet->universe_count; index++) { if (!(data_bits & (1 << index))) { From 3708bf7c800d1ff932ce3de6874aa9f456bfb6dc Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 5 Jul 2025 16:43:35 +0300 Subject: [PATCH 8/8] leds: validate artnet_dmx_leds, artnet_universe_count config --- main/leds_artnet.c | 4 ++-- main/leds_config.c | 43 +++++++++++++++++++++++++++++++++++++++++-- main/leds_config.h | 3 +++ main/leds_configtab.i | 4 ++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/main/leds_artnet.c b/main/leds_artnet.c index 43615880..cbeae749 100644 --- a/main/leds_artnet.c +++ b/main/leds_artnet.c @@ -14,7 +14,7 @@ #define SYNC_BIT(index) (1 << (index)) #define SYNC_BITS_MASK(count) ((1 << (count)) - 1) -static unsigned config_leds_artnet_universe_leds_count(const struct leds_config *config) +unsigned config_leds_artnet_universe_leds_count(const struct leds_config *config) { unsigned universe_leds_count = config->artnet_dmx_leds; @@ -25,7 +25,7 @@ static unsigned config_leds_artnet_universe_leds_count(const struct leds_config return universe_leds_count; } -static unsigned config_leds_artnet_universe_count(const struct leds_config *config) +unsigned config_leds_artnet_universe_count(const struct leds_config *config) { unsigned universe_leds_count = config_leds_artnet_universe_leds_count(config); unsigned universe_count = config->artnet_universe_count; diff --git a/main/leds_config.c b/main/leds_config.c index afa120c4..fde5f7d6 100644 --- a/main/leds_config.c +++ b/main/leds_config.c @@ -145,8 +145,47 @@ static int validate_artnet_leds_group (config_invalid_handler_t *handler, const if (universe_leds_count == 0) { handler(path, ctx, "LEDs group does not fit into one Art-NET universe with format %s", - config_enum_to_string(leds_format_enum, config->artnet_leds_format), - config->artnet_leds_group + config_enum_to_string(leds_format_enum, config->artnet_leds_format) + ); + + return 1; + } + + return 0; +} + +static int validate_artnet_dmx_leds (config_invalid_handler_t *handler, const struct config_path path, void *ctx) +{ + struct leds_config *config = path.tab->ctx; + + // maximum number of pixels that fit into one Art-Net universe + unsigned universe_leds_count = leds_format_count(ARTNET_DMX_SIZE, config->artnet_leds_format, config->artnet_leds_group); + + if (!config->artnet_dmx_leds) { + // auto, per artnet_leds_group + return 0; + + } else if (config->artnet_dmx_leds > universe_leds_count) { + handler(path, ctx, "LEDs do not fit into one Art-NET universe with format %s", + config_enum_to_string(leds_format_enum, config->artnet_leds_format) + ); + + return 1; + } + + return 0; +} + +static int validate_artnet_universe_count (config_invalid_handler_t *handler, const struct config_path path, void *ctx) +{ + struct leds_config *config = path.tab->ctx; + + unsigned artnet_universe_count = config_leds_artnet_universe_count(config); + + if (artnet_universe_count > ARTNET_OUTPUTS_MAX) { + handler(path, ctx, "Art-Net universe count %d exceeds maximum of %d", + artnet_universe_count, + ARTNET_OUTPUTS_MAX ); return 1; diff --git a/main/leds_config.h b/main/leds_config.h index 469e48f5..e553d667 100644 --- a/main/leds_config.h +++ b/main/leds_config.h @@ -190,6 +190,9 @@ extern const struct config_file_path leds_sequence_paths[]; int config_leds(struct leds_state *state, const struct leds_config *config); +unsigned config_leds_artnet_universe_leds_count(const struct leds_config *config); +unsigned config_leds_artnet_universe_count(const struct leds_config *config); + #if CONFIG_LEDS_GPIO_ENABLED int config_leds_gpio(struct leds_state *state, const struct leds_config *config, enum leds_interface interface, struct leds_interface_options_gpio *options); #endif diff --git a/main/leds_configtab.i b/main/leds_configtab.i index bffe856a..599558a4 100644 --- a/main/leds_configtab.i +++ b/main/leds_configtab.i @@ -143,6 +143,8 @@ const struct configtab LEDS_CONFIGTAB[] = { { CONFIG_TYPE_UINT16, "artnet_universe_count", .description = "Output from multiple Art-Net DMX universes. Default 0 -> automatic, enough to fit all LEDs", .uint16_type = { .value = &LEDS_CONFIG.artnet_universe_count, .max = ARTNET_OUTPUTS_MAX }, + .validate_func = validate_artnet_universe_count, + .ctx = &LEDS_CONFIG, }, { CONFIG_TYPE_UINT16, "artnet_universe_step", .description = "Output from non-consecutive Art-Net DMX universes, at artnet_universe_start + i*artnet_universe_step. Default 1, special-case 0 -> output multiple copies of the same universe", @@ -156,6 +158,8 @@ const struct configtab LEDS_CONFIGTAB[] = { .alias = "artnet_universe_leds", .description = "Limit number of LEDs per Art-Net DMX universe. Default 0 -> as many LEDs as will fit with the given format (170 or 128).", .uint16_type = { .value = &LEDS_CONFIG.artnet_dmx_leds, .max = LEDS_ARTNET_UNIVERSE_LEDS }, + .validate_func = validate_artnet_dmx_leds, + .ctx = &LEDS_CONFIG, }, { CONFIG_TYPE_UINT16, "artnet_dmx_timeout", .description = "Clear LED outputs after given milliseconds without any DMX updates. Default 0 -> hold output.",