Skip to Content

Sensor de Gestos e Cor APDS-9960 com Arduino

Sensor de Gestos e Cor APDS-9960 com Arduino

Neste tutorial vais aprender a usar o Sensor de Gestos e Cor RGB APDS-9960 com um Arduino ou outros microcontroladores comuns como o ESP32 ou ESP8266.

O APDS-9960 é um sensor muito pequeno que utiliza luz infravermelha e fotodíodos para reconhecer quatro gestos diferentes (da esquerda para a direita, da direita para a esquerda, de cima para baixo, de baixo para cima). Também possui detetores integrados para luz ambiente, medições de cor (vermelho, verde, azul) e proximidade.

Componentes Necessários

Para este projeto vais precisar de um sensor APDS-9960 e de um microcontrolador. Usei um Arduino Uno, mas qualquer outro Arduino ou ESP32/ESP8266 serve, desde que forneça uma saída de 3,3V. Um dos exemplos também utiliza um servo, mas este não é um componente essencial para este projeto.

Sensor APDS-9960

Arduino

Arduino Uno

USB Data Sync cable Arduino

Cabo USB para Arduino UNO

Dupont wire set

Conjunto de Cabos Dupont

Half_breadboard56a

Breadboard

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Características do Sensor de Gestos e Cor RGB APDS-9960

O APDS-9960 é um chip minúsculo (3,94×2,36×1,35 mm) que oferece deteção avançada de gestos, deteção de proximidade, sensor digital de luz ambiente (ALS) e sensor de cor (RGBC). A imagem abaixo mostra o chip APDS-9960:

APDS-9960 chip
Chip APDS-9960

A deteção de gestos é feita por quatro fotodíodos direcionais que detetam a luz IR refletida (emitida pelo LED integrado), que é depois convertida em informação digital de movimento (ou seja, velocidade, direção e distância). Gestos simples de CIMA-BAIXO-DIREITA-ESQUERDA ou gestos mais complexos podem ser detetados.

A funcionalidade de deteção de cor e ALS do APDS-9960 fornece dados de intensidade de luz vermelha, verde, azul e clara. Cada canal R, G, B, C tem um filtro de bloqueio UV e IR e um conversor de dados dedicado que produz dados de 16 bits em simultâneo.

Diagrama de Blocos Funcional do APDS-9960

A imagem abaixo mostra o Diagrama de Blocos Funcional do APDS-9960. Podes identificar facilmente o LED emissor de IR, os quatro fotodíodos para deteção de cor (Clear, Red, Green, Blue) e os quatro fotodíodos para deteção de gestos (Up, Down, Left, Right).

Diagrama de Blocos Funcional do APDS-9960 (source)

O APDS-9960 comunica através de uma interface I2C (SCL, SDA) e tem uma saída de interrupção adicional (INT) que é ativada quando um limite programável é excedido (por exemplo, proximidade).

A tensão de funcionamento do APDS-9960 é de 2,4V-3,6V, o alcance de deteção de gestos é de 10-20cm e o endereço I2C é 0x39. Para mais informações detalhadas consulta o datasheet no link abaixo:

Placa Breakout para APDS-9960

O chip APDS-9960 é demasiado pequeno para ser ligado diretamente a um Arduino. Normalmente, vais precisar de uma placa breakout como a mostrada abaixo:

Front and Back of breakout board for APDS-9960
Frente e verso da placa breakout para APDS-9960

A maioria das placas breakout para o APDS-9960 tem os seguintes seis pinos:

  • VCC : Alimentação (2,4 – 3,6V)
  • GND : Terra
  • VL : Alimentação do LED IR
  • SDA : Sinal de dados I2C
  • SCL : Sinal de clock I2C
  • INT : Pino de interrupção

Normalmente só vais precisar dos pinos de alimentação (VCC, GND), dos pinos para comunicação I2C (SCL, SDA) e do pino de interrupção (INT). O VL permite fornecer alimentação externa ao LED IR, o que normalmente não é necessário.

