[SOLVED] Variable changes itself without being written to!

Hi everyone,

this one has me stuck for a couple of nights now and I think I need you help to figure it out. I'm writing to a strip of ws2812b LEDs and I am simulating every second one drop that is falling until it reaches a "surface" of solid color. On impact, the drop's color blends into the solid color.
To do that, I store the time of drop impact in a variable "DropImpactMillis" and then fade out in every loop() iteration according to the drop's age (Difference between millis() and the stored time). 750ms for the fade out looks good to me.
This works quite nicely and looks like I wanted it to. Unfortunately, this only works for short strips and high gravity (where I have more than the 750ms for the fade-out) because every new drop falling breaks the animation of the fade-out. It's weird to me because the two have nothing to so with oneanother and apparently my variable "DropImpactMillis" gets changed by an unknown function.

This is my code (should be sufficient to look at loop() only):

#include <DS1307RTC.h>
#include <TimeLib.h>
#include <DCF77.h>
#include <FastLED.h>


FASTLED_USING_NAMESPACE

bool inflight = false;
unsigned long DropImpactMillis = 0;
unsigned long DropFallingMillis = 0;
int FallingDropPos = 0;

time_t LastIChecked;

#define DATA_PIN_UpperRow    11 //Hardware SPI - data 11, clock 13
#define DATA_PIN_BottomRow    10 //USART SPI - data 1, clock 4
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS    14
#define LEDS_PER_M    60
#define STANDARD_GRAVITY 9.80665
//#define STANDARD_GRAVITY 0.5
#define FRAMES_PER_SECOND  120
#define MAXLENGTHDROPS 5
CRGB leds_UpperRow[NUM_LEDS];
CRGB leds_BottomRow[NUM_LEDS];
CRGB RGBs_Drop[MAXLENGTHDROPS];


