I've done an Art-Net to DMX project using a Nano and ENC28J60 Ethernet module and thought i'd share in the hope some people might give it a more thorough testing than what i can do (i've currently only 3 white LED strips) to see where it might fall down...
I've stripped out what i can to save memory. Compiled i'm using 29% of program memory and 70% of dynamic memory.
I've used DMX Workshop and Jinx to blink the LED strips on and off but i've noticed a slight delay after every few on/off cycles when i set it to a higher strobe rate and i'm not sure what that might be but aside from that, it appears to be working OK.
Here's my code:
/*
* Chris Lyttle
* Art-Net to DMX Controller Interface
*
* Programmed for Arduino Nano with ENC28J60 Ethernet shield
* Works with Arduino 1.6.8 using EtherCard & DmxSimple Library
* Takes ArtDMX packets sent via Ethernet from a PC and sends
* DMX lighting values out to the DMX universe
*
* Based on ARTNET RECEIVER by Christoph Guillermet and E1.31
* Receiver and pixel controller by Andrew Huxtable ported
* to use EtherCard library for ENC28J60 and Art-Net instead of
* E1.31.
*
* ENC26J60 pins wired as follows:
*
* Enc28j60 SO -> Arduino pin 12
* Enc28j60 SI -> Arduino pin 11
* Enc28j60 SCK -> Arduino pin 13
* Enc28j60 CS -> Arduino pin 10
* Enc28j60 VCC -> Arduino 5V pin (3V3 pin didn't work for me)
* Enc28j60 GND -> Arduino Gnd pin
*
*/
#include <EtherCard.h>
#include <DmxSimple.h>
#define bytes_to_short(h,l) ( ((h << 8) & 0xff00) | (l & 0x00FF) );
#define STATIC 1 // to use a static IP
#if STATIC
// ethernet interface ip address. please change to your own
//for direct link from PC ethernet to ENC board, make ip 192.168.2.2
//on PC, in ipv4 settings, make static IP 192.168.2.1, subnet mask 255.255.255.0 and gwip left empty
static byte myip[] = { 192,168,1,177 };
// gateway ip address not needed
//static byte gwip[] = { };
#endif
//ethernet mac address - //genetated from http://www.miniwebtool.com/mac-address-generator/ using Microchip OUI
//verified at http://sqa.fyicenter.com/Online_Test_Tools/MAC_Address_Format_Validator.php
static byte mymac[] = {0x01,0x00,0x00,0x45,0x08,0x11};
byte Ethernet::buffer[529]; // tcp/ip send and receive buffer should be 512 dmx channels + 17 bytes for header
const int number_of_channels=512; // 512 for one full DMX universe (THIS TAKES UP LOTS OF SRAM)
const int channel_position=1; // 1 if you want to read from channel 1
//byte buffer_dmx[number_of_channels+channel_position]; //buffer to store filetered DMX data ***bypassed this bit to save SRAM***
const int art_net_header_size=17;
const int max_packet_size=529; //should be 512 + 17 bytes
char ArtNetHead[8]="Art-Net"; //first byte of an ARTDMX packet contains "Art-Net"
short Opcode=0;
boolean is_opcode_is_dmx=0;
boolean is_opcode_is_artpoll=0;
// callback that deals with received packets
static void artnetPacket(uint16_t port, uint8_t ip[4], uint16_t src_port, const char *data, uint16_t len) {
//Serial.println( "artnetPacket reached"); //print to serial for testing
//Make sure the packet is an ArtDMX packet
int check = checkARTDMX(data, len);
if (check){
// It is so process the data to the LEDS
sendDMX(data);
}
}
/*
Do some checks to see if it's an ArtNet packet. First 17 bytes are the ArtNet header, the rest is DMX values
Bytes 1 to 8 of an Art-Net Packet contain "Art-Net" so check for "Art-Net" in the first 8 bytes
Bytes 8 and 9 contain the OpCode - a 16 bit number that tells if its ArtPoll or ArtDMX
Don't worry about the rest of the bytes until byte 18 on (DMX channel levels)
*/
int checkARTDMX(const char* messagein, int messagelength) { //messagein prob dont need to use a pointer here since we aren't writing to it
//Serial.println( "checkARTDMX reached"); //print to serial for testing
if(messagelength > art_net_header_size && messagelength <= max_packet_size) //check size to avoid unneeded checks
{
//read header
boolean match_artnet = 1;
for (int i=0;i<7;i++)
{
if(messagein[i] != ArtNetHead[i]) //tests first byte for "Art-Net"
{
match_artnet=0; break; //if not an artnet packet we stop reading
}
}
if (match_artnet==1)//continue if it matches
{
//check which type of packet it is: ArtPoll or ArtDMX
Opcode=bytes_to_short(messagein[9],messagein[8]);
if(Opcode==0x5000)//if opcode is DMX type
{
is_opcode_is_dmx=1; is_opcode_is_artpoll=0;
}
else if(Opcode==0x2000)//if opcode is artpoll
{
is_opcode_is_artpoll=1;is_opcode_is_dmx=0;
//( we should normally reply to it, giving ip address of the device)
}
}
}
return 1;
}
/*
function to send the dmx data out using DmxSimple library function
Reads data directly from the packetBuffer and sends straight out
*/
void sendDMX(const char* packetBuffer)
{
//Serial.println( "sendDMX reached"); //print to serial for testing
for(int i = channel_position-1; i < number_of_channels; i++)//channel position
{
//buffer_dmx[i]= byte(packetBuffer[i+17]); //bypassed the dmx buffer altogether to save memory
//DmxSimple.write(i,buffer_dmx[i]);
DmxSimple.write(i,packetBuffer[i+17]);
}
}
void setup()
{
//Serial.begin(57600); //for testing
DmxSimple.usePin(3); // DMX output is pin 3
DmxSimple.maxChannel(512); //should be 512
ether.begin(sizeof(Ethernet::buffer), mymac, 10); //10 at end initialises CS pin as pin 10 on arduino instead of default pin 8
// Static IP
ether.staticSetup(myip);
// Register listener
ether.udpServerListenOnPort(&artnetPacket, 6454); //artnetPacket function to handle event, listen on port 6454 (default artnet port)
//Serial.println( "register listener reached"); //print to serial for testing
}
void loop()
{
// this must be called for ethercard functions to work.
ether.packetLoop(ether.packetReceive());
}