Custom Nano Leaf project

Hello, everybody.

We all know the Nano Leaf wall light but they are a bit on the expensive side and since I have a 3d printer I decided to design and build my own version.

Here's the thing. I don't know yet how many units (leaves) I want to build, but what I want to do is have each unit comprised of three sets of three LEDs each (it is a triangular design, so one set in each corner.)

So if I just connect the three sections in series, essentially just extending he strip, I will end up with LED0 through LED8 (9 LEDs per unit) so the color will have to travel through all 9 of them before transferring to the adjacent unit and this is not quite the effect I'm looking for.

What I want is to have each unit light the same color and independent from one another, I thought this could be achieved by connecting the three sets in parallel to one another, essentially each unit will have three LED0, three LED1 and three LED2. for what I've read, it should be possible (providing the necessary power obviously), so each unit will only have a 3 pixel resolution (don't know if it's called that but anyway, I'm sure you get it) and I can live with that.

And to address the individuality of each unit, each signal line will be connected to its own data pin on the arduino nano I'm going to use as a controller. So only power will be shared between all units.

So far it's seem pretty simple to me, I haven't tried it yet, first I wanted to check if my thinking is on track and to get any advice on how to do it properly or even if it can be done.

I also wanted to add a sensor switch button under the pane but If I use 1 data pin pero unit and I make 10 units then I won't have enough pins available for the buttons. But that's a probelm for another day I suppose.

So, ideas? thoughts?

thanks, guys.

Not all of us. :wink:

If those are neopixels or other smart LEDs you can put them all on one string, use one data pin and by…

...some not especially clever programming and judicious use of the strip's show() method…

...get anything you want to happen when you want it to.

What kind of LEDs are you planning to use? Have you any experience with them yet?

a7

Hi there, I have the typical WS2812 strip, 180 LEDs in total. I have some experience with normal LEDs.
but as I wrote this I have a 30 LED section pf the strip doing a little dance atop my desk through the arduino.

OK good!

One pin plus software.

Now you get to post the code you are working with and give a larger idea of what you need to do.

Basically it is a matter of writing changes to pixels that change, leaving the others alone, then calling the show() method when you are ready to see the new values.

Some libraries facilitate "seeing" one strip as N number of logical strips which can be dealt with as if they were separate, on separate pins totally independent.

But it's not that hard to do yourself in code, which is what I do and why all I can tell you is that there are help from libraries, I think FastLED.

Forgadda welcome you to the forum!

a7

I don't have a sample code yet. But yes I was surfing through the FastLED examples before and one caught my eye and I think it might be what I need to get this but it uses one pin for each strip/unit/set, whatever.

I can almost see where you're going with the programming, but the issue remains that if I make, say 20 units with all 180 LEDs, the signal will still need to travel through all 179 LEDs before changing the last one, even if all 179 remain unchanged, right?

Get out your calculator and do the maths for me.

Updating 180 pixels will take… how many milliseconds?

a7

Hahaha, not a single clue. I know you can speed up or slow down the transitions but not to what extent.

But ok, I got it, obviously you know what you're talking about, one pin should work, and that saves up a looooot of wiring too.

So the setup will be just cutting up the strip in sections of three and re wire them as they were but extended so they can be fixed in each corner of each unit.

and then just figure out how to code the thing. did I get you right?

Yup. Meanwhile I found this

which I would not think less of you for using instead of coding it youself. :wink:

That post, but mebbe the hole thread would be, um, illuminating.

a7

it was very illuminating haha. unfortunately I'm new at this (only a few month) and because I have other things to do I haven't given it the dedication it's take to learn properly.

Here's the code I'm working on so far, but I can't seem to find a way to call on the group. i.e. telling group 1 to light red, 2 green, and 3 blue.

#include <Arduino.h>

#include <FastLED.h>

const uint8_t ledCount = 30;

const uint8_t startGroup1 = 1;

const uint8_t lengthGroup1 = 10;

const uint8_t startGroup2 = 11;

const uint8_t lengthGroup2 = 20;

const uint8_t startGroup3 = 21;

const uint8_t lengthGroup3 = 30;

const uint8_t ledPin = 3;

