Skip to content

libE131: a lightweight C/C++ library for the E1.31 (sACN) protocol

License

Notifications You must be signed in to change notification settings

hhromic/libe131

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libE131: a lightweight C/C++ library for the E1.31 (sACN) protocol

This is a lightweight C/C++ library that provides a simple API for packet, client and server programming to be used for communicating with devices implementing the E1.31 (sACN) protocol. Detailed information about E.131 (sACN) can be found on this Wiki article from which most informational content about E1.31 in this document comes from.

ACN is a suite of protocols (via Ethernet) that is the industry standard for lighting and control. ESTA, the creator of the standard, ratified a subset of this protocol for "lightweight" devices which is called sACN (E1.31). This lightweight version allows microcontroller based lighting equipment to communicate via ethernet, without all the overhead of the full ACN protocol.

The simplest way to think about E1.31 is that it is a way to transport a large number of lighting control channels over a traditional ethernet network connection. E1.31 transports those channels in "Universes", which is a collection of up to 512 channels together. E.131 is ethernet based and is the data sent via UDP. The two accepted transport methods are multicast and unicast, the most common implementation is multicast. When using multicast all the controller needs to do is subscribe to the multicast address for the universe that you want to receive data from.

Installation

To install libE131 in your system, download the latest release archive and use the standard autotools approach:

./configure --prefix=/usr \
&& make \
&& sudo make install

The last step requires root privileges. You can uninstall the library using:

sudo make uninstall

A development package for Arch Linux is also available on the AUR.

E1.31 (sACN) Packets

E1.31 network packets are based on ACN and are composed of three layers:

E1.31 Packet = ACN Root Layer + Framing Layer + Device Management Protocol (DMP) Layer

All packet contents are transmitted in network byte order (big endian). If you are accessing fields larger than one byte, you must read/write from/to them using the ntohs, ntohl, htons and htonl conversion functions.

Fortunately, most of the time you will not need to manipulate those fields directly because libE131 provides convenience functions for the most common fields. See the API documentation section for more information.

The following is the e131_packet_t type union provided by libE131 for sending/receiving E1.31 packets:

typedef union {
  PACK(struct {
    PACK(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) */
    }) root;

    PACK(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 */
    }) frame;

    PACK(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) */
    }) dmp;
  });

  uint8_t raw[638]; /* raw buffer view: 638 bytes */
} e131_packet_t;

This union provides two ways to access the data of an E1.31 packet:

  1. Directly using the raw member (typically used for receiving data from the network).
  2. Structurally using the root, frame and dmp members (typically used for processing a packet).

You can easily create and initialize a new E1.31 packet to be used for sending using the e131_pkt_init() function.

Refer to the examples section for how to use this data structure with libE131.

Framing Layer Options

The library provides two convenience functions, e131_get_option() and e131_set_option() to manipulate the options flag in the Framing Layer of an E1.31 packet. The following table describes the available option constants:

Option Constant Description
E131_OPT_TERMINATED The current packet is the last one in the stream. The receiver should stop processing further packets.
E131_OPT_PREVIEW The data in the packet should be only used for preview purposes, e.g. console display, and not to drive live fixtures.

Refer to the examples section for how to use Framing Layer options with libE131.

Packet Validation

The library provides a convenience function, e131_pkt_validate(), to check if an E1.31 packet is valid to be processed by your application. This function returns a validation status from the e131_error_t enumeration. The following table describes the available error constants:

Error Constant Description
E131_ERR_NONE Success (no validation error detected, you can process the packet).
E131_ERR_PREAMBLE_SIZE Invalid Preamble Size.
E131_ERR_POSTAMBLE_SIZE Invalid Post-amble Size.
E131_ERR_ACN_PID Invalid ACN Packet Identifier.
E131_ERR_VECTOR_ROOT Invalid Root Layer Vector.
E131_ERR_VECTOR_FRAME Invalid Framing Layer Vector.
E131_ERR_VECTOR_DMP Invalid Device Management Protocol (DMP) Layer Vector.
E131_ERR_TYPE_DMP Invalid DMP Address & Data Type.
E131_ERR_FIRST_ADDR_DMP Invalid DMP First Address.
E131_ERR_ADDR_INC_DMP Invalid DMP Address Increment.

The above error descriptions are also programatically obtainable using the e131_strerror() function. Refer to the API documentation section for more information.

Refer to the examples section for how to use packet validation and error reporting with libE131.

Unicast and Multicast Destinations

Most E1.31 software and hardware can be set up to communicate via two transport methods: Unicast and Multicast.

Unicast Transmission

Unicast is a method of sending data across a network where two devices, the control PC and the lighting controller, are directly connected (or through a network switch or router) and the channel control information meant for that specific controller is only sent to that controller. Unicast is a point to point protocol and the lighting channel information is only switched or routed to the device with the matching IP address.

You must have unique IP address in each controller. Using Unicast the data packets are sent directly to the device instead of being broadcast across the entire subnet.

In unicast transmission:

  • More channels of data are allowed to controllers and bridges (commonly 12 Universes vs. 7 for Multicast).
  • Channels can only be sent to one controller per Universe.

Multicast Transmission

Multicast is a method to send data across a network where a sender, typically a PC, broadcasts the data to all devices connected to the network subnet and the information about the channels are sent to all controllers connected to the network and every other device on the network.

