Driving multiple strings of NeoPixels from UNO

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My first foray into the world of NeoPixels was a success thanks to suggestions here.  I now have the desire to branch out a little, but not sure the best way to go about this.  

 

I am at the moment controlling a string of NEO's off of Pin 4 of the UNO.  I would like to control several different strings of different quantity from the same UNO using a few of the other pins.  I am currently using pin 4, but I would like to add strings to pins 5, 6, and 7.

 

Now if the strings were all the same quantity I could instead add a simple 1 of x demultiplexer and drive the strings that way, but in my case I would like to run strings of different quantities.

 

In the attached sketch I have removed most of the demo loops except for one called cascade, which is working on my test strip.

 

The problem I am running into is that there are two #defines that tell the code what PIN to use, and the LED COUNT at the start of the code.  These would need to be dynamic as opposed to static to accomplish what I want to do, but in looking at the library .h and .cpp files, these two constants are used in many places, so I am not so sure how to get this to work.

 

I did the noob thing and tried to add other #defines when I created another instance of the cascade to run after the first, but that caused the compiler to have a meltdown.  Which I was expecting.

 

I see these strips hanging down in storefronts everywhere doing exactly what I am trying to do so there must be something simple I am missing.  I do not need the animation, all teh LED's can remain lit solid, I just picked tha cascade for the test.  Have string one cascade, then string 2 etc.

 

Any tips would be appreciated

 

Jim

 

Attachment(s): 

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have a few copies of the function with each having the required port/bit. Not exactly efficient, but tough problems need tough solutions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I hacked something together some time ago. The port and pin are #defines too, but you can easily just copy the assembly routines and create some new defines. The code is intended for an Arduino nano on 8 MHz. If necessary, I can adjust it to 16 MHz.

And please don't laugh at me for writing such ugly code. It was just a quick test.

Attachment(s): 

"Some people die at 25 and aren't buried until 75." -Benjamin Franklin

 

What is life's greatest illusion?"  "Innocence, my brother." -Skyrim

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jim,

 

In the code:

Adafruit_NeoPixel leds = Adafruit_NeoPixel(LED_COUNT, PIN, NEO_GRB + NEO_KHZ800);

don't you just:

Adafruit_NeoPixel leds = Adafruit_NeoPixel(LED_COUNT, PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel leds2 = Adafruit_NeoPixel(3, 5, NEO_GRB + NEO_KHZ800);

or similar?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

void Adafruit_NeoPixel::show(void) {

  while(!canShow());
  // endTime is a private member (rather than global var) so that mutliple
  // instances on different pins can be quickly issued in succession (each
  // instance doesn't delay the next).

 

Above is a snippet from the Adafruit NeoPixel library.  I believe that it refers to using several Neo strings on different pins.  I suggest downloading this library and running one of the examples with several objects, one for each pin with a NeoPxl string.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks all for the suggestions.

 

pawi777 - I will review what you have and see what magic you cast.

 

Cliff - Yes, i thought of that, but the implementation I have to sti with my P.C. and stylus and work it out.

 

Simonetta - I have the library installed, but I missed that snippet.  Will have to look at that and for some documentation regarding it as well...hopefully Github has something.

 

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

UPDATE:

 

Cliffs suggestion:

Adafruit_NeoPixel leds2 = Adafruit_NeoPixel(3, 5, NEO_GRB + NEO_KHZ800);

Works as long as I create a second(or as many instances I create) function - in this case the Cascade for each that I want to use on that string.

 

Example. For a single string to control I would have:

#include <Adafruit_NeoPixel.h>
#include "WS2812_Definitions.h"
 #define PIN 4
 #define LED_COUNT 5
 
 // Create an instance of the Adafruit_NeoPixel class called "leds".
// That'll be what we refer to from here on...
Adafruit_NeoPixel leds = Adafruit_NeoPixel(LED_COUNT, PIN, NEO_GRB + NEO_KHZ800);

void setup()
{
    
    leds.begin();  // Call this to start up the LED strip.
    clearLEDs();   // This function, defined below, turns all LEDs off...
    leds.show();   // ...but the LEDs don't actually update until you call this.
    
}

void loop()
{
 
  // A light shower of spring green rain
  // This will run the cascade from top->bottom 20 times
  for (int i=0; i<20; i++)
  {
    // First parameter is the color, second is direction, third is ms between falls
    cascade(MEDIUMSPRINGGREEN, TOP_DOWN, 100); 
    
    
  }

}

// Sets all LEDs to off, but DOES NOT update the display;
// call leds.show() to actually turn them off after this.
void clearLEDs()
{
  for (int i=0; i<LED_COUNT; i++)
  {
    leds.setPixelColor(i, 0);
  }
}

// Cascades a single direction. One time.
void cascade(unsigned long color, byte direction, byte wait)
{
  if (direction == TOP_DOWN)
  {
    for (int i=0; i<=LED_COUNT; i++)
    {
      clearLEDs();  // Turn off all LEDs
      leds.setPixelColor(i, color);  // Set just this one
      leds.show();
      delay(wait);
    }
  }
  else
  {
    for (int i=LED_COUNT-1; i>=0; i--)
    {
      clearLEDs();
      leds.setPixelColor(i, color);
      leds.show();
      delay(wait);
    }
  }
}

 

But to do the same thing with a second string I had to do the following:

 

#include <Adafruit_NeoPixel.h>
#include "WS2812_Definitions.h"
 #define PIN 4
 #define LED_COUNT 5
#define LED_COUNT2 6

// Create an instance of the Adafruit_NeoPixel class called "leds".
// That'll be what we refer to from here on...
Adafruit_NeoPixel leds = Adafruit_NeoPixel(LED_COUNT, PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel leds2 = Adafruit_NeoPixel(LED_COUNT2, 5, NEO_GRB + NEO_KHZ800);

void setup()
{
  {  
    leds.begin();  // Call this to start up the LED strip.
    clearLEDs();   // This function, defined below, turns all LEDs off...
    leds.show();   // ...but the LEDs don't actually update until you call this.

    leds2.begin();  // Call this to start up the LED strip.
    clearLEDs2();   // This function, defined below, turns all LEDs off...
    leds2.show();   // ...but the LEDs don't actually update until you call this.
  }
}

void loop()
{
 
  // A light shower of spring green rain
  // This will run the cascade from top->bottom 20 times
  for (int i=0; i<20; i++)
  {
    // First parameter is the color, second is direction, third is ms between falls
    cascade(MEDIUMSPRINGGREEN, TOP_DOWN, 100); 
    cascade2(MEDIUMSPRINGGREEN, TOP_DOWN, 100);
    
  }

}


// Sets all LEDs to off, but DOES NOT update the display;
// call leds.show() to actually turn them off after this.
void clearLEDs()
{
  for (int i=0; i<LED_COUNT; i++)
  {
    leds.setPixelColor(i, 0);
  }
}


// Sets all LEDs to off, but DOES NOT update the display;
// call leds.show() to actually turn them off after this.
void clearLEDs2()
{
  for (int i=0; i<LED_COUNT2; i++)
  {
    leds2.setPixelColor(i, 0);
  }
}


// Cascades a single direction. One time.
void cascade(unsigned long color, byte direction, byte wait)
{
  if (direction == TOP_DOWN)
  {
    for (int i=0; i<=LED_COUNT; i++)
    {
      clearLEDs();  // Turn off all LEDs
      leds.setPixelColor(i, color);  // Set just this one
      leds.show();
      delay(wait);
    }
  }
  else
  {
    for (int i=LED_COUNT-1; i>=0; i--)
    {
      clearLEDs();
      leds.setPixelColor(i, color);
      leds.show();
      delay(wait);
    }
  }
}


// Cascades a single direction. One time on second string.
void cascade2(unsigned long color, byte direction, byte wait)
{
  if (direction == TOP_DOWN)
  {
    for (int i=0; i<=LED_COUNT2; i++)
    {
      clearLEDs2();  // Turn off all LEDs
      leds2.setPixelColor(i, color);  // Set just this one
      leds2.show();
      delay(wait);
    }
  }
  else
  {
    for (int i=LED_COUNT2-1; i>=0; i--)
    {
      clearLEDs2();
      leds2.setPixelColor(i, color);
      leds2.show();
      delay(wait);
    }
  }
}

So as you can see I need two calls to clear the LED strings, and two calls to separate cascade functions...one for each string.

 

My next quest is to see if there is a way to use one function - in this case a single clear LEDs and a single CASCADE for both strings.  One after the other

 

Thanks all for the tips.  Hopefully someone else might find this as helpful as I am.

 

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
My next quest is to see if there is a way to use one function
Surely you have code where you can just pass &leds or &leds2 to it and it does the same for either? Something like:

void clearLEDs2()
{
  for (int i=0; i<LED_COUNT2; i++)
  {
    leds2.setPixelColor(i, 0);
  }
}

becomes:

void clearLEDs(Adafruit_Neopixel & led_set)
{
  for (int i=0; i<LED_COUNT2; i++)
  {
    led_set.setPixelColor(i, 0);
  }
}

then invoke with:

clearLEDs(&leds);
clearLEDs(&leds2);

or something along those lines? I haven't actually tried this in a test program (as I normally would to verify my facts) but I think it's the way to do it.

 

You might also want to consider and array of leds[] rather than leds, leds2, leds3 etc then you could iterate over all the strings.

 

If some are different lengths to others I wonder if the class has an accessor function to read back the length of any particular instance? In that case, given:

Adafruit_NeoPixel leds = Adafruit_NeoPixel(LED_COUNT, PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel leds2 = Adafruit_NeoPixel(LED_COUNT2, 5, NEO_GRB + NEO_KHZ800);

where LED_COUNT and LED_COUNT2 are 5 and 6 you read back the length with:

void clearLEDs(Adafruit_Neopixel & led_set)
{
  for (int i=0; i < led_set.get_count(); i++)
  {
    led_set.setPixelColor(i, 0);
  }
}

because in the absence of that you would have to pass it in:

void clearLEDs(Adafruit_Neopixel & led_set, int count)
{
  for (int i=0; i < count; i++)
  {
    led_set.setPixelColor(i, 0);
  }
}

and invoke as:

clearLEDs(&leds, LED_COUNT);
clearLEDs(&leds2, LED_COUNT2);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah ha, looking at the Adafruit code it appears it has:

 

https://github.com/adafruit/Adaf...

 

uint16_t Adafruit_NeoPixel::numPixels(void) const {
  return numLEDs;
}

so when I said led_sets.get_count() I really meant led_sets.numPixels() in fact. That is:

void clearLEDs(Adafruit_Neopixel & led_set)
{
  for (int i=0; i < led_set.numPixels(); i++)
  {
    led_set.setPixelColor(i, 0);
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm reading this with interest but stay out as I get confused easily with Arduino stuff. (everyone say "Really??)

 

When I started messing around with Neopixel I wrote my own ASM driver that can drive 1 or more (up to 8) channels simultaneously with the same data and from the same buffer. The channels need to be on the same port.

 

The C interface is something like:

send_pixel_buffer ((uint8_t*) &Pixel_buffer_1, LEDs_in_string_1, (Channel_1 | Channel_2 | Channel_3 | Channel_4));

is this what you want to achieve?

 

at the ASM end those parameters are received in the following registers

// Neopixel driver M644 @18.432MHz

#include <avr/io.h> 

#include "project_definitions.h"
#include "JTAMP03_definitions.h"

#define	temp2 R22					// 3rd temp register
#define	temp1 R25					// Second temp register
#define	temp R24					// temp register

	.section .text

	.global	send_pixel_buffer
	.func send_pixel_buffer 

send_pixel_buffer:
	push	XL
	push	XH
	push	ZL
	push	ZH
	push	temp
	push	temp1
	movw	XL, temp             // 1st paramter pointer to buffer in R24, R25
	movw	ZL, R22              // 2nd paramter number of LEDs*3 in R22, R23
	mov	R21,R20              // 3rd parameter channel(2) number mask in R20
	com	R21                  // Copy to R21 for negative mask

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Really??