Atenção que o APDS-9960 funciona a 3,3V e que a maioria das placas breakout não tem regulador de tensão.

Ligar o APDS-9960 ao Arduino

Graças à interface I2C do APDS-9960, ligá-lo ao Arduino é simples. Primeiro, liga o pino SCL da placa breakout do APDS-9960 ao A5 do Arduino. Da mesma forma, liga o SDA ao A4 do Arduino. Depois, liga o GND ao GND e o 3,3V ao VCC do APDS-9960.

Connecting APDS-9960 to Arduino
Ligação do APDS-9960 ao Arduino

Certifica-te de que usas 3,3V como alimentação. O sensor APDS-9960 não suporta 5V e as placas breakout para o APDS-9960 normalmente não têm regulador de tensão.

Instalar Biblioteca para APDS-9960

Existem duas bibliotecas principais para o APDS-9960 no Arduino. A Adafruit_APDS9960 biblioteca e a SparkFun_APDS-9960_Sensor_Arduino_Library. Neste projeto vou usar a Adafruit_APDS9960 biblioteca.

Para instalar a biblioteca Adafruit_APDS9960, abre o LIBRARY MANAGER, procura por “APDS9960”, encontra a biblioteca da Adafruit e instala-a, como mostrado abaixo:

Installing APDS9960 Library by Adafruit via Library Manager
Instalar Biblioteca APDS9960 da Adafruit via Library Manager

O instalador pode pedir para instalar dependências da biblioteca. Não há problema. Basta carregar em “INSTALL ALL”

Installing dependencies of  APDS9960 Library by Adafruit
Instalar dependências da Biblioteca APDS9960 da Adafruit

Com a biblioteca instalada, vamos escrever algum código de teste para experimentar o sensor. Carrega o código seguinte e passa a mão em frente ao sensor a cerca de 5cm de distância.

#include "Adafruit_APDS9960.h"

Adafruit_APDS9960 sensor;

void setup() {
  Serial.begin(9600);
  if (!sensor.begin()) {
    Serial.println("failed to initialize device!");
  }
  sensor.enableProximity(true);
  sensor.enableGesture(true);
}


void loop() {
  if(sensor.readGesture()) {
    Serial.println("Movement detected");
  }  
}

Se vires a mensagem “Movement detected” impressa no Serial Monitor está tudo bem. No entanto, se vires “failed to initialize device!” as coisas ficam um pouco mais complicadas e deves ler a próxima secção. Se estiver tudo bem podes saltar essa parte.

Adafruit_APDS9960: failed to initialize device!

Primeiro, verifica se a cablagem está correta (SCL->A5, SDA->A4, VCC->3V3, GND->GND). Se a ligação estiver certa e o sensor não estiver avariado, então há duas razões principais para que sensor.begin() possa falhar. Um endereço I2C diferente ou um chip ID diferente. Vamos verificar primeiro o endereço I2C.

Endereço I2C diferente do APDS9960

O endereço I2C habitual do APDS9960 é 0x39 e a biblioteca Adafruit_APDS9960 espera este endereço I2C. Para confirmar isto, instala e executa o seguinte código I2C scanner:

#include "I2CScanner.h"

I2CScanner scanner;

void setup() {
  Serial.begin(9600);
  scanner.Init();
}

void loop() {
  scanner.Scan();
  delay(5000);
}

O scanner I2C deve encontrar um dispositivo I2C com o endereço 0x39 e imprimir isto no Serial Monitor.

I2C device found at address 0x39  !
--- Scan finished ---

Se vires um endereço I2C diferente, então tens de alterar o código na função setup como segue e substituir “addr” pelo endereço I2C que estás a ver.

void setup() {
  ...
  if (!sensor.begin(10, APDS9960_AGAIN_4X, addr, &Wire)) {
    Serial.println("failed to initialize device!");
  }
  ...
}

