Files
led-driver-8/firmware/main.c
Jimmy f7beac2095 Replace MicroPython runtime with Pico C + W5500 firmware.
Ship UDP RGB panel firmware (eight WS2812 strips) and drop the ESP32 Wi-Fi/MicroPython stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 21:52:52 +12:00

366 lines
9.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Portal panel firmware — W5500 UDP + WS2812 (Pico SDK).
*
* UDP protocol (port 50007):
* - N×3 bytes: raw RGB → strip 0 (N = 1…MAX_LEDS)
* - 1 + N×3 bytes: strip_id (byte 0) + RGB
* strip_id 0…MAX_STRIPS-1 → that strip
* strip_id 255 → all active strips
* - 5 bytes "PIN\\0" + gpio: bind strip 0 to gpio
* - 8 bytes "PIN\\0" + strip + gpio + len_lo + len_hi: bind strip (len=0 → unset)
* - 4 bytes "SHOW": reserved (frames already show on receive)
*
* One Pico can drive up to MAX_STRIPS displays on separate GPIOs.
* Each strips pixel count comes from its own UDP frame length (strips may differ).
* MAX_LEDS is per-strip capacity.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "board_config.h"
#include "wizchip_conf.h"
#include "dhcp.h"
#include "socket.h"
#include "timer.h"
#include "wizchip_spi.h"
#include "ws2812_led.h"
#include "hardware/gpio.h"
#include "pico/stdlib.h"
#define SOCKET_UDP 1
#define SOCKET_DHCP 0
#define ETH_BUF_SIZE 2048
#define DHCP_RETRY_MAX 10
static wiz_NetInfo g_net_info = {
.mac = MAC_ADDR,
.ip = STATIC_IP,
.sn = STATIC_SN,
.gw = STATIC_GW,
.dns = STATIC_DNS,
#if USE_DHCP
.dhcp = NETINFO_DHCP,
#else
.dhcp = NETINFO_STATIC,
#endif
};
static uint8_t g_eth_buf[ETH_BUF_SIZE];
static uint8_t g_pixel_buf[MAX_STRIPS][LED_RGB_BYTES];
static uint16_t g_frame_pixels[MAX_STRIPS];
static uint16_t g_strip_leds[MAX_STRIPS];
static uint8_t g_frame_dirty[MAX_STRIPS];
static volatile uint16_t g_ms_tick;
static uint8_t g_dhcp_ready;
static void dhcp_timer_cb(void) {
g_ms_tick++;
if (g_ms_tick >= 999) {
g_ms_tick = 0;
DHCP_time_handler();
}
}
static void dhcp_assign(void) {
getIPfromDHCP(g_net_info.ip);
getGWfromDHCP(g_net_info.gw);
getSNfromDHCP(g_net_info.sn);
getDNSfromDHCP(g_net_info.dns);
g_net_info.dhcp = NETINFO_DHCP;
network_initialize(g_net_info);
print_network_information(g_net_info);
g_dhcp_ready = 1;
}
static void dhcp_conflict(void) {
printf("DHCP conflict\n");
}
static int network_bring_up(void) {
#if USE_DHCP
uint8_t retries = 0;
DHCP_init(SOCKET_DHCP, g_eth_buf);
reg_dhcp_cbfunc(dhcp_assign, dhcp_assign, dhcp_conflict);
wizchip_1ms_timer_initialize(dhcp_timer_cb);
while (!g_dhcp_ready && retries < DHCP_RETRY_MAX) {
int8_t rv = DHCP_run();
if (rv == DHCP_IP_LEASED) {
g_dhcp_ready = 1;
break;
}
if (rv == DHCP_FAILED) {
retries++;
}
wizchip_delay_ms(250);
}
if (!g_dhcp_ready) {
printf("DHCP failed, using static IP\n");
g_net_info.dhcp = NETINFO_STATIC;
network_initialize(g_net_info);
print_network_information(g_net_info);
}
#else
network_initialize(g_net_info);
print_network_information(g_net_info);
#endif
return 0;
}
static int udp_socket_open(void) {
int8_t sn = socket(SOCKET_UDP, Sn_MR_UDP, UDP_PORT, 0);
if (sn != SOCKET_UDP) {
printf("UDP socket open failed: %d\n", sn);
return -1;
}
printf("UDP listening on port %d\n", UDP_PORT);
return 0;
}
static void led_boot_test(unsigned int strip) {
if (!ws2812_strip_active(strip)) {
return;
}
uint8_t one[3] = {255, 0, 0};
ws2812_set_rgb(strip, one, 1);
ws2812_show(strip);
sleep_ms(150);
one[0] = 0;
one[1] = 255;
ws2812_set_rgb(strip, one, 1);
ws2812_show(strip);
sleep_ms(150);
one[1] = 0;
one[2] = 255;
ws2812_set_rgb(strip, one, 1);
ws2812_show(strip);
sleep_ms(150);
one[2] = 0;
ws2812_set_rgb(strip, one, 1);
ws2812_show(strip);
}
static int buffer_frame(unsigned int strip, const uint8_t *rgb, uint16_t nbytes) {
if (strip >= MAX_STRIPS || !ws2812_strip_active(strip)) {
return -1;
}
if (nbytes == 0 || (nbytes % 3u) != 0) {
return -1;
}
uint16_t pixels = nbytes / 3u;
if (pixels > MAX_LEDS) {
pixels = MAX_LEDS;
nbytes = (uint16_t)(pixels * 3u);
}
memcpy(g_pixel_buf[strip], rgb, nbytes);
if (nbytes < LED_RGB_BYTES) {
memset(g_pixel_buf[strip] + nbytes, 0, LED_RGB_BYTES - nbytes);
}
g_frame_pixels[strip] = pixels;
g_frame_dirty[strip] = 1;
return 0;
}
static void buffer_frame_all(const uint8_t *rgb, uint16_t nbytes) {
for (unsigned int s = 0; s < MAX_STRIPS; s++) {
if (ws2812_strip_active(s)) {
buffer_frame(s, rgb, nbytes);
}
}
}
static void show_dirty_frames(void) {
for (unsigned int s = 0; s < MAX_STRIPS; s++) {
if (!g_frame_dirty[s]) {
continue;
}
ws2812_set_rgb(s, g_pixel_buf[s], g_frame_pixels[s]);
ws2812_show(s);
g_frame_dirty[s] = 0;
}
}
static int pin_is_reserved(uint8_t pin) {
switch (pin) {
case PIN_CS:
case PIN_SCK:
case PIN_MOSI:
case PIN_MISO:
case PIN_RST:
case PIN_LED_STATUS:
return 1;
default:
return pin > 29;
}
}
static void handle_pin_command(uint8_t strip, uint8_t pin, uint16_t leds) {
if (strip >= MAX_STRIPS) {
printf("PIN strip %u out of range (max %u)\n", (unsigned)strip, MAX_STRIPS);
return;
}
if (pin_is_reserved(pin)) {
printf("PIN GP%u rejected (reserved)\n", (unsigned)pin);
return;
}
if (leds > MAX_LEDS) {
printf("PIN leds %u clamped to max_leds=%u\n", (unsigned)leds, MAX_LEDS);
leds = MAX_LEDS;
}
for (unsigned int s = 0; s < MAX_STRIPS; s++) {
if (s != strip && ws2812_strip_active(s) && ws2812_get_pin(s) == pin) {
printf("PIN GP%u: releasing strip %u\n", (unsigned)pin, s);
ws2812_release_strip(s);
g_strip_leds[s] = 0;
g_frame_pixels[s] = 0;
g_frame_dirty[s] = 0;
}
}
if (ws2812_set_pin(strip, pin) != 0) {
printf("PIN strip %u GP%u failed (no PIO SM?)\n", (unsigned)strip, (unsigned)pin);
return;
}
g_strip_leds[strip] = leds;
if (leds) {
printf("strip %u ws2812=GP%u leds=%u\n", (unsigned)strip,
(unsigned)ws2812_get_pin(strip), (unsigned)leds);
} else {
printf("strip %u ws2812=GP%u\n", (unsigned)strip, (unsigned)ws2812_get_pin(strip));
}
}
static void handle_packet(const uint8_t *data, int16_t len) {
if (len == 4 && memcmp(data, "SHOW", 4) == 0) {
return;
}
if (len == 5 && data[0] == 'P' && data[1] == 'I' && data[2] == 'N' && data[3] == 0) {
handle_pin_command(0, data[4], 0);
return;
}
if (len == 8 && data[0] == 'P' && data[1] == 'I' && data[2] == 'N' && data[3] == 0) {
uint16_t leds = (uint16_t)data[6] | ((uint16_t)data[7] << 8);
handle_pin_command(data[4], data[5], leds);
return;
}
if (len >= 3 && (len % 3) == 0) {
buffer_frame(0, data, (uint16_t)len);
return;
}
if (len >= 4 && ((len - 1) % 3) == 0) {
uint8_t id = data[0];
uint16_t nbytes = (uint16_t)(len - 1);
if (id == 255) {
buffer_frame_all(data + 1, nbytes);
return;
}
if (id < MAX_STRIPS) {
buffer_frame(id, data + 1, nbytes);
return;
}
if (id == (uint8_t)PANEL_ID) {
buffer_frame(0, data + 1, nbytes);
return;
}
}
}
static void poll_udp(void) {
while (1) {
uint16_t rx = getSn_RX_RSR(SOCKET_UDP);
if (rx == 0) {
return;
}
if (rx > ETH_BUF_SIZE) {
rx = ETH_BUF_SIZE;
}
uint8_t src_ip[4];
uint16_t src_port;
int16_t n = recvfrom(SOCKET_UDP, g_eth_buf, rx, src_ip, &src_port);
if (n <= 0) {
return;
}
handle_packet(g_eth_buf, n);
}
}
static void print_strip_map(void) {
printf("strips=%d", MAX_STRIPS);
for (unsigned int s = 0; s < MAX_STRIPS; s++) {
if (ws2812_strip_active(s)) {
if (g_strip_leds[s]) {
printf(" [%u]=GP%u/%u", s, ws2812_get_pin(s), g_strip_leds[s]);
} else {
printf(" [%u]=GP%u", s, ws2812_get_pin(s));
}
}
}
printf("\n");
}
int main(void) {
stdio_init_all();
sleep_ms(2000);
gpio_init(PIN_LED_STATUS);
gpio_set_dir(PIN_LED_STATUS, GPIO_OUT);
printf("portal panel firmware (Pico SDK)\n");
printf("max_leds=%d max_strips=%d panel_id=%d mac=02:50:52:54:4C:%02X "
"ip=%u.%u.%u.%u %s\n",
MAX_LEDS, MAX_STRIPS, PANEL_ID, (unsigned)PANEL_ID, g_net_info.ip[0],
g_net_info.ip[1], g_net_info.ip[2], g_net_info.ip[3],
USE_DHCP ? "dhcp" : "static");
wizchip_spi_initialize();
wizchip_cris_initialize();
wizchip_reset();
wizchip_initialize();
wizchip_check();
ws2812_init(MAX_LEDS);
memset(g_pixel_buf, 0, sizeof(g_pixel_buf));
memset(g_frame_pixels, 0, sizeof(g_frame_pixels));
memset(g_strip_leds, 0, sizeof(g_strip_leds));
memset(g_frame_dirty, 0, sizeof(g_frame_dirty));
static const unsigned int default_pins[MAX_STRIPS] = DEFAULT_STRIP_PINS;
for (unsigned int s = 0; s < MAX_STRIPS; s++) {
if (ws2812_set_pin(s, default_pins[s]) == 0) {
led_boot_test(s);
} else {
printf("default strip %u GP%u failed\n", s, default_pins[s]);
}
}
print_strip_map();
network_bring_up();
if (udp_socket_open() != 0) {
while (1) {
gpio_xor_mask(1u << PIN_LED_STATUS);
sleep_ms(100);
}
}
uint32_t heartbeat = 0;
while (1) {
#if USE_DHCP
DHCP_run();
#endif
poll_udp();
show_dirty_frames();
if (++heartbeat >= 500) {
gpio_xor_mask(1u << PIN_LED_STATUS);
heartbeat = 0;
}
sleep_ms(1);
}
}