Multi Function Shield mit Drehgeber zum Einstellen der Farbe einer RGB LED nutzen

Das Multi Function Shield, das ich ja bereits vorgestellt habe und das mittlerweile ja auch um einen Drehgeber erweitert wurde, bietet mit der Möglichkeit zur einfachen Anzeige von Daten auf seinem 4-fach-7-Segment-Display und den freien Anschlusspins eine gute Grundlage, Sensoren ausprobieren.

Diesmal wollen wir die Farbe einer RGB-LED frei einstellen. Dazu nutzen wir Tasten und Drehgeber, um den Rot-, Grün- und Blauanteil schnell und genau einzustellen.

Hardware: RGB und Mehrfarb-LEDs

LEDs gibt es seit den 1960ern Jahren, zuerst nur eher schwach leuchtend in rot, dann auch in rot, grün und orange. Damit waren die Ampelfarben vorhanden. Bis blau als Farbe für LEDs dazu kam, sollte es noch lange dauern. Schließlich kamen auch weiße LEDs dazu, deren Entwicklung immer noch nicht abgeschlossen ist und die immer heller und stromsparender werden (wichtig z. B. für Taschenlampen).

Neben den sichtbaren Farben, die es mittlerweile auch in Klarsichtgehäusen gibt (rot ist dann erst als rot zu erkennen, wenn die LED leuchtet) gibt es auch LEDs für den Infrarot und Ultraviolett-Bereich. Eine bekannte Anwendung für IR-LEDs sind die Fernseh-Fernbedienungen. Mit UV-LEDs kann man z. B. Sicherheitsmerkmale auf Banknoten sichtbar machen.

Wir beschäftigen uns heute mit LEDs, die mehr als eine Farbe in ihrem Gehäuse unterbringen und somit auch mehr als zwei Beinchen haben:

Allen Bauformen in der Liste haben eine gemeinsame Kathode (Minuspol). Pro Farbe wird jeweils eine separate Anode (Pluspol) benötigt. Daher haber die rot/grün-LEDs auch drei Beine, während die rot/grün/blau-LEDs derer vier haben.

Die SMD und die nackte Variante kommen ohne Vorwiderstände. Man kann einen einzigen Vorwiderstand (150 bis 200 Ω) an der GND-Leitung zwischenschalten. Das ist die Vorgehensweise für Faule (oder wenn es nur um einen Test geht). Für eine bessere Farbmischung sollten man jeder Farbe einen anderen Vorwiderstand gönnen: Sind alle drei Farbe mit der maximal-Spannung von 5V versorgt und gleichzeitig an, dann sollte sich ein weiß ergeben. Ist eine Farbe dominant (etwa einen Rotstich), dann muss der entsprechende Widerstand erhöht werden. Gegenfalls sollte man mit drei Potis (oder eben der hier vorgestellten Schaltung) experimentieren, um die richtigen Widerstaände für eine Farbechtheit herauszufinden.

Alternativ kann man das Mischverhältnis natürlich in der Software ausgleichen, etwa mit einem neuerWert=map(0,255,0,228);. Dann übernimmt das Programm die Funktion des Widerstands und lässt die LED weniger hell leuchten.

Software

Mit dem Programm sollen die einzelnen Farbwerte für rot, grün und blau in einer Abstufung von 0 bis 255 eingestellt werden, so wie man das von einem RGB-Farbwert gewohnt ist.

Mit dem Taster 1 wechselt man zwischen den Farben durch. Ein R, G oder B an erster Stelle im Display zeigt dies an. Dahinter folgt der Farbwert (0-255), den man in Einzelschritten mit dem Drehgeber einstellen kann oder in 50er-Schritten mit den Tastern 2 (minus) und 3 (plus). Für die Taster gibt es keinen Überlauf wie beim Drehgeber, sondern diese stoppen beim Maximum (255 bzw. 0).

Zuvor aber wird eine kleine Demo abgespielt, die für rot, grün und blau: 1. die Maximalhelligkeit anzeigt, 2. von ganz dunkel bis ganz hell stufenweise heller wird und dann wieder stufenweise dunkler. Danach folgen für jeweils 2 Sekunden ein paar Farbbeispiele mit Anzeige des Farbnamen des Beispiels.

Ich habe für die jeweilige Module ein kleines Video aufgenommen, da ein Bild (und erst recht ein Video) mehr zu sagen vermag als tausend Worte:

