Files
portal/firmware/panel/main.c
Jimmy d5cab2efdf Add multi-panel Pico UDP firmware and Python LED control.
Pico panels get static IPs on 10.1.1.10–14 with per-panel LED counts, Makefile deploy targets, and Python examples for animations, sync tests, and direct Pi SPI control.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 22:46:08 +12:00

220 lines
5.2 KiB
C
Raw 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 (N = NUM_LEDS), show immediately
* - N×3+1 bytes: panel_id (byte 0) + RGB; panel_id 255 = any panel
* - 4 bytes "SHOW": refresh buffered pixels (for split frame/show sync)
*/
#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[LED_RGB_BYTES];
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(void) {
printf("LED boot test on GP%d (%d pixels)\n", PIN_WS2812, NUM_LEDS);
ws2812_fill(255, 0, 0);
ws2812_show();
sleep_ms(500);
ws2812_fill(0, 255, 0);
ws2812_show();
sleep_ms(500);
ws2812_fill(0, 0, 0);
ws2812_show();
}
static int handle_frame(const uint8_t *rgb, uint16_t len) {
if (len != LED_RGB_BYTES) {
printf("frame reject len=%u want=%u\n", (unsigned)len, (unsigned)LED_RGB_BYTES);
return -1;
}
ws2812_set_rgb(rgb, NUM_LEDS);
ws2812_show();
return 0;
}
static void handle_packet(const uint8_t *data, int16_t len) {
if (len == 4 && memcmp(data, "SHOW", 4) == 0) {
ws2812_set_rgb(g_pixel_buf, NUM_LEDS);
ws2812_show();
return;
}
if (len == LED_RGB_BYTES) {
memcpy(g_pixel_buf, data, LED_RGB_BYTES);
handle_frame(data, (uint16_t)len);
return;
}
if (len == LED_RGB_BYTES + 1) {
uint8_t panel = data[0];
if (panel != 255 && panel != (uint8_t)PANEL_ID) {
return;
}
memcpy(g_pixel_buf, data + 1, LED_RGB_BYTES);
handle_frame(data + 1, LED_RGB_BYTES);
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);
}
}
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("LEDs=%d panel_id=%d mac=02:50:52:54:4C:%02X ip=10.1.1.%u\n",
NUM_LEDS, PANEL_ID, (unsigned)PANEL_ID, (unsigned)STATIC_IP_OCT4);
wizchip_spi_initialize();
wizchip_cris_initialize();
wizchip_reset();
wizchip_initialize();
wizchip_check();
ws2812_init(PIN_WS2812, NUM_LEDS);
memset(g_pixel_buf, 0, sizeof(g_pixel_buf));
led_boot_test();
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();
if (++heartbeat >= 500) {
gpio_xor_mask(1u << PIN_LED_STATUS);
heartbeat = 0;
}
sleep_ms(1);
}
}