diff --git a/Artnet.h b/Artnet.h index 5b2872a..675b1d9 100644 --- a/Artnet.h +++ b/Artnet.h @@ -131,7 +131,7 @@ namespace arx { #else template using Array = arx::vector; - using CallbackMap = arx::map; + using CallbackMap = arx::map; using namespace arx; #endif @@ -197,15 +197,6 @@ namespace arx { #endif virtual ~Sender_() {} - void net(const uint8_t n) { target_net = n & 0x7F; } - void subnet(const uint8_t s) { target_subnet = s & 0x0F; } - void universe(const uint8_t u) { target_universe = u & 0x0F; } - void universe15bit(const uint8_t u) { - net((u >> 8) & 0xFF); - subnet((u >> 4) & 0x0F); - universe((u >> 0) & 0x0F); - } - void set(const uint8_t* const data, const uint16_t size = 512) { packet[IDX(Index::PHYSICAL)] = phy; packet[IDX(Index::NET)] = target_net; @@ -216,14 +207,16 @@ namespace arx { } void set(const uint32_t universe_, const uint8_t* const data, const uint16_t size = 512) { - universe15bit(universe_); + target_net = (universe_ >> 8) & 0x7F; + target_subnet = (universe_ >> 4) & 0x0F; + target_universe = universe_ & 0x0F; set(data, size); } void set(const uint8_t net_, const uint8_t subnet_, const uint8_t universe_, const uint8_t* const data, const uint16_t size = 512) { - net(net_); - subnet(subnet_); - universe(universe_); + target_net = net_ & 0x7F; + target_subnet = subnet_ & 0x0F; + target_universe = universe_ & 0x0F; set(data, size); } @@ -259,67 +252,6 @@ namespace arx { void physical(const uint8_t i) const { phy = constrain(i, 0, 3); } - // void poll_reply(const IPAddress& remote_ip, const uint16_t remote_port) { - void poll_reply() { - ArtPollReply r; - for (size_t i = 0; i < ID_LENGTH; i++) r.id[i] = static_cast(ID[i]); - r.op_code_l = ((uint16_t)OpCode::PollReply >> 0) & 0x00FF; - r.op_code_h = ((uint16_t)OpCode::PollReply >> 8) & 0x00FF; -#ifdef ARTNET_ENABLE_WIFI - IPAddress my_ip = WiFi.localIP(); - IPAddress my_subnet = WiFi.subnetMask(); -#endif -#ifdef ARTNET_ENABLE_ETHER - IPAddress my_ip = Ethernet.localIP(); - IPAddress my_subnet = Ethernet.subnetMask(); -#endif - for (size_t i = 0; i < 4; ++i) r.ip[i] = my_ip[i]; - r.port_l = (DEFAULT_PORT >> 0) & 0xFF; - r.port_h = (DEFAULT_PORT >> 8) & 0xFF; - r.ver_h = (PROTOCOL_VER >> 8) & 0x00FF; - r.ver_l = (PROTOCOL_VER >> 0) & 0x00FF; - r.net_sw = 0; // TODO: - r.sub_sw = 0; // TODO: - r.oem_h = 0; // https://github.com/tobiasebsen/ArtNode/blob/master/src/Art-NetOemCodes.h - r.oem_l = 0xFF; // OemUnknown - r.ubea_ver = 0; // UBEA not programmed - r.status_1 = 0x00; // Unknown / Normal - r.esta_man_l = 0; // No EATA manufacture code - r.esta_man_h = 0; // No ESTA manufacture code - char short_name[18] = "Arduino ArtNet"; - memcpy(r.short_name, short_name, sizeof(short_name)); - char long_name[64] = "Arduino ArtNet Protocol by hideakitai/ArtNet"; - memcpy(r.long_name, long_name, sizeof(long_name)); - static size_t counts = 0; - char node_report[64]; - sprintf(node_report, "#0001 [%d] Arduino ArtNet enabled", counts++); - memcpy(r.node_report, node_report, sizeof(node_report)); - r.num_ports_h = 0; // Reserved - r.num_ports_l = 1; // This library implements only 1 port - for (size_t i = 0; i < 4; ++i) { - 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 transmittedj without error - r.sw_in[i] = 0; // TODO: - r.sw_out[i] = 0; // TODO: - } - 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 - memset(r.spare, 0x00, 3); - r.style = 0x00; // A DMX to / from Art-Net device - memset(r.mac, 0x00, 6); // Do not supply mac address - for (size_t i = 0; i < 4; ++i) r.bind_ip[i] = my_ip[i]; - r.bind_index = 0; - r.status_2 = 0x08; // sACN capable (maybe) - memset(r.filler, 0x00, 26); - - 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)); - stream->endPacket(); - } - uint8_t sequence() const { return seq; } protected: @@ -340,6 +272,11 @@ namespace arx { Array packet; IPAddress remote_ip; uint16_t remote_port; + uint8_t univ_net; + uint8_t univ_subnet; + String short_name{"Arduino ArtNet"}; + String long_name{"Ardino ArtNet Protocol by hideakitai/ArtNet"}; + String node_report{""}; CallbackMap callbacks; CallbackAllType callback_all; S* stream; @@ -373,6 +310,7 @@ namespace arx { case OPC(OpCode::Poll): { remote_ip = stream->S::remoteIP(); remote_port = (uint16_t)stream->S::remotePort(); + poll_reply(); return OpCode::Poll; } default: { @@ -411,10 +349,10 @@ namespace arx { inline uint8_t physical() const { return packet[IDX(Index::PHYSICAL)]; } - inline uint8_t net() const { + uint8_t net() const { return packet[IDX(Index::NET)] & 0x7F; } - inline uint8_t subnet() const { + uint8_t subnet() const { return (packet[IDX(Index::SUBUNI)] >> 4) & 0x0F; } inline uint8_t universe() const { @@ -436,15 +374,42 @@ namespace arx { return packet[HEADER_SIZE + i]; } + void subscribe_net(const uint8_t n) { + univ_net = n; + } + + void subscribe_subnet(const uint8_t sn) { + univ_subnet = sn; + } + template - inline auto subscribe(const uint32_t universe, F&& func) + inline auto subscribe(const uint8_t universe, F&& func) -> std::enable_if_t::value> { - callbacks.insert(make_pair(universe, arx::function_traits::cast(func))); + if (callbacks.size() >= 4) { + Serial.println(F("too many callbacks")); + } else { + if (universe > 0xF) { + Serial.println(F("universe out of bounds")); + return; + } else { + uint32_t u = ((uint32_t)univ_net << 8) | ((uint32_t)univ_subnet << 4) | (uint32_t)universe; + callbacks.insert(make_pair(u, arx::function_traits::cast(func))); + } + } } template - inline auto subscribe(const uint32_t universe, F* func) + inline auto subscribe(const uint8_t universe, F* func) -> std::enable_if_t::value> { - callbacks.insert(make_pair(universe, arx::function_traits::cast(func))); + if (callbacks.size() >= 4) { + Serial.println(F("too many callbacks")); + } else { + if (universe > 0xF) { + Serial.println(F("universe out of bounds")); + } else { + uint32_t u = ((uint32_t)univ_net << 8) | ((uint32_t)univ_subnet << 4) | (uint32_t)universe; + callbacks.insert(make_pair(u, arx::function_traits::cast(func))); + } + } } template inline auto subscribe(F&& func) @@ -456,20 +421,8 @@ namespace arx { -> std::enable_if_t::value> { callback_all = arx::function_traits::cast(func); } - template - inline auto subscribe(const uint8_t net, const uint8_t subnet, const uint8_t universe, F&& func) - -> std::enable_if_t::value> { - uint32_t u = ((uint32_t)net << 8) | ((uint32_t)subnet << 4) | (uint32_t)universe; - subscribe(u, func); - } - template - inline auto subscribe(const uint8_t net, const uint8_t subnet, const uint8_t universe, F* func) - -> std::enable_if_t::value> { - uint32_t u = ((uint32_t)net << 8) | ((uint32_t)subnet << 4) | (uint32_t)universe; - subscribe(u, func); - } - inline void unsubscribe(const uint32_t universe) { + inline void unsubscribe(const uint8_t universe) { auto it = callbacks.find(universe); if (it != callbacks.end()) callbacks.erase(it); @@ -477,10 +430,6 @@ namespace arx { inline void unsubscribe() { callback_all = nullptr; } - inline void unsubscribe(const uint8_t net, const uint8_t subnet, const uint8_t universe) { - uint32_t u = ((uint32_t)net << 8) | ((uint32_t)subnet << 4) | (uint32_t)universe; - unsubscribe(u); - } inline void clear_subscribers() { unsubscribe(); @@ -504,6 +453,17 @@ namespace arx { forward(u, leds, num); } #endif + void shortname(const String& sn) { + short_name = sn; + } + + void longname(const String& ln) { + long_name = ln; + } + + void nodereport(const String& nr) { + node_report = nr; + } protected: void attach(S& s) { @@ -519,6 +479,73 @@ namespace arx { const char* idptr = reinterpret_cast(p); return !strcmp(ID, idptr); } + + void poll_reply() { + ArtPollReply r; + for (size_t i = 0; i < ID_LENGTH; i++) r.id[i] = static_cast(ID[i]); + r.op_code_l = ((uint16_t)OpCode::PollReply >> 0) & 0x00FF; + r.op_code_h = ((uint16_t)OpCode::PollReply >> 8) & 0x00FF; +#ifdef ARTNET_ENABLE_WIFI + IPAddress my_ip = WiFi.localIP(); + IPAddress my_subnet = WiFi.subnetMask(); + WiFi.macAddress(r.mac); +#endif +#ifdef ARTNET_ENABLE_ETHER + IPAddress my_ip = Ethernet.localIP(); + IPAddress my_subnet = Ethernet.subnetMask(); + Ethernet.MACAddress(r.mac); +#endif + for (size_t i = 0; i < 4; ++i) r.ip[i] = my_ip[i]; + r.port_l = (DEFAULT_PORT >> 0) & 0xFF; + r.port_h = (DEFAULT_PORT >> 8) & 0xFF; + r.ver_h = (PROTOCOL_VER >> 8) & 0x00FF; + r.ver_l = (PROTOCOL_VER >> 0) & 0x00FF; + r.oem_h = 0; // https://github.com/tobiasebsen/ArtNode/blob/master/src/Art-NetOemCodes.h + r.oem_l = 0xFF; // OemUnknown + r.ubea_ver = 0; // UBEA not programmed + r.status_1 = 0x00; // Unknown / Normal + r.esta_man_l = 0; // No EATA manufacture code + r.esta_man_h = 0; // No ESTA manufacture code + memset(r.short_name, 0, 18); + memset(r.long_name, 0, 64); + memset(r.node_report, 0, 64); + memcpy(r.short_name, short_name.c_str(), short_name.length()); + 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(); // This library implements only 4 port + 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); + size_t i = 0; + for (const auto& pair : callbacks) { + r.net_sw = (pair.first >> 8) & 0x7F; // all callbacks have same value + r.sub_sw = (pair.first >> 4) & 0x0F; + 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 >= 4) break; + } + 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 + memset(r.spare, 0x00, 3); + r.style = 0x00; // A DMX to / from Art-Net device + for (size_t i = 0; i < 4; ++i) r.bind_ip[i] = my_ip[i]; + r.bind_index = 0; + r.status_2 = 0x08; // sACN capable + memset(r.filler, 0x00, 26); + + 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)); + stream->endPacket(); + } + }; // namespace artnet template @@ -533,16 +560,7 @@ namespace arx { } void parse() { - OpCode op_code = this->Receiver_::parse(); - switch (op_code) { - case OpCode::Poll: { - this->Sender_::poll_reply(); - break; - } - default: { - break; - } - } + this->Receiver_::parse(); } };