Infrarot LED über Android ColorPicker steuern

Hallo,

im zweiten Beitrag zu der LIRC Reihe, habe ich ein Youtube Video verlinkt, welches kurz gezeigt hat wie ich mein LED Streifen über eine Android App steuere. Damals hatte ich einfach die originale Fernbedienung in Android abgebildet und Button für Button nachgebaut. Vor kurzem aber habe ich das Projekt von LarsWekrmann auf GitHub entdeckt. Sein „HoloColorPicker“ sieht deutlich moderner aus, als das was man bei mir im Video sieht. Kurzerhand habe ich den ColorPicker bei mir in die App eingebaut. Hier zeige ich euch wie ich vorgegangen bin.


Den ColorPicker in Eclipse einbinden

Da ich trotz Android Studio 2.0 immer noch unter Eclipse arbeite, konnte ich sein „gradle“ Projekt nicht ohne weiteres einbinden. Unter Eclipse gibt es genug Plug-ins die „gradle“ Projekte unterstützen. Da wir aber sein Projekt als eine Art Bibliothek in das eigene Projekt übernehmen müssen, habe ich es ganz schnell manuell erledigt.

  • Das „HoloColorPicker“ Projekt als ZIP herunterladen (Download)
  • Im eigenem Android Projekt ein neues Package erstellen „com.larswerkman.holocolorpicker“
  • Alle Klassen aus
    HoloColorPicker-master\libary\src\main\java\com\larswerkman\holocolorpicker
    in das neu erstellte Package kopieren (die Fehler erstmal ignorieren)
  • Beide .xml Dateien (attrs.xml und dimens.xml) unter /res/values im eigenem Projekt entweder kopieren oder mit dem Inhalt aus
    „HoloColorPicker-master\libary\src\main\res\values“ erweitern (Im Normalfall muss die Datei dimens.xml ergänzt und die attrs.xml kopiert werden)
  • In jeder HoloColorPicker Klasse nun den Pfad für die Ressourcen anpassen.
    Von „com.larswerkman.holocolorpicker.R;“ auf das eigene Package zB. „de.bobeksoftware.RaspIR.R;“

Das ist alles. Nun können wir mit der Programmierung anfangen.


Layout anpassen

Da ich bereits ein Layout für meine LED Activity hatte, musste ich nur die Buttons für die Farben durch den ColorPicker ersetzen. Dafür kopiert man folgendes in die Layout xml Datei hinein:

<com.larswerkman.holocolorpicker.ColorPicker
    android:id="@+id/picker"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

Wie ihr das eigene Layout im Endeffekt anpasst ist mir völlig egal. Ich habe mich für fünf Buttons entschieden (ON/OFF/LIGHT UP/LIGHT DOWN/WHITE) und den eigentlichen ColorPicker. Dieser bietet zusätzlich noch drei Slider zum einbinden (Saturation/Opacity/Value). Da ich aber über Infrarot keine Möglichkeit habe die Deckkraft oder die Sättigung einzustellen, binde ich die „bars“ überhaupt nicht mit ein. Zusätzlich wollte ich die weiße Farbe irgendwie im ColorPicker einstellen können. Dafür habe ich ein Button mit einem transparenten Hintergrund über die mittlere Anzeige des ColorPickers gelegt. Die Transparenz des Buttons lässt sich über „android:background=“@android:color/transparent“ in der XML zuweisen. Durch ein langes halten des Buttons wird der Befehl für Weiß versendet. Mein Layout sieht nun so aus (in der Mitte ist der Button für Weiß markiert):

layout_led


Klasse erstellen/ergänzen

Ich werde hier nur die Programmierung des ColorPickers behandeln, besser ausgedrückt die Berechnung der Farben. Wie man Infrarot Befehle versendet und Buttons in die Activity einbindet habe ich in anderen Beiträgen zuvor ausführlich behandelt. Zuerst erstellen wir ein ColorPicker und ein Button (Weiß) Objekt und weisen ihnen in der Methode onCreate die richtigen Ressourcen zu.

public class LedActivity extends Activity{
  
  private ColorPicker picker;
  private Button btn_white;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    picker = (ColorPicker) findViewById(R.id.picker);
    btn_white = (Button) findViewById(R.id.btn_led_white);
  }
}

Kümmern wir uns doch erstmal ganz schnell um den Button für Weiß. Diesem lassen wir ein „onLongClickListener“ zuweisen. In diesem versenden wir dann den Befehl für Weiß über Infrarot und setzen die Farbe des mittleren Kreises vom ColorPicker auf diese. Die mittlere Farbe lässt sich über die Methode „setNewCenterColor“ setzen. Den Wert für Weiß holen wir uns von der Konstante aus der Android Klasse „Color“.

public class LedActivity extends Activity implements OnLongClickListener{
  
  private ColorPicker picker;
  private Button btn_white;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    picker = (ColorPicker) findViewById(R.id.picker);
    btn_white = (Button) findViewById(R.id.btn_led_white);
    
    picker.setShowOldCenterColor(false);
    
    btn_white.setOnLongClickListener(this);
  }

  @Override
  public boolean onLongClick(View v) {
    if (v.getId() == R.id.btn_led_white){
      
      //Versende Befehl über Infrarot (lies dazu die anderen Beiträge)
      
      picker.setNewCenterColor(Color.WHITE);
      return true;
    }
    return false;
  }
}

