E131 sender based on the library created by Shelby Merrick

4. März 2017 at 08:15
Print Friendly

In order to test the setup for a light show, I needed a tiny sender to be used during the tryout on stage.
The ESP8266 receiver modules are based on the library provided by Shelby Merrick .

So I extended this library for sending e1.31 packages: e131.h + e131.cpp download

Basically I have added buffers for transmittion (TX) based on a slightly different package structure.
This is easier when preparing root, frame and dmp for the submission.
This might be usefull for others but probably not coded well enough to inject this into the official library.

Below is the code used in the module:

#include "E131.h";
E131 e131;
uint16_t universeTX = 2;
uint8_t  DMXsequence = 1;
 
void e131_send_rgb(){
 
 if(DMXsequence > 0xFF)
 DMXsequence = 1;
 e131.setSequenceNumber(DMXsequence++);
 e131.setSourceName("ACN Tester - Thomas Hoeser");
 
 e131.setRGB(0,120,168,240);  // start, R, G, B
 e131.sendPacket(universeTX);
 delay (100);
}
/*
* E131.h
*
* Project: E131 - E.131 (sACN) library for Arduino
* Copyright (c) 2015 Shelby Merrick
* http://www.forkineye.com
*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
*
* The Author makes no warranty of any kind, express or implied, with regard
* to this program or the documentation contained in this document. The
* Author shall not be liable in any event for incidental or consequential
* damages in connection with, or arising out of, the furnishing, performance
* or use of these programs.
*
*/
 
#ifndef E131_H_
#define E131_H_
 
#include "Arduino.h"
#include "util.h"
 
/* Network interface detection. WiFi for ESP8266 and Ethernet for AVR */
#if defined (ARDUINO_ARCH_ESP8266)
# include <ESP8266WiFi.h>
# include <ESP8266WiFiMulti.h>
# include <WiFiUdp.h>
# define _UDP WiFiUDP
# define INT_ESP8266
# define INT_WIFI
#elif defined (ARDUINO_ARCH_AVR)
# include <Ethernet.h>
# include <EthernetUdp.h>
# include <avr/pgmspace.h>
# define _UDP EthernetUDP
# define INT_ETHERNET
# define NO_DOUBLE_BUFFER
#endif
 
/* Defaults */
#define E131_DEFAULT_PORT 5568
#define WIFI_CONNECT_TIMEOUT 10000 /* 10 seconds */
 
/* E1.31 Packet Offsets */
#define E131_ROOT_PREAMBLE_SIZE 0
#define E131_ROOT_POSTAMBLE_SIZE 2
#define E131_ROOT_ID 4
#define E131_ROOT_FLENGTH 16
#define E131_ROOT_VECTOR 18
#define E131_ROOT_CID 22
 
#define E131_FRAME_FLENGTH 38
#define E131_FRAME_VECTOR 40
#define E131_FRAME_SOURCE 44
#define E131_FRAME_PRIORITY 108
#define E131_FRAME_RESERVED 109
#define E131_FRAME_SEQ 111
#define E131_FRAME_OPT 112
#define E131_FRAME_UNIVERSE 113
 
#define E131_DMP_FLENGTH 115
#define E131_DMP_VECTOR 117
#define E131_DMP_TYPE 118
#define E131_DMP_ADDR_FIRST 119
#define E131_DMP_ADDR_INC 121
#define E131_DMP_COUNT 123
#define E131_DMP_DATA 125
 
/* E1.31 Private Constants */
const uint16_t _E131_PREAMBLE_SIZE = 0x0010;
const uint16_t _E131_POSTAMBLE_SIZE = 0x0000;
const uint8_t _E131_ACN_PID[] = {0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00};
const uint32_t _E131_ROOT_VECTOR = 0x00000004;
const uint32_t _E131_FRAME_VECTOR = 0x00000002;
const uint8_t _E131_FRAME_OPTIONS = 0x40;
const uint8_t _E131_DMP_VECTOR = 0x02;
const uint8_t _E131_DMP_TYPE = 0xa1;
const uint16_t _E131_DMP_FIRST_ADDR = 0x0000;
const uint16_t _E131_DMP_ADDR_INC = 0x0001;
 