void setup() {
//  // Serial Monitor Communication. Delete when done.
  Serial.begin(57600);
  while (!Serial) ; // wait until Arduino Serial Monitor opens

  //Bei jedem Einschalten: Zeit aus der RTC lesen
  setSyncProvider(RTC.get);     // the function to get the time from the RTC
  setSyncInterval(600);    // Set the number of seconds between re-sync (600 = 10 Minutes) --> now() triggers it
//  if(timeStatus()!= timeSet) 
//     Serial.println("Unable to sync with the RTC");
//  else
//     Serial.println("RTC has set the system time");      

  // tell FastLED about the LED strip configuration
  FastLED.addLeds<LED_TYPE,DATA_PIN_UpperRow, COLOR_ORDER>(leds_UpperRow,  NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<LED_TYPE,DATA_PIN_BottomRow,COLOR_ORDER>(leds_BottomRow, NUM_LEDS).setCorrection(TypicalLEDStrip);
  // set master brightness control
  LEDS.setBrightness(84);
  FastLED.setMaxPowerInMilliWatts(500);
}


void FadeOutDrop(CRGB (&leds)[NUM_LEDS], int beginning, int width, CRGB color, double decayMillis, double ageMillis){
  if (ageMillis<decayMillis) { 
    fill_solid(RGBs_Drop, MAXLENGTHDROPS, CRGB::Black); // clear the overlay
    fill_gradient_RGB(RGBs_Drop, 0, color, width, CRGB::Black);  // prepare the overlay
    int age = 255.0 * ageMillis / decayMillis; //"age" the overlay

    fadeToBlackBy(RGBs_Drop, MAXLENGTHDROPS, age);
    for( int i = 0 ; i < width ; i++) {   // put at the right place and simply add to what is already there.
      leds[i+beginning] += RGBs_Drop[i];
    }  
  }
}


int GetFallingDropPos(float freefallMillis, CRGB color){
  float f;

  f = STANDARD_GRAVITY * freefallMillis/1000; // Velocity
  f = f * 0.6; // Scale the length of the tail. Should be around 0.1 for long strips and Standard-Gravity.

  fill_solid(RGBs_Drop, MAXLENGTHDROPS, CRGB::Black); // clear the overlay
  fill_gradient_RGB(RGBs_Drop, 0, color, 3, CRGB::Black);  // prepare the overlay. Replace 3 with f but doesn't work. ??
  
  f = STANDARD_GRAVITY * freefallMillis/1000 * freefallMillis/1000 / 2; // Distance fallen in m

  return (f*LEDS_PER_M);
}


void loop() {

  EVERY_N_SECONDS( 1 ) {
    DropFallingMillis = millis();
    inflight = true;
    LastIChecked = now();
  }

 
  fill_solid(leds_UpperRow, NUM_LEDS, CRGB::Black);   // Clear the strip
  fill_gradient_RGB(leds_UpperRow, 10, CRGB::Red, 10+4, CRGB::Black);  // Write the time

  if (inflight) {
    FallingDropPos = GetFallingDropPos(millis()-DropFallingMillis, CRGB::Blue);
    if (FallingDropPos > 10) {   // Eigentlich: Größer als Abstand zur Oberfläche in Pixeln
      Serial.print("Set @ ");
      DropImpactMillis = millis(); // Initiate dissolving drop on contact with surface.
      Serial.println(DropImpactMillis);
      inflight = false; 
      FallingDropPos = 10; //Paint one last time, at the moment of impact
    } 
    for( int i = 0 ; i < MAXLENGTHDROPS ; i++) {   // put at the right place
      leds_UpperRow[FallingDropPos-i] = RGBs_Drop[i]; // tail fades out
    }
  }

  Serial.print(millis());
  Serial.print(" - ");
  Serial.print(DropImpactMillis);
  Serial.print(" = ");
  Serial.println(millis()-DropImpactMillis);
    
  FadeOutDrop(leds_UpperRow, 10, 4, CRGB::Green, 750.0, (millis()-DropImpactMillis));  // Let the drops glow after impact
    
  FastLED.show();
  FastLED.delay(1000/FRAMES_PER_SECOND); // Maybe unnecessary with all the other clock stuff I'll be running on the side.
  

}

As you can see, the variable is only ever set to 0 (on creation) and millis() (on every impact) but this is get from my debugging (DropImpactMillis is in the 2nd "column" of this output):

Set @ 6202  // on impact
6202 - 6202 = 1
6212 - 6202 = 10
6224 - 6202 = 22
6235 - 6202 = 33
6247 - 6202 = 45
6257 - 6202 = 55
6269 - 6202 = 67
6280 - 6202 = 78
6292 - 6202 = 90
6302 - 6202 = 100
6315 - 6202 = 113
6325 - 6202 = 123
6337 - 6202 = 135
6347 - 6202 = 145
6360 - 6202 = 158
6370 - 6202 = 168
6382 - 6202 = 180
6392 - 6202 = 190
6403 - 6202 = 201
6415 - 6202 = 213
6427 - 6202 = 225
6437 - 6202 = 235
6448 - 6202 = 246
6460 - 6202 = 258
6470 - 6202 = 268
6482 - 6202 = 280
6493 - 6202 = 291
6505 - 6202 = 303
6515 - 6202 = 313
6528 - 6202 = 326
6538 - 6202 = 336
6550 - 6202 = 348
6560 - 6202 = 358
6573 - 6202 = 371
6583 - 6202 = 381
6595 - 6202 = 393
6605 - 6202 = 403
6616 - 6202 = 414
6628 - 6202 = 426
6640 - 6202 = 438
6650 - 6202 = 448
6661 - 6202 = 459
6673 - 6202 = 471
6685 - 6202 = 483
6695 - 6202 = 493
6706 - 6202 = 504
6718 - 6202 = 516
6730 - 6202 = 528
6740 - 6202 = 538
6753 - 6202 = 551
6763 - 6202 = 561
6775 - 6202 = 573
6786 - 6202 = 584
6798 - 6202 = 596
6808 - 6202 = 606
6818 - 6202 = 616
6828 - 6202 = 627
6841 - 6202 = 639
6853 - 6202 = 651
6863 - 6202 = 661
6873 - 6202 = 672
6886 - 6202 = 684
6898 - 6202 = 696
6908 - 6202 = 706
6918 - 6202 = 717
6931 - 6202 = 729
6943 - 6202 = 741
6953 - 6202 = 751
6966 - 6202 = 764
6976 - 6202 = 774
6988 - 6202 = 786
6999 - 6202 = 797
7011 - 5570560 = 4289403748  // This is the time the new drop starts falling. Totally unrelated in my understanding!? And where is that value coming from!? It's the same value always, not time related.
7021 - 5570560 = 4289403758
7031 - 5570560 = 4289403768
7042 - 5570560 = 4289403778
7054 - 5570560 = 4289403791
7067 - 5570560 = 4289403803
7079 - 0 = 7079  // Here again! The variable nevery gets zero (except in its original initiation) but here it is!?
7090 - 0 = 7090
7102 - 0 = 7102
7114 - 0 = 7114
7124 - 0 = 7124
7137 - 0 = 7137
7147 - 0 = 7147
7159 - 0 = 7159
7170 - 0 = 7170
7182 - 0 = 7183
7192 - 0 = 7192
7204 - 0 = 7205

I've had some trouble with variable types as well, so don't be confused by my explicit and "colorful" type casting. It cannot be at the heart of this problem, however, because this one always is and unsigned long...

Thank you, your help is much appreciated!
Hajo

An otherwise mysterious value change to a variable which you have not explicitly set could, in this instance, be due to an array subscript going out of range causing some corruption of adjacent storage.

My first and unscientific approach would be to pad the definition of DropImpactMillis with a largish array on either side to see if the problem moves somewhere else.

    for( int i = 0 ; i < width ; i++) {   // put at the right place and simply add to what is already there.
      leds[i+beginning] += RGBs_Drop[i];
    }

This will write outside the array for any beginning > 0 if width were really the width of the array.

 FadeOutDrop(leds_UpperRow, 10, 4, CRGB::Green, 750.0, (millis()-DropImpactMillis));  // Let the drops glow after impact

10 is above 0, but a width of 4 fixes that

I'd probably also look here

      leds_UpperRow[FallingDropPos-i] = RGBs_Drop[i]; // tail fades out

to check that FallingDropPos is not less than 4 otherwise there is an undershoot.

Thanks guys!

I knew I didn't check for array boundaries rigorously and was delighted that it still worked none-the-less and thought the compiler somehow took care of that for me.

Writing out of bound would perfectly explain what I'm seeing and I will test for it tonight. The "undershoot" is the most likely candidate and I even did it on purpose because I thought an index of -1 would simply be ignored but I will do more boundary testing everywhere, now that I am aware of the problem.

Hope this solves it. Thanks again for the pointer!

Hajo

6v6gt:
I'd probably also look here

      leds_UpperRow[FallingDropPos-i] = RGBs_Drop[i]; // tail fades out

to check that FallingDropPos is not less than 4 otherwise there is an undershoot.

That's definitely stomping all over your variables, just by inspection of the loop:

    for( int i = 0 ; i < MAXLENGTHDROPS ; i++) {   // put at the right place
      leds_UpperRow[FallingDropPos-i] = RGBs_Drop[i]; // tail fades out
    }

You iterate 14 times - this can only ever be safe if FallingDropPos happens to equal 13

MarkT:
That's definitely stomping all over your variables, just by inspection of the loop:

    for( int i = 0 ; i < MAXLENGTHDROPS ; i++) {   // put at the right place

leds_UpperRow[FallingDropPos-i] = RGBs_Drop[i]; // tail fades out
   }



You iterate 14 times - this can only ever be safe if FallingDropPos happens to equal 13

He iterates only 5 times through that loop unless I am missing something.
The value of FallingDropPos is capped upwards at 10 before entering the loop. The full range of values it [FallingDropPos] can take, however, is not so clear to me without a reverse engineering effort which I don't want to make, but anything below 4 is trouble.

6v6gt:
He iterates only 5 times through that loop unless I am missing something.

Oh damn, thought I had it! Still there's likely some buffer-overrun issue somewhere!

In case you guys are curious: This is going to be a clock in the form of two 12-glass-block rows in my house. Each row is illuminated from below by 143 LEDs embedded in the concrete which will show the minutes and hours respectively (==> the filled "surface" the drop falls into). The passage of time is visualized as falling and mixing drops.
FallingDropPos can be anything between and including 0 and 143 (less, if already filled up with minutes/hours) and I fully see how you cannot attach a tail of 4 LEDs to a drop at position 2.

Incidentally, the ominous 5570560 that showed up in my DropImpactMillis variable turns out to be 0x550000, which looks like the pure red / green / blue RGB colors that I use for testing.

Thanks particularly @6v6gt, for taking the time to try to understand this. I will mark this thread as solved once I got it working.

Hajo

It sounds interesting. Post a picture of it when it is finished. Maybe in the Exhibition / Gallery part of this forum.