Arduino - Regenboog RGB Effect

Hoe zorg je ervoor dat je RGB lampje mooi vloeiend het hele kleurenspectrum bij langs gaat?

Na wat zoeken op internet vond ik veel vage, inefficiënte code, of erger nog, code die compleet het punt mist, zoals 3 genestelde loops voor R, G en B. (Dat werkt niet, noobs!)

Wat als we gewoon 1 for-loop konden gebruiken? Dan kom je al snel uit bij een HSV naar RGB conversie, maar omdat wij alleen om de "H" en niet om de "SV" geven kan het veel simpeler:

Elke kleur volgt een mooie ronde sinusgolf, en ze zijn allemaal 1/3e uit fase. Zo gaan de kleuren geleidelijk in elkaar op.

for (int i = 0; i < 360; ++i)
{
	analogWrite( RED, sin(i) );
	analogWrite( GREEN, sin((i+120)%360) );
	analogWrite( BLUE, sin((i+240)%360)) );
}

Probleem: sin gaat van 0 tot 2pi, en geeft waardes -1 tot 1. Dit kunnen we omrekenen naar resp. 0-360 en 0-255 maar dat zijn dure berekeningen vanwege het delen en vermenigvuldigen, en het gebruik van `floats`.

Het is veel slimmer om dan eerst een lookup table te genereren:

int steps = 360;
uint8_t sins[steps] = {0};

float pi = 3.141592653589793;
float conv = ((2.f*pi)/(float)steps);
float conv2 = (255.f/2.f);
for (int i = 0; i < steps; ++i)
{
	int s = (int)((1 + sin(i*conv))*conv2); // s voor sin

	sins[i] = s; // vul de table

	// print waardes
	if(i%36==0){
		printf("\n");
	}
	printf("%d,", s);
}

Hieruit volgt:

uint8_t sins[360] = {
	127,129,131,134,136,138,140,143,145,147,149,151,154,156,158,160,162,164,166,169,171,173,175,177,179,181,183,185,187,189,191,193,195,196,198,200,
	202,204,205,207,209,211,212,214,216,217,219,220,222,223,225,226,227,229,230,231,233,234,235,236,237,239,240,241,242,243,243,244,245,246,247,248,
	248,249,250,250,251,251,252,252,253,253,253,254,254,254,254,254,254,254,255,254,254,254,254,254,254,254,253,253,253,252,252,251,251,250,250,249,
	248,248,247,246,245,244,243,243,242,241,240,239,237,236,235,234,233,231,230,229,227,226,225,223,222,220,219,217,216,214,212,211,209,207,205,204,
	202,200,198,196,195,193,191,189,187,185,183,181,179,177,175,173,171,169,166,164,162,160,158,156,154,151,149,147,145,143,140,138,136,134,131,129,
	127,125,123,120,118,116,114,111,109,107,105,103,100,98,96,94,92,90,88,85,83,81,79,77,75,73,71,69,67,65,63,61,59,58,56,54,
	52,50,49,47,45,43,42,40,38,37,35,34,32,31,29,28,27,25,24,23,21,20,19,18,17,15,14,13,12,11,11,10,9,8,7,6,
	6,5,4,4,3,3,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,3,3,4,4,5,
	6,6,7,8,9,10,11,11,12,13,14,15,17,18,19,20,21,23,24,25,27,28,29,31,32,34,35,37,38,40,42,43,45,47,49,50,
	52,54,56,58,59,61,63,65,67,69,71,73,75,77,79,81,83,85,88,90,92,94,96,98,100,103,105,107,109,111,114,116,118,120,123,125
};

De volledige code wordt dan:

// r,g,b op PWM pins 9, 10, 11
#define RED 9
#define GREEN 10
#define BLUE 11
// de common anode gaat naar GND (of de common cathode naar 5V)

// onze sinus lookup table
uint8_t sins[360] = {
127,129,131,134,136,138,140,143,145,147,149,151,154,156,158,160,162,164,166,169,171,173,175,177,179,181,183,185,187,189,191,193,195,196,198,200,
202,204,205,207,209,211,212,214,216,217,219,220,222,223,225,226,227,229,230,231,233,234,235,236,237,239,240,241,242,243,243,244,245,246,247,248,
248,249,250,250,251,251,252,252,253,253,253,254,254,254,254,254,254,254,255,254,254,254,254,254,254,254,253,253,253,252,252,251,251,250,250,249,
248,248,247,246,245,244,243,243,242,241,240,239,237,236,235,234,233,231,230,229,227,226,225,223,222,220,219,217,216,214,212,211,209,207,205,204,
202,200,198,196,195,193,191,189,187,185,183,181,179,177,175,173,171,169,166,164,162,160,158,156,154,151,149,147,145,143,140,138,136,134,131,129,
127,125,123,120,118,116,114,111,109,107,105,103,100,98,96,94,92,90,88,85,83,81,79,77,75,73,71,69,67,65,63,61,59,58,56,54,
52,50,49,47,45,43,42,40,38,37,35,34,32,31,29,28,27,25,24,23,21,20,19,18,17,15,14,13,12,11,11,10,9,8,7,6,
6,5,4,4,3,3,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,3,3,4,4,5,
6,6,7,8,9,10,11,11,12,13,14,15,17,18,19,20,21,23,24,25,27,28,29,31,32,34,35,37,38,40,42,43,45,47,49,50,
52,54,56,58,59,61,63,65,67,69,71,73,75,77,79,81,83,85,88,90,92,94,96,98,100,103,105,107,109,111,114,116,118,120,123,125
};

void setup() {
	pinMode(RED, OUTPUT);
	pinMode(GREEN, OUTPUT);
	pinMode(BLUE, OUTPUT);
}

void loop () {
  for (int i = 0; i < 360; ++i)
  {
    analogWrite(RED, sins[i]);
    analogWrite(GREEN, sins[(i+120)%360]);
    analogWrite(BLUE, sins[(i+240)%360]);
    
    // deze code is zo snel dat de overgang sneller is dan het oog aan kan. We hebben dus een delay nodig.
    delay(5);
  }
}

En dít is hoe je met een arduino door de kleuren van een RGB ledje loopt.

Extra: vergeet niet de resistors. Om de waardes te berekenen gebruiken we:

Ohm's wet: I = U/R, oftewel R = U/I

Weerstand = (5V - spanningsval) / 0.02A

Zo krijgen we 160Ω voor rood, 120Ω voor groen en 90Ω voor blauw. Je kan beter omhoog afronden voor resistors. Je kan ook deze website gebruiken: ledcalculator.net

Eindproduct:

2020-07-27 in blog #programmeren #C #Arduino #RGB