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:
@@ -0,0 +1,14 @@
|
||||
#ifndef ARTNET_H
|
||||
#define ARTNET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool artnet_start(void);
|
||||
/** Re-bind listeners to current AP/STA IPs (call after SoftAP re-assert or STA join). */
|
||||
void artnet_refresh(void);
|
||||
/** Broadcast ArtPollReply (call after interface comes up). */
|
||||
void artnet_announce(void);
|
||||
/** Periodic discovery announces (call from main loop). */
|
||||
void artnet_poll(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef BOARD_H
|
||||
#define BOARD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** DMX universe A (port 0) TX pin → RS-485 DI */
|
||||
#define BOARD_DMX_A_PIN 0
|
||||
/** DMX universe B (port 1) TX pin → RS-485 DI */
|
||||
#define BOARD_DMX_B_PIN 1
|
||||
|
||||
#define BOARD_NUM_UNIVERSES 2
|
||||
#define BOARD_DMX_CHANNELS 512
|
||||
#define BOARD_DMX_FRAME_LEN (1 + BOARD_DMX_CHANNELS) /* start code + 512 */
|
||||
|
||||
#define BOARD_DMX_HZ 40
|
||||
#define BOARD_DMX_PERIOD_MS (1000 / BOARD_DMX_HZ)
|
||||
|
||||
#define BOARD_DEFAULT_UNIVERSE_A 0
|
||||
#define BOARD_DEFAULT_UNIVERSE_B 1
|
||||
|
||||
#define BOARD_AP_SSID_DEFAULT "ArtNetDMX"
|
||||
#define BOARD_AP_PASS_DEFAULT "artnetdmx"
|
||||
#define BOARD_HOSTNAME "artnetdmx"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef CHANNELS_H
|
||||
#define CHANNELS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "board.h"
|
||||
|
||||
void channels_init(void);
|
||||
|
||||
/** Returns pointer to 512 channel values for universe index 0 or 1 (no start code). */
|
||||
uint8_t *channels_get(unsigned universe);
|
||||
|
||||
/** Build a 513-byte DMX frame (start code 0 + 512 channels) into out. */
|
||||
void channels_build_frame(unsigned universe, uint8_t out[BOARD_DMX_FRAME_LEN]);
|
||||
|
||||
void channels_set(unsigned universe, unsigned channel, uint8_t value);
|
||||
void channels_set_range(unsigned universe, const uint8_t *values, size_t len);
|
||||
void channels_clear(unsigned universe);
|
||||
|
||||
void channels_note_artnet(unsigned universe);
|
||||
uint32_t channels_artnet_packets(void);
|
||||
uint32_t channels_ms_since_artnet(unsigned universe);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "board.h"
|
||||
|
||||
#define CONFIG_MAGIC 0x414E4458u /* 'ANDX' */
|
||||
#define CONFIG_VERSION 3
|
||||
|
||||
#define CONFIG_SSID_MAX 32
|
||||
#define CONFIG_PASS_MAX 64
|
||||
#define CONFIG_HOSTNAME_MAX 32
|
||||
|
||||
typedef enum {
|
||||
WIFI_MODE_AP = 0,
|
||||
WIFI_MODE_STA = 1,
|
||||
/** Legacy flash value; treated as STA (with AP fallback on failure). */
|
||||
WIFI_MODE_AP_STA = 2,
|
||||
} wifi_mode_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t crc;
|
||||
wifi_mode_t wifi_mode;
|
||||
char sta_ssid[CONFIG_SSID_MAX];
|
||||
char sta_pass[CONFIG_PASS_MAX];
|
||||
char ap_ssid[CONFIG_SSID_MAX];
|
||||
char ap_pass[CONFIG_PASS_MAX];
|
||||
uint16_t universe_a;
|
||||
uint16_t universe_b;
|
||||
/* v3: mDNS hostname without ".local" (e.g. "artnetdmx") */
|
||||
char mdns_host[CONFIG_HOSTNAME_MAX];
|
||||
} config_t;
|
||||
|
||||
void config_set_defaults(config_t *cfg);
|
||||
bool config_load(config_t *cfg);
|
||||
bool config_save(const config_t *cfg);
|
||||
const config_t *config_get(void);
|
||||
void config_init(void);
|
||||
/** Lowercase DNS-label sanitize into out; returns false if empty/invalid → uses default. */
|
||||
bool config_sanitize_hostname(const char *in, char *out, size_t out_sz);
|
||||
|
||||
static inline const char *wifi_mode_name(wifi_mode_t m) {
|
||||
switch (m) {
|
||||
case WIFI_MODE_STA:
|
||||
case WIFI_MODE_AP_STA:
|
||||
return "STA";
|
||||
case WIFI_MODE_AP:
|
||||
default:
|
||||
return "AP";
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef DMX_OUT_H
|
||||
#define DMX_OUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware/pio.h"
|
||||
|
||||
typedef struct {
|
||||
PIO pio;
|
||||
uint sm;
|
||||
uint dma;
|
||||
uint pin;
|
||||
uint prgm_offset;
|
||||
bool inited;
|
||||
} dmx_out_t;
|
||||
|
||||
bool dmx_out_init(dmx_out_t *out, uint pin, PIO pio);
|
||||
void dmx_out_write(dmx_out_t *out, const uint8_t *frame, uint length);
|
||||
bool dmx_out_busy(const dmx_out_t *out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef HTTP_SERVER_H
|
||||
#define HTTP_SERVER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool http_server_start(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef WIFI_MGR_H
|
||||
#define WIFI_MGR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
|
||||
/**
|
||||
* AP mode: SoftAP only.
|
||||
* STA mode: join LAN; if that fails, SoftAP comes up for recovery.
|
||||
*/
|
||||
bool wifi_mgr_start(const config_t *cfg);
|
||||
/** Call from main loop — runs deferred STA join (STA mode only). */
|
||||
void wifi_mgr_poll(void);
|
||||
wifi_mode_t wifi_mgr_active_mode(void);
|
||||
/** Primary IP for UI (STA if up, else AP). */
|
||||
const char *wifi_mgr_ip_str(void);
|
||||
const char *wifi_mgr_sta_ip_str(void);
|
||||
const char *wifi_mgr_ap_ip_str(void);
|
||||
const char *wifi_mgr_mdns_name(void);
|
||||
bool wifi_mgr_sta_up(void);
|
||||
bool wifi_mgr_ap_up(void);
|
||||
bool wifi_mgr_is_up(void);
|
||||
const char *wifi_mgr_debug(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user