/* E1.31 Public Constants */
const uint16_t E131_DEFAULT_PORT_srv = 5568;
const uint8_t E131_DEFAULT_PRIORITY = 0x64;
 
 
/* E1.31 Packet Structure */
typedef union {
 struct {
 /* Root Layer */
 uint16_t preamble_size;
 uint16_t postamble_size;
 uint8_t acn_id[12];
 uint16_t root_flength;
 uint32_t root_vector;
 uint8_t cid[16];
 
 /* Frame Layer */
 uint16_t frame_flength;
 uint32_t frame_vector;
 uint8_t source_name[64];
 uint8_t priority;
 uint16_t reserved;
 uint8_t sequence_number;
 uint8_t options;
 uint16_t universe;
 
 /* DMP Layer */
 uint16_t dmp_flength;
 uint8_t dmp_vector;
 uint8_t type;
 uint16_t first_address;
 uint16_t address_increment;
 uint16_t property_value_count;
 uint8_t property_values[513];
 } __attribute__((packed));
 
 uint8_t raw[638];
} e131_packet_t;
 
/* E1.31 Packet Type */
/* All packet contents shall be transmitted in network byte order (big endian) */
typedef union {
 struct {
 struct { /* ACN Root Layer: 38 bytes */
 uint16_t preamble_size; /* Preamble Size */
 uint16_t postamble_size; /* Post-amble Size */
 uint8_t acn_pid[12]; /* ACN Packet Identifier */
 uint16_t flength; /* Flags (high 4 bits) & Length (low 12 bits) */
 uint32_t vector; /* Layer Vector */
 uint8_t cid[16]; /* Component Identifier (UUID) */
 } __attribute__((packed)) root;
 
 struct { /* Framing Layer: 77 bytes */
 uint16_t flength; /* Flags (high 4 bits) & Length (low 12 bits) */
 uint32_t vector; /* Layer Vector */
 uint8_t source_name[64]; /* User Assigned Name of Source (UTF-8) */
 uint8_t priority; /* Packet Priority (0-200, default 100) */
 uint16_t reserved; /* Reserved (should be always 0) */
 uint8_t seq_number; /* Sequence Number (detect duplicates or out of order packets) */
 uint8_t options; /* Options Flags (bit 7: preview data, bit 6: stream terminated) */
 uint16_t universe; /* DMX Universe Number */
 } __attribute__((packed)) frame;
 
 struct { /* Device Management Protocol (DMP) Layer: 523 bytes */
 uint16_t flength; /* Flags (high 4 bits) / Length (low 12 bits) */
 uint8_t vector; /* Layer Vector */
 uint8_t type; /* Address Type & Data Type */
 uint16_t first_addr; /* First Property Address */
 uint16_t addr_inc; /* Address Increment */
 uint16_t prop_val_cnt; /* Property Value Count (1 + number of slots) */
 uint8_t prop_val[513]; /* Property Values (DMX start code + slots data) */
 } __attribute__((packed)) dmp;
 } __attribute__((packed));
 
 uint8_t raw[638]; /* raw buffer view: 638 bytes */
} e131_packet_tx_t;
 
/* Status structure */
typedef struct {
 uint32_t num_packets;
 uint32_t sequence_errors;
 uint32_t packet_errors;
} e131_stats_t;
 
/* Error Types */
typedef enum {
 ERROR_NONE,
 ERROR_ACN_ID,
 ERROR_PACKET_SIZE,
 ERROR_VECTOR_ROOT,
 ERROR_VECTOR_FRAME,
 ERROR_VECTOR_DMP
} e131_error_t;
 
/* E1.31 Listener Types */
typedef enum {
 E131_UNICAST,
 E131_MULTICAST
} e131_listen_t;
 
