Add ArtNetDMX Pico W firmware with dual DMX and docs.

Art-Net to two RS-485 outputs over Wi-Fi (AP or STA with SoftAP fallback), web UI, mDNS, and QLC+/ArtPoll notes for unicast on STA.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-11 00:00:23 +12:00
co-authored by Cursor
commit fea6dffa74
26 changed files with 3013 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/stdio_usb.h"
#include "pico/cyw43_arch.h"
#include "hardware/pio.h"
#include "board.h"
#include "config.h"
#include "channels.h"
#include "dmx_out.h"
#include "artnet.h"
#include "wifi_mgr.h"
#include "http_server.h"
static dmx_out_t g_dmx[BOARD_NUM_UNIVERSES];
static uint8_t g_frames[BOARD_NUM_UNIVERSES][BOARD_DMX_FRAME_LEN];
static bool dmx_timer_cb(repeating_timer_t *rt) {
(void)rt;
for (unsigned u = 0; u < BOARD_NUM_UNIVERSES; u++) {
if (dmx_out_busy(&g_dmx[u])) {
continue;
}
channels_build_frame(u, g_frames[u]);
dmx_out_write(&g_dmx[u], g_frames[u], BOARD_DMX_FRAME_LEN);
}
return true;
}
int main(void) {
stdio_init_all();
/* Give USB CDC a moment so early boot logs are visible after flash/reboot. */
for (int i = 0; i < 40 && !stdio_usb_connected(); i++) {
sleep_ms(50);
}
sleep_ms(200);
printf("\nArtNetDMX starting\n");
fflush(stdout);
config_init();
channels_init();
if (!dmx_out_init(&g_dmx[0], BOARD_DMX_A_PIN, pio0)) {
printf("DMX A init failed\n");
}
if (!dmx_out_init(&g_dmx[1], BOARD_DMX_B_PIN, pio1)) {
printf("DMX B init failed\n");
}
if (!wifi_mgr_start(config_get())) {
printf("WiFi start failed\n");
while (true) {
tight_loop_contents();
}
}
if (!http_server_start()) {
printf("HTTP start failed\n");
}
if (!artnet_start()) {
printf("Art-Net start failed\n");
}
static repeating_timer_t dmx_timer;
if (!add_repeating_timer_ms(-BOARD_DMX_PERIOD_MS, dmx_timer_cb, NULL, &dmx_timer)) {
printf("DMX timer failed\n");
} else {
printf("DMX output at %d Hz\n", BOARD_DMX_HZ);
}
printf("Ready — mode=%s primary=%s mDNS http://%s/\n",
wifi_mode_name(wifi_mgr_active_mode()),
wifi_mgr_ip_str(),
wifi_mgr_mdns_name());
if (wifi_mgr_ap_up()) {
printf("SoftAP http://%s/ (ssid ArtNetDMX)\n", wifi_mgr_ap_ip_str());
} else {
printf("Joining STA (SoftAP starts only if join fails)...\n");
}
while (true) {
wifi_mgr_poll();
artnet_poll();
tight_loop_contents();
sleep_ms(10);
}
}