#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define termios asmtermios #define winsize asmwinsize #define termio asmtermio #include #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_lo; // 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, ¤t_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; }