CRGB leds[ledCount];

CPixelView<CRGB> group1(leds, startGroup1, lengthGroup1);

CPixelView<CRGB> group2(leds, startGroup2, lengthGroup2);

CPixelView<CRGB> group3(leds, startGroup3, lengthGroup3);

void setup() {

FastLED.addLeds<WS2812B, ledPin, GRB>(leds, ledCount);

}

void loop() {

leds[group1] = CRGB::Red;

leds[group2] = CRGB::Green;

leds[group3] = CRGB::Blue;

FastLED.show();

}

Sry, couldn't resist.

Now I have gone too far on a limb, as I am not yet finding in FastLED a capability I thought it provided:

To have only one strip on one pin, but in the code be able to talk about a run of pixels as if it were a real strip on a real pin of its own, using simple indexing 0..N - 1.

Where N mightn't be the same for all sub-sections.

I will invite @gfvalvo to show us both how to do that, or to say I am wrong, that this is (still) something to be done by our own code, so simple a thing as that is.

If FastLED can do that, no doubt it would include not being able to mess with pixels beyond the range. Which would complicate an otherwise relatively simple offset when referring to the pixels in various regions or sections.

a7

I imitated some code I found, this cannot be the best way.

It shows assignment to an entire section.

It shows turning on one pixel a colour.

It shows further that this way does not keep you from writing past one section's boundary using that section's name.

I made the sections different sizes just so I could see that happen.


Wokwi_badge FastLED sections demo


// https://wokwi.com/projects/363128769711260673
// https://forum.arduino.cc/t/custom-nano-leaf-project/1119959

# include <FastLED.h>

const uint8_t startGroup1 = 0;
const uint8_t lengthGroup1 = 10;
const uint8_t startGroup2 = lengthGroup1;
const uint8_t lengthGroup2 = 7;
const uint8_t startGroup3 = startGroup2 + lengthGroup2;
const uint8_t lengthGroup3 = 13;
const uint8_t ledPin = 6;

const uint8_t ledCount = lengthGroup1 + lengthGroup2 + lengthGroup3;

CRGB leds[ledCount];

CRGBSet group1(leds, startGroup1, startGroup1 + lengthGroup1 - 1);
CRGBSet group2(leds, startGroup2, startGroup2 + lengthGroup2 - 1);
CRGBSet group3(leds, startGroup3, startGroup3 + lengthGroup3 - 1);

void setup() {

  FastLED.addLeds<WS2812B, ledPin, GRB>(leds, ledCount);

  group1 = CRGB::Red;
  group2 = CRGB::Green;
  group3 = CRGB::Blue;

  FastLED.show();
  delay(1777);

  group1[1] = CRGB::White;
  group2[3] = CRGB::Yellow;
  group3[5] = CRGB::Magenta;

  group1[19] = CRGB::Cyan;  // doesn't prevent
  group2[-2] = CRGB::Cyan;  // also doesn't prevent

  FastLED.show();
}

void loop() {}

So far it seems a long way to go to avoid just adding an offset to an index into one strip. Which is why I am sure I am still missing something.

a7

I worked for hours trying to make some arrays because for some reason with only about 70 lines (empty lines included) I was using 96% of the nano. It worked to an extent and copied some code from another project I did a few weeks ago and then I found the twinkle code for the fastled and it was and thoughtabout modify it tosuit my needs. But the adhd kicked in and got into doing other stuff (including laundry haha). Now I’m in bed watching stand up comedy on netflix and designing the case so I can print a sample tomorrow.
I’ll share where I’m at with code at some point tomorrow.

Thanks for your help!! I don’t usually go on forums because rarely have I received a reply or help from anyone. So you sir, are the GOAT hahaha.

I was about to post similar code. You got it right.

Not really. All the work happens up-front at compile time. Not needing to add a group-dependent offset is a big help if you're working with multiple, complex pattern animations over several groups.

Nope, like any other "C-style" array, that's on you.

1 Like

THX and a tip o' the hat.

a7

