multithreaded app
This commit is contained in:
		
							
								
								
									
										139
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
#include "rpi_artnet.h"
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "rpi_artnet_utils.h"
 | 
			
		||||
 | 
			
		||||
int udpSocket = -1;
 | 
			
		||||
char buffer[1024];
 | 
			
		||||
bool isUdpListenerRunning = false;
 | 
			
		||||
pthread_t udpListener;
 | 
			
		||||
uint8_t artDmxBufferArray[16][512];
 | 
			
		||||
 | 
			
		||||
static void *artnet_udp_handler(void *arg);
 | 
			
		||||
 | 
			
		||||
void artnet_send_poll_reply(struct sockaddr_in srcAddr);
 | 
			
		||||
 | 
			
		||||
void artnet_init() {}
 | 
			
		||||
 | 
			
		||||
void start_artnet_bg_worker() {
 | 
			
		||||
  isUdpListenerRunning = true;
 | 
			
		||||
 | 
			
		||||
  if (pthread_create(&udpListener, NULL, artnet_udp_handler, NULL) < 0) {
 | 
			
		||||
    perror("pthread_create");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void stop_artnet_bg_worker() {
 | 
			
		||||
  isUdpListenerRunning = false;
 | 
			
		||||
 | 
			
		||||
  if (pthread_join(udpListener, NULL) != 0) {
 | 
			
		||||
    perror("pthread_join");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData) {
 | 
			
		||||
  if (univerve > 8) {
 | 
			
		||||
    fprintf(stderr, "Universe %d out of bounds %d\n", univerve, 16);
 | 
			
		||||
    *dmxData = NULL;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  *dmxData = artDmxBufferArray[univerve];
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *artnet_udp_handler(void *arg) {
 | 
			
		||||
  struct sockaddr_in serverAddr;
 | 
			
		||||
  struct pollfd fds[1];
 | 
			
		||||
  int timeoutMs;
 | 
			
		||||
  int ret;
 | 
			
		||||
  int flags;
 | 
			
		||||
 | 
			
		||||
  /* Create UDP socket */
 | 
			
		||||
  udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
 | 
			
		||||
  if (udpSocket < 0) {
 | 
			
		||||
    perror("Opening socket failed");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Set non-blocking socket */
 | 
			
		||||
  flags = fcntl(udpSocket, F_GETFL, 0);
 | 
			
		||||
  fcntl(udpSocket, F_SETFL, flags | O_NONBLOCK);
 | 
			
		||||
 | 
			
		||||
  /* pollfd structure and timeout */
 | 
			
		||||
  memset(fds, 0, sizeof(fds));
 | 
			
		||||
  fds[0].fd = udpSocket;
 | 
			
		||||
  fds[0].events = POLLIN;
 | 
			
		||||
  timeoutMs = 10;
 | 
			
		||||
 | 
			
		||||
  /* Configure settings in address struct */
 | 
			
		||||
  serverAddr.sin_family = AF_INET;
 | 
			
		||||
  serverAddr.sin_port = htons(ARTNET_PORT);
 | 
			
		||||
  serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
 | 
			
		||||
  memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero));
 | 
			
		||||
 | 
			
		||||
  /* Bind socket with address struct */
 | 
			
		||||
  bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
 | 
			
		||||
 | 
			
		||||
  struct sockaddr_in srcAddr;
 | 
			
		||||
  socklen_t srcLen = sizeof(srcAddr);
 | 
			
		||||
 | 
			
		||||
  while (isUdpListenerRunning) {
 | 
			
		||||
 | 
			
		||||
    if ((ret = poll(fds, 1, timeoutMs)) == 1) {
 | 
			
		||||
      ssize_t bufferLen =
 | 
			
		||||
          recvfrom(udpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen);
 | 
			
		||||
 | 
			
		||||
      if (bufferLen <= ARTNET_MAX_BUFFER && bufferLen > sizeof(artnetHeader_t)) {
 | 
			
		||||
        artnetHeader_t *artnetHeader = (artnetHeader_t *)buffer;
 | 
			
		||||
 | 
			
		||||
        if (memcmp(artnetHeader->id, ARTNET_ID, sizeof(ARTNET_ID)) == 0) {
 | 
			
		||||
          switch (artnetHeader->opCode) {
 | 
			
		||||
          case OpDmx:
 | 
			
		||||
            if (bufferLen >= 20) {
 | 
			
		||||
              artDmx_t *artDmx = (artDmx_t *)buffer;
 | 
			
		||||
              uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
 | 
			
		||||
              uint8_t *artDmxBuffer = artDmxBufferArray[artDmx->subUni & 0x00ff];
 | 
			
		||||
              if (dmxLength <= 512) {
 | 
			
		||||
                // store for later use
 | 
			
		||||
                memcpy(artDmxBuffer, artDmx->data, dmxLength);
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          case OpPoll:
 | 
			
		||||
            artnet_send_poll_reply(srcAddr);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          default:
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if (ret < 0) {
 | 
			
		||||
      fprintf(stderr, "error polling %d: %s\n", udpSocket, strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  close(udpSocket);
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void artnet_send_poll_reply(struct sockaddr_in srcAddr) {
 | 
			
		||||
  /* Configure settings in address struct */
 | 
			
		||||
  srcAddr.sin_family = AF_INET;
 | 
			
		||||
  srcAddr.sin_port = htons(ARTNET_PORT);
 | 
			
		||||
  memset(srcAddr.sin_zero, '\0', sizeof(srcAddr.sin_zero));
 | 
			
		||||
 | 
			
		||||
  sendto(udpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0,
 | 
			
		||||
         (struct sockaddr *)&srcAddr, sizeof(srcAddr));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#if !defined(__RPI_ARTNET_H__)
 | 
			
		||||
#define __RPI_ARTNET_H__
 | 
			
		||||
 | 
			
		||||
#include "rpi_artnet_packets.h"
 | 
			
		||||
 | 
			
		||||
void artnet_init();
 | 
			
		||||
 | 
			
		||||
void start_artnet_bg_worker();
 | 
			
		||||
 | 
			
		||||
void stop_artnet_bg_worker();
 | 
			
		||||
 | 
			
		||||
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_ARTNET_H__
 | 
			
		||||
							
								
								
									
										128
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet_op_codes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet_op_codes.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
			
		||||
#if !defined(__RPI_ARTNET_OP_CODES_H__)
 | 
			
		||||
#define __RPI_ARTNET_OP_CODES_H__
 | 
			
		||||
 | 
			
		||||
/* List of hex values and discriptions of Opcodes */
 | 
			
		||||
 | 
			
		||||
/* This is an ArtPoll packet, no other data is contained in this UDP packet */
 | 
			
		||||
#define OpPoll 0x2000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtPollReply Packet. It contains device status information. */
 | 
			
		||||
#define OpPollReply 0x2100
 | 
			
		||||
 | 
			
		||||
/* Diagnostics and data logging packet. */
 | 
			
		||||
#define OpDiagData 0x2300
 | 
			
		||||
 | 
			
		||||
/* Used to send text based parameter commands. */
 | 
			
		||||
#define OpCommand 0x2400
 | 
			
		||||
 | 
			
		||||
/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single
 | 
			
		||||
 * Universe. */
 | 
			
		||||
#define OpOutput 0x5000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single
 | 
			
		||||
 * Universe. */
 | 
			
		||||
#define OpDmx 0x5000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtNzs data packet. It contains non-zero start code (except RDM) DMX512 information
 | 
			
		||||
 * for a single Universe. */
 | 
			
		||||
#define OpNzs 0x5100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtAddress packet. It contains remote programming information for a Node. */
 | 
			
		||||
#define OpAddress 0x6000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtInput packet. It contains enable – disable data for DMX inputs. */
 | 
			
		||||
#define OpInput 0x7000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTodRequest packet. It is used to request a Table of Devices (ToD) for RDM
 | 
			
		||||
 * discovery. */
 | 
			
		||||
#define OpTodRequest 0x8000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTodData packet. It is used to send a Table of Devices (ToD) for RDM discovery. */
 | 
			
		||||
#define OpTodData 0x8100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTodControl packet. It is used to send RDM discovery control messages. */
 | 
			
		||||
#define OpTodControl 0x8200
 | 
			
		||||
 | 
			
		||||
/* This is an ArtRdm packet. It is used to send all non discovery RDM messages. */
 | 
			
		||||
#define OpRdm 0x8300
 | 
			
		||||
 | 
			
		||||
/* This is an ArtRdmSub packet. It is used to send compressed, RDM Sub-Device data. */
 | 
			
		||||
#define OpRdmSub 0x8400
 | 
			
		||||
 | 
			
		||||
/* This is an ArtVideoSetup packet. It contains video screen setup information for nodes that
 | 
			
		||||
 * implement the extended video features. */
 | 
			
		||||
#define OpVideoSetup 0xa010
 | 
			
		||||
 | 
			
		||||
/* This is an ArtVideoPalette packet. It contains colour palette setup information for nodes that
 | 
			
		||||
 * implement the extended video features. */
 | 
			
		||||
#define OpVideoPalette 0xa020
 | 
			
		||||
 | 
			
		||||
/* This is an ArtVideoData packet. It contains display data for nodes that implement the extended
 | 
			
		||||
 * video features. */
 | 
			
		||||
#define OpVideoData 0xa040
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMacMaster packet. It is used to program the Node’s MAC address, Oem device type and
 | 
			
		||||
 * ESTA manufacturer code. This is for factory initialisation of a Node. It is not to be used by
 | 
			
		||||
 * applications. */
 | 
			
		||||
#define OpMacMaster 0xf000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMacSlave packet. It is returned by the node to acknowledge receipt of an
 | 
			
		||||
 * ArtMacMaster packet. */
 | 
			
		||||
#define OpMacSlave 0xf100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtFirmwareMaster packet. It is used to upload new firmware or firmware extensions to
 | 
			
		||||
 * the Node. */
 | 
			
		||||
#define OpFirmwareMaster 0xf200
 | 
			
		||||
 | 
			
		||||
/* This is an ArtFirmwareReply packet. It is returned by the node to acknowledge receipt of an
 | 
			
		||||
 * ArtFirmwareMaster packet or ArtFileTnMaster packet. */
 | 
			
		||||
#define OpFirmwareReply 0xf300
 | 
			
		||||
 | 
			
		||||
/* Uploads user file to node. */
 | 
			
		||||
#define OpFileTnMaster 0xf400
 | 
			
		||||
 | 
			
		||||
/* Downloads user file from node. */
 | 
			
		||||
#define OpFileFnMaster 0xf500
 | 
			
		||||
 | 
			
		||||
/* Node acknowledge for downloads. */
 | 
			
		||||
#define OpFileFnReply 0xf600
 | 
			
		||||
 | 
			
		||||
/* This is an ArtIpProg packet. It is used to reprogramme the IP, Mask and Port address of the Node.
 | 
			
		||||
 */
 | 
			
		||||
#define OpIpProg 0xf800
 | 
			
		||||
 | 
			
		||||
/* This is an ArtIpProgReply packet. It is returned by the node to acknowledge receipt of an
 | 
			
		||||
 * ArtIpProg packet. */
 | 
			
		||||
#define OpIpProgReply 0xf900
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMedia packet. It is Unicast by a Media Server and acted upon by a Controller. */
 | 
			
		||||
#define OpMedia 0x9000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMediaPatch packet. It is Unicast by a Controller and acted upon by a Media Server.
 | 
			
		||||
 */
 | 
			
		||||
#define OpMediaPatch 0x9100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMediaControl packet. It is Unicast by a Controller and acted upon by a Media
 | 
			
		||||
 * Server. */
 | 
			
		||||
#define OpMediaControl 0x9200
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMediaControlReply packet. It is Unicast by a Media Server and acted upon by a
 | 
			
		||||
 * Controller. */
 | 
			
		||||
#define OpMediaContrlReply 0x9300
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTimeCode packet. It is used to transport time code over the network. */
 | 
			
		||||
#define OpTimeCode 0x9700
 | 
			
		||||
 | 
			
		||||
/* Used to synchronise real time date and clock */
 | 
			
		||||
#define OpTimeSync 0x9800
 | 
			
		||||
 | 
			
		||||
/* Used to send trigger macros */
 | 
			
		||||
#define OpTrigger 0x9900
 | 
			
		||||
 | 
			
		||||
/* Requests a node's file list */
 | 
			
		||||
#define OpDirectory 0x9a00
 | 
			
		||||
 | 
			
		||||
/* Replies to OpDirectory with file list */
 | 
			
		||||
#define OpDirectoryReply 0x9b00
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_ARTNET_OP_CODES_H__
 | 
			
		||||
							
								
								
									
										178
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
#if !defined(__RPI_ARTNET_PACKETS_H__)
 | 
			
		||||
#define __RPI_ARTNET_PACKETS_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "rpi_artnet_op_codes.h"
 | 
			
		||||
#include "rpi_artnet_utils.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  char id[8];
 | 
			
		||||
  uint16_t opCode;
 | 
			
		||||
} __attribute__((__packed__)) artnetHeader_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  artnetHeader_t artnetHeader;
 | 
			
		||||
  uint16_t protVer;
 | 
			
		||||
  uint8_t sequence;
 | 
			
		||||
  uint8_t physical;
 | 
			
		||||
  uint8_t subUni;
 | 
			
		||||
  uint8_t net;
 | 
			
		||||
  uint8_t lengthHi;
 | 
			
		||||
  uint8_t lengthLo;
 | 
			
		||||
  uint8_t data[512];
 | 
			
		||||
} __attribute__((__packed__)) artDmx_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  char ID[8];
 | 
			
		||||
  uint16_t OpCode;
 | 
			
		||||
  uint8_t IPAddr[4];
 | 
			
		||||
  uint16_t Port;
 | 
			
		||||
  uint16_t VersionInfo; // The node's current FIRMWARE VERS
 | 
			
		||||
  // Bits 14-8 of the 15 bit universe number are encoded into the bottom 7 bits of this field.
 | 
			
		||||
  uint8_t NetSwitch;
 | 
			
		||||
  // This is used in combination with SubSwitch and Swin[] or Swout[] to produce the full universe
 | 
			
		||||
  // address. Bits 7-4 of the 15 bit universe number are encoded into the bottom 4 bits of this
 | 
			
		||||
  // field.
 | 
			
		||||
  uint8_t SubSwitch;
 | 
			
		||||
  uint16_t Oem; // Manufacturer code, bit 15 set if
 | 
			
		||||
  // extended features avail
 | 
			
		||||
  uint8_t UbeaVersion; // Firmware version of UBEA
 | 
			
		||||
  uint8_t Status;
 | 
			
		||||
  // bit 0 = 0 UBEA not present
 | 
			
		||||
  // bit 0 = 1 UBEA present
 | 
			
		||||
  // bit 1 = 0 Not capable of RDM (Uni-directional DMX)
 | 
			
		||||
  // bit 1 = 1 Capable of RDM (Bi-directional DMX)
 | 
			
		||||
  // bit 2 = 0 Booted from flash (normal boot)
 | 
			
		||||
  // bit 2 = 1 Booted from ROM (possible error condition)
 | 
			
		||||
  // bit 3 = Not used
 | 
			
		||||
  // bit 54 = 00 Universe programming authority unknown
 | 
			
		||||
  // bit 54 = 01 Universe programming authority set by front panel controls
 | 
			
		||||
  // bit 54 = 10 Universe programming authority set by network
 | 
			
		||||
  // bit 76 = 00 Indicators Normal
 | 
			
		||||
  // bit 76 = 01 Indicators Locate
 | 
			
		||||
  // bit 76 = 10 Indicators Mute
 | 
			
		||||
  uint8_t EstaMan[2]; // ESTA manufacturer id, lo byte
 | 
			
		||||
  char ShortName[18]; // short name defaults to IP
 | 
			
		||||
  char LongName[64];
 | 
			
		||||
  // Text feedback of Node status or errors  also used for debug info
 | 
			
		||||
  char NodeReport[64];
 | 
			
		||||
 | 
			
		||||
  uint8_t NumPortsHi; // 0
 | 
			
		||||
  uint8_t NumPortsLo; // 4 If num i/p ports is dif to output ports, return biggest
 | 
			
		||||
 | 
			
		||||
  uint8_t PortTypes[4];
 | 
			
		||||
  // bit 7 is output
 | 
			
		||||
  // bit 6 is input
 | 
			
		||||
  // bits 0-5 are protocol number (0= DMX, 1=MIDI)
 | 
			
		||||
  // for DMX-Hub ={0xc0,0xc0,0xc0,0xc0};
 | 
			
		||||
 | 
			
		||||
  uint8_t GoodInput[4];
 | 
			
		||||
  // bit 7 is data received
 | 
			
		||||
  // bit 6 is data includes test packets
 | 
			
		||||
  // bit 5 is data includes SIP's
 | 
			
		||||
  // bit 4 is data includes text
 | 
			
		||||
  // bit 3 set is input is disabled
 | 
			
		||||
  // bit 2 is receive errors
 | 
			
		||||
  // bit 1-0 not used, transmitted as zero.
 | 
			
		||||
  // Don't test for zero!
 | 
			
		||||
 | 
			
		||||
  uint8_t GoodOutput[4];
 | 
			
		||||
  // bit 7 is data is transmitting
 | 
			
		||||
  // bit 6 is data includes test packets
 | 
			
		||||
  // bit 5 is data includes SIP's
 | 
			
		||||
  // bit 4 is data includes text
 | 
			
		||||
  // bit 3 output is merging data.
 | 
			
		||||
  // bit 2 set if DMX output short detected on power up
 | 
			
		||||
  // bit 1 set if DMX output merge mode is LTP
 | 
			
		||||
  // bit 0 not used, transmitted as zero.
 | 
			
		||||
 | 
			
		||||
  uint8_t SwIn[4];
 | 
			
		||||
  // Bits 3-0 of the 15 bit universe number are encoded into the low nibble
 | 
			
		||||
  // This is used in combination with SubSwitch and NetSwitch to produce the full universe address.
 | 
			
		||||
  // THIS IS FOR INPUT - ART-NET or DMX
 | 
			
		||||
  // NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO.
 | 
			
		||||
 | 
			
		||||
  uint8_t SwOut[4];
 | 
			
		||||
  // Bits 3-0 of the 15 bit universe number are encoded into the low nibble
 | 
			
		||||
  // This is used in combination with SubSwitch and NetSwitch to produce the full universe address.
 | 
			
		||||
  // data belongs
 | 
			
		||||
  // THIS IS FOR OUTPUT - ART-NET or DMX.
 | 
			
		||||
  // NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO.
 | 
			
		||||
 | 
			
		||||
  uint8_t SwVideo;
 | 
			
		||||
  // Low nibble is the value of the video
 | 
			
		||||
  // output channel
 | 
			
		||||
 | 
			
		||||
  uint8_t SwMacro;
 | 
			
		||||
  // Bit 0 is Macro input 1
 | 
			
		||||
  // Bit 7 is Macro input 8
 | 
			
		||||
 | 
			
		||||
  uint8_t SwRemote;
 | 
			
		||||
  // Bit 0 is Remote input 1
 | 
			
		||||
  // Bit 7 is Remote input 8
 | 
			
		||||
 | 
			
		||||
  uint8_t Spare1; // Spare, currently zero
 | 
			
		||||
  uint8_t Spare2; // Spare, currently zero
 | 
			
		||||
  uint8_t Spare3; // Spare, currently zero
 | 
			
		||||
  uint8_t Style;  // Set to Style code to describe type of equipment
 | 
			
		||||
 | 
			
		||||
  uint8_t Mac[6]; // Mac Address, zero if info not available
 | 
			
		||||
 | 
			
		||||
  uint8_t BindIp[4]; // If this unit is part of a larger or modular product, this is the IP of the
 | 
			
		||||
                     // root device.
 | 
			
		||||
  uint8_t BindIndex; // Set to zero if no binding, otherwise this number represents the order of
 | 
			
		||||
                     // bound devices. A lower number means closer to root device.
 | 
			
		||||
 | 
			
		||||
  uint8_t Status2;
 | 
			
		||||
  // bit 0 = 0 Node does not support web browser
 | 
			
		||||
  // bit 0 = 1 Node supports web browser configuration
 | 
			
		||||
 | 
			
		||||
  // bit 1 = 0 Node's IP address is manually configured
 | 
			
		||||
  // bit 1 = 1 Node's IP address is DHCP configured
 | 
			
		||||
 | 
			
		||||
  // bit 2 = 0 Node is not DHCP capable
 | 
			
		||||
  // bit 2 = 1 Node is DHCP capable
 | 
			
		||||
 | 
			
		||||
  // bit 2-7 not implemented, transmit as zero
 | 
			
		||||
 | 
			
		||||
  uint8_t Filler[26]; // Filler bytes, currently zero.
 | 
			
		||||
} artPollReply_t;
 | 
			
		||||
 | 
			
		||||
static artPollReply_t artPollReply = {
 | 
			
		||||
    .ID = "Art-Net",
 | 
			
		||||
    .OpCode = OpPollReply,
 | 
			
		||||
    .IPAddr = {0},
 | 
			
		||||
    .Port = ARTNET_PORT,
 | 
			
		||||
    .VersionInfo = 1,
 | 
			
		||||
    .NetSwitch = 0,
 | 
			
		||||
    .SubSwitch = 0,
 | 
			
		||||
    .Oem = 0x0190,
 | 
			
		||||
    .UbeaVersion = 0,
 | 
			
		||||
    .Status = 0,
 | 
			
		||||
    .EstaMan = {0, 0},
 | 
			
		||||
    .ShortName = "Tropicananass",
 | 
			
		||||
    .LongName = "Tropicananass Artnetnode",
 | 
			
		||||
    .NodeReport = {0},
 | 
			
		||||
    .NumPortsHi = 0,
 | 
			
		||||
    .NumPortsLo = 1,
 | 
			
		||||
    .PortTypes = {0x80, 0, 0, 0},
 | 
			
		||||
    .GoodInput = {0},
 | 
			
		||||
    .GoodOutput = {0},
 | 
			
		||||
    .SwIn = {0},
 | 
			
		||||
    .SwOut = {0},
 | 
			
		||||
    .SwVideo = 0,
 | 
			
		||||
    .SwMacro = 0,
 | 
			
		||||
    .SwRemote = 0,
 | 
			
		||||
    .Spare1 = 0,
 | 
			
		||||
    .Spare2 = 0,
 | 
			
		||||
    .Spare3 = 0,
 | 
			
		||||
    .Style = 0,
 | 
			
		||||
    .Mac = {0},
 | 
			
		||||
    .BindIp = {0},
 | 
			
		||||
    .BindIndex = 0,
 | 
			
		||||
    .Status2 = 0b00000000,
 | 
			
		||||
    .Filler = {0},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_ARTNET_PACKETS_H__
 | 
			
		||||
							
								
								
									
										25
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet_utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								RpiLedBars/src/tasks/artnet/rpi_artnet_utils.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
#if !defined(__RPI_ARTNET_UTILS_H__)
 | 
			
		||||
#define __RPI_ARTNET_UTILS_H__
 | 
			
		||||
 | 
			
		||||
#define ARTNET_PORT 0x1936
 | 
			
		||||
 | 
			
		||||
// Buffers
 | 
			
		||||
#define ARTNET_MAX_BUFFER 530
 | 
			
		||||
#define DMX_MAX_BUFFER 512
 | 
			
		||||
 | 
			
		||||
// Packet constants
 | 
			
		||||
#define ARTNET_ID "Art-Net"
 | 
			
		||||
#define ARTNET_DMX_START_LOC 18
 | 
			
		||||
 | 
			
		||||
// Packet confines
 | 
			
		||||
#define ARTNET_SHORT_NAME_MAX_LENGTH 17
 | 
			
		||||
#define ARTNET_LONG_NAME_MAX_LENGTH 63
 | 
			
		||||
 | 
			
		||||
// DMX settings
 | 
			
		||||
#define DMX_MAX_OUTPUTS 4
 | 
			
		||||
#define DMX_MS_BETWEEN_TICKS 25
 | 
			
		||||
 | 
			
		||||
// RDM
 | 
			
		||||
#define DMX_RDM_STARTCODE 0xCC
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_ARTNET_UTILS_H__
 | 
			
		||||
							
								
								
									
										146
									
								
								RpiLedBars/src/tasks/cava/rpi_cava.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								RpiLedBars/src/tasks/cava/rpi_cava.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
#include "rpi_cava.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include <Windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define MAXLINECHAR 128 * (5 + 1) + 1
 | 
			
		||||
 | 
			
		||||
pid_t cavaPid;
 | 
			
		||||
bool isFifoReaderRunning = false;
 | 
			
		||||
pthread_t fifoReader;
 | 
			
		||||
int cavaFifo;
 | 
			
		||||
char lineBuffer[MAXLINECHAR];
 | 
			
		||||
uint16_t buffer[128 + 2];
 | 
			
		||||
 | 
			
		||||
static void *fifo_to_buffer(void *arg);
 | 
			
		||||
 | 
			
		||||
void setup_cava() {
 | 
			
		||||
  if ((cavaPid = fork()) == -1) {
 | 
			
		||||
    perror("fork");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (cavaPid == 0) {
 | 
			
		||||
    /* Child process*/
 | 
			
		||||
    pthread_setschedprio(pthread_self(), 30);
 | 
			
		||||
    char *args[] = {"/usr/local/bin/cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL};
 | 
			
		||||
 | 
			
		||||
    if (execv(args[0], args) != 0) {
 | 
			
		||||
      perror("execv");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  } else {
 | 
			
		||||
    sleep(1);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void close_cava() {
 | 
			
		||||
  stop_cava_bg_worker();
 | 
			
		||||
  kill(cavaPid, SIGTERM);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void start_cava_bg_worker() {
 | 
			
		||||
  isFifoReaderRunning = true;
 | 
			
		||||
  pthread_create(&fifoReader, NULL, fifo_to_buffer, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void stop_cava_bg_worker() {
 | 
			
		||||
  isFifoReaderRunning = false;
 | 
			
		||||
  if (pthread_join(fifoReader, NULL) != 0) {
 | 
			
		||||
    perror("pthread_join");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int get_cava_buffer(uint16_t **buffer_dst) {
 | 
			
		||||
  *buffer_dst = buffer;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t valueIndex = 0, charOffset = 0;
 | 
			
		||||
char strValue[6] = "0\0";
 | 
			
		||||
bool hasToBeDiscarded = false;
 | 
			
		||||
 | 
			
		||||
static void *fifo_to_buffer(void *arg) {
 | 
			
		||||
  struct pollfd fds[1];
 | 
			
		||||
  int timeoutMs;
 | 
			
		||||
  int ret;
 | 
			
		||||
 | 
			
		||||
  if ((cavaFifo = open("/tmp/cava_output", O_RDONLY | O_NONBLOCK)) < 0) {
 | 
			
		||||
    perror("open");
 | 
			
		||||
    close_cava();
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  memset(fds, 0, sizeof(fds));
 | 
			
		||||
  fds[0].fd = cavaFifo;
 | 
			
		||||
  fds[0].events = POLLIN;
 | 
			
		||||
  timeoutMs = 10;
 | 
			
		||||
 | 
			
		||||
  hasToBeDiscarded = true;
 | 
			
		||||
 | 
			
		||||
  while (isFifoReaderRunning) {
 | 
			
		||||
 | 
			
		||||
    if ((ret = poll(fds, 1, timeoutMs)) == 1) {
 | 
			
		||||
      int nread;
 | 
			
		||||
      nread = read(cavaFifo, lineBuffer, 128 + 1);
 | 
			
		||||
 | 
			
		||||
      if (nread >= 0) {
 | 
			
		||||
        for (size_t i = 0; i < nread; ++i) {
 | 
			
		||||
          char current = lineBuffer[i];
 | 
			
		||||
 | 
			
		||||
          if (hasToBeDiscarded) {
 | 
			
		||||
            if (current == '\n') {
 | 
			
		||||
              charOffset = 0;
 | 
			
		||||
              strValue[charOffset] = '\0';
 | 
			
		||||
              valueIndex = 0;
 | 
			
		||||
              hasToBeDiscarded = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
          } else {
 | 
			
		||||
            if ('0' <= current && current <= '9') {
 | 
			
		||||
              strValue[charOffset++] = current;
 | 
			
		||||
            } else if (current == '\n' || current == ';') {
 | 
			
		||||
              strValue[charOffset] = '\0';
 | 
			
		||||
              charOffset = 0;
 | 
			
		||||
              buffer[valueIndex++] = atoi(strValue);
 | 
			
		||||
 | 
			
		||||
              if (current == '\n' || valueIndex > 129) {
 | 
			
		||||
                valueIndex = 0;
 | 
			
		||||
 | 
			
		||||
                if (valueIndex > 129) {
 | 
			
		||||
                  fprintf(stderr, "Buffer overflow, \\n missed, discarding next\n");
 | 
			
		||||
                  hasToBeDiscarded = true;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
              fprintf(stderr, "Unexpected char %d [%c]\n", current, current);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        if (errno != EAGAIN) {
 | 
			
		||||
          perror("read");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if (ret < 0) {
 | 
			
		||||
      fprintf(stderr, "error polling %d: %s\n", cavaFifo, strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  close(cavaFifo);
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								RpiLedBars/src/tasks/cava/rpi_cava.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								RpiLedBars/src/tasks/cava/rpi_cava.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
#if !defined(__RPI_CAVA_H__)
 | 
			
		||||
#define __RPI_CAVA_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
void setup_cava();
 | 
			
		||||
 | 
			
		||||
int get_cava_buffer(uint16_t **buffer_dst);
 | 
			
		||||
 | 
			
		||||
void close_cava();
 | 
			
		||||
 | 
			
		||||
void start_cava_bg_worker();
 | 
			
		||||
 | 
			
		||||
void stop_cava_bg_worker();
 | 
			
		||||
 | 
			
		||||
#endif /* __RPI_CAVA_H__ */
 | 
			
		||||
		Reference in New Issue
	
	Block a user