O VL53L0X é um sensor de distância infravermelho de alta precisão divulgado pela STMicroelectronics (sua fabricante) como o menor no mercado. Ele faz uso de um VCSEL (Laser Emissor de Superfície de Cavidade Vertical) com filtros que evitam a interferência de luzes externas e proporcionam um maior alcance ao sensor.
Ele funciona como um sensor reflexivo do tipo Time of Flight (ToF), emitindo uma luz invisível ao olho humano que retorna ao sensor após ser refletida por um obstáculo. A duração desse processo é então usada para calcular a distância até o obstáculo detectado, podendo alcançar até 2 metros.
O módulo VL53L0X (também encontrado como GY-VL53L0XV2, CJVL53L0XV2 ou VL53L0XV2) é uma escolha perfeita para projetos com os mais diversos tipos de placas Arduino pois além de ser compacto e preciso, ele também possui um regulador de tensão integrado que o permite ser alimentado tanto por 3V quanto por 5V. Ele pode ser encontrado em uma variedade de cores incluindo azul, preto, verde e roxo; todas com as mesmas funcionalidades.
A comunicação do módulo VL53L0X é realizada através da interface I2C. Porém, o Arduino possui um número bastante limitado de portas contendo esta interface.
Imagine que temos um robô ou um carrinho de controle remoto e precisamos usar mais de um sensor para criar um sistema de detecção de colisão. O que fazer nesse caso? É isso que vamos ensinar nessa postagem.
Componentes necessários
Para seguir este tutorial, você precisará dos seguintes itens:
- 1x Placa UNO R3 ATmega328 + Cabo USB (Arduino Compatível).
- 2x Sensor de Distância Laser GY-VL53L0XV2 30 – 1000mm.
- 1x Módulo Buzzer Passivo 5V.
- 1x Protoboard 400 Pontos.
- Jumpers Macho/Macho – 20cm.
O datasheet do VL53L0X pode ser encontrado aqui.
Módulo VL53L0X
Pinagem
Além de pinos de alimentação (VIN e GND), o módulo VL53L0X possui dois pinos dedicados a comunicação I2C (SCL e SDA), um pino de reset (XSHUT) e um pino de saída de dados (GPIO1) que pode ser utilizado para programar interrupções no microcontrolador ao qual o sensor está ligado.
Imagem 1 — Módulo VL53L0X
Na tabela abaixo estão descritas as funcionalidades de cada pino.
Imagem 2 — Descrição dos pinos do VL53L0X
Endereçamento
O módulo possui um endereçamento de 7-bits que por padrão possui o valor binário 0101001 (41 na base decimal) quando ligado. Porém, ele permite sobrescrever os registradores de configuração do sensor para que um novo endereço seja atribuído. Este novo endereço será válido apenas enquanto o sensor permanecer ligado, voltando à configuração padrão ao ser reiniciado ou desligado.
Pino XSHUT
O pino XSHUT possui um resistor de pull-up que o leva a estado lógico alto (HIGH) quando o VL53L0X é alimentado. O reset é ativado quando levado a nível lógico baixo (LOW), fazendo o sensor entrar em modo standby.
Para que possamos configurar diferentes endereços para diferentes módulos VL53L0X conectados às mesmas portas I2C, precisamos desligar sequencialmente cada placa usando o pino XSHUT (cada um conectado a um pino digital diferente do Arduino) e em seguida ligar os módulos para atribuir seus novos endereços.
É importante ressaltar que o XSHUT não é tolerante a tensões de 5V, não sendo possível realizar operações de escrita nesse pino a partir do Arduino Uno ou outras placas de 5V. Mas então como vamos manipular este pino?
Inicializando o XSHUT como OUTPUT, ele entrará em um estado de baixa impedância e será ativado. Mudando o pinMode para INPUT, o pino entra em um estado de alta impedância, sendo “puxado” para uma tensão de 2.8V por seu resistor de pull-up.
Montando o Circuito
Em nosso circuito de exemplo, vamos supor que estamos construindo um carrinho com um sistema de detecção de colisão nas partes dianteira e traseira. Além do sensor de distância, usaremos também o módulo de buzzer passivo YL-44 que será acionado quando o sensor detectar algum objeto. A distância de detecção será configurada mais à frente através do código.
O módulo YL-44 possui três pinos. Um para alimentação de 5V (VCC), outro para o terra (GND) e o pino do meio (I/O) é ligado a um transistor que controla o sinal que chega ao pino e faz com que o som emitido pelo buzzer saia de maneira mais limpa. O pino I/O será ligado ao pino digital 13 do Arduino.
Os pinos SDA e SCL de ambos os VL53L0X devem ser ligados respectivamente aos pinos analógicos A4 e A5 de nosso Arduino Uno, que são os pinos utilizados pela placa para a interface I2C. Também temos acesso aos pinos SDA e SCL de nosso Arduino Uno acima do pino AREF como ilustrado na imagem.
O XSHUT de cada sensor deve ser ligado a um pino digital diferente. Usaremos os pinos 6 e 7 do Arduino.
Para alimentar os sensores, devemos ligar o VIN ao 5V do Arduino e o GND ao GND do Arduino.
Imagem 3 — Montagem do circuito
Caso o seu VL53L0X seja novo, não esqueça de remover a película de plástico protetora para que o sensor funcione corretamente.
Programando
VL53L0X
Antes de começarmos a escrever nosso código, devemos baixar a biblioteca que nos permitirá controlar nosso sensor de distância.
Na barra de menu no topo da IDE do Arduino, navegue até Sketch -> Incluir Biblioteca -> Gerenciador de Bibliotecas… e pesquise por “VL53L0X.” Em seguida, instale a biblioteca VL53L0X criada pela Pololu Robotics and Electronics.
Após a instalação, a primeira coisa que devemos fazer é importar a biblioteca do sensor e também a biblioteca Wire, pois o sensor faz uso de ambas:
1 2 | #include <Wire.h> #include <VL53L0X.h> |
Em seguida vamos definir os pinos do Arduino aos quais os pinos XSHUT dos sensores VL53L0X estão conectados:
1 2 | #define XSHUT_Sensor1 6 #define XSHUT_Sensor2 7 |
E o endereço de cada sensor. Lembrando que o endereço padrão do sensor é 41.
1 2 | #define Sensor1_endereco 42 #define Sensor2_endereco 43 |
Como estamos usando dois sensores, bastaria configurar o endereço apenas do segundo sensor, mas vamos mostrar o processo completo para que você saiba como expandir seu projeto a partir deste tutorial.
Vamos agora criar os objetos responsáveis pelo controle de cada sensor:
1 2 | VL53L0X Sensor1; VL53L0X Sensor2; |
E definir um limiar de detecção para os sensores. Como estes valores não serão alterados durante a execução do código, eles serão declarados como constantes.
1 2 | const int distanciaMin = 0; const int distanciaMax = 25; |
Vamos fazer a medição em centímetros, detectando obstáculos entre 0 e 25cm dos sensores. Objetos mais distantes que isso não serão detectados.
Caso queira uma distância diferente, basta alterar os valores destas variáveis.
Teremos também uma variável de controle para indicar a detecção de objetos dentro do limite demarcado:
1 | bool detectado = false; |
setup()
Entrando na função setup(), vamos desligar todos os sensores configurando os pinos XSHUT como OUTPUT:
1 2 | pinMode(XSHUT_Sensor1, OUTPUT); pinMode(XSHUT_Sensor2, OUTPUT); |
Agora devemos ligar os sensores um a um configurando-os como INPUT e em seguida atribuir o novo endereço através do método setAddress().
1 2 3 4 5 6 7 8 9 | Wire.begin(); pinMode(XSHUT_Sensor2, INPUT); delay(10); Sensor2.setAddress(Sensor2_endereco); pinMode(XSHUT_Sensor1, INPUT); delay(10); Sensor1.setAddress(Sensor1_endereco); |
Depois inicializamos os sensores:
1 2 | Sensor1.init(); Sensor2.init(); |
E definimos um timeout em milissegundos:
1 2 | Sensor1.setTimeout(500); Sensor2.setTimeout(500); |
Se o sensor não estiver conectado, preparado ou funcionando corretamente, a operação de leitura será abortada após o período de timeout que definimos.
Por último, precisamos definir um tipo de leitura para os sensores. Vamos usar uma leitura contínua para que os obstáculos sejam detectados o mais rápido possível. Isso é configurado através do método startContinuous():
1 2 | Sensor1.startContinuous(); Sensor2.startContinuous(); |
Também podemos configurar as leituras para serem acionadas em intervalos fixos de tempo passando um valor em milissegundos para esse método.
loop()
No loop() vamos chamar uma função que criaremos com o nome lerSensores():
1 2 3 | void loop() { lerSensores(); } |
Dentro de lerSensores(), vamos fazer a leitura dos sensores utilizando o método readRangeContinuousMillimeters() e convertê-la para centímetros ao multiplicar o resultado por 0,1.
1 2 | int measure1 = Sensor1.readRangeContinuousMillimeters()*0.1; int measure2 = Sensor2.readRangeContinuousMillimeters()*0.1; |
E será verificado se o objeto que o sensor detectou está dentro dos limites que definimos no início do código. Se estiver, o valor da variável detectado se torna true.
1 2 3 4 5 6 7 | if ((measure1 > distanciaMin) && (measure1 <= distanciaMax) || (measure2 > distanciaMin) && (measure2 <= distanciaMax)) { Serial.println("Detectou!"); detectado = true; } else { Serial.println ("Nenhum objeto detectado."); } |
Lembrando que para imprimir valores no monitor serial devemos antes inicializá-lo dentro do setup() usando o método Serial.begin().
Buzzer
Para controlar o buzzer, vamos nos basear no exemplo BlinkWithoutDelay disponível na IDE do Arduino que usa a função millis() para detectar a passagem de tempo e determinar quando está na hora de acionar ou desligar um LED (no nosso caso, substituiremos o LED por um buzzer). Esta função retorna a quantidade de tempo (em milissegundos) que se passou desde que o programa foi iniciado.
Faremos isso para evitar o uso da função delay(), que pode interferir nas leituras do VL53L0X.
Como variáveis globais teremos três constantes: o pino ao qual o buzzer está ligado (pino 13), a frequência do som que vai emitir e o intervalo de tempo (em milissegundos) entre o estado lógico alto e estado lógico baixo (equivalente a um delay).
1 2 3 | const byte buzzer = 13; // pino do buzzer const int frequencia = 200; // frequência do buzzer const long intervalo = 500; // intervalo do blink (milissegundos) |
Teremos também duas variáveis, uma para armazenar o momento em que o estado do buzzer foi trocado pela última vez e outra para armazenar o estado atual do buzzer.
1 2 | unsigned long previousMillis = 0; byte signalState = LOW; |
Como estamos trabalhando com tempo, é importante que previousMillis seja capaz de armazenar valores grandes e por isso ela deve ser declarada como unsigned long.
Dentro do setup(), o buzzer deve ser configurado como OUTPUT:
1 | pinMode(buzzer, OUTPUT); |
No loop(), além da função lerSensores(), chamaremos uma segunda função que vamos criar com o nome processaBuzzer():
1 2 3 4 | void loop() { lerSensores(); processaBuzzer(); } |
Esta função será responsável por nosso “blink sem delay” e será ativada apenas quando um obstáculo for detectado pelo VL53L0X (verificado pelo primeiro if). Será então verificado se a diferença entre o tempo de execução do programa e a última vez em que o buzzer foi tocado é maior ou igual ao intervalo que definimos no início do código (nosso “delay”). Se esse for o caso, o estado do buzzer é alterado e o tempo atual é armazenado para uma verificação futura.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void processaBuzzer() { if(detectado) { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= intervalo) { previousMillis = currentMillis; if (signalState == LOW) { signalState = HIGH; tone(buzzer, frequencia); } else { signalState = LOW; noTone(buzzer); detectado = !detectado; } } } } |
Código completo
Segue o código completo de nosso projeto. Ele pode ser incrementado para adicionar outros sensores VL53L0X, possibilitando assim a detecção de colisão em outros ângulos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | #include <Wire.h> #include <VL53L0X.h> //Definição dos pinos aos quais o XSHUT está ligado. #define XSHUT_Sensor2 7 #define XSHUT_Sensor1 6 //Definição dos endereços dos sensores VL53L0X. Endereço padrão: 0b0101001 ou 41. #define Sensor1_endereco 42 #define Sensor2_endereco 43 VL53L0X Sensor1; VL53L0X Sensor2; //Constantes: const byte buzzer = 13; //Pino do buzzer. const int frequencia = 200; //Frequência do buzzer. const int distanciaMin = 0; //Distância mínima de detecção do VL53L0X. const int distanciaMax = 25; //Distância máxima de detecção do VL53L0X. const long intervalo = 500; //Delay do buzzer em milisegundos. // Variáveis de controle: boolean detectado = false; byte signalState = LOW; // Indica a última vez em que o sinal do buzzer foi alterado. unsigned long previousMillis = 0; void setup() { //Configura o buzzer como saída. pinMode(buzzer, OUTPUT); //Desliga todos os VL53L0X. pinMode(XSHUT_Sensor1, OUTPUT); pinMode(XSHUT_Sensor2, OUTPUT); //Inicia a comunicação serial. Serial.begin(9600); Wire.begin(); //Liga os sensores e altera seus endereços. pinMode(XSHUT_Sensor2, INPUT); delay(10); Sensor2.setAddress(Sensor2_endereco); pinMode(XSHUT_Sensor1, INPUT); delay(10); Sensor1.setAddress(Sensor1_endereco); //Inicializa os sensores. Sensor1.init(); Sensor2.init(); //Define timeout para os sensores. Sensor1.setTimeout(500); Sensor2.setTimeout(500); //Inicia o modo de leitura contínuo dos VL53L0X. Sensor1.startContinuous(); Sensor2.startContinuous(); } void loop() { lerSensores(); //Lê os sensores. processaBuzzer(); //Liga ou desliga o buzzer de acordo com a leitura dos sensores. } void lerSensores() { //Lê a distância em centímetros. int measure1 = Sensor1.readRangeContinuousMillimeters()*0.1; int measure2 = Sensor2.readRangeContinuousMillimeters()*0.1; //Mostra o resultado no monitor serial. Serial.print(measure1); Serial.println("cm"); Serial.print(','); Serial.print(measure2); Serial.println("cm"); Serial.println(); //Verifica se a leitura está dentro dos parâmetros de detecção. if((measure1 > distanciaMin) && (measure1 <= distanciaMax) || (measure2 > distanciaMin) && (measure2 <= distanciaMax)) { Serial.println("Detectou!"); detectado = true; } else { Serial.println ("Nenhum objeto detectado."); //IMPRIME O TEXTO NO MONITOR SERIAL } } void processaBuzzer() { if(detectado) { //Pega o tempo atual. unsigned long currentMillis = millis(); //Verifica se está na hora de alterar o estado do buzzer; ou seja, se a diferença //entre o tempo atual e a última vez que o estado foi alterado é maior que o //intervalo do blink. if (currentMillis - previousMillis >= intervalo) { //Salva a última vez em que o estado do buzzer foi alterado. previousMillis = currentMillis; //Se o buzzer está desligado, liga ele e vice-versa. if (signalState == LOW) { signalState = HIGH; tone(buzzer, frequencia); //Aciona o buzzer. } else { signalState = LOW; noTone(buzzer); //Desliga o buzzer. detectado = !detectado; //Após tocar o buzzer uma vez, permite uma nova detecção pelo sensor. } } } } |
Gostou? Deixe seu comentário logo abaixo, não deixe de conferir outras postagens do nosso blog. Confira também a nossa loja virtual e encontre todos os componentes utilizados no projeto no post.
Deixe um Comentário