diff --git a/Artnet.h b/Artnet.h new file mode 100644 index 0000000..a93e0e3 --- /dev/null +++ b/Artnet.h @@ -0,0 +1,417 @@ +#pragma once +#ifndef ARDUINO_ARTNET_H +#define ARDUINO_ARTNET_H + +// Spec (Art-Net 4) : http://artisticlicence.com/WebSiteMaster/User%20Guides/art-net.pdf +// Packet Summary : https://art-net.org.uk/structure/packet-summary-2/ +// Packet Definition : https://art-net.org.uk/structure/streaming-packets/artdmx-packet-definition/ + +#include +#ifndef __AVR__ + #include + #include + #include +#else + #include "RingBuffer.h" +#endif +#ifdef ESP_PLATFORM + #include + #include +#elif defined (ESP8266) + #include + #include +#elif defined (TEENSYDUINO) || defined (__AVR__) + #include + #include +#endif + + +namespace arduino +{ + namespace artnet + { + enum class OpCode : uint16_t + { + // Device Discovery + Poll = 0x2000, + PollReply = 0x2100, + // Device Configuration + Address = 0x6000, + Input = 0x7000, + IpProg = 0xF800, + IpProgReply = 0xF900, + Command = 0x2400, + // Streaming Control + Dmx = 0x5000, + Nzs = 0x5100, + Sync = 0x5200, + // RDM + TodRequest = 0x8000, + TodData = 0x8100, + TodControl = 0x8200, + Rdm = 0x8300, + RdmSub = 0x8400, + // Time-Keeping + TimeCode = 0x9700, + TimeSync = 0x9800, + // Triggering + Trigger = 0x9900, + // Diagnostics + DiagData = 0x2300, + // File Transfer + FirmwareMaster = 0xF200, + FirmwareReply = 0xF300, + Directory = 0x9A00, + DirectoryReply = 0x9B00, + FileTnMaster = 0xF400, + FileFnMaster = 0xF500, + FileFnReply = 0xF600 + }; + + constexpr uint16_t OPC(OpCode op) { return static_cast(op); } + + enum class Index : uint16_t + { + ID = 0, + OP_CODE_L = 8, + OP_CODE_H = 9, + PROTOCOL_VER_H = 10, + PROTOCOL_VER_L = 11, + SEQUENCE = 12, + PHYSICAL = 13, + SUBUNI = 14, + NET = 15, + LENGTH_H = 16, + LENGTH_L = 17, + DATA = 18 + }; + + constexpr uint16_t IDX(Index i) { return static_cast(i); } + + constexpr uint16_t DEFAULT_PORT { 6454 }; + constexpr uint16_t HEADER_SIZE { 18 }; + constexpr uint16_t PACKET_SIZE { 530 }; + constexpr uint16_t PROTOCOL_VER { 0x0014 }; + constexpr uint8_t ID_LENGTH { 8 }; + constexpr char ID[ID_LENGTH] { "Art-Net" }; + constexpr float DEFAULT_FPS { 40. }; + constexpr uint32_t DEFAULT_INTERVAL_MS { (uint32_t)(1000. / DEFAULT_FPS) }; + + static const uint8_t NUM_PIXELS_PER_UNIV { 170 }; + + #ifdef __AVR__ + template + class Array + { + uint8_t buffer[SIZE]; + public: + const uint8_t* data() const { return buffer; } + uint8_t* data() { return buffer; } + uint16_t size() const { return SIZE; } + uint8_t operator[] (const size_t i) const { return buffer[i]; } + uint8_t& operator[] (const size_t i) { return buffer[i]; } + }; + #endif + + template + class Sender_ + { + #ifdef __AVR__ + Array packet; + #else + std::array packet; + #endif + + const char* ip; + uint16_t port {DEFAULT_PORT}; + uint8_t target_net {0}; + uint8_t target_subnet {0}; + uint8_t target_universe {0}; + uint8_t seq {0}; + uint8_t phy {0}; + + uint32_t prev_send_ms {0}; + S* stream; + + public: + + virtual ~Sender_() {} + + void net(uint8_t n) { target_net = n & 0x7F; } + void subnet(uint8_t s) { target_subnet = s & 0x0F; } + void universe(uint8_t u) { target_universe = u & 0x0F; } + void universe15bit(uint8_t u) + { + net((u >> 8) & 0xFF); + subnet((u >> 4) & 0x0F); + universe((u >> 0) & 0x0F); + } + + void set(const uint8_t* const data, uint16_t size = 512) + { + packet[IDX(Index::PHYSICAL)] = phy; + packet[IDX(Index::NET)] = target_net; + packet[IDX(Index::SUBUNI)] = (target_subnet << 4) | target_universe; + packet[IDX(Index::LENGTH_H)] = (size >> 8) & 0xFF; + packet[IDX(Index::LENGTH_L)] = (size >> 0) & 0xFF; + memcpy((&packet[IDX(Index::DATA)]), data, size); + } + + void set(const uint32_t universe_, const uint8_t* const data, uint16_t size = 512) + { + universe15bit(universe_); + set(data, size); + } + + void set(const uint8_t net_, const uint8_t subnet_, const uint8_t universe_, const uint8_t* const data, uint16_t size = 512) + { + net(net_); + subnet(subnet_); + universe(universe_); + set(data, size); + } + + void send() + { + packet[IDX(Index::SEQUENCE)] = seq++; + stream->beginPacket(ip, port); + stream->write(packet.data(), packet.size()); + stream->endPacket(); + } + void send(const uint8_t* const data, uint16_t size = 512) + { + set(data, size); + send(); + } + void send(const uint32_t universe_, const uint8_t* const data, uint16_t size = 512) + { + set(universe_, data, size); + send(); + } + void send(const uint8_t net_, const uint8_t subnet_, const uint8_t universe_, const uint8_t* const data, uint16_t size = 512) + { + set(net_, subnet_, universe_, data, size); + send(); + } + + void streaming() + { + if ((millis() - prev_send_ms) > DEFAULT_INTERVAL_MS) + { + send(); + prev_send_ms = millis(); + } + } + + void physical(uint8_t i) { phy = constrain(i, 0, 3); } + uint8_t sequence() const { return seq; } + + protected: + + void attach(S& s, const char* user_ip, uint16_t user_port = DEFAULT_PORT) + { + stream = &s; + ip = user_ip; + port = user_port; + for (size_t i = 0; i < ID_LENGTH; i++) packet[IDX(Index::ID) + i] = static_cast(ID[i]); + packet[IDX(Index::OP_CODE_H)] = (OPC(OpCode::Dmx) >> 8) & 0x00FF; + packet[IDX(Index::OP_CODE_L)] = (OPC(OpCode::Dmx) >> 0) & 0x00FF; + packet[IDX(Index::PROTOCOL_VER_H)] = (PROTOCOL_VER >> 8) & 0x00FF; + packet[IDX(Index::PROTOCOL_VER_L)] = (PROTOCOL_VER >> 0) & 0x00FF; + } + }; + + + template + class Receiver_ + { + #ifdef __AVR__ + typedef void (*CallbackType)(uint8_t* data, uint16_t size); + struct Map { uint32_t universe; CallbackType func; }; + using CallbackMap = RingBuffer; + Array packet; + #else + using CallbackType = std::function; + struct Map { uint32_t universe; CallbackType func; }; + using CallbackMap = std::vector; + std::array packet; + #endif + + IPAddress remote_ip; + uint16_t remote_port; + CallbackMap callbacks; + S* stream; + + public: + + virtual ~Receiver_() {} + + bool parse() + { + const size_t size = stream->parsePacket(); + if (size == 0) return false; + + uint8_t d[size]; + stream->read(d, size); + + if (checkID(d)) + { + if (opcode(d) == OPC(OpCode::Dmx)) + { + // TODO: std::move... + memcpy(packet.data(), d, size); + remote_ip = stream->S::remoteIP(); + remote_port = (uint16_t)stream->S::remotePort(); + for (auto& c : callbacks) + { + if (universe15bit() == c.universe) c.func(data(), size); + } + return true; + } + } + return false; + } + + inline const IPAddress& ip() const { return remote_ip; } + inline uint16_t port() const { return remote_port; } + + inline String id() const + { + String str; + for (uint8_t i = 0; i < ID_LENGTH; ++i) str += packet[IDX(Index::ID) + i]; + return str; + } + inline uint16_t opcode() const + { + return (packet[IDX(Index::OP_CODE_H)] << 8) | packet[IDX(Index::OP_CODE_L)]; + } + inline uint16_t opcode(const uint8_t* p) const + { + return (p[IDX(Index::OP_CODE_H)] << 8) | p[IDX(Index::OP_CODE_L)]; + } + inline uint16_t version() const + { + return (packet[IDX(Index::PROTOCOL_VER_H)] << 8) | packet[IDX(Index::PROTOCOL_VER_L)]; + } + inline uint8_t sequence() const + { + return packet[IDX(Index::SEQUENCE)]; + } + inline uint8_t physical() const + { + return packet[IDX(Index::PHYSICAL)]; + } + inline uint8_t net() const + { + return packet[IDX(Index::NET)] & 0x7F; + } + inline uint8_t subnet() const + { + return (packet[IDX(Index::SUBUNI)] >> 4) & 0x0F; + } + inline uint8_t universe() const + { + return packet[IDX(Index::SUBUNI)] & 0x0F; + } + inline uint16_t universe15bit() const + { + return (packet[IDX(Index::NET)] << 8) | packet[IDX(Index::SUBUNI)]; + } + inline uint16_t length() const + { + return (packet[IDX(Index::LENGTH_H)] << 8) | packet[IDX(Index::LENGTH_L)]; + } + inline uint16_t size() const + { + return length(); + } + inline uint8_t* data() + { + return &(packet[HEADER_SIZE]); + } + inline uint8_t data(const uint16_t i) const + { + return packet[HEADER_SIZE + i]; + } + + inline void subscribe(const uint32_t universe, const CallbackType& func) + { + callbacks.push_back(Map{universe, func}); + } + inline void subscribe(const CallbackType& func) + { + subscribe(0, func); + } + inline void subscribe(const uint8_t net, const uint8_t subnet, const uint8_t universe, const CallbackType& func) + { + uint32_t u = ((uint32_t)net << 8) | ((uint32_t)subnet << 4) | (uint32_t)universe; + subscribe(u, func); + } + + protected: + + void attach(S& s) { stream = &s; } + + private: + + inline bool checkID() const + { + const char* idptr = reinterpret_cast(packet.data()); + return !strcmp(ID, idptr); + } + inline bool checkID(const uint8_t* p) const + { + const char* idptr = reinterpret_cast(p); + return !strcmp(ID, idptr); + } + }; + + template + class Manager : public Sender_, public Receiver_ + { + S stream; + public: + void begin(const char* send_ip, uint32_t send_port = DEFAULT_PORT, uint32_t recv_port = DEFAULT_PORT) + { + stream.begin(recv_port); + this->Sender_::attach(stream, send_ip, send_port); + this->Receiver_::attach(stream); + } + }; + + template + class Sender : public Sender_ + { + S stream; + public: + void begin(const char* ip, uint32_t port = DEFAULT_PORT) + { + this->Sender_::attach(stream, ip, port); + } + }; + + template + class Receiver : public Receiver_ + { + S stream; + public: + void begin(uint32_t port = DEFAULT_PORT) + { + stream.begin(port); + this->Receiver_::attach(stream); + } + }; + } +} + +#if defined (ESP_PLATFORM) || defined (ESP8266) +using Artnet = arduino::artnet::Manager; +using ArtnetSender = arduino::artnet::Sender; +using ArtnetReceiver = arduino::artnet::Receiver; +#elif defined (TEENSYDUINO) || defined (__AVR__) +using Artnet = arduino::artnet::Manager; +using ArtnetSender = arduino::artnet::Sender; +using ArtnetReceiver = arduino::artnet::Receiver; +#endif + +#endif // ARDUINO_ARTNET_H diff --git a/README.md b/README.md index 7bc9341..b68a9fa 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,231 @@ -# Artnet -Artnet library for Arduino (Ethernet, WiFi) +# ArtNet + +Art-Net Sender/Receiver for Arduino (Ethernet, WiFi) + +## Feature + +- support Art-Net with both Ethernet and WiFi +- register multiple callbacks depending on universe +- flexible net/subnet/universe setting +- etc. + +### Supported Platforms + +- ESP32, ESP8266 (WiFi) +- Teensy 3.x (Ethernet) +- Arduino Uno/Mega etc. (Ethernet) + +#### Note + +Some boards without enough memory may not be able to use integrated sender/receiver. +Please use only sender OR receiver. + +## Usage + +This library has following Art-Net controller options. +Please use them depending on the situation. + +- ArtnetSender +- ArtnetReveiver +- Artnet (Integrated Sender/Receiver) + +### Art-Net Sender + +```C++ +#include + +// declarations for Ethernet/WiFi + +ArtnetSender artnet; + +void setup() +{ + // setup Ethernet/WiFi... + + artnet.begin("127.0.0.1"); // set destination ip +} + +void loop() +{ + // change send data as you want + + artnet.set(universe, data_ptr, size); + artnet.streaming(); // automatically send set data in 40fps +} +``` + +### Art-Net Receiver + +```C++ +#include + +// declarations for Ethernet/WiFi + +ArtnetSender artnet; + +void callback(uint8_t* data, uint16_t size) +{ + // you can also use pre-defined callbacks +} + +void setup() +{ + // setup Ethernet/WiFi... + + artnet.begin(); // waiting for Art-Net in default port + + // if Artnet packet comes to this universe, this function is called + artnet.subscribe(universe1, [](uint8_t* data, uint16_t size) + { + // use received data[], and size + }); + + // you can also use pre-defined callbacks + artnet.subscribe(universe2, callback); +} + +void loop() +{ + artnet.parse(); // check if artnet packet has come and execute callback +} + +``` + +### Art-Net Sender + +```C++ +#include + +// declarations for Ethernet/WiFi + +Artnet artnet; + +void setup() +{ + // setup Ethernet/WiFi... + + artnet.begin("127.0.0.1"); // send to localhost and listen to default port + + // if Artnet packet comes to this universe, this function is called + artnet.subscribe(universe, [&](uint8_t* data, uint16_t size) + { + // do something with data coming to universe + }); +} + +void loop() +{ + artnet.parse(); // check if artnet packet has come and execute callback + + // change send data as you want + + artnet.set(universe, data, size); // set send data + artnet.streaming(); // automatically send set data in 40fps +} +``` + +## Other Settings + +### Net, Sub-Net, Universe, and Universe(15bit) + +You can set Net, Sub-Net, Universe and Universe(15bit) flexibly. + +#### Sender + +```C++ +// set separately +artent.net(n); +artent.subnet(s); +artent.universe(u); +artnet.set(data, size); + +// set as 15bit universe +artent.universe15bit(u); +artnet.set(data, size); + +// set with data and size +artnet.set(universe15bit, data, size); +artnet.set(net, subnet, universe, data, size); +``` + +#### Receiver + +```C++ +artnet.subscribe(universe15bit, function); +artnet.subscribe(net, subnet, universe, function); +``` + +### One Time Sending (Not Streaming) + +In Sender class, you can also send Art-Net packet once. +This sends only 1 packet (NOT streaming). + +```C++ +artnet.send(data, size); +artnet.send(universe15bit, data, size); +artnet.send(net, subnet, universe, data, size); +``` + +### Set Non-Default Port + +```C++ +// ArtnetSender +artnet.begin(ip); // default +artnet.begin(ip, port); // set your own +// ArtnetReceiver +artnet.begin(); // default +artnet.begin(port); // set your own +// Artnet (integrated) +artnet.begin(ip); // default send/receiver +artnet.begin(ip, send_port, recv_port); // set your own +``` + +## APIs + +### ArtnetSender + +```C++ +void net(uint8_t n) +void subnet(uint8_t s) +void universe(uint8_t u) +void universe15bit(uint8_t u) +void set(const uint8_t* const data, uint16_t size = 512) +void set(const uint32_t universe_, const uint8_t* const data, uint16_t size = 512) +void set(const uint8_t net_, const uint8_t subnet_, const uint8_t universe_, const uint8_t* const data, uint16_t size = 512) +void send() +void send(const uint8_t* const data, uint16_t size = 512) +void send(const uint32_t universe_, const uint8_t* const data, uint16_t size = 512) +void send(const uint8_t net_, const uint8_t subnet_, const uint8_t universe_, const uint8_t* const data, uint16_t size = 512) +void streaming() +void physical(uint8_t i) +uint8_t sequence() const +``` + +### ArtnetReceiver + +```C++ +bool parse() +inline const IPAddress& ip() const { return remote_ip; } +uint16_t port() const { return remote_port; } +String id() const +uint16_t opcode() const +uint16_t opcode(const uint8_t* p) const +uint16_t version() const +uint8_t sequence() const +uint8_t physical() const +uint8_t net() const +uint8_t subnet() const +uint8_t universe() const +uint16_t universe15bit() const +uint16_t length() const +uint16_t size() const +uint8_t* data() +uint8_t data(const uint16_t i) const +void subscribe(const uint32_t universe, const CallbackType& func) +void subscribe(const CallbackType& func) +void subscribe(const uint8_t net, const uint8_t subnet, const uint8_t universe, const CallbackType& func) +``` + +## License + +MIT diff --git a/RingBuffer.h b/RingBuffer.h new file mode 100644 index 0000000..ca64206 --- /dev/null +++ b/RingBuffer.h @@ -0,0 +1,122 @@ +#pragma once + +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +template +class RingBuffer +{ +public: + + inline size_t capacity() const { return SIZE; }; + inline size_t size() const { return (tail_ - head_); }; + inline const T* data() const { return &(queue_[head_]); } + inline T* data() { return &(queue_[head_]); } + inline bool empty() const { return tail_ == head_; }; + inline void clear() { head_ = 0; tail_ = 0; }; + inline void pop() + { + if (size() == 0) return; + if (size() == 1) clear(); + else head_++; + }; + inline void pop_back() + { + if (size() == 0) return; + if (size() == 1) clear(); + else tail_--; + }; + inline void push(const T& data) + { + queue_[(tail_++) % SIZE] = data; + if (size() > SIZE) head_++; + }; + inline void push(T&& data) + { + queue_[(tail_++) % SIZE] = data; + if (size() > SIZE) head_++; + }; + inline void push_back(const T& data) + { + queue_[(tail_++) % SIZE] = data; + if (size() > SIZE) head_++; + }; + inline void push_back(T&& data) + { + queue_[(tail_++) % SIZE] = data; + if (size() > SIZE) head_++; + }; + + inline const T& front() const // throw(Exception) + { + // if(empty()) throw Exception(); + return *(queue_ + head_ % SIZE); + }; + inline T& front() // throw(Exception) + { + // if(empty()) throw Exception(); + return *(queue_ + head_ % SIZE); + }; + + inline const T& back() const // throw(Exception) + { + // if(empty()) throw Exception(); + return *(queue_ + (SIZE + tail_ - 1) % SIZE); + } + inline T& back() // throw(Exception) + { + // if(empty()) throw Exception(); + return *(queue_ + (SIZE + tail_ - 1) % SIZE); + } + + inline const T& operator[] (uint8_t index) const + { + return *(queue_ + (head_ + index) % SIZE); + } + inline T& operator[] (uint8_t index) + { + return *(queue_ + (head_ + index) % SIZE); + } + + inline const T* begin() const { return &(queue_[head_]); } + inline T* begin() { return &(queue_[head_]); } + inline const T* end() const { return &(queue_[tail_]); } + inline T* end() { return &(queue_[tail_]); } + + inline T* erase(T* p) + { + if (p == end()) return p; + for (T* pos = p + 1; pos != end(); ++pos) + *(pos - 1) = *pos; + --tail_; + return p; + } + + inline void resize(size_t sz) + { + size_t s = size(); + if (sz > size()) + { + for (size_t i = 0; i < sz - s; ++i) push(T()); + } + else if (sz < size()) + { + for (size_t i = 0; i < s - sz; ++i) pop(); + } + } + + inline void assign(const T* const first, const T* const end) + { + clear(); + const char* p = first; + while (p != end) push(*p++); + } + +private: + + T queue_[SIZE]; + volatile size_t head_ {0}; + volatile size_t tail_ {0}; +}; + +#endif // RINGBUFFER_H diff --git a/examples/Ethernet/receiver/receiver.ino b/examples/Ethernet/receiver/receiver.ino new file mode 100644 index 0000000..5f0e7e7 --- /dev/null +++ b/examples/Ethernet/receiver/receiver.ino @@ -0,0 +1,43 @@ +#include + +// Ethernet stuff +const IPAddress ip(192, 168, 1, 201); +uint8_t mac[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB}; + +ArtnetReceiver artnet; +uint32_t universe1 = 1; +uint32_t universe2 = 2; + +void callback(uint8_t* data, uint16_t size) +{ + // you can also use pre-defined callbacks +} + +void setup() +{ + Serial.begin(115200); + + Ethernet.begin(mac, ip); + artnet.begin(); + + // if Artnet packet comes to this universe, this function is called + artnet.subscribe(universe1, [&](uint8_t* data, uint16_t size) + { + Serial.print("artnet data (universe : "); + Serial.print(universe1); + Serial.println(") = "); + for (size_t i = 0; i < size; ++i) + { + Serial.print(data[i]); Serial.print(","); + } + Serial.println(); + }); + + // you can also use pre-defined callbacks + artnet.subscribe(universe2, callback); +} + +void loop() +{ + artnet.parse(); // check if artnet packet has come and execute callback +} diff --git a/examples/Ethernet/send_receive/send_receive.ino b/examples/Ethernet/send_receive/send_receive.ino new file mode 100644 index 0000000..d37176e --- /dev/null +++ b/examples/Ethernet/send_receive/send_receive.ino @@ -0,0 +1,50 @@ +#include + +// Ethernet stuff +const IPAddress ip(192, 168, 1, 201); +uint8_t mac[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB}; + +Artnet artnet; + +const uint16_t size = 512; +uint8_t data[size]; +uint8_t value = 0; +uint32_t universe = 1; + +void setup() +{ + Serial.begin(115200); + + Ethernet.begin(mac, ip); + artnet.begin("127.0.0.1"); // send to localhost + + Serial.println("set subscriber"); + + // if Artnet packet comes to this universe, this function is called + artnet.subscribe(universe, [](uint8_t* data, uint16_t size) + { + Serial.print("artnet data (universe : "); + Serial.print(universe); + Serial.println(") = "); + for (size_t i = 0; i < size; ++i) + { + Serial.print(data[i]); Serial.print(","); + } + Serial.println(); + }); + + Serial.println("start"); +} + +void loop() +{ + artnet.parse(); // check if artnet packet has come and execute callback + + value = millis() % 256; + memset(data, value, size); + + artnet.set(universe, data, size); + artnet.streaming(); // automatically send set data in 40fps + + // Serial.println("loop"); +} diff --git a/examples/Ethernet/sender/sender.ino b/examples/Ethernet/sender/sender.ino new file mode 100644 index 0000000..c3ed9f0 --- /dev/null +++ b/examples/Ethernet/sender/sender.ino @@ -0,0 +1,27 @@ +#include + +// Ethernet stuff +const IPAddress ip(192, 168, 1, 201); +uint8_t mac[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB}; + +ArtnetSender artnet; +uint32_t universe = 1; + +const uint16_t size = 512; +uint8_t data[size]; +uint8_t value = 0; + +void setup() +{ + Ethernet.begin(mac, ip); + artnet.begin("127.0.0.1"); +} + +void loop() +{ + value = millis() % 256; + memset(data, value, size); + + artnet.set(universe, data, size); + artnet.streaming(); // automatically send set data in 40fps +} diff --git a/examples/WiFi/receiver/receiver.ino b/examples/WiFi/receiver/receiver.ino new file mode 100644 index 0000000..1b47e28 --- /dev/null +++ b/examples/WiFi/receiver/receiver.ino @@ -0,0 +1,51 @@ +#include + +// WiFi stuff +const char* ssid = "your-ssid"; +const char* pwd = "your-password"; +const IPAddress ip(192, 168, 1, 201); +const IPAddress gateway(192, 168, 1, 1); +const IPAddress subnet(255, 255, 255, 0); + +ArtnetReceiver artnet; +uint32_t universe1 = 1; +uint32_t universe2 = 2; + +void callback(uint8_t* data, uint16_t size) +{ + // you can also use pre-defined callbacks +} + +void setup() +{ + Serial.begin(115200); + + // WiFi stuff + WiFi.begin(ssid, pwd); + WiFi.config(ip, gateway, subnet); + while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } + Serial.print("WiFi connected, IP = "); Serial.println(WiFi.localIP()); + + artnet.begin(); + + // if Artnet packet comes to this universe, this function (lambda) is called + artnet.subscribe(universe1, [&](uint8_t* data, uint16_t size) + { + Serial.print("lambda : artnet data (universe : "); + Serial.print(universe1); + Serial.println(") = "); + for (size_t i = 0; i < size; ++i) + { + Serial.print(data[i]); Serial.print(","); + } + Serial.println(); + }); + + // you can also use pre-defined callbacks + artnet.subscribe(universe2, callback); +} + +void loop() +{ + artnet.parse(); // check if artnet packet has come and execute callback +} diff --git a/examples/WiFi/send_receive/send_receive.ino b/examples/WiFi/send_receive/send_receive.ino new file mode 100644 index 0000000..e735475 --- /dev/null +++ b/examples/WiFi/send_receive/send_receive.ino @@ -0,0 +1,52 @@ +#include + +// WiFi stuff +const char* ssid = "your-ssid"; +const char* pwd = "your-password"; +const IPAddress ip(192, 168, 1, 201); +const IPAddress gateway(192, 168, 1, 1); +const IPAddress subnet(255, 255, 255, 0); + +Artnet artnet; + +const uint16_t size = 512; +uint8_t data[size]; +uint8_t value = 0; +uint32_t universe = 1; + +void setup() +{ + Serial.begin(115200); + + // WiFi stuff + WiFi.begin(ssid, pwd); + WiFi.config(ip, gateway, subnet); + while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } + Serial.print("WiFi connected, IP = "); Serial.println(WiFi.localIP()); + + artnet.begin("127.0.0.1"); // send to localhost + + // if Artnet packet comes to this universe, this function is called + artnet.subscribe(universe, [&](uint8_t* data, uint16_t size) + { + Serial.print("artnet data (universe : "); + Serial.print(universe); + Serial.println(") = "); + for (size_t i = 0; i < size; ++i) + { + Serial.print(data[i]); Serial.print(","); + } + Serial.println(); + }); +} + +void loop() +{ + artnet.parse(); // check if artnet packet has come and execute callback + + value = millis() % 256; + memset(data, value, size); + + artnet.set(universe, data, size); + artnet.streaming(); // automatically send set data in 40fps +} diff --git a/examples/WiFi/sender/sender.ino b/examples/WiFi/sender/sender.ino new file mode 100644 index 0000000..34ea6ab --- /dev/null +++ b/examples/WiFi/sender/sender.ino @@ -0,0 +1,35 @@ +#include + +// WiFi stuff +const char* ssid = "your-ssid"; +const char* pwd = "your-password"; +const IPAddress ip(192, 168, 1, 201); +const IPAddress gateway(192, 168, 1, 1); +const IPAddress subnet(255, 255, 255, 0); + +ArtnetSender artnet; +uint32_t universe = 1; + +const uint16_t size = 512; +uint8_t data[size]; +uint8_t value = 0; + +void setup() +{ + // WiFi stuff + WiFi.begin(ssid, pwd); + WiFi.config(ip, gateway, subnet); + while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } + Serial.print("WiFi connected, IP = "); Serial.println(WiFi.localIP()); + + artnet.begin("127.0.0.1"); +} + +void loop() +{ + value = millis() % 256; + memset(data, value, size); + + artnet.set(universe, data, size); + artnet.streaming(); // automatically send set data in 40fps +} diff --git a/library.json b/library.json new file mode 100644 index 0000000..6e425e7 --- /dev/null +++ b/library.json @@ -0,0 +1,20 @@ +{ + "name": "ArtNet", + "keywords": "Art-Net,Artnet,Ethernet,WiFi", + "description": "Art-Net Sender/Receiver for Arduino (Ethernet, WiFi)binary data packer / unpacker", + "repository": + { + "type": "git", + "url": "https://github.com/hideakitai/ArtNet.git" + }, + "authors": + { + "name": "Hideaki Tai", + "url": "https://github.com/hideakitai", + "maintainer": true + }, + "version": "0.1.0", + "license": "MIT", + "frameworks": "*", + "platforms": "*" +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..ee363b4 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=ArtNet +version=0.1.0 +author=hideakitai +maintainer=hideakitai +sentence=Art-Net Sender/Receiver for Arduino (Ethernet, WiFi) +paragraph=Art-Net Sender/Receiver for Arduino (Ethernet, WiFi) +category=Communication +url=https://github.com/hideakitai +architectures=*