APDS-9960 – das Multitalent

Über den Beitrag

Dieser Beitrag über den APDS-9960, genauer gesagt das APDS-9960 Modul, ist der erste in einer Reihe über Abstands-, Bewegungs-, Farb- und Lichtsensoren. Der APDS-9960 ist unter diesen Sensoren sozusagen das Schweizer Taschenmesser, denn er bietet:

  • Gestenerkennung
  • Farberkennung (RGB)
  • Abstandsmessung
  • Umgebungslichtmessung

Über die Abstandsmessung ist der APDS-9960 auch als Bewegungssensor einsetzbar, allerdings nur innerhalb seines begrenzten Arbeitsbereiches von bis zu ca. 30 cm. Das folgende Video gibt einen Überblick über die verschiedenen Features. 

Der APDS-9960 besitzt eine erstaunliche Zahl an Einstellmöglichkeiten und viele Register. Das in einem Sketch zu implementieren, ist ein erheblicher Aufwand. Deshalb bietet es sich an auf eine fertige Bibliothek zurückzugreifen die schon mal eine Reihe von Voreinstellungen übernimmt. Ich werde in diesem Beitrag die Bibliothek von Sparkfun nutzen. Die Bibliothek von Adafruit habe ich auch ausprobiert und sie funktioniert ebenso problemlos. Es sind aber noch weitere auf Github erhältlich.

Auch wenn Ihr eine dieser Bibliotheken benutzt, würde ich trotzdem dazu raten mal ein wenig in das Datenblatt des APDS-9960 zu schauen und sich mit der Bibliothek selbst auseinanderzusetzen, denn nicht alles was das Teil kann ist auch in den Beispielen dokumentiert. Darüber hinaus könnte es sich für die eine oder andere Anwendung anbieten, nicht einfach kritiklos die Voreinstellungen zu übernehmen.  

Grundsätzliches über den APDS-9960

Der APDS-9960 Sensor

Der eigentliche APDS-9960 Sensor ist der kleine rechteckige, schwarze Baustein mit den zwei linsenähnlichen Öffnungen. Das Datenblatt dazu ist zum Beispiel hier erhältlich. Es lohnt sich aus meiner Sicht auf jeden Fall da mal hineinzuschauen. 

Der eigentliche APDS-9960 Sensor
Der APDS-9960 Sensor

Der APDS-9960 besitzt eine IR-LED, deren reflektiertes Licht für die Gesten- und Abstandsdetektion verwendet wird. Da er über vier gerichtete Photodioden verfügt, ist er in der Lage einfache Bewegungen zu detektieren. Diese Photodioden dienen auch der Abstandsmessung. Weitere Photodioden messen das rote, grüne, blaue und weiße Licht für die Farb- und Umgebungslichtmessung. 

Die Kommunikation mit dem APDS-9960 erfolgt über I2C mit einer Taktrate bis 400 kHz. Dabei ist die Adresse mit 0x39 festgelegt und kann nicht geändert werden. Das Auslesen der I2C Adresse ist immer ein guter erster Schritt um zu sehen ob man alles richtig verkabelt hat. Dafür bietet sich ein I2C Scanner Sketch an.  

Das APDS-9960 Modul

Zwei APDS-9960 Module
Zwei APDS-9960 Module

Theoretisch kann man natürlich den blanken Sensor kaufen und die notwendige Elektronik drumherum selbst zusammenlöten. Meine Fertigkeiten übersteigt das jedoch und deshalb bin ich froh, dass es den APDS-9960 als Modul gibt. 

Solche Module kann man z.B. bei Amazon interessanterweise in zwei Preisklassen kaufen. Die eine liegt zwischen drei und zehn Euro und umfasst im Wesentlichen Hersteller aus China, die andere liegt bei Anfang-Mitte zwanzig, z.B. von Sparkfun. Ob der Preisunterschied gerechtfertigt ist kann ich nicht sagen, da ich nur einige der Billigheimer ausprobiert habe. Ich kann aber zumindest bestätigen, dass letztere Probleme mit der Verstärkung haben (dazu später mehr). Das wird auch an anderer Stelle, z.B. hier, beschrieben. 

