summaryrefslogtreecommitdiffstats
path: root/sw/ttyartnet.c
diff options
context:
space:
mode:
Diffstat (limited to 'sw/ttyartnet.c')
-rw-r--r--sw/ttyartnet.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/sw/ttyartnet.c b/sw/ttyartnet.c
new file mode 100644
index 0000000..544f773
--- /dev/null
+++ b/sw/ttyartnet.c
@@ -0,0 +1,235 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <error.h>
+#include <fcntl.h>
+#define termios asmtermios
+#define winsize asmwinsize
+#define termio asmtermio
+#include <asm/termios.h>
+#undef termios
+#undef winsize
+#undef termio
+#define S0(x) (x ? x : "")
+// start bit: low, 2 stop bits high
+struct artnet {
+ char name[8]; // Art-Net\0
+ char opcode[2]; // 0x5000 in LE: { 0x00, 0x50 }
+ uint8_t version_hi; // 0
+ uint8_t version_li; // 14
+ uint8_t sequence; // set to 0 to disable sequencing
+ uint8_t physical; // original universe
+ uint8_t sub_uni;
+ uint8_t net;
+ uint8_t length_hi;
+ uint8_t length_lo;
+ unsigned char data[512];
+
+} __attribute__((packed));
+int samomor = 0;
+void handle_me (int s __attribute__((unused))) {
+ samomor++;
+}
+int rate (int what_rate, int uart) {
+ int r = 0;
+ struct termios2 uartattr2;
+ if (ioctl(uart, TCGETS2, &uartattr2) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "ioctl TCGETS2, fd: %d", uart);
+ r = 1;
+ goto r;
+ }
+ uartattr2.c_cflag &= ~CBAUD;
+ uartattr2.c_cflag |= BOTHER;
+ uartattr2.c_ispeed = what_rate;
+ uartattr2.c_ospeed = what_rate;
+ if (ioctl(uart, TCSETS2, &uartattr2) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "ioctl TCSETS2");
+ r = 2;
+ goto r;
+ }
+r:
+ return r;
+}
+int main (int argc, char ** argv) {
+ int r = 0;
+ if (argc != 1+1)
+ error_at_line(1, 0, __FILE__, __LINE__, "usage: %s /dev/ttyUSB0", S0(argv[0]));
+ struct sigaction act = {
+ .sa_handler = handle_me,
+ .sa_flags = SA_RESTART
+ };
+ if (sigaction(SIGINT, &act, NULL) == -1)
+ error_at_line(2, errno, __FILE__, __LINE__, "sigaction");
+ int sock = -1;
+ int uart = open(argv[1], O_WRONLY | O_NOCTTY | O_NDELAY | O_CLOEXEC);
+ if (uart == -1)
+ error_at_line(3, errno, __FILE__, __LINE__, "open");
+ struct termios uartattr;
+ if (tcgetattr(uart, &uartattr) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "tcgetattr");
+ r = 4;
+ goto r;
+ }
+ uartattr.c_iflag = 0;
+ uartattr.c_oflag = 0;
+ uartattr.c_cflag = CS8 | CSTOPB;
+ uartattr.c_lflag = 0;
+ if (tcflush(uart, TCIOFLUSH) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "tcflush");
+ r = 5;
+ goto r;
+ }
+ if (tcsetattr(uart, TCSANOW, &uartattr) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "tcsetattr");
+ r = 6;
+ goto r;
+ }
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "socket");
+ r = 7;
+ goto r;
+ }
+ int z = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &z, sizeof z) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "setsockopt");
+ r = 8;
+ goto r;
+ }
+ struct sockaddr_in bind_address = {
+ .sin_family = AF_INET,
+ .sin_port = htons(6454),
+ .sin_addr = {
+ .s_addr = INADDR_ANY
+ }
+ };
+ if (bind(sock, (struct sockaddr *) &bind_address, sizeof bind_address) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "bind");
+ r = 9;
+ goto r;
+ }
+ long long last_dmx_burst = 0;
+ struct artnet udp;
+ while (!samomor) {
+ struct pollfd pollfds[2];
+ pollfds[0].fd = sock;
+ pollfds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+ // pollfds[1].fd = uart;
+ // pollfds[1].events = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
+ int poll_return;
+fprintf(stderr, "before poll\n");
+ if ((poll_return = poll(pollfds, 1 /* corr: I don't care about uart */, -1)) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "poll");
+ r = 10;
+ goto r;
+ }
+fprintf(stderr, "after poll\n");
+ if (!poll_return) { // this can't happen, timeout is -1
+ error_at_line(0, 0, __FILE__, __LINE__, "this can't happen, poll returned 0 and timeout was -1\n");
+ r = 11;
+ goto r;
+ }
+ int both_events = pollfds[0].revents /* | pollfds[1].revents */;
+ if ((both_events & POLLERR) | (both_events & POLLHUP) | (both_events & POLLNVAL)) {
+ error_at_line(0, 0, __FILE__, __LINE__, "(both_events & POLLERR) | (both_events & POLLHUP) | (both_events & POLLNVAL)");
+ r = 12;
+ goto r;
+ }
+ struct sockaddr_in sender;
+ socklen_t sender_len = sizeof sender;
+ ssize_t bytes = recvfrom(sock, &udp, sizeof udp, MSG_DONTWAIT, (struct sockaddr *) &sender, &sender_len);
+ if (bytes == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "recvfrom");
+ r = 13;
+ goto r;
+ } else {
+ fprintf(stderr, "received %ld bytes from %s:%d\n", bytes, inet_ntoa(sender.sin_addr), ntohs(sender.sin_port));
+ }
+ if (udp.opcode[0] != 0x00 || udp.opcode[1] != 0x50)
+ continue;
+ // if (bytes < 530)
+ // raise(SIGINT);
+ for (int i = 0; i < 512; i++)
+ if (udp.data[i] != 0)
+ fprintf(stderr, "\tDMX channel %d is %u\n", i+1, udp.data[i]);
+ struct timespec current_time;
+ if (clock_gettime(CLOCK_MONOTONIC, &current_time) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "clock_gettime");
+ r = 14;
+ goto r;
+ }
+ long long current_time_ms = current_time.tv_sec*1000+current_time.tv_nsec/1000000;
+ if (current_time_ms >= last_dmx_burst+50) {
+ last_dmx_burst = current_time_ms;
+ } else
+ continue;
+ if (tcflush(uart, TCIOFLUSH) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "tcflush");
+ r = 15;
+ goto r;
+ }
+ switch (rate(10000, uart)) {
+ case 1:
+ r = 16;
+ goto r;
+ break;
+ case 2:
+ r = 17;
+ goto r;
+ break;
+ default:
+ break;
+ }
+ if (tcflush(uart, TCIOFLUSH) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "tcflush");
+ r = 18;
+ goto r;
+ }
+ usleep(10000);
+ if (write(uart, "\0", 1) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "write packet start sequence");
+ r = 19;
+ goto r;
+ }
+ usleep(1000);
+ switch (rate(250000, uart)) {
+ case 1:
+ r = 20;
+ goto r;
+ break;
+ case 2:
+ r = 21;
+ goto r;
+ break;
+ default:
+ break;
+ }
+ udp.data[-1] = '\0';
+#pragma GCC diagnostic ignored "-Wstringop-overread"
+ if (write(uart, udp.data-1, 513) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "write packet data");
+ r = 21;
+ goto r;
+ }
+#pragma GCC diagnostic pop
+ }
+r:
+ fprintf(stderr, "closing fds and returning\n");
+ if (close(uart) == -1)
+ error_at_line(22, errno, __FILE__, __LINE__, "close(uart)");
+ if (sock != -1)
+ if (close(sock) == -1)
+ error_at_line(23, errno, __FILE__, __LINE__, "close(sock)");
+ return r;
+}