class E131 {
 private:
 /* Constants for packet validation */
 static const uint8_t ACN_ID[];
 static const uint32_t VECTOR_ROOT = 4;
 static const uint32_t VECTOR_FRAME = 2;
 static const uint8_t VECTOR_DMP = 2;
 
 e131_packet_t pbuff1; /* Packet buffer */
 e131_packet_tx_t pbuff3; /* Packet buffer */
#ifndef NO_DOUBLE_BUFFER
 e131_packet_t pbuff2; /* Double buffer */
 e131_packet_tx_t pbuff4; /* Double buffer */
#endif
 e131_packet_t *pwbuff; /* Pointer to working packet buffer */
 e131_packet_tx_t *pwbuffTX; /* Pointer to working packet TX buffer */
 uint8_t sequence; /* Sequence tracker */
 _UDP udp; /* UDP handle */
 
 /* Internal Initializers */
 int initWiFi(const char *ssid, const char *passphrase);
 int initEthernet(uint8_t *mac, IPAddress ip, IPAddress netmask,
 IPAddress gateway, IPAddress dns);
 void initUnicast();
 void initMulticast(uint16_t universe);
 uint16_t swapf_uint16(uint16_t x);
 uint32_t swapf_uint32(uint32_t x);
 
 public:
 uint8_t *data; /* Pointer to DMX channel data */
 uint8_t       ddStartCode;          /* DD start code data*/
 uint16_t universe; /* DMX Universe of last valid packet */
 e131_packet_t *packet; /* Pointer to last valid packet */
 e131_packet_tx_t *packetTX; /* Pointer to last valid TX packet */
 e131_stats_t stats; /* Statistics tracker */
 
 E131();
 
 /* Generic UDP listener, no physical or IP configuration */
 void begin(e131_listen_t type, uint16_t universe = 1);
 void stopUdp(void);
 void connectMulticast(uint16_t universe);
 void dumpPacket(int packetNo);
 void setRGB(const uint8_t channel,const uint8_t dRed,const uint8_t dGreen,const uint8_t dBlue);
 void setSourceName(const char *source_name); //
 void setSequenceNumber(const int seq_number);
 void setData(const int channel, const int dmxVal );
 int setPacketHeader(const uint16_t universe, const uint16_t num_channels);
 void fillSendBuffer(uint8_t fillData);
 void clearSendBuffer(void );
 
/****** START - Wireless ifdef block ******/
#if defined (INT_WIFI) || defined (INT_ESP8266)
 /* Unicast WiFi Initializers */
 int begin(const char *ssid, const char *passphrase);
 int begin(const char *ssid, const char *passphrase,
 IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns);
#endif
/****** END - Wireless ifdef block ******/
 
/****** START - ESP8266 ifdef block ******/
#if defined (INT_ESP8266)
 /* Multicast WiFi Initializers -- ESP8266 Only */
 int beginMulticast(const char *ssid, const char *passphrase,
 uint16_t universe);
 int beginMulticast(const char *ssid, const char *passphrase,
 uint16_t universe, IPAddress ip, IPAddress netmask,
 IPAddress gateway, IPAddress dns);
#endif
/****** END - ESP8266 ifdef block ******/
 
/****** START - Ethernet ifdef block ******/
#if defined (INT_ETHERNET)
 /* Unicast Ethernet Initializers */
 int begin(uint8_t *mac);
 void begin(uint8_t *mac,
 IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns);
 
 /* Multicast Ethernet Initializers */
 int beginMulticast(uint8_t *mac, uint16_t universe);
 void beginMulticast(uint8_t *mac, uint16_t universe,
 IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns);
#endif
/****** END - Ethernet ifdef block ******/
 
 /* Diag functions */
 void dumpError(e131_error_t error);
 