Se o teu endereço I2C for 0x39 ou se já alteraste o endereço I2C como descrito acima mas sensor.begin() continua a falhar, então o próximo candidato é um chip ID diferente. Se o scanner I2C não encontrar nenhum dispositivo, provavelmente o teu sensor está avariado (ou a cablagem ainda está errada).

Chip ID diferente do APDS9960

Se fores ver o código da função begin() na biblioteca Adafruit_APDS9960 vais ver que o código usa primeiro o endereço I2C addr. Depois verifica o chip ID e espera que seja 0xAB.

boolean Adafruit_APDS9960::begin(uint16_t iTimeMS, apds9960AGain_t aGain,
                                 uint8_t addr, TwoWire *theWire) {

  if (i2c_dev)
    delete i2c_dev;
  i2c_dev = new Adafruit_I2CDevice(addr, theWire);
  if (!i2c_dev->begin()) {
    return false;
  }

  /* Make sure we're actually connected */
  uint8_t x = read8(APDS9960_ID);
  if (x != 0xAB) {
    return false;
  }

No entanto, tenho um APDS9960 com o chip ID 0xA8 (não 0xAB) e a SparkFun_APDS-9960_Sensor_Arduino_Library lista outro chip ID, que é 0x9C.

Como o chip ID está hard-coded, temos de alterar o código. A forma mais fácil de o fazer é descarregar dois ficheiros Adafruit_APDS9960.h e Adafruit_APDS9960.cpp para a tua pasta do projeto, ao lado do teu sketch, por exemplo MySketch.ino:


Adafruit library files copied into sketch folder
Ficheiros da biblioteca Adafruit copiados para a pasta do sketch

Depois vais ver estes dois novos ficheiros aparecerem como separadores no teu Arduino IDE:

Ficheiros da biblioteca Adafruit no Arduino IDE

Agora abre o separador com o ficheiro Adafruit_APDS9960.cpp, encontra a função begin e simplesmente remove ou comenta o bloco anotado com /* Make sure we're actually connected * / como mostrado abaixo:

boolean Adafruit_APDS9960::begin(uint16_t iTimeMS, apds9960AGain_t aGain,
                                 uint8_t addr, TwoWire *theWire) {

  if (i2c_dev)
    delete i2c_dev;
  i2c_dev = new Adafruit_I2CDevice(addr, theWire);
  if (!i2c_dev->begin()) {
    return false;
  }

  /* Make sure we're actually connected */
  // uint8_t x = read8(APDS9960_ID);
  // if (x != 0xAB) {
  //  return false;
  // }

Em alternativa, também podes imprimir “x” para descobrir o ID do teu chip e alterar o teste if (x != 0xAB), conforme necessário.

Se não quiseres alterar o código tu mesmo, criei uma versão modificada dos dois ficheiros, que podes descarregar aqui (link). Ainda tens de descompactar os ficheiros e copiá-los para o teu projeto. Com esta correção, o código de teste acima deve funcionar.

Reconhecer Gestos com o APDS-9960

O código seguinte é uma pequena extensão do código de teste acima. Como antes, na função setup inicializamos o sensor com sensor.begin() e depois ativamos a deteção de proximidade e de gestos:

#include "Adafruit_APDS9960.h"

Adafruit_APDS9960 sensor;

void setup() {
  Serial.begin(9600);
  if (!sensor.begin()) {
    Serial.println("failed to initialize device!");
  }
  sensor.enableProximity(true);
  sensor.enableGesture(true);
}


void loop() {
  uint8_t gesture = sensor.readGesture();
  if (gesture == APDS9960_DOWN) Serial.println("DOWN");
  if (gesture == APDS9960_UP) Serial.println("UP");
  if (gesture == APDS9960_LEFT) Serial.println("LEFT");
  if (gesture == APDS9960_RIGHT) Serial.println("RIGHT");
}

Na função loop chamamos sensor.readGesture() e, dependendo do valor de retorno, imprimimos o gesto detetado no Serial Monitor.

Agora, passa a mão sobre o sensor a uma distância de cerca de 5-10 cm e, dependendo da direção, deves ver o gesto detetado impresso no Serial Monitor como mostrado abaixo:

Detected Gestures on Serial Monitor
Gestos Detetados no Serial Monitor

Em vez de imprimir no Serial Monitor, podes agora alterar o código para acionar LEDs, controlar servos ou realizar qualquer outra ação que queiras, dependendo do gesto. As próximas secções mostram um exemplo simples de como controlar um servo com gestos.

Controlar servo com o Sensor de Gestos APDS-9960

Primeiro precisamos de ligar o servo ao Arduino. Se tiveres um daqueles pequenos Micro Servos SG90 podes ligá-los diretamente ao Arduino (não é preciso alimentação separada). Basta ligar o fio vermelho (pino do meio) do servo ao 5V, o fio castanho ao GND e o fio laranja/amarelo ao pino 13, como mostrado abaixo.

Connecting Servo to Arduino
Ligar Servo ao Arduino

Se precisares de mais informações sobre como ligar e usar estes servos, consulta o tutorial How to control servo motors with Arduino.

O código para controlar o servo com um gesto é uma variação simples do código anterior de deteção de gestos.

#include "Adafruit_APDS9960.h"
#include "Servo.h"

Adafruit_APDS9960 sensor;
Servo servo;

const int servoPin = 13;

void setup() {
  if (!sensor.begin()) {
    Serial.println("failed to initialize device!");
  }
  sensor.enableProximity(true);
  sensor.enableGesture(true);
  servo.attach(servoPin);
  servo.write(90);
}

void loop() {
  uint8_t gesture = sensor.readGesture();
  if (gesture == APDS9960_DOWN) servo.write(90);
  if (gesture == APDS9960_UP) servo.write(90);
  if (gesture == APDS9960_LEFT) servo.write(10);
  if (gesture == APDS9960_RIGHT) servo.write(170);
}

Primeiro, incluímos a biblioteca padrão Servo (não é preciso instalar biblioteca). Depois criamos o objeto servo e definimos o pino ao qual o servo está ligado (servoPin).

Na função setup ligamos o servo ao pino e orientamos inicialmente o servo a 90°. Na função loop substituímos as chamadas à função print por comandos write para o servo. No caso de um gesto para cima ou para baixo, movemos o servo para a posição de 90°. Um gesto para a esquerda move o servo para 10° e um gesto para a direita move-o para 170°. O pequeno vídeo abaixo mostra o código em ação:

Controlling Servo with Gestures and APDS-9960
Controlar Servo com Gestos e APDS-9960

Com isto já tens um controlo simples por gestos para um pequeno servo, que podes usar, por exemplo, para abrir ou fechar uma caixa sem toque. No próximo exemplo de código vamos experimentar o sensor de cor do APDS-9960

Medir Cores com o APDS-9960

O código abaixo usa o sensor de cor do APDS-9960 para medir os valores de cor vermelho, verde, azul e claro, e imprime-os no Serial Monitor.

Começamos por incluir a biblioteca Adafruit e criar o objeto do sensor. Na função setup inicializamos o sensor como habitual com sensor.begin(), e depois ativamos o sensor de cor com sensor.enableColor(true).

#include "Adafruit_APDS9960.h"

Adafruit_APDS9960 sensor;

void setup() {
  Serial.begin(9600);
  if (!sensor.begin()) {
    Serial.println("failed to initialize device!.");
  }
  sensor.enableColor(true);
}

void loop() {
  uint16_t r, g, b, c, tmp, lux;

  while (!sensor.colorDataReady()) {
    delay(5);
  }

  sensor.getColorData(&r, &g, &b, &c);
  Serial.print("red:");
  Serial.println(r);

  Serial.print("green:");
  Serial.println(g);

  Serial.print("blue:");
  Serial.println(b);

  Serial.print("clear:");
  Serial.println(c);

  tmp = sensor.calculateColorTemperature(r, g, b);
  Serial.print("tmp:");
  Serial.println(tmp);

  lux = sensor.calculateLux(r, g, b);
  Serial.print("lux:");
  Serial.println(lux);

  Serial.println();

  delay(1000);
}

Na função loop, chamamos sensor.colorDataReady() para obter os valores de cor vermelho (r), verde (g), azul (b) e claro (c), e depois imprimimos no Serial Monitor.

A biblioteca Adafruit_APDS9960 tem duas funções adicionais que permitem calcular a temperatura de cor e o brilho em lux com base nos valores de cor medidos. Também imprimimos estes valores no Serial Monitor.

Se mudares a intensidade da luz ambiente ou colocares LEDs de diferentes cores em frente ao sensor, vais ver que os valores de cor mudam.

Testar a deteção de cor do APDS-9960

Para testar a deteção de cor do APDS-9960, usei LEDs vermelho, verde e azul. Os valores abaixo são o que medi quando coloquei um LED vermelho perto do sensor:

red:329
green:26
blue:47
clear:342
tmp:24902
lux:65436

Como esperado, o valor do vermelho é muito mais alto do que os valores de verde e azul. Nota também que os valores de temperatura de cor e brilho em lux são extremamente altos. Parece que a luz do LED vermelho confunde estas medições.

A seguir, usei um LED verde. O LED verde era relativamente fraco e o valor do verde aumentou apenas um pouco. O valor do vermelho também subiu. Pelo valor de lux podes ver que o brilho do meu LED verde era baixo.

red:15
green:16
blue:6
clear:42
tmp:3045
lux:15

Por fim, experimentei um LED azul e obtive os valores de cor mostrados abaixo. O valor do azul foi muito alto, mas também houve um aumento no componente verde.

red:40
green:561
blue:2507
clear:2751
tmp:1754
lux:64574

A resposta do sensor de cor depende obviamente do brilho e do comprimento de onda da luz colorida. Os meus LEDs podem não estar na gama mais sensível, o que pode explicar a reação fraca ao LED azul. Mas também podes ver pelo Gráfico de Resposta Espectral abaixo que o APDS-9960 tem geralmente uma sensibilidade muito menor para luz azul (B).

Spectral Response of APDS-9960 Color Sensor
Resposta Espectral do Sensor de Cor APDS-9960 (source)

Reagir a Interrupções com o APDS-9960

Como último exemplo, quero mostrar-te como usar o pino de interrupção do APDS-9960. Para isso, precisamos de ligar o pino INT do APDS-9960 a um pino de interrupção do Arduino. No caso do Arduino Uno, apenas os pinos 2 e 3 são pinos de interrupção. Para saberes quais são os pinos de interrupção noutras placas, vê aqui (link).

No diagrama de ligação abaixo estou a ligar a saída INT do APDS-9960 ao pino 3 do Arduino.

Connecting Interrupt Pin of APDS-9960 with Arduino
Ligar o Pino de Interrupção do APDS-9960 ao Arduino

O código seguinte dispara um sinal de interrupção se um objeto se aproximar do sensor e depois imprime o valor de proximidade.

#include "Adafruit_APDS9960.h"

Adafruit_APDS9960 sensor;
const int intPin = 3;

void setup() {
  Serial.begin(9600);
  if (!sensor.begin()) {
    Serial.println("failed to initialize device!");
  }
  sensor.enableProximity(true);
  sensor.setProximityInterruptThreshold(0, 100);
  sensor.enableProximityInterrupt();
  pinMode(intPin, INPUT_PULLUP);
}

void loop() {
  if (!digitalRead(intPin)) {
    Serial.println(sensor.readProximity());
    sensor.clearInterrupt();
  }
}

Começamos o código por incluir a biblioteca, criar o objeto do sensor e definir o pino de interrupção. Na função setup ativamos a deteção de proximidade, definimos os limites de proximidade e ativamos a interrupção de proximidade. Também é necessário colocar o pino de interrupção intPin em modo INPUT.

Na função loop estamos a verificar o estado do intPin. Se o estado ficar baixo, significa que foi disparada uma interrupção e lemos o valor de proximidade e imprimimos.

Os valores de proximidade vão de 255 (mais próximo) a 0 (mais distante). Como definimos um limite de proximidade de 100, o código começa a imprimir valores de proximidade se um objeto se aproximar a menos de 100, o que corresponde a uma distância de cerca de 10mm.

Regular o brilho de um LED proporcionalmente à Proximidade

Podes usar este código para controlar o brilho de um LED dependendo da proximidade de um objeto. Para isso, adicionamos um LED ao circuito. Liga o cátodo (perna curta) do LED ao GND e o ânodo (perna longa) através de uma resistência de 220Ω ao pino 11. Qualquer outro pino PWM também serve.

Connecting an LED with Arduino
Ligar um LED ao Arduino

De seguida, vamos estender um pouco o código. Definimos o ledPin e colocamo-lo em modo OUTPUT na função setup. Na função loop lemos o valor de proximidade prox, e como é um valor entre 0 e 255, podemos usá-lo diretamente para controlar o brilho do LED usando analogWrite(ledPin, prox):

#include "Adafruit_APDS9960.h"

Adafruit_APDS9960 sensor;
const int intPin = 3;
const int ledPin = 11;

void setup() {
  Serial.begin(9600);
  if (!sensor.begin()) {
    Serial.println("failed to initialize device!");
  }
  sensor.enableProximity(true);
  sensor.setProximityInterruptThreshold(0, 0);
  sensor.enableProximityInterrupt();
  pinMode(intPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  if (!digitalRead(intPin)) {
    uint8_t prox = sensor.readProximity();
    analogWrite(ledPin, prox);
    sensor.clearInterrupt();
  } 
}

O pequeno vídeo abaixo mostra como o brilho do LED muda dependendo da proximidade da mão ao sensor:

Regular o Brilho do LED com o APDS-9960 conforme a Proximidade

E é tudo por hoje! Espero que te tenhas divertido a experimentar o Sensor APDS-9960.

Conclusão

Neste tutorial aprendeste a usar o Sensor de Gestos e Cor APDS-9960 com um Arduino para detetar gestos e medir valores de cor e brilho.

Em comparação com o Sensor de Proximidade APDS-9930, o APDS-9960 consegue detetar quatro gestos integrados (cima, baixo, esquerda, direita) e tem um sensor de cor RGB, enquanto o APDS-9930 só tem sensor de proximidade e de luz ambiente. Ambos os sensores foram desenhados especialmente para aplicações em telemóveis ou tablets para ativar funções (como ligar altifalantes, microfones, ecrãs) e controlar o brilho do ecrã.

Se precisares de mais gestos, vê o PAJ7620U2, que consegue detetar até 13 gestos, e o seu irmão mais pequeno, o PAJ7620, que ainda consegue detetar 9 gestos.

O alcance do sensor de proximidade no APDS-9930 e APDS-9960 é propositadamente muito limitado. Se quiseres medir distâncias maiores com precisão, por exemplo para aplicações de robótica, é melhor usares sensores de distância por infravermelhos como o GP2Y0A710K0F que usam triangulação para determinar a distância a um objeto. Ou sensores de distância a laser Time-of-Flight (ToF) como o TOF10120 ou a biblioteca VL53L1X.

Se tiveres dúvidas, deixa-as na secção de comentários.

Boas experiências ; )