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:
- Modul KY-016 mit einer RGB-LED in 5 mm-Bauweise inkl. Vorwiderstände
- Modul KY-009 mit einer RGB-LED in SMD-Bauweise ohne Vorwiderstände
- "nackte" RGB-LED in 5mm-Bauform
- Modul KY-011 mit einer rot/grün-LED in 5mm bzw. als KY-029 in 3 mm
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:
- rot: 180-200 Ω
- grün: 100 Ω
- blau: 100 Ω
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
Modul KY-009 mit einer RGB-LED in SMD-Bauweise
"nackte" RGB-LED in 5mm-Bauform
Modul KY-011 mit einer rot/grün-LED in 5mm
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.