ArtNet to WS2812 Pixel LED driver : Using Wemos D1 (arduino-esp8266)

This works fine with the current install of the ESP8266 for Arduino libraries
Just set your own router SSID and Password, and run using Jinx software available from http://www.live-leds.de/

This forms the basis of a few projects I am running using multiple universes and browser-based configuration

/*
  SmartShow AirPixel ONE - Single Universe ArtNet to WS2812 Driver - For Wemos D1
  You can set the Device IP, and universe number below
  Works perfectly with Jinx LED software
*/

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

#define WSout 5  // Pin D1
#define WSbit (1<<WSout)

// ARTNET CODES
#define ARTNET_DATA 0x50
#define ARTNET_POLL 0x20
#define ARTNET_POLL_REPLY 0x21
#define ARTNET_PORT 6454
#define ARTNET_HEADER 17

WiFiUDP udp;

uint8_t uniData[514];
uint16_t uniSize;
uint8_t hData[ARTNET_HEADER + 1];
uint8_t net = 0;
uint8_t universe = 0;
uint8_t subnet = 0;

const char* ssid     = "RouterSSID";
const char* password = "RouterPASS";

IPAddress local_ip(192, 168, 1, 200);
IPAddress gateway_ip(192, 168, 1, 1);
IPAddress subnet_ip(255, 255, 255, 0);

void setup() {
  Serial.begin(115200);
  delay(10);
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  WiFi.config(local_ip, gateway_ip, subnet_ip);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  udp.begin(ARTNET_PORT); // Open ArtNet port

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  pinMode(WSout, OUTPUT);
}



void ICACHE_FLASH_ATTR sendWS() {
  uint32_t writeBits;
  uint8_t  bitMask, time;
  os_intr_lock();
  for (uint16_t t = 0; t < uniSize; t++) { // outer loop counting bytes
    bitMask = 0x80;
    while (bitMask) {
      // time=0ns : start by setting bit on
      time = 4;
      while (time--) {
        WRITE_PERI_REG( 0x60000304, WSbit );  // do ON bits // T=0
      }
      if ( uniData[t] & bitMask ) {
        writeBits = 0;  // if this is a '1' keep the on time on for longer, so dont write an off bit
      }
      else {
        writeBits = WSbit;  // else it must be a zero, so write the off bit !
      }
      time = 4;
      while (time--) {
        WRITE_PERI_REG( 0x60000308, writeBits );  // do OFF bits // T='0' time 350ns
      }
      time = 6;
      while (time--) {
        WRITE_PERI_REG( 0x60000308, WSbit );  // switch all bits off  T='1' time 700ns
      }
      // end of bite write time=1250ns
      bitMask >>= 1;
    }
  }
  os_intr_unlock();
}

void loop() {
  if (udp.parsePacket()) {
    udp.read(hData, ARTNET_HEADER + 1);
    if ( hData[0] == 'A' && hData[1] == 'r' && hData[2] == 't' && hData[3] == '-' && hData[4] == 'N' && hData[5] == 'e' && hData[6] == 't') {
      if ( hData[8] == 0x00 && hData[9] == ARTNET_DATA && hData[15] == net ) {
        if ( hData[14] == (subnet << 4) + universe ) { // UNIVERSE
          uniSize = (hData[16] << 8) + (hData[17]);
          udp.read(uniData, uniSize);
          //Serial.print("ArtNet packet RX Uni 0 - size:");
          //Serial.println(uniSize);
          sendWS();
        }
      } // if Artnet Data
    }
  }
}

I have expanded this to output 16 universes across 4 output pins, enough to drive 2720 WS2812 RGB Pixel LEDs

2720 * 0.6A when all are full on (bright white) = 163A! Hope you have some hefty 5V switching power supplies around.

CrossRoads:
2720 * 0.6A when all are full on (bright white) = 163A! Hope you have some hefty 5V switching power supplies around.

I have just bought some !
Although I still dont have that many WS2812s to drive/test, but I cant do this in blocks of 4 universes (680pix)

Hi,

I just bought a Wemos D1 to drive a 24x16 LED Matrix with WS2812.

The WiFi connection seems to work.

How would the cabling look like? D1 to the LED Pixel, GND to a common ground?

Can I see if the connection between Jinx! and the Wemos D1 has been successfully been established?

Kind regards
diversyy

Hi,
Firstly, 24*16 pixels is 384 Pixels, which is more than one universe, its even more than two !