 //------------------------------------------------------------------------------------------------
 inline uint16_t sendPacket(uint16_t universe) {
 IPAddress ipMultiE131 = IPAddress(239, 255, ((universe >> 8) & 0xff),
 ((universe >> 0) & 0xff));
 setPacketHeader(universe,512);
 udp.beginPacketMulticast(ipMultiE131, E131_DEFAULT_PORT,WiFi.localIP());
 udp.write(pwbuffTX->raw, sizeof(pwbuffTX->raw) );
 // udp.write(pwbuff->raw, sizeof(pwbuff->raw) );
 udp.endPacket();
 }
 
 //------------------------------------------------------------------------------------------------
 /* Main packet parser */
 inline uint16_t parsePacket() {
 e131_error_t error;
 uint16_t retval = 0;
 
 int size = udp.parsePacket();
 if (size) {
 udp.readBytes(pwbuff->raw, size);
 error = validate();
 if (!error) {
#ifndef NO_DOUBLE_BUFFER
 e131_packet_t *swap = packet;
 packet = pwbuff;
 pwbuff = swap;
#endif
 universe = htons(packet->universe);
 ddStartCode = *packet->property_values;
 data = packet->property_values + 1;
 retval = htons(packet->property_value_count) - 1;
 if (packet->sequence_number != sequence++) {
 stats.sequence_errors++;
 sequence = packet->sequence_number + 1;
 }
 stats.num_packets++;
 } else {
 if (Serial)
 dumpError(error);
 stats.packet_errors++;
 }
 }
 return retval;
 }
 
 /* Packet validater */
 inline e131_error_t validate() {
#ifdef ARDUINO_ARCH_AVR
 if (memcmp_P(pwbuff->acn_id, ACN_ID, sizeof(pwbuff->acn_id)))
#else
 if (memcmp(pwbuff->acn_id, ACN_ID, sizeof(pwbuff->acn_id)))
#endif
 return ERROR_ACN_ID;
 if (htonl(pwbuff->root_vector) != VECTOR_ROOT)
 return ERROR_VECTOR_ROOT;
 if (htonl(pwbuff->frame_vector) != VECTOR_FRAME)
 return ERROR_VECTOR_FRAME;
 if (pwbuff->dmp_vector != VECTOR_DMP)
 return ERROR_VECTOR_DMP;
 return ERROR_NONE;
 }
};
 
#endif /* E131_H_ */
e131.cpp

/*
* E131.cpp
*
* Project: E131 - E.131 (sACN) library for Arduino
* Copyright (c) 2015 Shelby Merrick
* http://www.forkineye.com
*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
*
* The Author makes no warranty of any kind, express or implied, with regard
* to this program or the documentation contained in this document. The
* Author shall not be liable in any event for incidental or consequential
* damages in connection with, or arising out of, the furnishing, performance
* or use of these programs.
*
*/
 
#include "E131.h"
#include <string.h>
 
 
 
/* E1.17 ACN Packet Identifier */
#ifdef ARDUINO_ARCH_AVR
const PROGMEM byte E131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
#else
const byte E131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
#endif
 
/* Constructor */
E131::E131() {
#ifdef NO_DOUBLE_BUFFER
 memset(pbuff1.raw, 0, sizeof(pbuff1.raw));
 packet = &pbuff1;
 pwbuff = packet;
#else
 memset(pbuff1.raw, 0, sizeof(pbuff1.raw));
 memset(pbuff2.raw, 0, sizeof(pbuff2.raw));
 memset(pbuff3.raw, 0, sizeof(pbuff3.raw));
 memset(pbuff4.raw, 0, sizeof(pbuff4.raw));
 packet = &pbuff1;
 pwbuff = &pbuff2;
 packetTX = &pbuff3;
 pwbuffTX = &pbuff4;
#endif
 
 sequence = 0;
 stats.num_packets = 0;
 stats.sequence_errors = 0;
 stats.packet_errors = 0;
}
 
uint16_t E131::swapf_uint16(uint16_t x) {
 // return ( ( (x) <<8) | (( (x) >>8)&0xFF ) );
 return ( htons(x) );
}
 