Die meisten APDS-9960 Module haben die folgenden Pins:

  • GND – Ground
  • VCC – Spannungsversorgung: 2.4 – 3.6 Volt
  • SDA/SCL – I2C Anschlüsse
  • INT – low aktiver Interrupt
  • VL – Spannungsversorgung für die IR-LED: 3.0 – 4.5 Volt

Man kann den APDS-9960 über den 3.3 Volt Ausgang eines Arduino UNO versorgen, hat dann aber immer noch das Problem, dass die I2C Kommunikationsleitungen auf 5 Volt laufen. Das kann den APDS-9960 beschädigen. Deswegen nimmt man entweder einen Microcontroller der mit 3.3 Volt betrieben wird, wie z.B. den ESP-8266, man baut Spannungsteiler in die Leitungen oder man nimmt Levelkonverter, wie z.B. diesen hier.

Auf den meisten Modulen gibt es zwei Jumper und zwar  „PS“ und „I2C PU“:

  • PS – ist der Kontakt geschlossen, dann wird die IR-LED über die Spannung an VCC versorgt. Ist der Kontakt offen, so muss die LED separat über VL versorgt werden. Es kann Sinn machen PS offen zu lassen und die LED über VL an- und auszuschalten. Die IR-LED kann laut Datenblatt die Farb- und Lichtmessung beeinflussen obwohl die Sensoren einen IR-Filter besitzen.
  • I2C PU – ist der Kontakt geschlossen, dann werden die I2C Leitungen mit einem Pull-Up Widerstand verbunden, was ziemlich praktisch ist. 

Wie man oben auf dem Foto mit den zwei APDS-9960 Modulen erkennen kann, hat das eine die Jumper geschlossen, bei dem anderen sind sie offen (beide so gekauft). Also schaut, welche Version ihr habt. 

Schaltung: APDS-9960 Modul am Arduino UNO

Die Beschaltung ist aufgrund der vorherigen Erklärungen keine Überraschung. 

APDS 9960 Schaltung am Arduino UNO
Beschaltung APDS 9960 am Arduino UNO

Ob VL – wie abgebildet – wirklich mit 3.3 V verbunden werden muss, hängt wie gesagt vom Zustand des Jumpers PS ab. Die I2C Leitungen haben keinen externen Pull-up, da der Jumper I2C PU bei mir geschlossen war. Die LED an D13 wird für einige Beispielsketche benötigt. Das gleiche gilt für den Interrupt an D2. Der Levelkonverter braucht eine Spannungsversorgung zumindest auf der 5 Volt Seite. 

Die Sparkfun Bibliothek für den APDS-9960

Die Bibliothek ist hier auf Github erhältlich. Wie gewohnt holt man sich die Zip Datei und entpackt sie im library Ordner. Dort findet man dann auch die Beispielsketche die ich gleich vorstelle. 

Gestendetektion

Die Bibliothek hat die folgenden Gesten implementiert:

  • Up/Down
  • Right/Left
  • Far/Near
  • None 

Die ersten beiden Paare sind selbsterklärend. Ein „Far“ wird gemeldet wenn man die Hand sehr nah an den Sensor hält und dann senkrecht dazu entfernt bis sie sich außerhalb des Erfassungsbereiches (ca. 30 cm) befindet. Für ein „Near“ nähert man sich senkrecht und zieht die Hand dann zur Seite weg. Das „Near“ wird erst gemeldet, wenn die Hand weggezogen wurde. Ein „None“ wird bei uneindeutigen Bewegungen gemeldet. 

Bibliotheksintern werden die Richtungen als DIR_UP, DIR_DOWN, DIR_RIGHT, usw. bezeichnet. 

Der mitgelieferte Beispielsketch für die Gestendetektion heißt GestureTest.ino. Ich habe hier die allgemeinen Kommentare aus dem Sketchkopf entfernt. Dann habe ich ihm lediglich noch die wichtige (!) Zeile 34 apds.setGestureGain(GGAIN_1X) spendiert. Dazu gleich mehr. Ansonsten ist der Sketch aus meiner Sicht selbsterklärend.  

#include <Wire.h>
#include <SparkFun_APDS9960.h>

// Pins
#define APDS9960_INT    2 // Needs to be an interrupt pin

// Constants

// Global Variables
SparkFun_APDS9960 apds = SparkFun_APDS9960();
int isr_flag = 0;

