Search code examples
carduinorgbhsv

Smooth animation for rgb led arduino


I'm trying to make a smooth transition with arduino, it receives rgb values from serial correctly, but it seems to don't reach the target color because it oscillates between different values. I'm using this library to convert in hsv RGBConverter. I tried to convert rgb value in hsv and to change gradually hsv values. This is my code:

RGBConverter converter = RGBConverter();
my_color cur_color;
void setup() {

  pinMode(RED_LED, OUTPUT);  
  pinMode(GREEN_LED, OUTPUT);  
  pinMode(BLUE_LED, OUTPUT); 
  Serial.begin(BAUD_RATE);
  cur_color={0,0,0};

  }

void loop() {                  
      my_color final={Serial.read(),Serial.read(),Serial.read()};
      double hsv[3];
      converter.rgbToHsv(final.red,final.green,final.blue,hsv);
      reach_color(&cur_color,hsv);                
  }

Color is a struct that contains three byte values named red, green and blue

This is the reach_color method

void reach_color(struct color *start, double hsv_final[]){
  double hsv[3];
  byte rgb[3];

  while(true){
    converter.rgbToHsv(start->red,start->green,start->blue,hsv);
    if ( hsv[0] >hsv_final[0]&&hsv[0]>0 ) hsv[0] -= 0.01;
    else if(hsv[0]<hsv_final[0]&&hsv[0]<1) hsv[0]+= 0.01;
    else if(hsv[1]>hsv_final[1]&&hsv[1]>0) hsv[1]-= 0.01;
    else if(hsv[1]<hsv_final[1]&&hsv[1]<1) hsv[1]+= 0.01;
    else if(hsv[2]>hsv_final[2]&&hsv[2]>0) hsv[2]-= 0.01;
    else if(hsv[2]<hsv_final[2]&&hsv[2]<1) hsv[2]+= 0.01;
    else return;

    converter.hsvToRgb(hsv[0],hsv[1], hsv[2], rgb);
    start->red=rgb[0];
    start->green=rgb[1];
    start->blue=rgb[2];
    display_color(*start);
    delay(30);
    }
}

and this is display method

void display_color(struct color c){
analogWrite(GREEN_LED, c.green);  
analogWrite(BLUE_LED, c.blue);  
analogWrite(RED_LED, c.red); 
}

I need to gradually move from current color to final color, but while never goes in the else clause and current color oscillates between different values, does anyone know where is the error? Thank you all in advance for the help


Solution

  • I think that my error is inside the algorithm reach_color

    Right. Consider e. g. hsv[0] is 0.005 and hsv_final[0] is 0; the line

        if ( hsv[0] >hsv_final[0]&&hsv[0]>0 ) hsv[0] -= 0.01;
    

    will set hsv[0] to −0.005 (which is outside the range [0, 1] assumed by hsvToRgb(), whatever that does with it). In the next loop cycle, the line

        else if(hsv[0]<hsv_final[0]&&hsv[0]<1) hsv[0]+= 0.01;
    

    will set hsv[0] to +0.005 anew - and so it goes on oscillating. In general, the repeated addition or subtraction of 0.01 is unlikely to yield exactly the same value that rgbToHsv() returned.
    Also you should be aware that 0.01 like most decimal fractions cannot be exactly represented in a double (it results in approximately 0.01000000000000000021).
    Moreover, the repeated calling of rgbToHsv() in the loop, recalculating hsv[] values based on the previous rgb[] values, makes the algorithm's behavior more difficult to predict.

    So, we have to allow for a tolerance of the HSV values for the comparisons, and better use only the hsv[] values for the iteration:

    void reach_color(struct color *start, double hsv_final[])
    {
      double hsv[3];
      byte rgb[3];
    
      converter.rgbToHsv(start->red, start->green, start->blue, hsv);
      while (true)
      {
             if (hsv[0] - 0.01 >= hsv_final[0]) hsv[0] -= 0.01;
        else if (hsv[0] + 0.01 <= hsv_final[0]) hsv[0] += 0.01;
        else if (hsv[1] - 0.01 >= hsv_final[1]) hsv[1] -= 0.01;
        else if (hsv[1] + 0.01 <= hsv_final[1]) hsv[1] += 0.01;
        else if (hsv[2] - 0.01 >= hsv_final[2]) hsv[2] -= 0.01;
        else if (hsv[2] + 0.01 <= hsv_final[2]) hsv[2] += 0.01;
        else return;
    
        converter.hsvToRgb(hsv[0], hsv[1], hsv[2], rgb);
        start->red   = rgb[0];
        start->green = rgb[1];
        start->blue  = rgb[2];
        display_color(*start);
        delay(30);
      }
    }