I was getting ready to post my surrender as this apparently is way above my pay grade. So I was gonna settle for connecting each unit independently to the Arduino (each unit to a different PIN) and just use some predetermined effect available.
Then I remembered you posted this code and I tried and apparently it works, I can't try it fully because I can't find a 5v power supply to used, I only have a 12v and don't have any converters, so I'm stuck apparently.

In the meantime, I'll try to assign and randomize different effects. I'll post the crappy code I was trying to write, figure you guys could use a laugh or two xD.
Any comment on it would be great. I'll also post a couple of pictures of the unit I already printed for testing.

This was my attempt at coding.

//   THE MAIN IDEA WAS TO HAVE 10 UNITS (LEAVES) AND EACH SOMEWHAT INDEPENDENT FROM ONE ANOTHER. I.E. 
//   THEY EACH DO A DIFFERENT EFFECT AT RANDOM (I WAS TRYING TO ADD DIFFERENT EFFECTS IN EACH TAB). 
//   EACH UNIT WOULD HAVE AN INDEPENDENT SIGNAL LINE CONNECTED TO THEIR OWN PIN ON THE BOARD. ONLY POWER WOULD
//   RUN NORMALLY THROUGHOUT.
//   IDEALLY, A TOUCH SENSOR BUTTON (TTP223) ON EACH UNIT WOULD ALLOW ME TO CHANGE EFFECTS, FOR EXAMPLE.
//   EACH UNIT WOULD HAVE THREE SETS OF THREE LEDs IN EACH CORNER (see pictures), FOR A TOTAL OF 90 LEDs, 
//   DRAWING ABOUT 5A AT MAX OUTPUT IF I CALCULATED CORRECTLY.

//   I LEFT GREYED OUT WHAT I WAS TRYING TO DO FOR REVIEW AND THE REST OF THE CODE WORKS, IT FADES AND COLOR CHANGES 
//   THE 9 LED SECTION I HAVE CONNECTED FOR TESTING (see pictures).
//   I DID MANAGE TO RANDOMIZE THE FADE IN AND FADE OUT SPEEDS USING CODE I HAVE USED TO THE SAME PURPOSE BEFORE.
//   ALSO THOSE SAME 3 LINES ALLOWED ME ON THAT PROJECT TO PICK A PIN AT RANDOM, THAT DID NOT WORK IN THIS INSTANCE.



#include <FastLED.h>

#define numLeds 9 // N° of LEDs per unit (leaf)
#define numUnits 10 // N° of units for the project

// each pin to its corresponding unit. ledPin1 is unit 1 (or 0?) and so on until unit 10 (or 9?)
#define ledPin1 3
#define ledPin2 4
#define ledPin3 5
#define ledPin4 6
#define ledPin5 7
#define ledPin6 8
#define ledPin7 9
#define ledPin8 10
#define ledPin9 11
#define ledPin10 12

//    MY ATTEMPT AT PUTTING THE PINS ON AN ARRAY
//int ledPins[10] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; // unit pins
//int pinsOfLed = sizeof(ledPins)/sizeof(int); // number of pins
//int pinSelect = random(pinsOfLed); // choose pin randomly

//   ----- I THINK THIS TWO ARE ESSENTIALLY THE SAME -------

//  TRIED TO MAKE THE CRGB LED INTO AN ARRAY, COULDN'T DO IT.
//int leds[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // each unit ID available
//int ledUnits = sizeof(leds)/sizeof(int); // number of units
//int unitSelect = random(ledUnits); // random choose unit

//each unit ID? I guess we can call it that
CRGB leds[numLeds];
//CRGB leds2[numLeds];
//CRGB leds3[numLeds];
//CRGB leds4[numLeds];
//CRGB leds5[numLeds];
//CRGB leds6[numLeds];
//CRGB leds7[numLeds];
//CRGB leds8[numLeds];
//CRGB leds9[numLeds];
//CRGB leds10[numLeds];


void setup() {

	FastLED.addLeds<WS2812B, ledPin1, GRB>(leds, numLeds);
 
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds2, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds3, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds4, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds5, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds6, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds7, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds8, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds9, numLeds);
  //FastLED.addLeds<WS2812B, pinSelect, GRB>(leds10, numLeds);

}


