1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

X86/64 Arduino+ws2811 and the gamma issue.

Discussion in 'Software Support' started by kokoko3k, 14 March 2019.

  1. kokoko3k

    kokoko3k New Member

    Messages:
    6
    Hardware:
    +Arduino
    Hi, i'm finally playing with hyperion and a ws2811 led strip.

    I'm using PC -> Arduino+fastled (adalight) -> led strip.

    After i configured the gamma ramp, i'm facing the problem that the lower btightness appears just black.
    So i documented myself and i've found that the fastled library actually implements temporal dithering, but the way it does it is not directly accessible by hyperion, because, as stated here:
    I'm using a sketch from here, and in the code i read the following:

    Code:
        leds[i].r = r;
        leds[i].g = g;
        leds[i].b = b;
      }
      // shows new values
    FastLED.show();
    
    ...but what if we do something like this:

    Code:
    //imagine we want to show a perfect gray (127,127,127)
       leds[i].r = 255;
       leds[i].g = 255;
       leds[i].b = 255;
    FastLED.setBrightness(127);
    FastLED.show();
    
    Basically, my idea is to stretch the luma component to full range, and then call setBrightness accordingly to scale them down so that the fastled temporal dithering would kick in.
    I suppose we also need to add FastLED.delay() into the sketch main loop (it is not clear to me if it needs to be done or not)

    Q: "Why don't you test it by yourself?"
    A: "Because i will be back at home in a month, and i just have had this idea :)"


    What do you think? would it work?
     
  2. kokoko3k

    kokoko3k New Member

    Messages:
    6
    Hardware:
    +Arduino
    The following works:


    Code:
    #include "FastLED.h"
    
    #define NUM_LEDS 33 //<-how many leds in your strip
    #define DATA_PIN 3
    #define CLOCK_PIN 13
    
    #define COLOR_ORDER RGB
    #define INITIAL_DELAY 100
    
    #define serialRate 500000
    
    #define Gamma 2.6
    
    CRGB leds[NUM_LEDS];
    
    float gamma[256];
    
    
    
    uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk, i;
    float fR;
    float fG;
    float fB;
    float fMax;
    float fNfactor;
    float fGammaOrig;
    float fGammaadj;
    float fMaximum;
    byte r, g, b;
    
    //Calculate gamma and return it as float
    float fCalculate_Gamma( uint8_t brightness, float gamma) {
      float fGammaOrig;
      float fGammaadj;
      fGammaOrig = (float)(brightness) / (255.0);
      fGammaadj =  pow( fGammaOrig, gamma)  * (255.0);
      return fGammaadj;
    }
    
    //Find the maximum of the three
    float maximum( float a, float b, float c )
    {
      float max = ( a < b ) ? b : a;
      return ( ( max < c ) ? c : max );
    }
    
    //Display something to notify that arduino is working
    void StartupNotification() {
      for (uint8_t i = 0; i < NUM_LEDS; i++) {
      leds[i].r = 255;
      leds[i].g = 255;
      leds[i].b = 255;
      }
    
      FastLED.setBrightness(255);
      FastLED.show();
      //FastLED.delay(1000);
    
      for (uint8_t i = 0; i < NUM_LEDS; i++) {
      leds[i].r = 0;
      leds[i].g = 0;
      leds[i].b = 0;
      }
      FastLED.show();
    }
    
    
    void setup() {
      FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
      Serial.begin(serialRate);
      Serial.print("Ada\n"); // Send "Magic Word" string to host
    
      //Create and fill gamma lookup tables
      for (i = 0; i < 255; i++) {
      gamma[i] = fCalculate_Gamma(i, Gamma);
      //Serial.print(gamma[i]);
      //Serial.print("\n");
      }
      gamma[255] = 255; //cant' cycle to 256 because i is byte (0..255)
    
      delay(INITIAL_DELAY);
      StartupNotification();
    
    }
    
    
    
    void loop() {
      // wait for first byte of Magic Word
      for (i = 0; i < sizeof prefix; ++i) {
      waitLoop: while (!Serial.available()) { }
      // Check next byte in Magic Word
      if (prefix[i] == Serial.read()) continue;
      // otherwise, start over
      i = 0;
       
      goto waitLoop;
      }
    
      // Hi, Lo, Checksum
      while (!Serial.available()) ;;
      hi = Serial.read();
      while (!Serial.available()) ;;
      lo = Serial.read();
      while (!Serial.available()) ;;
      chk = Serial.read();
    
      // if checksum does not match go back to wait
      if (chk != (hi ^ lo ^ 0x55))
      {
      i = 0;
       
      goto waitLoop;
      }
      memset(leds, 0, NUM_LEDS * sizeof(struct CRGB));
    
    
      int iMaximum;
    
      // read the transmission data and store LED values
      for (uint8_t i = 0; i < NUM_LEDS; i++) {
      while (!Serial.available());
      r = Serial.read();
      while (!Serial.available());
      g = Serial.read();
      while (!Serial.available());
      b = Serial.read();
      leds[i].r = r ;
      leds[i].g = g ;
      leds[i].b = b ;
      }
    
      // Normalize the strip
      //find maximum value of the "gammed" strip
      float Maximum_found=0;
      for (uint8_t i = 0; i < NUM_LEDS; i++) {
      fR = gamma[leds[i].r];
      if ( fR > Maximum_found ) { Maximum_found = fR ;}
      fG = gamma[leds[i].g];
      if ( fG > Maximum_found ) { Maximum_found = fG ;}
      fB = gamma[leds[i].b];
      if ( fB > Maximum_found ) { Maximum_found = fB ;}
      }
      fNfactor = 255 / Maximum_found;
     
     
      //normalize the strip and apply gamma
      for (uint8_t i = 0; i < NUM_LEDS; i++) {
      fR = gamma[leds[i].r];
      leds[i].r = (uint8_t)(fNfactor * fR);
      fG = gamma[leds[i].g];
      leds[i].g = (uint8_t)(fNfactor * fG);
      fB = gamma[leds[i].b];
      leds[i].b = (uint8_t)(fNfactor * fB);
      }
     
      // shows new values
      int newBrightness;
      newBrightness=(uint8_t)(Maximum_found);
      if (newBrightness == 0) {newBrightness=1;};
    
    Serial.print("Maximum_found:") ;Serial.println(Maximum_found);
    Serial.print("fNfactor:") ;Serial.println(fNfactor);
    Serial.print("newBrightness:") ;Serial.println(newBrightness);
    
      FastLED.setBrightness(newBrightness);
      FastLED.show();
    }
    
    What one should expect from the code above is to gain more precision at lower brightness, NOT better color fidelity.
    In fact, you actually lose the ability to set different gamma values for r,g,b.

    So, a dark color will appear dithered (better), but a color like this: 255,255,1 would not gain anything; this is because FastLed library does not provide different brightness per channel, just a global one; that means that 255,255,1 wil still be reproduced (after applying gamma) as 255,255,0.

    You will notice that gamma correction is done inside the sketch (#define Gamma 2.6), and it is done using float values; this is needed to gain extra precision (or hyperion would just cut it to integer).

    What the code does:
    1 reads the color to display, and compute its gamma and store it in float.
    2 Find the maximum value and use it as the master brightness factor
    3 stretch/normalize the color to full range/brightness
    4 display

    That way, the FastLed dithering should work; one thing i'm not 100% sure is still where to put FastLED.delay.
    By now i've put it inside the main waitloop, and it will wait/dither in 16ms steps; this means that arduino will see if there is data available on the serial every 16ms; this could introuduce some lag (well...16ms is not that much).

    I hope to receive some code review ;)
     
    Last edited: 18 March 2019