Modul KY-016 mit einer RGB-LED in 5 mm-Bauweise



Demo mit den Beispielfarben mit und ohne Diffusor, wobei die Farben stimmig sind. Die verbauten Vorwiderständen sind also richtig. Beispiel der Mischung eines Pink-Tons mi Tastern und Drehregler.

Modul KY-009 mit einer RGB-LED in SMD-Bauweise



Test mit einem gemeinsamen Vorwiderstand von 150 Ω an der gemeinsamen Kathode mit und ohne Diffusor. Beim Anschluss fiel auf, dass rot und grün bei der Anschlussbezeichnung vertauscht waren. Die Einzelfarben sind gut. Weiß hatten einen Gelbstich. Durch Herunterregeln von Rot auf 224 statt auf 255 wurde dies ausgeglichen.

"nackte" RGB-LED in 5mm-Bauform



Die Version ohne Modul (und dafür sehr viel günstiger) verhält sich von der Farbwiedergabe sehr ähnlich wie das SMD-Modul, was wohl wieder auf gemeinsamen Vorwiederstand liegt. Auch hier muss Rot auf ca. 228 heruntergeregelt werden.

Modul KY-011 mit einer rot/grün-LED in 5mm



Da es hier nur die Farben rot und grün gibt, passiert beim blau natürlich gar nichts. Rot oder grün sind aber dennoch einstellbar und so lassen sich Farbwerte aus Rot- und Grünanteilen zusammenmischen.

Source-Code

Wer das selbst nachbasteln möchte, hier ist der Source-Code für das gezeigte Programm:

//////////////////////////////////////////////////////// // (C) 2018 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include <TimerOne.h> // für Timings und Interrupts #include <MultiFuncShield.h> // API für das Multi Function Shield #define PinDrehClock 0 // durch MFS-Header vorgegeben #define PinDrehData 1 // durch MFS-Header vorgegeben #define SchrittweiteTaster 50 #define SchrittweiteGeber 1 #define PinR 5 #define PinG 6 #define PinB 9 void setup() { Timer1.initialize(); MFS.initialize(&Timer1); // initialize multi-function shield library pinMode (PinDrehClock, INPUT); pinMode (PinDrehData, INPUT); pinMode (PinR, OUTPUT); pinMode (PinG, OUTPUT); pinMode (PinB, OUTPUT); } void showRGB(byte R, byte G, byte B) { analogWrite (PinR, R); analogWrite (PinG, G); Timer1.pwm(PinB, B*4, 1000); // expects duty cycle to be 10 bit (1024) } void loop() { // Variablen für den Drehgeber byte links = digitalRead(PinDrehClock); byte rechts = digitalRead(PinDrehData); byte linksBefore = links; byte rechtsBefore = rechts; int drehung = 0; int position = 0; int pause = 1; byte farbe = 0; // rot=0 / grün=1 / blau=2 byte farbwert[3]={0,0,0}; // 0-255 für RGB char farbname[3]="RGB"; byte rgbLedPin[3] = {PinR, PinG, PinB}; // 3x4 Header, sind alle PWM char ausgabe[6]; byte btn=0; // warten auf Tastendruck MFS.beep(1,5,2); while (MFS.getButton() != BUTTON_1_PRESSED) delay (100); // DEMO //////////////////////////////////// MFS.write ("rot"); digitalWrite (PinR, 1); delay (1000); for (int i = 0; i < 256; i++) { analogWrite (PinR, i); delay (5); } for (int i = 255; i > -1; i--) { analogWrite (PinR, i); delay (5); } MFS.write ("grun"); digitalWrite (PinG, 1); delay (1000); for (int i = 0; i < 256; i++) { analogWrite (PinG, i); delay (5); } for (int i = 255; i > -1; i--) { analogWrite (PinG, i); delay (5); } // Sonderbehandlung für Pin9 (PinB/blau) MFS.write ("blau"); digitalWrite (PinB, 1); delay (1000); for (int i = 0; i < 1023; i+=4) { Timer1.pwm(PinB, i, 1000); // expects duty cycle to be 10 bit (1024) delay (5); } for (int i = 1023; i > -1; i-=4) { Timer1.pwm(PinB, i, 1000); // expects duty cycle to be 10 bit (1024) delay (5); } MFS.write("rosa"); showRGB(205,8,20); delay(2000); MFS.write("orng"); showRGB(255,8,0); delay(2000); MFS.write("gelb"); showRGB(255,100,0); delay(2000); MFS.write("pink"); showRGB(255,0,25); delay(2000); MFS.write("hell"); showRGB(255,255,255); delay(2000); MFS.write("warm"); showRGB(50,150,0); delay(2000); MFS.write("kalt"); showRGB(0,50,200); delay(2000); while (1) { // Buttons auswerten ///////////////////////////////////// btn = MFS.getButton(); if (btn == BUTTON_1_PRESSED) { MFS.beep(1); farbe++; if (farbe >2) farbe=0; } else if (btn == BUTTON_2_PRESSED) { MFS.beep(1); if (farbwert[farbe] - SchrittweiteTaster < 0) { farbwert[farbe] = 0; } else { farbwert[farbe] -= SchrittweiteTaster; } } else if (btn == BUTTON_3_PRESSED) { MFS.beep(1); if (farbwert[farbe] + SchrittweiteTaster > 255) { farbwert[farbe] = 255; } else { farbwert[farbe] += SchrittweiteTaster; } } // Drehgeber auswerten ////////////////////////////////// links = digitalRead(PinDrehClock); rechts = digitalRead(PinDrehData); if (links != linksBefore) { // rechtsdrehung // warten, bis anderer Pin nachgezogen while (digitalRead(PinDrehData) != digitalRead(PinDrehClock)) delay (pause); if (digitalRead(PinDrehClock) != links) { // nach Halbschritt wieder zurück gedreht drehung=0; } else { drehung = SchrittweiteGeber; } // MFS.beep(1); links = digitalRead(PinDrehClock); rechts = digitalRead(PinDrehData); linksBefore=links; rechtsBefore=links; } else if (rechts != rechtsBefore){ // linksdrehung // warten, bis anderer Pin nachgezogen while (digitalRead(PinDrehData) != digitalRead(PinDrehClock)) delay (pause); drehung=0; if (digitalRead(PinDrehData) != rechts) { // nach Halbschritt wieder zurück gedreht drehung=0; } else { drehung = -SchrittweiteGeber; } // MFS.beep (1); links = digitalRead(PinDrehClock); rechts = digitalRead(PinDrehData); linksBefore=rechts; rechtsBefore=rechts; } else { drehung = 0; } farbwert[farbe] += drehung; sprintf(ausgabe, "%c.%3d", farbname[farbe], farbwert[farbe]); MFS.write(ausgabe); analogWrite(rgbLedPin[0],farbwert[0]); analogWrite(rgbLedPin[1],farbwert[1]); // Pin 9 wird irgendwie durch die Timer1-Library vereinnahmt // analogWrite funtkioniert nicht richtig, macht Sprung bei 254 // also benutzen wir die Timer1-Funktion fürs PWM für Pin 9 // dieser erwartet einen Duty von 1023 und nicht 255 Timer1.pwm(rgbLedPin[2], farbwert[2]*4, 1000); delay(pause); } } Bei der Entwicklung hatte ich ein seltsames Phänomen: die blaue LED zeigte ein seltsamen Verhalten bei Verwendung von analogWrite(). Von 0 bis 254 wurde stufenlos ein Farbwert eingestellt, der etwa der Hälfte des übergebenen Wertes entsprach. Bei Wechsel von 254 auf 255 wurde die LED plötzlich doppelt so hell wie zuvor.

Das PWM funktionierte allerdings mit einem Testprogramm auf Pin 9 ohne Multifunction-Shield und dessen Libraries einwandfrei und ohne "Helligkeitsexplosion". Also musste es in der MFS oder TimerOne-Library liegen.

Nachdem ich ein wenig im Source-Code der TimerOne-Library recherchiert hatte, um dem Fehler auf die Schliche zu kommen, fiel mir dort die Funktion Timer1.pwm() auf, die scheinbar das gleiche tut wie ein analogWrite(), nur wohl über den Hardware-Timer. Also habe ich kurzerhand diese Funktion für den Pin 9 benutzt, was gut funktioniert. Einzige Unterschied ist, dass Timer1.pwm() 1023 statt 255 steps erwartet. Darum wird der Farbwert vervierfacht.

Die Verwendung von Timer1.pwm() scheint keine negativen Auswirkungen auf die anderen Funktionen der MFS-Library zu haben.