uint32_t E131::swapf_uint32(uint32_t x) {
 return ( htonl(x) );
}
 
#define swap_uint16(x) ( ( (x) <<8) | (( (x) >>8)&0xFF ) ) // surprisingly this does not work with printf
// ( ( 0x89<<8) | (( 0x1289 >>8)&0xFF ) )
 
void E131::initUnicast() {
 delay(100);
 udp.begin(E131_DEFAULT_PORT);
 if (Serial) {
 Serial.print(F("- Unicast port: "));
 Serial.println(E131_DEFAULT_PORT);
 }
}
 
void E131::setRGB(const uint8_t channel,const uint8_t dRed,const uint8_t dGreen,const uint8_t dBlue) {
 // first channel on [1]; [0] has to be 0
 pwbuffTX->dmp.prop_val[channel+1] = dRed;
 pwbuffTX->dmp.prop_val[channel+2] = dGreen;
 pwbuffTX->dmp.prop_val[channel+3] = dBlue;
}
 
void E131::clearSendBuffer() {
 memset(&pwbuffTX->dmp.prop_val[0], 0, sizeof(pwbuffTX->dmp.prop_val));
}
 
void E131::fillSendBuffer(const uint8_t fillData) {
 memset(&pwbuffTX->dmp.prop_val[1],fillData,sizeof(pwbuffTX->dmp.prop_val)-1);
}
 
void E131::setSourceName(const char *source_name) {
 memcpy(pwbuffTX->frame.source_name, source_name, strlen(source_name) );
}
void E131::setSequenceNumber(const int seq_number) {
 pwbuffTX->frame.seq_number = seq_number;
}
void E131::setData(const int channel, const int dmxVal ) {
 pwbuffTX->dmp.prop_val[channel] = dmxVal;
}
 