Multicast is a point to multipoint broadcast where the controllers need to listen to and only respond to information they are configured to use. Your PC sequencing software or streaming tool sends multicast packets with an address of 239.255.<UHB>.<ULB> where UHB is the Universe high byte and LHB is the Universe low byte. As an example, the address for universe 1 would be 239.255.0.1. This is why using multicast addressing can be simpler to configure since this address is always the same for any device using that universe.

Depending on the device and the number of universes of data sent it can swamp the device and possibly end up causing a loss of data. Note however that this is not an issue for most networks until you get into the dozens of universes so it's not an issue for most users.

The data is received on multicast IP address not on the individual device IP address.

In multicast transmission:

  • Less channels of data are allowed to controllers and bridges (commonly 7 Universes vs. 12 for Unicast).
  • Simpler network configuration since controllers don't need data address information.
  • Channels can be mirrored on multiple controllers since the same Universe can be used by multiple controllers.

E1.31 (sACN) Universes

E1.31 transports lighting information in "Universes", which is a collection of up to 512 channels together. You can chose any universe number from 1-63999 and assign it to a block of channels in your sequencing software. While a Universe can contain up to 512 channels, it does not have to, and can be any number between 1-512 channels.

API Documentation

Public Library Constants

  • const uint16_t E131_DEFAULT_PORT: default network port for E1.31 UDP data (5568).
  • const uint8_t E131_DEFAULT_PRIORITY: default E1.31 packet priority (100).

Public Library Functions

The library provides a number of functions to help you develop clients and servers that communicate using the E1.31 protocol. The following is a descriptive list of all functions provided.

See the examples sections to see how the most common API functions are used with libE131.

  • int e131_socket(void): Create a socket file descriptor suitable for E1.31 communication. On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errno is set appropriately.

  • int e131_bind(int sockfd, const uint16_t port): Bind a socket file descriptor to a port number for E1.31 communication. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • int e131_unicast_dest(e131_addr_t *dest, const char *host, const uint16_t port): Initialize a unicast E1.31 destination using a host and port number. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • int e131_multicast_dest(e131_addr_t *dest, const uint16_t universe, const uint16_t port): Initialize a multicast E1.31 destination using a universe and port number. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • int e131_dest_str(char *str, const e131_addr_t *dest): Describe an E1.31 destination into a string (must be at least 22 bytes). On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • int e131_multicast_iface(int sockfd, const int ifindex): Configure a socket file descriptor to use a specific network interface for outgoing multicast data. Interface index zero is the system default interface. On error, -1 is returned, and errno is set appropriately.

  • int e131_multicast_join(int sockfd, const uint16_t universe): Join a socket file descriptor to an E1.31 multicast group using a universe. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • int e131_multicast_join_iface(int sockfd, const uint16_t universe, const int ifindex): Join a socket file descriptor to an E1.31 multicast group using a universe and a specific network interface. On error, -1 is returned, and errno is set appropriately.

  • int e131_multicast_join_ifaddr(int sockfd, const uint16_t universe, const char *ifaddr): Join a socket file descriptor to an E1.31 multicast group using a universe and an IP address to bind to. On error, -1 is returned, and errno is set appropriately.

  • int e131_pkt_init(e131_packet_t *packet, const uint16_t universe, const uint16_t num_slots): Initialize an E1.31 packet using a universe and a number of slots. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • bool e131_get_option(const e131_packet_t *packet, const e131_option_t option): Get the state of a framing option in an E1.31 packet.

  • int e131_set_option(e131_packet_t *packet, const e131_option_t option, const bool state): Set the state of a framing option in an E1.31 packet. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • ssize_t e131_send(int sockfd, const e131_packet_t *packet, const e131_addr_t *dest): Send an E1.31 packet to a socket file descriptor using a destination. On success, the number of bytes sent is returned. On error, -1 is returned, and errno is set appropriately.

  • ssize_t e131_recv(int sockfd, e131_packet_t *packet): Receive an E1.31 packet from a socket file descriptor. This function returns the number of bytes received, or -1 if an error occurred. On error, errno is set appropriately.

  • e131_error_t e131_pkt_validate(const e131_packet_t *packet): Validate that an E1.31 packet is well-formed. See the Packet Validation section.

  • bool e131_pkt_discard(const e131_packet_t *packet, const uint8_t last_seq_number): Check if an E1.31 packet should be discarded (sequence number out of order). This function uses the standard out-of-sequence detection algorithm defined in the E1.31 specification.

  • int e131_pkt_dump(FILE *stream, const e131_packet_t *packet): Dump an E1.31 packet to a stream (i.e. stdout, stderr). The output is formatted for human readable output. On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

  • const char *e131_strdest(const e131_addr_t *dest): Return a string describing an E1.31 destination.

  • const char *e131_strerror(const e131_error_t error): Return a string describing an E1.31 error.

Examples

The examples/test_client.c example demonstrates how to create a simple E1.31 client. To compile it:

gcc -Wall test_client.c -o test_client -le131

The examples/test_server.c example demonstrates how to create a simple E1.31 server. To compile it:

gcc -Wall test_server.c -o test_server -le131

Projects using libE131

The following projects use libE131:

Also check out the Node.js port of libE131.

License

This software is under the Apache License 2.0.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.