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

Feature: send one art port reply per subscribed universe #89

Merged
Merged
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
19 changes: 8 additions & 11 deletions Artnet/ArtPollReply.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class ArtPollReply
String node_report {""};

public:
Packet generate_reply(const IPAddress &my_ip, const uint8_t my_mac[6], const CallbackMap &callbacks, uint8_t net_switch, uint8_t sub_switch)
Packet generate_reply(const IPAddress &my_ip, const uint8_t my_mac[6], uint32_t universe, uint8_t net_switch, uint8_t sub_switch)
{
Packet r;
for (size_t i = 0; i < ID_LENGTH; i++) r.id[i] = static_cast<uint8_t>(ARTNET_ID[i]);
Expand All @@ -84,23 +84,20 @@ class ArtPollReply
memcpy(r.long_name, long_name.c_str(), long_name.length());
memcpy(r.node_report, node_report.c_str(), node_report.length());
r.num_ports_h = 0; // Reserved
r.num_ports_l = (callbacks.size() > NUM_POLLREPLY_PUBLIC_PORT_LIMIT) ? NUM_POLLREPLY_PUBLIC_PORT_LIMIT : callbacks.size();
r.num_ports_l = 1;
memset(r.sw_in, 0, 4);
memset(r.sw_out, 0, 4);
memset(r.port_types, 0, 4);
memset(r.good_input, 0, 4);
memset(r.good_output, 0, 4);
r.net_sw = net_switch & 0x7F;
r.sub_sw = sub_switch & 0x0F;
size_t i = 0;
for (const auto& pair : callbacks) {
r.sw_in[i] = pair.first & 0x0F;
r.sw_out[i] = i; // dummy: output port is flexible
r.port_types[i] = 0xC0; // I/O available by DMX512
r.good_input[i] = 0x80; // Data received without error
r.good_output[i] = 0x80; // Data transmitted without error
if (++i >= NUM_POLLREPLY_PUBLIC_PORT_LIMIT) break;
}
// https://github.com/hideakitai/ArtNet/issues/81
r.sw_in[0] = universe & 0x0F;
r.sw_out[0] = 0; // dummy: output port is flexible
r.port_types[0] = 0xC0; // I/O available by DMX512
r.good_input[0] = 0x80; // Data received without error
r.good_output[0] = 0x80; // Data transmitted without error
r.sw_video = 0; // Video display shows local data
r.sw_macro = 0; // No support for macro key inputs
r.sw_remote = 0; // No support for remote trigger inputs
Expand Down
36 changes: 36 additions & 0 deletions Artnet/ArtSync.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once
#ifndef ARTNET_ART_SYNC_H
#define ARTNET_ART_SYNC_H

#include "Common.h"
#include <stdint.h>
#include <stddef.h>

namespace art_net {
namespace art_sync {

enum Index : uint16_t {
ID = 0,
OP_CODE_L = 8,
OP_CODE_H = 9,
PROTOCOL_VER_H = 10,
PROTOCOL_VER_L = 11,
AUX1 = 12,
AUX2 = 13,
};

inline void set_header(uint8_t *packet)
{
for (size_t i = 0; i < ID_LENGTH; i++) packet[i] = static_cast<uint8_t>(ARTNET_ID[i]);
packet[OP_CODE_L] = (static_cast<uint16_t>(OpCode::Sync) >> 0) & 0x00FF;
packet[OP_CODE_H] = (static_cast<uint16_t>(OpCode::Sync) >> 8) & 0x00FF;
packet[PROTOCOL_VER_H] = (PROTOCOL_VER >> 8) & 0x00FF;
packet[PROTOCOL_VER_L] = (PROTOCOL_VER >> 0) & 0x00FF;
packet[AUX1] = 0;
packet[AUX2] = 0;
}

} // namespace art_sync
} // namespace art_net

#endif // ARTNET_ART_SYNC_H
2 changes: 2 additions & 0 deletions Artnet/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ constexpr uint16_t PACKET_SIZE {530};

using CallbackAllType = std::function<void(const uint32_t universe, const uint8_t* data, const uint16_t size)>;
using CallbackType = std::function<void(const uint8_t* data, const uint16_t size)>;
using CallbackArtSync = std::function<void(void)>;
using CallbackArtTrigger = std::function<void(uint16_t oem, uint8_t key, uint8_t sub_key, const uint8_t *payload, uint16_t size)>;

#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11
template <uint16_t SIZE>
Expand Down
58 changes: 49 additions & 9 deletions Artnet/Receiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Common.h"
#include "ArtDmx.h"
#include "ArtPollReply.h"
#include "ArtTrigger.h"

namespace art_net {

Expand All @@ -17,6 +18,8 @@ class Receiver_ {
uint8_t sub_switch; // subnet of universe
CallbackMap callbacks;
CallbackAllType callback_all;
CallbackArtSync callback_artsync;
CallbackArtTrigger callback_arttrigger;
S* stream;
bool b_verbose {false};
artpollreply::ArtPollReply artpollreply_ctx;
Expand Down Expand Up @@ -52,25 +55,38 @@ class Receiver_ {

OpCode op_code = OpCode::NA;
if (checkID(d)) {
remote_ip = stream->S::remoteIP();
remote_port = (uint16_t)stream->S::remotePort();
OpCode received_op_code = static_cast<OpCode>(opcode(d));
switch (received_op_code) {
case OpCode::Dmx: {
memcpy(packet.data(), d, size);
remote_ip = stream->S::remoteIP();
remote_port = (uint16_t)stream->S::remotePort();
if (callback_all) callback_all(universe15bit(), data(), size - HEADER_SIZE);
for (auto& c : callbacks)
if (universe15bit() == c.first) c.second(data(), size - HEADER_SIZE);
op_code = OpCode::Dmx;
break;
}
case OpCode::Poll: {
remote_ip = stream->S::remoteIP();
remote_port = (uint16_t)stream->S::remotePort();
poll_reply();
op_code = OpCode::Poll;
break;
}
case OpCode::Trigger: {
if (callback_arttrigger) {
uint16_t oem = (d[art_trigger::OEM_H] << 8) | d[art_trigger::OEM_L];
uint8_t key = d[art_trigger::KEY];
uint8_t sub_key = d[art_trigger::SUB_KEY];
callback_arttrigger(oem, key, sub_key, d + art_trigger::PAYLOAD, size - art_trigger::PAYLOAD);
}
op_code = OpCode::Trigger;
break;
}
case OpCode::Sync: {
if (callback_artsync) callback_artsync();
op_code = OpCode::Sync;
break;
}
default: {
if (b_verbose) {
Serial.print(F("Unsupported OpCode: "));
Expand Down Expand Up @@ -178,6 +194,22 @@ class Receiver_ {
inline auto subscribe(F* func) -> std::enable_if_t<arx::is_callable<F>::value> {
callback_all = arx::function_traits<F>::cast(func);
}
template <typename F>
inline auto subscribeArtSync(F&& func) -> std::enable_if_t<arx::is_callable<F>::value> {
callback_artsync = arx::function_traits<F>::cast(func);
}
template <typename F>
inline auto subscribeArtSync(F* func) -> std::enable_if_t<arx::is_callable<F>::value> {
callback_artsync = arx::function_traits<F>::cast(func);
}
template <typename F>
inline auto subscribeArtTrigger(F&& func) -> std::enable_if_t<arx::is_callable<F>::value> {
callback_arttrigger = arx::function_traits<F>::cast(func);
}
template <typename F>
inline auto subscribeArtTrigger(F* func) -> std::enable_if_t<arx::is_callable<F>::value> {
callback_arttrigger = arx::function_traits<F>::cast(func);
}

inline void unsubscribe(const uint8_t universe) {
auto it = callbacks.find(universe);
Expand All @@ -186,6 +218,12 @@ class Receiver_ {
inline void unsubscribe() {
callback_all = nullptr;
}
inline void unsubscribeArtSync() {
callback_artsync = nullptr;
}
inline void unsubscribeArtTrigger() {
callback_arttrigger = nullptr;
}

inline void clear_subscribers() {
unsubscribe();
Expand Down Expand Up @@ -262,11 +300,13 @@ class Receiver_ {
const IPAddress my_subnet = subnetMask();
uint8_t my_mac[6];
macAddress(my_mac);
artpollreply::Packet r = artpollreply_ctx.generate_reply(my_ip, my_mac, callbacks, net_switch, sub_switch);
static const IPAddress local_broadcast_addr = IPAddress((uint32_t)my_ip | ~(uint32_t)my_subnet);
stream->beginPacket(local_broadcast_addr, DEFAULT_PORT);
stream->write(r.b, sizeof(artpollreply::Packet));
stream->endPacket();
for (const auto &cb_pair : callbacks) {
artpollreply::Packet r = artpollreply_ctx.generate_reply(my_ip, my_mac, cb_pair.first, net_switch, sub_switch);
static const IPAddress local_broadcast_addr = IPAddress((uint32_t)my_ip | ~(uint32_t)my_subnet);
stream->beginPacket(local_broadcast_addr, DEFAULT_PORT);
stream->write(r.b, sizeof(artpollreply::Packet));
stream->endPacket();
}
}

#ifdef ARTNET_ENABLE_WIFI
Expand Down
7 changes: 7 additions & 0 deletions Artnet/Sender.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Common.h"
#include "ArtDmx.h"
#include "ArtTrigger.h"
#include "ArtSync.h"

namespace art_net {

Expand Down Expand Up @@ -100,6 +101,12 @@ class Sender_ {
send_raw(ip, DEFAULT_PORT, packet.data(), packet.size());
}

// ArtSync
void sync(const String& ip) {
art_sync::set_header(packet.data());
send_raw(ip, DEFAULT_PORT, packet.data(), packet.size());
}

protected:
void attach(S& s) {
stream = &s;
Expand Down
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ If you have already installed this library, please follow:
- One-line send to desired destination
- Flexible net/subnet/universe setting
- Easy data forwarding to [FastLED](https://github.com/FastLED/FastLED)
- Supports following protocols:
- ArtDmx
- ArtPoll/ArtPollReply
- ArtTrigger
- ArtSync

## Supported Platforms

Expand Down Expand Up @@ -284,23 +289,32 @@ void set_subkey(uint8_t subkey);
void set_payload(const uint8_t* const payload, uint16_t size);
// send ArtTrigger based on the config above
void trigger(const String& ip);
// send ArtSync
void sync(const String& ip);
```

### ArtnetReceiver

```C++
OpCode parse();
// subscribers
template <typename F> inline auto subscribe(const uint8_t universe, F&& func);
template <typename F> inline auto subscribe(const uint8_t universe, F* func);
template <typename F> inline auto subscribe(F&& func);
template <typename F> inline auto subscribe(F* func);
template <typename F> inline auto subscribe(const uint8_t universe, CallbackType);
template <typename F> inline auto subscribe(CallbackAllTyp);
template <typename F> inline auto subscribeArtSync(CallbackArtSync);
template <typename F> inline auto subscribeArtTrigger(CallbackArtTrigger);
// callback definitions for subscribers
using CallbackAllType = std::function<void(const uint32_t universe, const uint8_t* data, const uint16_t size)>;
using CallbackType = std::function<void(const uint8_t* data, const uint16_t size)>;
using CallbackArtSync = std::function<void(void)>;
using CallbackArTrigger = std::function<void(uint16_t oem, uint8_t key, uint8_t sub_key, const uint8_t *payload, uint16_t size)>;
// for FastLED
inline void forward(const uint8_t universe, CRGB* leds, const uint16_t num);
// unsubscribe
inline void unsubscribe(const uint8_t universe);
inline void unsubscribe();
inline void clear_subscribers();
inline void unsubscribeArtSync();
inline void unsubscribeArtTrigger();
// ArtPollReply information
void shortname(const String& sn);
void longname(const String& ln);
Expand Down
Loading