/* Initialize an E1.31 packet using a universe and a number of slots */
int E131::setPacketHeader(const uint16_t universe, const uint16_t num_channels) {
 if (pwbuffTX == NULL || universe < 1 || universe > 63999 || num_channels < 1 || num_channels > 512) {
 // errno = EINVAL;
 return -1;
 }
 
 // compute packet layer lengths
 uint16_t prop_val_cnt = num_channels + 1;
 uint16_t dmp_length = prop_val_cnt +
 sizeof pwbuffTX->dmp - sizeof pwbuffTX->dmp.prop_val;
 uint16_t frame_length = sizeof pwbuffTX->frame + dmp_length;
 uint16_t root_length = sizeof pwbuffTX->root.flength +
 sizeof pwbuffTX->root.vector + sizeof pwbuffTX->root.cid + frame_length;
 
 // clear packet
 memset(packet, 0, sizeof *packet);
 
 // set Root Layer values
 pwbuffTX->root.preamble_size = swapf_uint16(_E131_PREAMBLE_SIZE);
 pwbuffTX->root.postamble_size = swapf_uint16(_E131_POSTAMBLE_SIZE);
 memcpy(pwbuffTX->root.acn_pid, _E131_ACN_PID, sizeof pwbuffTX->root.acn_pid);
 pwbuffTX->root.flength = swapf_uint16(0x7000 | root_length);
 pwbuffTX->root.vector = swapf_uint32(_E131_ROOT_VECTOR);
 
 // set Framing Layer values
 pwbuffTX->frame.flength = swapf_uint16(0x7000 | frame_length);
 pwbuffTX->frame.vector = swapf_uint32(_E131_FRAME_VECTOR);
 pwbuffTX->frame.priority = E131_DEFAULT_PRIORITY;
 pwbuffTX->frame.options = _E131_FRAME_OPTIONS;
 pwbuffTX->frame.universe = swapf_uint16(universe);
 
 // set Device Management Protocol (DMP) Layer values
 pwbuffTX->dmp.flength = swapf_uint16(0x7000 | dmp_length);
 pwbuffTX->dmp.vector = _E131_DMP_VECTOR;
 pwbuffTX->dmp.type = _E131_DMP_TYPE;
 pwbuffTX->dmp.first_addr = swapf_uint16(_E131_DMP_FIRST_ADDR);
 pwbuffTX->dmp.addr_inc = swapf_uint16(_E131_DMP_ADDR_INC);
 pwbuffTX->dmp.prop_val_cnt = swapf_uint16(prop_val_cnt);
 
 return 0;
}
 
 
void E131::dumpPacket(int packetNo) {
 
 e131_packet_t *dmpbuff;
 
 if (packetNo==0) {
 dmpbuff = packet;
 Serial.print("\n --------------------------------- packet \n");
 }
 else {
 dmpbuff = pwbuff;
 Serial.print("\n --------------------------------- pwbuff \n");
 }
 
 
 Serial.print("E1.31 Root Layer\n");
 printf(" Preamble Size .......... %04x\n" , swapf_uint16(dmpbuff->preamble_size) ); // SWAP // uint16_t = 2 bytes = [0-65535] or [0x0000-0xFFFF] = unsigned int
 printf(" Post-amble Size ........ %04x\n" , swapf_uint16(dmpbuff->postamble_size) ); // uint16_t = 2 bytes = [0-65535] or [0x0000-0xFFFF] = unsigned int
 printf(" ACN Packet Identifier .. %s\n" , dmpbuff->acn_id); // uint8_t = 1 bytes = [0-255] or [0x00-0xFF] = unsigned char
 printf(" Flags & Length ......... %04x\n" , swapf_uint16(dmpbuff->root_flength) ); // uint16_t = 2 bytes = [0-65535] or [0x0000-0xFFFF] = unsigned int
 printf(" Layer Vector ........... %08x\n" , swapf_uint32(dmpbuff->root_vector) ); // uint32_t = 4 bytes = unsigned long
 printf(" Component Identifier ... ");
 for (size_t pos=0, total=sizeof dmpbuff->cid; pos<total; pos++)
 printf("%02x", dmpbuff->cid[pos]);
 printf("\n");
 
 Serial.print("E1.31 Framing Layer\n");
 printf(" Flags & Length ......... %04x\n" , swapf_uint16(dmpbuff->frame_flength)); // uint16_t
 printf(" Layer Vector ........... %08x\n" , swapf_uint32(dmpbuff->frame_vector)); // uint32_t
 printf(" Source Name ............ %s\n" , dmpbuff->source_name); // uint8_t
 printf(" Packet Priority ........ %02x = %u\n" , dmpbuff->priority,dmpbuff->priority); // ok // uint8_t
 printf(" Reserved ............... %04x\n" , swapf_uint16(dmpbuff->reserved)); // uint16_t
 printf(" Sequence Number ........ %02x\n" , dmpbuff->sequence_number); // ok // uint8_t
 printf(" Options Flags .......... %02x\n" , dmpbuff->options); // uint8_t
 printf(" DMX Universe Number .... %04x = %u\n" , swapf_uint16(dmpbuff->universe),swapf_uint16(dmpbuff->universe));// SWAP // uint16_t
 
 Serial.print("E1.31 Device Management Protocol (DMP) Layer\n");
 printf(" Flags & Length ......... %04x\n" , swapf_uint16(dmpbuff->dmp_flength)); // uint16_t = 2 bytes = [0-65535] or [0x0000-0xFFFF] = unsigned int
 printf(" Layer Vector ........... %02x\n" , dmpbuff->dmp_vector); // uint8_t = 1 bytes = [0-255] or [0x00-0xFF] = unsigned char
 printf(" Address & Data Type .... %02x\n" , dmpbuff->type); // uint8_t = 1 bytes = [0-255] or [0x00-0xFF] = unsigned char
 printf(" First Address .......... %04x\n" , swapf_uint16(dmpbuff->first_address)); // uint16_t = 2 bytes = [0-65535] or [0x0000-0xFFFF] = unsigned int
 printf(" Address Increment ...... %04x\n" , swapf_uint16(dmpbuff->address_increment)); // uint16_t = 2 bytes = [0-65535] or [0x0000-0xFFFF] = unsigned int
 printf(" Property Value Count ... %04x\n" , swapf_uint16(dmpbuff->property_value_count)); // uint16_t = 2 bytes = [0-65535] or [0x0000-0xFFFF] = unsigned int
 
 Serial.print("E1.31 DMP Property Values\n ");
 for (size_t pos=0, total=(dmpbuff->property_value_count); pos<total; pos++)
 printf(" %02x", dmpbuff->property_values[pos]);
 
 Serial.print("\n");
 
}
 
