Skip to content

Commit

Permalink
[dmx] Multiple improvements, add support for enttex dmx usb pro mk2
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Feb 22, 2024
1 parent 4cfd287 commit bd6b69f
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 66 deletions.
20 changes: 12 additions & 8 deletions src/ossia/protocols/artnet/artnet_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

#include <chrono>

#define ARTNET_NODE_SHORT_NAME "libossia"
#define ARTNET_NODE_LONG_NAME "Libossia Artnet Protocol"

namespace ossia::net
{
dmx_buffer::dmx_buffer()
Expand All @@ -22,9 +19,8 @@ dmx_buffer::dmx_buffer()

dmx_buffer::~dmx_buffer() = default;

static constexpr int artnet_port_id = 0;
artnet_protocol::artnet_protocol(
ossia::net::network_context_ptr ctx, const dmx_config& conf)
ossia::net::network_context_ptr ctx, const dmx_config& conf, std::string_view host)
: dmx_protocol_base{ctx, conf}
{
if(conf.frequency < 1 || conf.frequency > 44)
Expand All @@ -38,19 +34,27 @@ artnet_protocol::artnet_protocol(
// update at higher frequencies => Work TODO

// Do not specify ip address for now, artnet will choose one
m_node = artnet_new(nullptr, 1);
#if defined(_NDEBUG)
bool verbose = 0;
#else
bool verbose = 1;
#endif
m_node = artnet_new(host.data(), verbose);

if(m_node == NULL)
throw std::runtime_error("Artnet new failed");

static constexpr int artnet_port_id = 0;
artnet_set_port_type(m_node, artnet_port_id, ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX);
artnet_set_port_addr(m_node, artnet_port_id, ARTNET_OUTPUT_PORT, m_conf.universe);

artnet_set_short_name(m_node, ARTNET_NODE_SHORT_NAME);
artnet_set_long_name(m_node, ARTNET_NODE_LONG_NAME);
artnet_set_short_name(m_node, "libossia");
artnet_set_long_name(m_node, "libossia artnet protocol");
artnet_set_node_type(m_node, ARTNET_RAW);

artnet_dump_config(m_node);
std::fflush(stdout);
std::fflush(stderr);
if(artnet_start(m_node) != ARTNET_EOK)
throw std::runtime_error("Artnet Start failed");
}
Expand Down
3 changes: 2 additions & 1 deletion src/ossia/protocols/artnet/artnet_protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ struct dmx_config;
class OSSIA_EXPORT artnet_protocol final : public dmx_protocol_base
{
public:
artnet_protocol(ossia::net::network_context_ptr, const dmx_config& conf);
artnet_protocol(
ossia::net::network_context_ptr, const dmx_config& conf, std::string_view host);
~artnet_protocol();

void set_device(ossia::net::device_base& dev) override;
Expand Down
35 changes: 30 additions & 5 deletions src/ossia/protocols/artnet/dmxusbpro_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ namespace ossia::net
{
dmxusbpro_protocol::dmxusbpro_protocol(
ossia::net::network_context_ptr ctx, const dmx_config& conf,
const ossia::net::serial_configuration& socket)
const ossia::net::serial_configuration& socket, int version)
: dmx_protocol_base{ctx, conf}
, m_port{ctx->context}
, m_version{version}
{
if(conf.frequency < 1 || conf.frequency > 44)
throw std::runtime_error("DMX 512 update frequency must be in the range [1, 44] Hz");
Expand All @@ -28,6 +29,26 @@ dmxusbpro_protocol::dmxusbpro_protocol(

m_timer.set_delay(std::chrono::milliseconds{
static_cast<int>(1000.0f / static_cast<float>(conf.frequency))});

switch(m_version)
{
case 1:
break;
case 2: {

// SEND_DMX_PORT1: 0x06,
// SEND_DMX_PORT2: 0xCA,
// SET_API_KEY: 0x0D,
// SET_PORT_ASSIGNMENT: 0x93
static constexpr unsigned char set_api_key[]{0x7E, 0x0D, 0x00, 0x04, 0xC9,
0xA4, 0x03, 0xE4, 0xE7};
boost::asio::write(m_port, boost::asio::buffer(set_api_key));

static constexpr unsigned char set_port_assignment[]{0x7E, 0x93, 0x00, 0x02,
0x01, 0x01, 0xE7};
boost::asio::write(m_port, boost::asio::buffer(set_port_assignment));
}
}
}

dmxusbpro_protocol::~dmxusbpro_protocol()
Expand All @@ -38,16 +59,20 @@ dmxusbpro_protocol::~dmxusbpro_protocol()
void dmxusbpro_protocol::set_device(ossia::net::device_base& dev)
{
dmx_protocol_base::set_device(dev);
m_timer.start([this] { this->update_function(); });

int command = 0x06;
if(m_version == 2 && this->m_conf.universe >= 1)
command = 0xCA;
m_timer.start([this, command] { this->update_function(command); });
}

void dmxusbpro_protocol::update_function()
void dmxusbpro_protocol::update_function(uint8_t command)
{
try
{
// https://cdn.enttec.com/pdf/assets/70304/70304_DMX_USB_PRO_API.pdf
// 1: 0x7E
// 1: message code (0x6 for DMX send)
// 1: message code: 0x6 for DMX send / 0xCA for DMX send on port 2 (DMX USB PRO Mk2)
// 1: size LSB
// 1: size MSB
// N: message data: [
Expand All @@ -63,7 +88,7 @@ void dmxusbpro_protocol::update_function()
constexpr uint8_t data_size_lsb = data_size & 0x00FF;
constexpr uint8_t data_size_msb = (data_size & 0xFF00) >> 8;

unsigned char buf[buffer_size]{0x7E, 0x6, data_size_lsb, data_size_msb, 0};
unsigned char buf[buffer_size]{0x7E, command, data_size_lsb, data_size_msb, 0};

for(uint32_t i = 0; i < channels; i++)
buf[5 + i] = m_buffer.data[i];
Expand Down
5 changes: 3 additions & 2 deletions src/ossia/protocols/artnet/dmxusbpro_protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ class OSSIA_EXPORT dmxusbpro_protocol final : public dmx_protocol_base
public:
dmxusbpro_protocol(
ossia::net::network_context_ptr, const dmx_config& conf,
const ossia::net::serial_configuration& socket);
const ossia::net::serial_configuration& socket, int version);

~dmxusbpro_protocol();

void set_device(ossia::net::device_base& dev) override;

private:
void update_function();
void update_function(uint8_t command);

boost::asio::serial_port m_port;
int m_version = 1;
};
}
#endif
153 changes: 103 additions & 50 deletions src/ossia/protocols/artnet/e131_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
#include <artnet/artnet.h>

#include <chrono>
#include <iostream>

namespace
{
#pragma pack(push, 1)
struct e131_acn_root_layer
{ /* ACN Root Layer: 38 bytes */
Expand Down Expand Up @@ -62,56 +65,6 @@ enum class e131_option_t
E131_OPT_PREVIEW = 7,
};

#pragma pack(pop)
static_assert(sizeof(e131_packet) == 638);

namespace ossia::net
{
static boost::asio::ip::address_v4
e131_host(const dmx_config& conf, const ossia::net::socket_configuration& socket)
{
if(conf.multicast)
{
return boost::asio::ip::address_v4(0xefff0000 | conf.universe);
}
else
{
return boost::asio::ip::address_v4::from_string(socket.host);
}
}

e131_protocol::e131_protocol(
ossia::net::network_context_ptr ctx, const dmx_config& conf,
const ossia::net::socket_configuration& socket)
: dmx_protocol_base{ctx, conf}
, m_socket{e131_host(conf, socket), socket.port, ctx->context}
{
if(conf.frequency < 1 || conf.frequency > 44)
throw std::runtime_error("DMX 512 update frequency must be in the range [1, 44] Hz");

m_socket.connect();

if(conf.multicast && !socket.host.empty())
{
m_socket.m_socket.set_option(boost::asio::ip::multicast::outbound_interface(
boost::asio::ip::address_v4::from_string(socket.host)));
}

m_timer.set_delay(std::chrono::milliseconds{
static_cast<int>(1000.0f / static_cast<float>(conf.frequency))});
}

e131_protocol::~e131_protocol()
{
stop_processing();
}

void e131_protocol::set_device(ossia::net::device_base& dev)
{
dmx_protocol_base::set_device(dev);
m_timer.start([this] { this->update_function(); });
}

/* Initialize an E1.31 packet using a universe and a number of slots */
static int
e131_pkt_init(e131_packet* packet, const uint16_t universe, const uint16_t num_slots)
Expand Down Expand Up @@ -174,6 +127,57 @@ e131_pkt_init(e131_packet* packet, const uint16_t universe, const uint16_t num_s
return 0;
}

#pragma pack(pop)
static_assert(sizeof(e131_packet) == 638);
}

namespace ossia::net
{
static boost::asio::ip::address_v4
e131_host(const dmx_config& conf, const ossia::net::socket_configuration& socket)
{
if(conf.multicast)
{
return boost::asio::ip::address_v4(0xefff0000 | conf.universe);
}
else
{
return boost::asio::ip::address_v4::from_string(socket.host);
}
}

e131_protocol::e131_protocol(
ossia::net::network_context_ptr ctx, const dmx_config& conf,
const ossia::net::socket_configuration& socket)
: dmx_protocol_base{ctx, conf}
, m_socket{e131_host(conf, socket), socket.port, ctx->context}
{
if(conf.frequency < 1 || conf.frequency > 44)
throw std::runtime_error("DMX 512 update frequency must be in the range [1, 44] Hz");

m_socket.connect();

if(conf.multicast && !socket.host.empty())
{
m_socket.m_socket.set_option(boost::asio::ip::multicast::outbound_interface(
boost::asio::ip::address_v4::from_string(socket.host)));
}

m_timer.set_delay(std::chrono::milliseconds{
static_cast<int>(1000.0f / static_cast<float>(conf.frequency))});
}

e131_protocol::~e131_protocol()
{
stop_processing();
}

void e131_protocol::set_device(ossia::net::device_base& dev)
{
dmx_protocol_base::set_device(dev);
m_timer.start([this] { this->update_function(); });
}

void e131_protocol::update_function()
{
static std::atomic_int seq = 0;
Expand All @@ -200,6 +204,55 @@ void e131_protocol::update_function()
}
}

e131_input_protocol::e131_input_protocol(
ossia::net::network_context_ptr ctx, const dmx_config& conf,
const ossia::net::socket_configuration& socket)
: dmx_protocol_base{ctx, conf}
, m_socket{socket, ctx->context}
{
if(conf.frequency < 1 || conf.frequency > 44)
throw std::runtime_error("DMX 512 update frequency must be in the range [1, 44] Hz");

m_socket.open();
}

e131_input_protocol::~e131_input_protocol()
{
stop_processing();
}

void e131_input_protocol::set_device(ossia::net::device_base& dev)
{
dmx_protocol_base::set_device(dev);
m_socket.receive(
[](const char* bytes, int sz) { std::cerr << (int)bytes[0] << "\n"; });
}

void e131_input_protocol::update_function()
{
static std::atomic_int seq = 0;
try
{
e131_packet pkt;
e131_pkt_init(&pkt, this->m_conf.universe, 512);

for(size_t pos = 0; pos < 512; pos++)
pkt.dmp.prop_val[pos + 1] = m_buffer.data[pos];
pkt.frame.seq_number = seq.fetch_add(1, std::memory_order_relaxed);

// m_socket.write(reinterpret_cast<const char*>(&pkt), sizeof(pkt));

m_buffer.dirty = false;
}
catch(std::exception& e)
{
ossia::logger().error("write failure: {}", e.what());
}
catch(...)
{
ossia::logger().error("write failure");
}
}
}

#endif

0 comments on commit bd6b69f

Please sign in to comment.