Hi all,
The subject was kind of hard to put into words, but please read on.
I really, really don't know what's causing this weird issue...
I'm trying to recreate a bought lantern\candle with my own NeoPixel Stick (the 8 LED stick, unknown brand (I didn't knew better then)) and ATtiny85.
This is my first project ever with a brand new Arduino Uno R4 Minima. I'm using the FastLED library because this actually works (with some effort) with the R4's.
The code works when using the Arduino itself. But I wanted it to work with an ATtiny85.
Using ATTinyCore i've burned the bootloader (well, set the fuses) to 8MHz.
When uploading (with using the Arduino as a programmer) the blink sketch, it works flawlessly. When using FastLED in combination with some simple self-written code it also works great. Basically, I loop this:
fill_solid(leds, NUM_LEDS, CHSV(random(0,256),random(0,256),random(50, BRIGHTNESS)));
FastLED.show();
delay(random(10, 200));
No problems here.
When I uploaded my actual sketch, all LEDs stayed off. After a lot of debugging I seem to have it tracked to either a large IF-statement, doing something in the else within the if or something FastLED related. I know if it's stuck somewhere because the bottom 3 LEDs are separately pulsing. If the code breaks, everything is dark.
- When replacing the IF-statement with (i == 1), it works.
- When commenting out setting an 2-dimensional array item, it works.
- When moving setting the array below the IF-statement, it works.
Another odd thing is, when adding FastLED code to turn a LED on, to within the IF-statement (or it's else) it also breaks when it DID work before with above edits.
I also thought it could have something to do with volatile vars in combination with the IF-statement, but this didn't seem to work as well.
The issue seems to be within this IF-statement:
if ((timing_LED_OnOff[i] == 1 && timeNow >= (timings[i] - 2) && timeNow <= (arr_fade[i][1] + 2)) || (timing_LED_OnOff[i] == 0 && timeNow >= (arr_fade[i][1] - 2) && timeNow <= (timings[i] + 2))) {
When commenting out this piece within the else of the above IF-statement it also works (as in the bottom LEDs turn on\code is not stuck):
arr_fade[i][2] = DEFAULT_BRIGHTNESS;
Now for the code:
#include <FastLED.h>
//CRGB(255,94,5)); // flame-like orange (RGB)
//CHSV(20, 255, 255); // 20 = flame-like orange (HSV) (h=20 for naked eye, or h=60 with red foil)
#define DATA_PIN 4 // Arduino pin 6, ATtiny85 pin 4 (4 for testing, 1 for production)
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS 8
CRGB leds[NUM_LEDS];
const PROGMEM int16_t BRIGHTNESS = 255; // This is a hard limit in the loop! Always set to MAX(255) if not otherwise required.
const PROGMEM int16_t FLAMECOLOR = 20; // Flame-like orange (h=20 for naked eye, or h=60 with red foil)
const PROGMEM int16_t HUE = 255; // full orange, no saturation (h=255)
const PROGMEM int16_t MAX_BRIGHTNESS = 255; // The max set brightness of the flames. 192 Is about 75% of max.
const PROGMEM int16_t MIN_BRIGHTNESS = 28; // The minimum brightness to keep the light orange and not red.
const PROGMEM int16_t DEFAULT_BRIGHTNESS = (MAX_BRIGHTNESS * 0.5);
const PROGMEM int16_t FADE_START = 100;
const PROGMEM int16_t END_TIME = 2450;
const PROGMEM unsigned long DEVIATION = 2;
unsigned long timeNow = millis();
unsigned long Sequence_Start;
unsigned long progress_base;
int16_t pulse_1_old = DEFAULT_BRIGHTNESS;
int16_t pulse_1 = DEFAULT_BRIGHTNESS;
unsigned long timer_1 = 500; // one half of a second
unsigned long LED_1_start = millis();
const PROGMEM int16_t timing_count = 39;
const PROGMEM int16_t timings[] = {10,75,140,205,270,340,410,410,440,520,575,640,705,780,840,910,1010,1075,1140,1240,1310,1370,1510,1575,1640,1640,1700,1705,1770,1850,1875,1940,2000,2040,2070,2130,2140,2210,END_TIME};
const PROGMEM int16_t timing_LEDs[] = {4,5,6,6,6,6,4,5,4,5,6,7,7,6,5,4,4,5,6,6,5,4,4,5,4,6,5,7,6,7,4,5,6,4,7,5,6,7,7};
const PROGMEM int16_t timing_LED_OnOff[] = {1,1,1,0,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,0,0,0,0};
int16_t arr_fade[timing_count][4];
void setup() {
/*
Serial.begin(9600); // For debugging
// while the serial stream is not open, do nothing:
while (!Serial) ;
*/
// Create the fade array. Set the fade value (in ms) to 100 of the timing it has since the last 'on' if it is less then 100. It (should) always begin(s) with a LED to on and ends with a LED to off.
for (int i = 0; i <= timing_count - 2; i++) { // note the - 2 instead of the usual -1. This is because I don't need the last array item 'END_TIME'.
arr_fade[i][0] = timing_LEDs[i];
if (timing_LED_OnOff[i] == 0) { // LED that turn off
for (int j = i - 1; j >= 0; j--) {
if (timing_LEDs[i] == timing_LEDs[j]) {
arr_fade[i][1] = timings[i] - timings[j];
if (arr_fade[i][1] < FADE_START) {
arr_fade[i][1] = timings[j] + DEVIATION;
} else if (arr_fade[i][1] < 0) {
arr_fade[i][1] = 0;
} else {
arr_fade[i][1] = timings[i] - FADE_START;
}
break;
}
}
} else { // LED that turn on (aka: the rest)
for (int j = i + 1; j <= timing_count; j++) {
if (timing_LEDs[i] == timing_LEDs[j]) {
arr_fade[i][1] = timings[j] - timings[i];
if (arr_fade[i][1] < FADE_START) {
arr_fade[i][1] = timings[j] - DEVIATION;
} else if (arr_fade[i][1] > END_TIME) {
arr_fade[i][1] = END_TIME;
} else {
arr_fade[i][1] = timings[i] + FADE_START;
}
break;
}
}
}
}
// tell FastLED about the LED strip configuration
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS); // set master brightness control
FastLED.clear(); // clear all leds
FastLED.show(); // show... well... nothing. Not required, but does forces the clear.
}
void loop() {
// put your main code here, to run repeatedly:
if (millis() - Sequence_Start > END_TIME) {
Sequence_Start = millis();
}
// Pulse the bottom leds 0, 1, 2. Always
progress_base = (millis() - LED_1_start);
if (progress_base <= timer_1) {
if (pulse_1 > MAX_BRIGHTNESS) {
pulse_1 = MAX_BRIGHTNESS;
}
int16_t LED_1_brightness = map(progress_base, 0, timer_1, pulse_1_old, pulse_1); // syntax: map(value, fromLow, fromHigh, toLow, toHigh)
toggle_led(0, 1, LED_1_brightness);
toggle_led(1, 1, LED_1_brightness);
toggle_led(2, 1, LED_1_brightness);
} else {
LED_1_start = millis(); // restart fade again
pulse_1_old = pulse_1;
// An extra random chance for a LED to shine brighter
if (random(0, 10) < 8) {
pulse_1 = random(DEFAULT_BRIGHTNESS * 0.65, DEFAULT_BRIGHTNESS * 1.4);
timer_1 = random(100, 250);
} else {
pulse_1 = random(DEFAULT_BRIGHTNESS * 0.9, DEFAULT_BRIGHTNESS * 1.75);
timer_1 = random(75, 175);
}
}
timeNow = (millis() - Sequence_Start);
// Loop the whole array and check if there are times to fade in\out
for (int i = 0; i <= (timing_count - 2); i++) {
//if ((timing_LED_OnOff[i] == 1 && timeNow >= low_milli(timings[i]) && timeNow <= high_milli(arr_fade[i][1])) || (timing_LED_OnOff[i] == 0 && timeNow >= low_milli(arr_fade[i][1]) && timeNow <= high_milli(timings[i]))) {
if ((timing_LED_OnOff[i] == 1 && timeNow >= (timings[i] - 2) && timeNow <= (arr_fade[i][1] + 2)) || (timing_LED_OnOff[i] == 0 && timeNow >= (arr_fade[i][1] - 2) && timeNow <= (timings[i] + 2))) {
//if (i == 1) {
int16_t func_brightness = set_LED_brightness(i);
fade_LED(arr_fade[i][0], func_brightness);
} else {
//leds[7] = CHSV(FLAMECOLOR, HUE, 255);
//FastLED.show();
arr_fade[i][2] = DEFAULT_BRIGHTNESS;
}
}
// Set all changes to the LEDs! Without this, no NeoPixel will change state
FastLED.show();
}
// Return the imput milliseconds - 2 for a bit of margin on the low end (milliseconds are not always exactly reached)
unsigned long low_milli(unsigned long cur_milli) {
//unsigned long return_milli = (cur_milli - DEVIATION);
//return return_milli;
}
// Return the imput milliseconds + 2 for a bit of margin on the high end (milliseconds are not always exactly reached)
unsigned long high_milli(unsigned long cur_milli) {
//unsigned long return_milli = (cur_milli + DEVIATION);
//return return_milli;
}
// Set the chosen led to the default color and hue to a given brightness
void fade_LED(int16_t lednr, int16_t brightness) {
/*
// Fade the LED to it's new brightness value. If the brightness is less then the color allows (28 in the case of the flameorange), just turn the LED off.
if (brightness < MIN_BRIGHTNESS) {
toggle_led(lednr, 0, 0);
} else {
if (lednr == 4) {
leds[lednr - 1] = CHSV(FLAMECOLOR, HUE, brightness);
}
leds[lednr] = CHSV(FLAMECOLOR, HUE, brightness);
}
*/
}
// Toggle the given LED on or off. If the LED is nr 4, automatically do the same with 3. If it's on, use the given brightness. If off, always set brightness to 0 for off.
void toggle_led(int16_t lednr, int16_t OnOff, int16_t func_Brightness) {
if (OnOff == 1) {
if (lednr == 4) {
leds[lednr - 1] = CHSV(FLAMECOLOR, HUE, func_Brightness);
}
leds[lednr] = CHSV(FLAMECOLOR, HUE, func_Brightness);
} else {
if (lednr == 4) {
leds[lednr - 1] = CHSV(FLAMECOLOR, HUE, 0);
}
leds[lednr] = CHSV(FLAMECOLOR, HUE, 0);
}
}
// Fade a given led. It is faded within the timeframe of 50ms (of less if the last action of that LEDnr was less ms ago). From the default brightness to 0 if time allows.
int16_t set_LED_brightness(int16_t arraynr) {
/*
arr_fade[arraynr][3] = millis() - arr_fade[arraynr][3];
if (timing_LED_OnOff[arraynr] == 0) {
arr_fade[arraynr][2] = map(arr_fade[arraynr][3], 0, timings[arraynr] - arr_fade[arraynr][2], DEFAULT_BRIGHTNESS, 0);
if (arr_fade[arraynr][2] < 0) {
arr_fade[arraynr][2] = 0;
}
}
if (timing_LED_OnOff[arraynr] == 1) {
arr_fade[arraynr][2] = map(arr_fade[arraynr][3], 0, timings[arraynr] - arr_fade[arraynr][2], 0, DEFAULT_BRIGHTNESS);
if (arr_fade[arraynr][2] > DEFAULT_BRIGHTNESS) {
arr_fade[arraynr][2] = DEFAULT_BRIGHTNESS;
}
}
return arr_fade[arraynr][2];
*/
return 20; // TEST TEST TEST
}
Do note that I commented out some code within functions to have less code to debug.
Also, it may or may well not have nothing something to do with this, but I would've expected ATTinyCore to include an AVR platform, but it seems to include the library: "FastLED\src\platforms\esp\32\clockless_rmt_esp32.cpp".
I hope someone will get my probably badly written story.
Can someone please help me track this down?
Kind regards,
Tri.