Yes, D1 is the output to the Pixels, as Data out
Ground is ground and pick the 5v supply up from somewhere else as the Wemos cannot supply 5V, even if you pulled it through the USB it will be far too much current for the USB, the max Pixels I would drive direct from USB is 10 (30 channels = 500ma ish)

Why dont you light another Port/LED when the right IP/ArtNet/Universe is seen from the Wemos ?

Regards
Bob

Hi,

thank you very much for the response.

I tried to uncomment the following lines in your sketch, since it looked like I could see artnet packets coming in with that:

//Serial.print("ArtNet packet RX Uni 0 - size:");
//Serial.println(uniSize);

Today I received the LED stripes and also the 5V power-supplies. I will try to put it together tomorrow.

Regarding the universes I need for the project...could you give me a hint, how I can use multiple universes with your code?

Kind regards
Christian

After the initial filter of the desired universe you can do something like this to pick up universe+1

        if ( hData[14] == (subnet << 4) + universe +1 ) { // UNIVERSE
          uniSize2 = (hData[16] << 8) + (hData[17]);
          udp.read(uniData2, uniSize2);
          Serial.print("ArtNet packet RX Uni 1 - size:");
          Serial.println(uniSize2);
          sendWS2();
        }

Notice I have used uniSize2, uniData2 and sendWS2(), these are to save the second universe data and display it. You can either write/adjust a new sendWS2() routine to output the second uni on a separate pin, or work out a way to add the second universe after the first universe on the SAME pin

Magnificent piece of code :slight_smile: ... The first one which do not flicker leds. But I'm totally newbie in arduino coding :frowning:
I have put your code in the NodeMcu and it works like charm but my construction have 503 pixels [ 3 artnet universes ] .. can you give me a little helping hand with expanding your code to contain this 3 universes ?

Hi, you need to read the header part, determine what universe has just come in (but not yet read), then assign an array for it to be read into

