first
parent
17620e501c
commit
3958e708d3
@ -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 <Arduino.h>
|
||||||
|
#ifndef __AVR__
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#else
|
||||||
|
#include "RingBuffer.h"
|
||||||
|
#endif
|
||||||
|
#ifdef ESP_PLATFORM
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#elif defined (ESP8266)
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#elif defined (TEENSYDUINO) || defined (__AVR__)
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <EthernetUdp.h>
|
||||||
|
#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<uint16_t>(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<uint16_t>(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 <uint16_t SIZE>
|
||||||
|
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 <typename S>
|
||||||
|
class Sender_
|
||||||
|
{
|
||||||
|
#ifdef __AVR__
|
||||||
|
Array<PACKET_SIZE> packet;
|
||||||
|
#else
|
||||||
|
std::array<uint8_t, PACKET_SIZE> 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<uint8_t>(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 <typename S>
|
||||||
|
class Receiver_
|
||||||
|
{
|
||||||
|
#ifdef __AVR__
|
||||||
|
typedef void (*CallbackType)(uint8_t* data, uint16_t size);
|
||||||
|
struct Map { uint32_t universe; CallbackType func; };
|
||||||
|
using CallbackMap = RingBuffer<Map, 8>;
|
||||||
|
Array<PACKET_SIZE> packet;
|
||||||
|
#else
|
||||||
|
using CallbackType = std::function<void(uint8_t* data, uint16_t size)>;
|
||||||
|
struct Map { uint32_t universe; CallbackType func; };
|
||||||
|
using CallbackMap = std::vector<Map>;
|
||||||
|
std::array<uint8_t, PACKET_SIZE> 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<const char*>(packet.data());
|
||||||
|
return !strcmp(ID, idptr);
|
||||||
|
}
|
||||||
|
inline bool checkID(const uint8_t* p) const
|
||||||
|
{
|
||||||
|
const char* idptr = reinterpret_cast<const char*>(p);
|
||||||
|
return !strcmp(ID, idptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
class Manager : public Sender_<S>, public Receiver_<S>
|
||||||
|
{
|
||||||
|
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_<S>::attach(stream, send_ip, send_port);
|
||||||
|
this->Receiver_<S>::attach(stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
class Sender : public Sender_<S>
|
||||||
|
{
|
||||||
|
S stream;
|
||||||
|
public:
|
||||||
|
void begin(const char* ip, uint32_t port = DEFAULT_PORT)
|
||||||
|
{
|
||||||
|
this->Sender_<S>::attach(stream, ip, port);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
class Receiver : public Receiver_<S>
|
||||||
|
{
|
||||||
|
S stream;
|
||||||
|
public:
|
||||||
|
void begin(uint32_t port = DEFAULT_PORT)
|
||||||
|
{
|
||||||
|
stream.begin(port);
|
||||||
|
this->Receiver_<S>::attach(stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined (ESP_PLATFORM) || defined (ESP8266)
|
||||||
|
using Artnet = arduino::artnet::Manager<WiFiUDP>;
|
||||||
|
using ArtnetSender = arduino::artnet::Sender<WiFiUDP>;
|
||||||
|
using ArtnetReceiver = arduino::artnet::Receiver<WiFiUDP>;
|
||||||
|
#elif defined (TEENSYDUINO) || defined (__AVR__)
|
||||||
|
using Artnet = arduino::artnet::Manager<EthernetUDP>;
|
||||||
|
using ArtnetSender = arduino::artnet::Sender<EthernetUDP>;
|
||||||
|
using ArtnetReceiver = arduino::artnet::Receiver<EthernetUDP>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ARDUINO_ARTNET_H
|
@ -1,2 +1,231 @@
|
|||||||
# Artnet
|
# ArtNet
|
||||||
Artnet library for Arduino (Ethernet, WiFi)
|
|
||||||
|
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 <Artnet.h>
|
||||||
|
|
||||||
|
// 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 <Artnet.h>
|
||||||
|
|
||||||
|
// 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 <Artnet.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef RINGBUFFER_H
|
||||||
|
#define RINGBUFFER_H
|
||||||
|
|
||||||
|
template<typename T, size_t SIZE>
|
||||||
|
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
|
@ -0,0 +1,43 @@
|
|||||||
|
#include <Artnet.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
#include <Artnet.h>
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
#include <Artnet.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
#include <Artnet.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
#include <Artnet.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
#include <Artnet.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
@ -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": "*"
|
||||||
|
}
|
@ -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=*
|
Loading…
Reference in New Issue