void E131::stopUdp(void) {
 
#ifdef INT_ESP8266
 udp.stop();
#endif
 
}
 
void E131::connectMulticast(uint16_t universe) {
 int retval=0;
 delay(100);
 IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff),
 ((universe >> 0) & 0xff));
#ifdef INT_ESP8266
 retval = udp.beginMulticast(WiFi.localIP(), address, E131_DEFAULT_PORT);
#endif
 if (Serial) {
 Serial.print(F("udp> Universe: "));
 Serial.print(universe);
 Serial.print(F(" / Multicast address: "));
 Serial.print(address);
 if (retval > 0)
 Serial.print(F(" udp ok\n"));
 else
 Serial.print(F(" ERROR: udp no sockets available\n"));
 
 }
}
 
 
void E131::initMulticast(uint16_t universe) {
 int retval=0;
 delay(100);
 IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff),
 ((universe >> 0) & 0xff));
#ifdef INT_ESP8266
 retval = udp.beginMulticast(WiFi.localIP(), address, E131_DEFAULT_PORT);
#endif
 if (Serial) {
 Serial.print(F("- Universe: "));
 Serial.println(universe);
 Serial.print(F("- Multicast address: "));
 Serial.print(address);
 if (retval > 0)
 Serial.print(F(" udp ok\n"));
 else
 Serial.print(F(" ERROR: udp no sockets available\n"));
 
 }
}
 
/****** START - Wireless ifdef block ******/
#if defined (INT_ESP8266) || defined (INT_WIFI)
 
/* WiFi Initialization */
int E131::initWiFi(const char *ssid, const char *passphrase) {
 /* Switch to station mode and disconnect just in case */
 // WiFi.mode(WIFI_STA); // removed so we can operate in STA_AP
 WiFi.disconnect();
 delay(100);
 
 if (Serial) {
 Serial.println("");
 Serial.print(F("Connecting to "));
 Serial.print(ssid);
 }
 
 if (passphrase != NULL)
 WiFi.begin(ssid, passphrase);
 else
 WiFi.begin(ssid);
 
 uint32_t timeout = millis();
 uint8_t retval = 1;
 while (WiFi.status() != WL_CONNECTED) {
 delay(500);
 if (Serial)
 Serial.print(".");
 if (millis() - timeout > WIFI_CONNECT_TIMEOUT) {
 retval = 0;
 if (Serial) {
 Serial.println("");
 Serial.println(F("*** Failed to connect ***"));
 }
 break;
 }
 }
 
 return retval;
}
 
void E131::begin(e131_listen_t type, uint16_t universe) {
 if (type == E131_UNICAST)
 initUnicast();
 if (type == E131_MULTICAST)
 initMulticast(universe);
}
 
int E131::begin(const char *ssid, const char *passphrase) {
 if (initWiFi(ssid, passphrase)) {
 if (Serial) {
 Serial.println("");
 Serial.print(F("Connected DHCP with IP: "));
 Serial.println(WiFi.localIP());
 }
 initUnicast();
 }
 return WiFi.status();
}
 
int E131::begin(const char *ssid, const char *passphrase,
 IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns) {
 if (initWiFi(ssid, passphrase)) {
 WiFi.config(ip, gateway, netmask, dns);
 if (Serial) {
 Serial.println("");
 Serial.print(F("Connected with Static IP: "));
 Serial.println(WiFi.localIP());
 }
 initUnicast();
 }
 return WiFi.status();
}
 