void loop() {

  fadeAnimation(255, 255, 255);   // White
  fadeAnimation(255, 0, 0);       // Red
  fadeAnimation(255, 128, 0);     // Orange
  fadeAnimation(255, 255, 0);     // Yellow
  fadeAnimation(0, 255, 0);       // Green
  fadeAnimation(0, 0, 255);       // Blue
  fadeAnimation(128, 0, 255);     // Purple
  fadeAnimation(255, 128, 128);   // Pink
  FastLED.show();
 

}

I don't think I can upload videos.



Hey, guys.

So I've been playing around and I decided to discard the idea of controlling the modules (leaves) separate because with only 9 LEDs per unit it was very limited what I could do.
Also, for some reason, the LED strip didn't like to be split up.

Instead I chose to run the strip along the edges of the units and just play with the effects and I was very satisfied at how it looks.

Now, I don't want the same effect to run all the time (I still want some control) so after MUCH trial an error I ended up coding a state machine that'd allow me to change the effect with a button (TTP223) and the code compiles and uploads but the LEDs are frozen so I added some serial print lines and in the monitor it seems to respond as expected, but the LEDs remain frozen. I cannot figure this out.
I have the main code and each effect on different tabs (it seems more organized to me that way), I don't know how to add separate tabs here so I'll just put everything into one and comment where each tab begins and ends.

#include <FastLED.h>
#include <Arduino.h>

#define NUM_LEDS 125
#define LED_PIN 3
#define BUTT_PIN 5
#define BRIGHTNESS 255
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

#define UPDATES_PER_SECOND 60

CRGBPalette16 currentPalette( CRGB::Black);
CRGBPalette16 targetPalette( PartyColors_p );

void changeEffect();

void setup() {

	delay( 3000 ); // power-up safety delay
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );
    Serial.begin(9600);
}

void loop() {
  //twinkleFox();
  //fadeAnimationWrapper();
  //cylon();
  
  changeEffect();
}

void changeEffect() {
  enum class effectState : uint8_t {
    twinkleFox,
    fadeAnimationWrapper,
    cylon,
  };

    static effectState currEffect = effectState::twinkleFox;
 static effectState prevEffect = effectState::twinkleFox;
  static int buttonState = LOW;
  static int prevButtonState = LOW;

  buttonState = digitalRead(BUTT_PIN);

  if (buttonState != prevButtonState) {
    if (buttonState == HIGH) {
      Serial.println("Button Pressed!");

      switch (currEffect) {
        case effectState::twinkleFox:
          currEffect = effectState::fadeAnimationWrapper;
          Serial.println("Fade Running!!");
          break;
        case effectState::fadeAnimationWrapper:
          currEffect = effectState::cylon;
          Serial.println("Cylon Running!!");
          break;
        case effectState::cylon:
          currEffect = effectState::twinkleFox;
          Serial.println("TwinkleFox Running!!");
          break;
      }
      prevEffect = currEffect;
    }
    prevButtonState = buttonState;
  }
}

// 
// TAB 2 - CYLON EFFECT BEGINS

#include <FastLED.h>

#define CLOCK_PIN 13

void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }

void cylon() { 
	static uint8_t hue = 0;
	Serial.print("x");
	// First slide the led in one direction
	for(int i = 0; i < NUM_LEDS; i++) {
		// Set the i'th led to red 
		leds[i] = CHSV(hue++, 255, 255);
		// Show the leds
		FastLED.show(); 
		// now that we've shown the leds, reset the i'th led to black
		// leds[i] = CRGB::Black;
		fadeall();
		// Wait a little bit before we loop around and do it again
		delay(10);
	}
	
	// Now go in the other direction.  
	for(int i = (NUM_LEDS)-1; i >= 0; i--) {
		// Set the i'th led to red 
		leds[i] = CHSV(hue++, 255, 255);
		// Show the leds
		FastLED.show();
		// now that we've shown the leds, reset the i'th led to black
		// leds[i] = CRGB::Black;
		fadeall();
		// Wait a little bit before we loop around and do it again
		delay(10);
	}
}

// TAB 2 - CYLON EFFECT ENDS 

//  TAB 3 - TWINKLEFOX EFFECT BEGINS 

