Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send support #35

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 167 additions & 1 deletion E131.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,30 @@ E131::E131() {
memset(pbuff2.raw, 0, sizeof(pbuff2.raw));
packet = &pbuff1;
pwbuff = &pbuff2;
memset(pbuff3.raw, 0, sizeof(pbuff3.raw));
memset(pbuff4.raw, 0, sizeof(pbuff4.raw));
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);
Expand Down Expand Up @@ -76,13 +94,161 @@ void E131::initMulticast(uint16_t universe, uint8_t n) {
}
}

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"));
}
}

/****** 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);
// WiFi.mode(WIFI_STA); // removed so we can operate in STA_AP
WiFi.disconnect();
delay(100);

Expand Down
86 changes: 86 additions & 0 deletions E131.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@
#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 {
Expand Down Expand Up @@ -105,6 +122,44 @@ typedef union {
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;

/* Error Types */
typedef enum {
ERROR_NONE,
Expand All @@ -125,6 +180,7 @@ typedef enum {
/* Status structure */
typedef struct {
uint32_t num_packets;
uint32_t sequence_errors;
uint32_t packet_errors;
IPAddress last_clientIP;
uint16_t last_clientPort;
Expand All @@ -139,29 +195,48 @@ class E131 {
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 */
_UDP udp; /* UDP handle */
e131_packet_tx_t *pwbuffTX; /* Pointer to working packet TX buffer */
uint8_t sequence; /* Sequence tracker */


/* 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, uint8_t n = 1);
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, uint8_t n = 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)
Expand Down Expand Up @@ -201,6 +276,17 @@ class E131 {
/* 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;
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
E131 - E1.31 (sACN) library for Arduino
=======================================

## Fork using code with option to send based on code from hoeser-medien.de/2017/03/e131-sender-based-on-library-created-by-forkineye/

### ** This library is no longer maintained. If you are on an ESP platform, you should be using [ESPAsyncE131](https://github.com/forkineye/ESPAsyncE131)

This library is to simplify the validation and handling of E1.31 sACN (DMX over Ethernet) traffic. It supports both Unicast and Multicast configurations and exposes the full E1.31 packet to the user. Currently, development is targeted for the ESP8266 WiFi module and traditional Ethernet shields.
Expand Down
32 changes: 32 additions & 0 deletions examples/E131-Send/E131-Send.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include <ESP8266WiFi.h>
#include <E131.h>

#include "wifi.h"
// Create file with the following
// *************************************************************************
// #define SECRET_SSID ""; /* Replace with your SSID */
// #define SECRET_PSK ""; /* Replace with your WPA2 passphrase */
// *************************************************************************

const char ssid[] = SECRET_SSID; /* Replace with your SSID */
const char passphrase[] = SECRET_PSK; /* Replace with your WPA2 passphrase */

E131 e131;

void setup() {
Serial.begin(115200);
delay(10);

/* Choose one to begin listening for E1.31 data */
// e131.begin(ssid, passphrase); /* via Unicast on the default port */
e131.beginMulticast(ssid, passphrase, 1); /* via Multicast for Universe 1 */

// e131.begin(E131_MULTICAST);
}

void loop() {

e131.setData(2,random(0,255));
e131.sendPacket(1);
delay(10);
}