Files
waveform-draw/draw_parra/artnet_receiver.c
2025-12-17 02:24:38 +01:00

136 lines
4.5 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
/* --------------------------------------------------------------------
* artnet_receiver.c tiny ArtNet DMX receiver
* -------------------------------------------------------------------- */
#define _POSIX_C_SOURCE 200112L
#include "artnet_receiver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>
#include <errno.h>
#define ARTNET_PORT 6454
#define ARTNET_HEADER_SIZE 18 /* "Art-Net\0" + OpCode + … */
#define OP_DMX 0x5000 /* lowbyte first (littleendian) */
/* --------------------------------------------------------------- */
static int artnet_sock = -1;
static pthread_t receiver_thread;
static volatile bool run_thread = false;
/* -----------------------------------------------------------------
* Helper: map a DMX byte (0255) to a signed or unsigned integer
* ----------------------------------------------------------------- */
static inline int map_range_uint8(uint8_t v, int dst_min, int dst_max)
{
return dst_min + (int)((v * (dst_max - dst_min)) / 255.0);
}
/* -----------------------------------------------------------------
* The thread that receives ArtNet packets
* ----------------------------------------------------------------- */
static void *receiver_func(void *arg)
{
(void)arg; /* unused */
struct sockaddr_in from;
socklen_t fromlen = sizeof(from);
uint8_t buf[1500]; /* big enough for a full packet */
while (run_thread) {
ssize_t n = recvfrom(artnet_sock, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &fromlen);
if (n < 0) {
if (errno == EINTR) continue;
perror("recvfrom");
continue;
}
/* ------------------ quick sanity checks ------------------- */
if (n < ARTNET_HEADER_SIZE) continue; /* too short */
if (memcmp(buf, "Art-Net\0", 8) != 0) continue; /* not ArtNet */
uint16_t opcode = buf[8] | (buf[9] << 8); /* littleendian */
if (opcode != OP_DMX) continue; /* not ArtDMX */
/* --------------------- DMX payload ----------------------- */
uint16_t universe = buf[14] | (buf[15] << 8); /* not used */
uint16_t length = buf[16] << 8 | buf[17]; /* bigendian */
if (length + ARTNET_HEADER_SIZE != (size_t)n) continue; /* malformed */
uint8_t *dmx = buf + ARTNET_HEADER_SIZE; /* first DMX slot */
/* --------------------- update shared variables ------------ */
/* 0 … 11 → rotation angles (0180°) */
for (int i = 0; i < 12 && i < length; ++i) {
rotAnglesDeg[i] = map_range_uint8(dmx[i], 0, 180);
}
///* channel 12 → line thickness (110) */
//if (length > 12) {
// LINE_THICKNESS = map_range_uint8(dmx[12], 1, 10);
//}
///* channel 13 → top margin (0200px) */
//if (length > 13) {
// MARGIN_TOP = map_range_uint8(dmx[13], 0, 200);
//}
///* channel 14 → bottom margin (0200px) */
//if (length > 14) {
// MARGIN_BOTTOM = map_range_uint8(dmx[14], 0, 200);
//}
/* (Add more channels here if you need them) */
}
return NULL;
}
/* -----------------------------------------------------------------
* Public API
* ----------------------------------------------------------------- */
bool artnet_init(void)
{
struct sockaddr_in sa;
artnet_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (artnet_sock < 0) {
perror("socket");
return false;
}
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(ARTNET_PORT);
if (bind(artnet_sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind");
close(artnet_sock);
artnet_sock = -1;
return false;
}
run_thread = true;
if (pthread_create(&receiver_thread, NULL, receiver_func, NULL) != 0) {
perror("pthread_create");
close(artnet_sock);
artnet_sock = -1;
run_thread = false;
return false;
}
return true;
}
void artnet_stop(void)
{
if (!run_thread) return;
run_thread = false;
/* Wake the thread if it is blocked in recvfrom */
shutdown(artnet_sock, SHUT_RDWR);
pthread_join(receiver_thread, NULL);
close(artnet_sock);
artnet_sock = -1;
}