void setup() {

  // Set interrupt pin as input
  pinMode(APDS9960_INT, INPUT);

  // Initialize Serial port
  Serial.begin(9600);
  Serial.println();
  Serial.println(F("--------------------------------"));
  Serial.println(F("SparkFun APDS-9960 - GestureTest"));
  Serial.println(F("--------------------------------"));
  
  // Initialize interrupt service routine
  attachInterrupt(0, interruptRoutine, FALLING);

  // Initialize APDS-9960 (configure I2C and initial values)
  if ( apds.init() ) {
    Serial.println(F("APDS-9960 initialization complete"));
  } else {
    Serial.println(F("Something went wrong during APDS-9960 init!"));
  }
  apds.setGestureGain(GGAIN_1X); // ohne diese Zeile geht es nicht zuverlässig
  
  // Start running the APDS-9960 gesture sensor engine
  if ( apds.enableGestureSensor(true) ) {
    Serial.println(F("Gesture sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during gesture sensor init!"));
  }
}

void loop() {
  if( isr_flag == 1 ) {
    detachInterrupt(0);
    handleGesture();
    isr_flag = 0;
    attachInterrupt(0, interruptRoutine, FALLING);
  }
}

void interruptRoutine() {
  isr_flag = 1;
}

void handleGesture() {
    if ( apds.isGestureAvailable() ) {
    switch ( apds.readGesture() ) {
      case DIR_UP:
        Serial.println("UP");
        break;
      case DIR_DOWN:
        Serial.println("DOWN");
        break;
      case DIR_LEFT:
        Serial.println("LEFT");
        break;
      case DIR_RIGHT:
        Serial.println("RIGHT");
        break;
      case DIR_NEAR:
        Serial.println("NEAR");
        break;
      case DIR_FAR:
        Serial.println("FAR");
        break;
      default:
        Serial.println("NONE");
    }
  }
}

 

Das Problem mit der Verstärkung

Der APDS-9960 hat für die Gestendetektion die Verstärkungsfaktoren 1, 2, 4 und 8 vorgesehen, die die Empfindlichkeit der Messung steuern. Bibliotheksintern werden diese als GGAIN_1X, GGAIN_2X, usw. bezeichnet. Bei der Initialisierung (apds.init()) wird DEFAULT_GAIN (= GGAIN_4x) in das entsprechend Kontrollregister eingetragen. Leider kommen die Module mit diesem Verstärkungsfaktor nicht klar. Das gilt zumindest die preisgünstigen Teile, ich aus drei verschiedenen chinesischen Quellen bezogen habe. Mit acht- und vierfacher Verstärkung ging es gar nicht, mit zweifacher Verstärkung funktionierte es unzuverlässig, mit einfacher Verstärkung hingegen ging es wunderbar. Es ist auch kein Fehler der Bibliothek, da die Adafruit Bibliothek dasselbe Verhalten zeigte. Das ganze hat mich viel Zeit und Nerven gekostet. 

Wenn man übrigens die GGAIN Einstellung erst nach apds.enableGestureSensor vornimmt, dann erscheint beim Starten des Sketches die Gestenmeldung „None“, ohne das man irgendetwas getan hätte. Also besser erst die Verstärkung einstellen und dann die Gestendetektion einschalten.  

Andere Einstellungen der Gestendetektion

Wie schon erwähnt, ist man von den Einstelloptionen beim Blick ins Datenblatt zunächst erschlagen. Aber abgesehen von dem GGAIN Problem funktionieren die anderen von der Bibliothek gewählten Voreinstellungen sehr gut. Trotzdem möchtet ihr vielleicht auch mit ihnen mal „herumspielen“. Allerdings sind nicht alle Parameter über öffentliche Funktionen zugänglich. 

  •  IR-LED Strom: setGestureLEDDrive;
    • durch den Strom wird die Reichweite der Gestendetektion gesteuert 
    •  möglich sind 12.5, 25, 50 und 100 mA
      • LED_DRIVE_12_5MA, LED_DRIVE_25_MA, usw.
    •  mit 100 mA (Voreinstellung) liegt man bei 30 cm Reichweite und erreicht bei ca. 5cm Abstand den maximalen Proximity Wert
    • da die IR-LED pulst, ist der effektive Stromverbrauch viel geringer als der IR-LED Strom
  • IR_LED Boost: setLEDBoost 
    • private Funktion, nicht direkt zugänglich
    • um den Wert zu ändern müsstet ihr in die Funktion enableGestureSensor in Sparkfun_APDS9960.cpp gehen und dort den setLEDBoost Aufruf editieren oder eine öffentliche Funktion daraus machen
    • Default Wert ist 300, das ist schon das Maximum
    • für weitere Details bitte ins Datenblatt schauen 
  • Pulslänge/Pulscount der IR-LED:
    • nicht als Funktion implementiert
    • variierbar durch Veränderung von DEFAULT_GESTURE_PPULSE in Sparkfun_APDS9960.h
  • Gesture Waiting Time: setGestureWaitTime
    • kontrolliert die Zeit im Low Power Mode zwischen zwei Gestendetektionszyklen
    • 8 Werte zwischen 0 und 39.2 ms sind möglich; die Parameter in der Bibliothek dazu heißen: GWTIME_0MS bis GWTIME_39_2MS; default: 2.8 ms
  • plus einiges mehr -> wenn ihr wirklich mehr wissen wollt: schaut in die Bibliothek und ins Datenblatt. 

