Hello chaps and chapettes!
First time poster, I hope I get this right.
I am attempting to build a small LED desk light.
Within said desk light I am using:
- Arduino Nano
- 5V/10A PSU
- 70x WS2812B LEDs
- The FastLED library.
- An assortment of various resistors, pots and caps.
I've got some basic code set up, which, mind you, is basically a beginners amalgamation of multiple FastLED examples, youtube tutorials, reddit threads, naïve optimism and metaphorical duct tape.
The code is working. The electronics and the prototype LED light is working. It is doing what I am asking it to do.
The problem:
The discrete step nature of (basic) LED-array animation is quite underwhelming to look at.
If I want slower animations, they will instead look choppy in the current code.
I can increase the animation speed to make it flow a bit better, but I don't want it moving that fast.
I've tried looking at existing code for Precision Draw from Dave's Garage EP09 (I read it was not allowed to share code that I have not written myself). After hours of playing around with it I could not modify it so that it suited my needs.
The gif below is the version with slower refresh rate (80ms). This gives me a slower animation, but it also looks very laggy in real life.

The second gif below has a reasonable refresh rate (20ms). This gives smoother animation, but it's quite too fast.

The question:
How can I modify my code so that the animation speed can be slowed down without causing the discrete steps to be noticeable?
I am using the following code, it has been trimmed so that the vital functions are there. It works.
// #########################################################################################################
#include <FastLED.h>
#define LED_PIN 2
#define POT_PIN_BRIGHTNESS A3
#define NUM_LEDS 70
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define MAX_MILLIAMPS 500
CRGB leds[NUM_LEDS];
uint8_t brightnessAverage;
uint8_t brightnessAverage_map;
uint8_t startIndex = 0;
uint8_t potBrightness;
CRGBPalette16 currentPalette;
CRGBPalette16 targetPalette;
// #########################################################################################################
void setup() {
delay(1500); // power-up safety delay.
Serial.begin(9600); // Initialize serial coms @ 9600 baud.
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); // define led-strip.
FastLED.setMaxPowerInVoltsAndMilliamps(5, MAX_MILLIAMPS); // Upper limits for current draw (when only connected to usb)
FastLED.setBrightness(50); // initial brightness, not needed due to potBrightness.
FastLED.clear(); // clear the fastled data-line, I think.
currentPalette = CreateRandomHuePalette(); // Picks a randomized new starting palette.
targetPalette = currentPalette; // Sets target palette to the starting palette so that initial nblend-function doesn't go crazy.
}
// #########################################################################################################
void loop() {
EVERY_N_MILLISECONDS(20) { // (ANIMATION SPEED / REFRESH RATE)
startIndex++; // Increments the palette index, causes it to move.
potBrightness = BrightnessFromPot(); // Fetches current brightness value from pot
fill_palette(leds, NUM_LEDS, startIndex, 4, currentPalette, potBrightness, LINEARBLEND); // Fills LED-data line with color palette values.
nblendPaletteTowardPalette(currentPalette, targetPalette, 8); // Blends the current palette with the new target palette, causing a clean cross fade.
FastLED.show(); // Push out rgb data in the LED-array (leds[]) to the physical LEDs.
}
EVERY_N_SECONDS(15) { // Every 15 seconds, do
targetPalette = CreateRandomHuePalette();
}
}
// ###########################################################################################################
// This is probably far from the most efficient way of doing this. I am not a clever man, however.
// Basically, create a 16 color palette. Randomise a hue.
// Set an additional hue to the color wheel opposite of the first hue (sort of).
// Add them to a specific [x,y] in the 4x4 color palette array.
// Offset the hues a specific amount for a more interesting effect.
CRGBPalette16 CreateRandomHuePalette() {
CRGBPalette16 randPalette;
uint8_t randomHue1 = random8();
uint8_t randomHue2 = randomHue1 - 96;
uint8_t counter = 0;
for (int i = 0; i < 13; i += 4) {
if (counter == 0) {
randPalette[i] = CRGB::Black;
randPalette[i + 1] = CRGB::Black;
randPalette[i + 2] = CHSV(randomHue1, 255, 255);
randPalette[i + 3] = CHSV(randomHue2 + 10, 255, 255);
}
if (counter == 4) {
randPalette[i] = CRGB::Black;
randPalette[i + 1] = CRGB::Black;
randPalette[i + 2] = CHSV(randomHue1 + 3, 255, 255);
randPalette[i + 3] = CHSV(randomHue2 + 7, 255, 255);
}
if (counter == 8) {
randPalette[i] = CRGB::Black;
randPalette[i + 1] = CRGB::Black;
randPalette[i + 2] = CHSV(randomHue1 + 7, 255, 255);
randPalette[i + 3] = CHSV(randomHue2 + 3, 255, 255);
}
if (counter == 12) {
randPalette[i] = CRGB::Black;
randPalette[i + 1] = CRGB::Black;
randPalette[i + 2] = CHSV(randomHue1 + 10, 255, 255);
randPalette[i + 3] = CHSV(randomHue2, 255, 255);
}
counter = counter + 4;
}
return randPalette;
}
// ###################################### - READ FROM POTENTIOMETERS - #######################################
uint8_t BrightnessFromPot() {
uint16_t potRead_b = analogRead(POT_PIN_BRIGHTNESS); // Reads analog value from potentiometer.
uint8_t brightness = map(potRead_b, 0, 1023, 60, 255); // Maps the 16-bit analog value to an 8-bit value.
uint8_t temp_bright_a = (1.0 / 10.0) * brightness; // Half of running average to stabilize jitter in the analog value.
uint8_t temp_bright_b = ((10.0 - 1.0) / 10.0) * brightnessAverage; // Other half
brightnessAverage = temp_bright_a + temp_bright_b; // Averaged brightness level over N=10 samples. (Thank you /u/stockvu!)
brightnessAverage_map = map(brightnessAverage, 60, 241, 65, 255); // Averaged values only go from 60 - 241 instead of 0 - 255, this re-maps them to 65-255
return brightnessAverage_map;
}
// ###########################################################################################################
Thank you for your time.
And If you think that my code is a dumpster fire, I am always interested in feedback - let me know how I can do better!