if ( hData[14] == (subnet << 4) + universe ) { // UNIVERSE
          uniSize = (hData[16] << 8) + (hData[17]);
          udp.read(uniData[hData], uniSize);

You will also need to store the universe size so that you know how big each universe is when concatinating them together, I have managed to read and store 16 universes using this method

As I said before :confused: I'm total newbie in arduino and when I paste your code into .ino in place of orginal one this give me error

116: error: invalid types 'uint8_t [514] {aka unsigned char [514]}[uint8_t [18] {aka unsigned char [18]}]' for array subscript

           udp.read(uniData[hData], uniSize);

This is the only change in the code udp.read(uniData, uniSize); or am I missing something ?

You would also have to define the variable for uniData differently at the beginning of the code

where it says

uint8_t uniData[512];

change the array format to show the number of universes you are wanting to hold

uint8_t uniData[NUMBER_OF_UNIVERSES][512];

Thanx for help but it is too complicated to me :frowning:

in header I put something like this :

#define NUMBER_OF_UNIVERSES 4
WiFiUDP udp;

uint8_t uniData[NUMBER_OF_UNIVERSES][514];

then put your code :

udp.read(uniData[hData], uniSize);

but getting error message like this:

115: error: invalid types 'uint8_t [4][514] {aka unsigned char [4][514]}[uint8_t [18] {aka unsigned char [18]}]' for array subscript

           udp.read(uniData[hData], uniSize);

In the code in start of this topic you put uniData [514] but yesterday you used 512.. tryied both with same error :frowning:

Sorry, my mistake, the line should be

udp.read(uniData[uni], uniSize);
where 'uni' is the decoded universe number from the hData[14] portion of the UDP packet

  1. decode the universe from the packet header
  2. make sure this is the universe you want
  3. use the universe to index the right uniData array for reading the rest of the udp message

Hi Bob.

Have you managed to test this with your 16 universes of LEDs? does it manage to not flicker or freeze?

My project has 2016 x Ws2812 LEDs split into three panels.
Im hoping to run this from 3xESP8266 as Artnet receivers (1 per panel) and a single ESP8266 as a WIFI Access point.

Thanks for your good work,
This code seems to work very well and i'm hoping that with my limited knowledge, i can learn how to expand to 4-5 universes per unit and add a web based setup.
Cheers
Tom

Hi Tom,
I am currently up to 32 universes of SPI driven LEDs in one continuous line
I have tested 16 universes (split on 4 ports) at 25Hz refresh and there is no glitching
I make these up into finished products which people buy off me
Good Luck
Bob

@ mcnobby

Hello and thank you for this great code.

I am using Jinx with ESP8266-01 with a frame of 10x10 WS2812 and the ArnetArduino (It works fine).

I am also using the McLighting with another ESP (https://github.com/toblum/McLighting) wich is a very nice webserver including more than 50 effects.

My goal is to combine both features, i.e., using 1 ESP with McLighting Webserver (websocket), plus a switching link to allow me to activate the Artnet (For Jinx, using UDP)

I now that websocket and UDP are difficult to manage together ...

Any idea ?

Thank you very much for your help

Dan

I also use UDP and HTTP in the same program, but never at the same time (kind of...)

I use a 0.5 sec timer that only times-out when artnet UDP has stopped running.
Once timed-out I then check for HTTP requests.
If during the time-out an artNet UDP packet arrives, the timer is reset and HTTP requests are ignored until the the artnet stops again

Project has now been expanded to handle 32 universes of Pixel data from Jinx, splitting the output over 8 pins (4 consecutive universes per pin)

ey mcnobby!

I am trying out your amazing code, and it is working nicely, but there are 2 weirds things going on:

The first pixel is always displaying some degree of green, regardless what I send there is always a little bit of green. I tried different strips and I always get the same result.

Also, specially in the first pixel but not exclusively, there is some flickering. I trid with and without a 470Ohm resistor and it flickers both ways.

I copy the code as I am using it here:

/*
  SmartShow AirPixel ONE - Single Universe ArtNet to WS2812 Driver - For Wemos D1
  You can set the Device IP, and universe number below
  Works perfectly with Jinx LED software
*/

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

#define WSout 5  // Pin D1
#define WSbit (1<<WSout)

// ARTNET CODES
#define ARTNET_DATA 0x50
#define ARTNET_POLL 0x20
#define ARTNET_POLL_REPLY 0x21
#define ARTNET_PORT 6454
#define ARTNET_HEADER 17

WiFiUDP udp;

uint8_t uniData[514];
uint16_t uniSize;
uint8_t hData[ARTNET_HEADER + 1];
uint8_t net = 0;
uint8_t universe = 0;
uint8_t subnet = 0;

const char* ssid     = "ARTNET_LINK";
const char* password = "M-Tork144";

IPAddress local_ip(2, 0, 0, 22);
IPAddress gateway_ip(2, 0, 0, 1);
IPAddress subnet_ip(255, 0, 0, 0);

void setup() {
  Serial.begin(115200);
  delay(10);
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  WiFi.config(local_ip, gateway_ip, subnet_ip);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  udp.begin(ARTNET_PORT); // Open ArtNet port

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  pinMode(WSout, OUTPUT);
}



void ICACHE_FLASH_ATTR sendWS() {
  uint32_t writeBits;
  uint8_t  bitMask, time;
  os_intr_lock();
  for (uint16_t t = 0; t < uniSize; t++) { // outer loop counting bytes
    bitMask = 0x80;
    while (bitMask) {
      // time=0ns : start by setting bit on
      time = 4;
      while (time--) {
        WRITE_PERI_REG( 0x60000304, WSbit );  // do ON bits // T=0
      }
      if ( uniData[t] & bitMask ) {
        writeBits = 0;  // if this is a '1' keep the on time on for longer, so dont write an off bit
      }
      else {
        writeBits = WSbit;  // else it must be a zero, so write the off bit !
      }
      time = 4;
      while (time--) {
        WRITE_PERI_REG( 0x60000308, writeBits );  // do OFF bits // T='0' time 350ns
      }
      time = 6;
      while (time--) {
        WRITE_PERI_REG( 0x60000308, WSbit );  // switch all bits off  T='1' time 700ns
      }
      // end of bite write time=1250ns
      bitMask >>= 1;
    }
  }
  os_intr_unlock();
}

void loop() {
  if (udp.parsePacket()) {
    udp.read(hData, ARTNET_HEADER + 1);
    if ( hData[0] == 'A' && hData[1] == 'r' && hData[2] == 't' && hData[3] == '-' && hData[4] == 'N' && hData[5] == 'e' && hData[6] == 't') {
      if ( hData[8] == 0x00 && hData[9] == ARTNET_DATA && hData[15] == net ) {
        if ( hData[14] == (subnet << 4) + universe ) { // UNIVERSE
          uniSize = (hData[16] << 8) + (hData[17]);
          udp.read(uniData, uniSize);
          //Serial.print("ArtNet packet RX Uni 0 - size:");
          //Serial.println(uniSize);
          sendWS();
        }
      } // if Artnet Data
    }
  }
}

Did you already posted the project that handles 32 universes?

Also, is there no way to control the rgb order in the sketch right? The order is not correct as we speak.

Thank you so much for this!