Näherungsdetektion

Der Näherungssensor liefert keine Abstände in Zenti- oder Millimeter, sondern einen Wert zwischen 0 und 255. Der korrespondierende Abstand zu diesem Wert hängt unter anderem vom LED Strom und der Verstärkung ab. Der mitgelieferte Beispielsketch heißt ProximitySensor.ino. Hier ist er, allerdings ohne die einleitenden Kommentarzeilen und mit einer kleinen Veränderung auf die ich gleich noch eingehen werde: 

#include <Wire.h>
#include <SparkFun_APDS9960.h>

// Global Variables
SparkFun_APDS9960 apds = SparkFun_APDS9960();
uint8_t proximity_data = 0;

void setup() {
  
  // Initialize Serial port
  Serial.begin(9600);
  Serial.println();
  Serial.println(F("------------------------------------"));
  Serial.println(F("SparkFun APDS-9960 - ProximitySensor"));
  Serial.println(F("------------------------------------"));
  
  // Initialize APDS-9960 (configure I2C and initial values)
  if ( apds.init() ) {
    Serial.println(F("APDS-9960 initialization complete"));
  } else {
    Serial.println(F("Something went wrong during APDS-9960 init!"));
  }
  
  // Start running the APDS-9960 proximity sensor (no interrupts)
  if ( apds.enableProximitySensor(false) ) {
    Serial.println(F("Proximity sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during sensor init!"));
  }
  // Adjust the Proximity sensor gain
  if ( !apds.setProximityGain(PGAIN_2X) ) { // muss nach enableProximitySensor aufgerufen werden
    Serial.println(F("Something went wrong trying to set PGAIN"));
  }
}


void loop() {
  
  // Read the proximity value
  if ( !apds.readProximity(proximity_data) ) {
    Serial.println("Error reading proximity value");
  } else {
    Serial.print("Proximity: ");
    Serial.println(proximity_data);
  }
  
  // Wait 250 ms before next reading
  delay(250);
}

 

Die Veränderung die ich vorgenommen habe, ist das Vertauschen der Reihenfolge der Funktionsaufrufe apds.enableProximitySensor und apds.setProximityGain. Letzterer setzt den Verstärkungsfaktor (PGAIN_1X, …_2X, …4X, …_8X) für den Näherungssensor. Allerdings wird in der enable Funktion der Default Wert gesetzt, nämlich PGAIN_4X. Will man ihn verändern, muss man das also nach der enable Funktion tun. Ich habe diesen kleinen Bug auf Github als „issue“ gemeldet, vielleicht ist er ja schon behoben wenn ihr die Bibliothek installiert.

Andere Einstellungen der Näherungsdetektion

Die Einstellungen ähneln sehr den Einstellungen der Gestendetektion:

  • IR-LED Strom: apds.LEDDrive; 
    • 12.5, 25, 50 und 100 mA sind wählbar
  • IR-LED Boost: identisch mit IR-LED Boost bei der Gestendetektion

Weiter will ich das nicht ausführen – wieder mit dem Verweis auf Bibliothek und Datenblatt für diejenigen, die tiefer einsteigen wollen.

Näherungsdetektion mit Interrupt