Standardmäßig wird beim ColorPicker beim Auswahl der Farbe der mittlere Kreis halbiert. Die alte zuvor ausgewählte Farbe wird auf der linken Seite angezeigt, während man auf der rechten Seite die aktuelle Farbe sieht. Dieses Verhalten lässt sich über diese Funktion deaktivieren.

picker.setShowOldCenterColor(false);

Der ColorPicker verfügt über zwei Listener. Der „OnColorSelectedListener“ wird immer ausgeführt wenn man den Ring loslässt. Der „OnColorChangedListener“ wird immer ausgeführt wenn sich die Farbe ändert. Eigentlich ist es kein großer Unterschied welchen Listener wird dem Objekt zuweisen. Wir werden einfach mit dem „OnColorSelectedListener“ arbeiten.

public class LedActivity extends Activity implements OnLongClickListener, OnColorSelectedListener{
  
  private ColorPicker picker;
  private Button btn_white;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    picker = (ColorPicker) findViewById(R.id.picker);
    btn_white = (Button) findViewById(R.id.btn_led_white);
    
    picker.setShowOldCenterColor(false);
    
    btn_white.setOnLongClickListener(this);
    picker.setOnColorSelectedListener(this);
  }

  @Override
  public boolean onLongClick(View v) {
    if (v.getId() == R.id.btn_led_white){
      
      //Versende Befehl über Infrarot (lies dazu die anderen Beiträge)
      
      picker.setNewCenterColor(Color.WHITE);
      return true;
    }
    return false;
  }

  @Override
  public void onColorSelected(int color) {
    
  }
}

Der Listener liefert uns die gesetzte Farbe als Integer zurück. Jetzt stellt sich natürlich die Frage, wie man den Bereich für die jeweilige Farbe des Ringes einem Fest zugeordnetem Wert der LED Fernbedienung zuweist. Meine Fernbedienung hat fest definierte Farben und nur diese kann ich auch über Infrarot auswählen. Das bedeutet ich muss ein Bereich innerhalb des ColorPicker Kreises einer fest definierten Farbe der Fernbedienung zuweisen. Hier zum Bespiel für die Farbe Rot:

fernbedienung-colorpciker

Da wir keinen flüssigen Verlauf der Farben mit der Fernbedienung abbilden können, müssen wir innerhalb des Ringes Bereiche „von“ „bis“ für jede einzelne Farbe festlegen. Der Integer „color“ aus dem Listener liefert uns die Farbe im ARGB-Farbraum Format zurück. Das heißt der Wert ist eine Mischung aus der Deckkraft und der Farben Rot, Grün und Blau. Das bringt uns leider nichts, weil wir hier ein festen Bereich ohne weiteres nicht festlegen können. Hier hilft uns aber der HSV-Farbraum (Farbwert H, Sättigung S, Dunkelstufe V).

HSV_cone

Über die Grafik lässt sich schnell entnehmen, dass wir hier den Wert H (Farbwert) benötigen. Dieser gibt uns nichts anderes zurück als Werte von 0 bis 360 für den jeweiligen Winkel des Kreises, wobei ab 0° Rot, 120° Grün und 240° Blau anfängt. Für die Umwandlung zu einem HSV Wert benötigen wir daher ein Array vom Typ float, welcher die Werte für H(ue), S(aturation) und V(alue) abspeichert. Über die statische Methode „RGBtoHSV“ aus der Klasse Color können wir den Farbraum in das Array speichern. Die einzelnen Werte für die Farben Rot, Grün und Blau lassen sich aus dem Integer über die einzelnen Methoden (Color.red, Color.green, Color.blue) auslesen.

@Override
  public void onColorSelected(int color) {
    float[] hsv = new float[3];
    Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), hsv);
    
    Log.i("ColorHSV","SelectedColor:" + String.valueOf(hsv[0]));
  }

Jetzt kommt die eigentliche Arbeit, da wir für jede Farbe der Fernbedienung einen H(SV) Bereich von bis festlegen müssen. Dazu führt ihr einfach die APP aus und schreibt die Werte die euch das LogCat ausgibt für die jeweilige Farbe auf. Das heißt ihr fängt zum Beispiel mit Rot an (bei mir sind es zum Beispiel die Werte von 340° bis 13°) und geht jede einzelne Farbe ab. Merkt euch das der „von-Wert“ ja immer der „bis-Wert“ der letzten Farbe ist, damit keine Lücken im Kreis entstehen. Wenn alle Bereiche festgelegt worden sind, kann man über eine Hilfsmethode den richtigen Infrarot Befehl zuweisen.

@Override
  public void onColorSelected(int color) {
    float[] hsv = new float[3];
    Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), hsv);
    
    //Log.i("ColorHSV","SelectedColor:" + String.valueOf(hsv[0]));
    
    String selColor = getLED(hsv[0]);
    //Versende selColor über Infrarot
  }
  
  public String getLED(float hue){
    //RED
    if (hue >= 340 || hue < 13) return "R1";
    if (hue >=13 && hue < 20) return "R2";
    ...
    //GREEN
    if (hue >=80 && hue < 153) return "G1";
    if (hue >=153 && hue < 165) return "G2";
    ...
    //BLUE
    if (hue >=212 && hue < 254) return "B1";
    if (hue >=254 && hue < 275) return "B2";
    ...
    if (hue >=296 && hue < 340) return "B5";
    return "";
  }

Am Ende habe ich euch ein aktualisiertes Video hochgeladen (habe im Video meine LED Birne benutzt die mit der gleichen Fernbedienung funktioniert).

Viel Spaß beim basteln.

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.