#endif
/****** END - Wireless ifdef block ******/
 
/****** START - ESP8266 ifdef block ******/
#if defined (INT_ESP8266)
int E131::beginMulticast(const char *ssid, const char *passphrase,
 uint16_t universe) {
 if (initWiFi(ssid, passphrase)) {
 if (Serial) {
 Serial.println("");
 Serial.print(F("Connected DHCP with IP: "));
 Serial.println(WiFi.localIP());
 }
 initMulticast(universe);
 }
 return WiFi.status();
}
 
int E131::beginMulticast(const char *ssid, const char *passphrase,
 uint16_t universe, IPAddress ip, IPAddress netmask,
 IPAddress gateway, IPAddress dns) {
 if (initWiFi(ssid, passphrase)) {
 WiFi.config(ip, dns, gateway, netmask);
 if (Serial) {
 Serial.println("");
 Serial.print(F("Connected with Static IP: "));
 Serial.println(WiFi.localIP());
 }
 initMulticast(universe);
 }
 return WiFi.status();
}
#endif
/****** END - ESP8266 ifdef block ******/
 
/****** START - Ethernet ifdef block ******/
#if defined (INT_ETHERNET)
 
/* Unicast Ethernet Initializers */
int E131::begin(uint8_t *mac) {
 int retval = 0;
 
 if (Serial) {
 Serial.println("");
 Serial.println(F("Requesting Address via DHCP"));
 Serial.print(F("- MAC: "));
 for (int i = 0; i < 6; i++)
 Serial.print(mac[i], HEX);
 Serial.println("");
 }
 
 retval = Ethernet.begin(mac);
 
 if (Serial) {
 if (retval) {
 Serial.print(F("- IP Address: "));
 Serial.println(Ethernet.localIP());
 } else {
 Serial.println(F("** DHCP FAILED"));
 }
 }
 
 if (retval)
 initUnicast();
 
 return retval;
}
 
void E131::begin(uint8_t *mac, IPAddress ip, IPAddress netmask,
 IPAddress gateway, IPAddress dns) {
 Ethernet.begin(mac, ip, dns, gateway, netmask);
 if (Serial) {
 Serial.println("");
 Serial.println(F("Static Configuration"));
 Serial.println(F("- MAC: "));
 for (int i = 0; i < 6; i++)
 Serial.print(mac[i], HEX);
 Serial.print(F("- IP Address: "));
 Serial.println(Ethernet.localIP());
 }
 
 initUnicast();
}
 
/* Multicast Ethernet Initializers */
int E131::beginMulticast(uint8_t *mac, uint16_t universe) {
 //TODO: Add ethernet multicast support
}
 
void E131::beginMulticast(uint8_t *mac, uint16_t universe,
 IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns) {
 //TODO: Add ethernet multicast support
}
#endif
/****** END - Ethernet ifdef block ******/
 
void E131::dumpError(e131_error_t error) {
 switch (error) {
 case ERROR_ACN_ID:
 Serial.print(F("INVALID PACKET ID: "));
 for (int i = 0; i < sizeof(ACN_ID); i++)
 Serial.print(pwbuff->acn_id[i], HEX);
 Serial.println("");
 break;
 case ERROR_PACKET_SIZE:
 Serial.println(F("INVALID PACKET SIZE: "));
 break;
 case ERROR_VECTOR_ROOT:
 Serial.print(F("INVALID ROOT VECTOR: 0x"));
 Serial.println(htonl(pwbuff->root_vector), HEX);
 break;
 case ERROR_VECTOR_FRAME:
 Serial.print(F("INVALID FRAME VECTOR: 0x"));
 Serial.println(htonl(pwbuff->frame_vector), HEX);
 break;
 case ERROR_VECTOR_DMP:
 Serial.print(F("INVALID DMP VECTOR: 0x"));
 Serial.println(pwbuff->dmp_vector, HEX);
 }
}