Man kann ein oberes und ein unteres Limit für den Proximity Wert setzen, bei dem ein Interrupt ausgelöst wird. Setzt man als unteres Limit (PROX_INT_LOW) die 0, dann wird nur ein Nah-Interrupt ausgelöst, da 0 aufgrund des Rauschens nie erreicht wird.

Der mitgelieferte Beispielsketch dazu heißt ProximityInterrupt.ino. Ich habe hier wieder die Funktion für die Einstellung des PGAIN Wertes hinter die enable Funktion gesetzt, da sie sonst wirkungslos wäre. Ansonsten sollte der Sketch selbsterklärend sein. 

#include <Wire.h>
#include <SparkFun_APDS9960.h>

// Pins
#define APDS9960_INT    2  // Needs to be an interrupt pin
#define LED_PIN         13 // LED for showing interrupt

// Constants
#define PROX_INT_HIGH   200 // Proximity level for interrupt
#define PROX_INT_LOW    0  // No far interrupt

// Global variables
SparkFun_APDS9960 apds = SparkFun_APDS9960();
uint8_t proximity_data = 0;
int isr_flag = 0;

void setup() {
  
  // Set LED as output
  pinMode(LED_PIN, OUTPUT);
  pinMode(APDS9960_INT, INPUT);
  
  // Initialize Serial port
  Serial.begin(9600);
  Serial.println();
  Serial.println(F("---------------------------------------"));
  Serial.println(F("SparkFun APDS-9960 - ProximityInterrupt"));
  Serial.println(F("---------------------------------------"));
  
  // Initialize interrupt service routine
  attachInterrupt(0, interruptRoutine, FALLING);
  
  // Initialize APDS-9960 (configure I2C and initial values)
  if ( apds.init() ) {
    Serial.println(F("APDS-9960 initialization complete"));
  } else {
    Serial.println(F("Something went wrong during APDS-9960 init!"));
  }
  
   // Set proximity interrupt thresholds
  if ( !apds.setProximityIntLowThreshold(PROX_INT_LOW) ) {
    Serial.println(F("Error writing low threshold"));
  }
  if ( !apds.setProximityIntHighThreshold(PROX_INT_HIGH) ) {
    Serial.println(F("Error writing high threshold"));
  }
  
  // Start running the APDS-9960 proximity sensor (interrupts)
  if ( apds.enableProximitySensor(true) ) {
    Serial.println(F("Proximity sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during sensor init!"));
  }
   // Adjust the Proximity sensor gain
  if ( !apds.setProximityGain(PGAIN_4X) ) {
    Serial.println(F("Something went wrong trying to set PGAIN"));
  }
}

void loop() {
  
  // If interrupt occurs, print out the proximity level
  if ( isr_flag == 1 ) {
  
    // Read proximity level and print it out
    if ( !apds.readProximity(proximity_data) ) {
      Serial.println("Error reading proximity value");
    } else {
      Serial.print("Proximity detected! Level: ");
      Serial.println(proximity_data);
    }
    
    // Turn on LED for a half a second
    digitalWrite(LED_PIN, HIGH);
    delay(500);
    digitalWrite(LED_PIN, LOW);
    
    // Reset flag and clear APDS-9960 interrupt (IMPORTANT!)
    isr_flag = 0;
    if ( !apds.clearProximityInt() ) {
      Serial.println("Error clearing interrupt");
    }
    
  }
}

void interruptRoutine() {
  isr_flag = 1;
}

 

Setzt Ihr einen Wert für  PROX_INT_LOW oberhalb des Grundrauschens, z.B. 50, dann habt ihr zwei Grenzen, bei denen ein Interrupt ausgelöst wird. 

Umgebungslicht- und Farbmessung

Umgebungslicht- und Farbmessung lassen sich gut zusammen abhandeln. Viel zu erklären gibt es eigentlich auch nicht. Der Beispielsketch AmbientLightInterrupt.ino umfasst die wesentlichen Funktionen einschließlich Interrupts. Die Stärke des Umgebungslichtes und der Farbkanäle wird jeweils als dimensionslose 16 Bit Zahl geliefert. Eine Umrechnung in Lux o. ä. gibt es nicht. Für das Umgebungslicht können zwei Interruptlimits gesetzt werden. 

#include <Wire.h>
#include <SparkFun_APDS9960.h>

// Pins
#define APDS9960_INT    2  // Needs to be an interrupt pin
#define LED_PIN         13 // LED for showing interrupt