void twinkleFox()
{
  ChangePalettePeriodically();

  uint8_t maxChanges = random8(48); // values 0-48
  nblendPaletteTowardPalette( currentPalette, targetPalette, maxChanges);

  static uint8_t startIndex = 0;
  startIndex = startIndex + 1; /* motion speed */
  FillLEDsFromPaletteColors( startIndex);

  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);
}

void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
  uint8_t brightness = 255;
  
  for( int i = 0; i < NUM_LEDS; i++) {
    leds[i] = ColorFromPalette( currentPalette, colorIndex + sin8(i*16), brightness);
    colorIndex += 3;
  }
}


void ChangePalettePeriodically()
{
  uint8_t secondHand = (millis() / 1000) % 60;
  
  switch(secondHand) {
    case 0:
      targetPalette = RainbowColors_p;
      break;
    case 10:
      targetPalette = CRGBPalette16( CHSV(HUE_GREEN, 255, 255), CHSV(HUE_PURPLE, 255, 255), CRGB::Black, CRGB::Black, CHSV(HUE_PURPLE, 255, 255), CHSV(HUE_GREEN, 255, 255), CRGB::Black, CRGB::Black, CHSV(HUE_PURPLE, 255, 255), CHSV(HUE_GREEN, 255, 255), CRGB::Black, CRGB::Black, CHSV(HUE_PURPLE, 255, 255), CHSV(HUE_GREEN, 255, 255), CRGB::Black, CRGB::Black );
      break;
    case 20:
      targetPalette = CRGBPalette16( CRGB::Black, CRGB::Black, CRGB::Black, CRGB::White, CRGB::Black, CRGB::Black, CRGB::Black, CRGB::White, CRGB::Black, CRGB::Black, CRGB::Black, CRGB::White, CRGB::Black, CRGB::Black, CRGB::Black, CRGB::White );
      break;
    case 30:
      targetPalette = LavaColors_p;
      break;
    case 40:
      targetPalette = CloudColors_p;
      break;
    case 50:
      targetPalette = PartyColors_p;
      break;
  }
}


// TAB 3 - TWINKLEFOX EFFECT ENDS
//  TAB 4 - FADE ANIMATION EFFECT BEGINS 


void fadeAnimationWrapper() {
  fadeAnimation(random(256), random(256), random(256));
}

void fadeAnimation(int red, int green, int blue) {
  float r, g, b;
  uint32_t startTime = millis();
  uint32_t totalTime = 1000; // total time for fade in and out, in milliseconds

  // Define array of durations for fade in and fade out
  uint32_t fadeInDurations[] = {600, 800, 1000, 1200, 1600, 2000, 2500};
  uint32_t fadeOutDurations[] = {600, 800, 1000, 1200, 1600, 2000, 2500};
  int numFadeInDurations = sizeof(fadeInDurations) / sizeof(fadeInDurations[0]);
  int numFadeOutDurations = sizeof(fadeOutDurations) / sizeof(fadeOutDurations[0]);

  // Randomly select duration for fade in and fade out
  uint32_t fadeInTime = fadeInDurations[random(numFadeInDurations)];
  uint32_t fadeOutTime = fadeOutDurations[random(numFadeOutDurations)];

  // FADE IN
  while (millis() - startTime <= fadeInTime) {
    float progress = (float)(millis() - startTime) / (float)fadeInTime;
    r = progress * red;
    g = progress * green;
    b = progress * blue;
    fill_solid(leds, NUM_LEDS, CRGB(r, g, b));
    FastLED.show();
  }

  // FADE OUT
  startTime = millis(); // reset start time for fade out
  while (millis() - startTime <= fadeOutTime) {
    float progress = 1.0 - ((float)(millis() - startTime) / (float)fadeOutTime);
    r = progress * red;
    g = progress * green;
    b = progress * blue;
    fill_solid(leds, NUM_LEDS, CRGB(r, g, b));
    FastLED.show();
  }
}
// TAB 4 - FADE ANIMATION EFFECT ENDS 
//

each effect works perfectly on its own. I tested this by commenting or uncommenting respectively on the loop() function

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.