// Constants
#define LIGHT_INT_HIGH  800 // High light level for interrupt
#define LIGHT_INT_LOW   100   // Low light level for interrupt

// Global variables
SparkFun_APDS9960 apds = SparkFun_APDS9960();
uint16_t ambient_light = 0;
uint16_t red_light = 0;
uint16_t green_light = 0;
uint16_t blue_light = 0;
int isr_flag = 0;
uint16_t threshold = 0;

void setup() {
  
  // Set LED as output
  pinMode(LED_PIN, OUTPUT);
  pinMode(APDS9960_INT, INPUT);
  
  // Initialize Serial port
  Serial.begin(9600);
  Serial.println();
  Serial.println(F("-------------------------------------"));
  Serial.println(F("SparkFun APDS-9960 - Light Interrupts"));
  Serial.println(F("-------------------------------------"));
  
  // Initialize interrupt service routine
  attachInterrupt(0, interruptRoutine, FALLING);
  
  // Initialize APDS-9960 (configure I2C and initial values)
  if ( apds.init() ) {
    Serial.println(F("APDS-9960 initialization complete"));
  } else {
    Serial.println(F("Something went wrong during APDS-9960 init!"));
  }
  
  // Set high and low interrupt thresholds
  if ( !apds.setLightIntLowThreshold(LIGHT_INT_LOW) ) {
    Serial.println(F("Error writing low threshold"));
  }
  if ( !apds.setLightIntHighThreshold(LIGHT_INT_HIGH) ) {
    Serial.println(F("Error writing high threshold"));
  }
  
  // Start running the APDS-9960 light sensor (no interrupts)
  if ( apds.enableLightSensor(false) ) {
    Serial.println(F("Light sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during light sensor init!"));
  }
  
  // Read high and low interrupt thresholds
  if ( !apds.getLightIntLowThreshold(threshold) ) {
    Serial.println(F("Error reading low threshold"));
  } else {
    Serial.print(F("Low Threshold: "));
    Serial.println(threshold);
  }
  if ( !apds.getLightIntHighThreshold(threshold) ) {
    Serial.println(F("Error reading high threshold"));
  } else {
    Serial.print(F("High Threshold: "));
    Serial.println(threshold);
  }
  
  // Enable interrupts
  if ( !apds.setAmbientLightIntEnable(1) ) {
    Serial.println(F("Error enabling interrupts"));
  }
  
  // Wait for initialization and calibration to finish
  delay(500);
}

void loop() {
  
  // If interrupt occurs, print out the light levels
  if ( isr_flag == 1 ) {
    
    // Read the light levels (ambient, red, green, blue) and print
    if (  !apds.readAmbientLight(ambient_light) ||
          !apds.readRedLight(red_light) ||
          !apds.readGreenLight(green_light) ||
          !apds.readBlueLight(blue_light) ) {
      Serial.println("Error reading light values");
    } else {
      Serial.print("Interrupt! Ambient: ");
      Serial.print(ambient_light);
      Serial.print(" R: ");
      Serial.print(red_light);
      Serial.print(" G: ");
      Serial.print(green_light);
      Serial.print(" B: ");
      Serial.println(blue_light);
    }
    
    // Turn on LED for a half a second
    digitalWrite(LED_PIN, HIGH);
    delay(500);
    digitalWrite(LED_PIN, LOW);
    
    // Reset flag and clear APDS-9960 interrupt (IMPORTANT!)
    isr_flag = 0;
    if ( !apds.clearAmbientLightInt() ) {
      Serial.println("Error clearing interrupt");
    }
    
  }
}

void interruptRoutine() {
  isr_flag = 1;
}

 

Auch hier gibt es wieder verschiedene Einstellmöglichkeiten, vor allem den Verstärkungsfaktor (AGAIN_YX mit Y=1, 4, 16, 64), den ihr über die öffentliche Funktion setAmbientLightGain einstellen könnt. 

Fazit

Ich finde den APDS-9960 mit seinen vielen Funktionen und Optionen wirklich sehr beeindruckend. Besonders schön ist die Gestenfunktion mit der man so schöne Sachen machen kann wie z.B. mit bestimmten Bewegungen das Licht an- oder auszuschalten. Vielleicht teilt ihr ja meine Freude. Viel Spaß! 

Schreibe einen Kommentar

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