Curso Robótica – #8 – Controle Remoto IR (RC5)
O controle remoto com uma lanterna testada durante a parte #6 do curso robôs era muito simples, mas tinha muitas limitações.
Desta vez, vamos lidar com conectividade sem fio real! Em vez de luz visível, usaremos infravermelho. O robot pode então ser controlado usando o controle remoto da TV.
Diferentes métodos de comunicação sem fio
Mais e mais fabricantes equipam os seus dispositivos com módulos Bluetooth e WiFi. Estas soluções, sem dúvida, têm muitas vantagens, mas nem sempre precisamos de uma conexão tão extensa. Além disso, o uso de padrões de comunicação mais avançados exige de nós dispositivos de transmissão complicados, como telefone/tablet. Nem sempre é conveniente.
É por isso que vale lembrar sobre a conectividade sem fio que vem funcionando nas TVs há anos. Por meio de um sinal de infravermelho (IR) adequado, podemos nos comunicar com vários dispositivos. Se o controle remoto IR funciona bem ao mudar de canal de TV, você também pode usá-lo para controlar robôs!
Informações adicionais sobre infravermelho (IR) em produtos eletrônicos podem ser encontradas no artigo:
Vários aplicativos de infravermelho – visão geral da solução
Padrão RC5
A comunicação por infravermelho pode ocorrer de várias maneiras. De longe, o padrão mais popular entre os amadores é o RC5, que foi desenvolvido há mais de 30 anos pela Philips. Este protocolo foi preparado principalmente para o propósito de equipamentos RTV.
O controle remoto transmitido no padrão RC5 envia um infravermelho com uma frequência de 36 kHz. Cada vez que 14 bits de dados são enviados formam um quadro de dados. Se o botão do controle remoto for pressionado, os quadros de dados serão transmitidos o tempo todo (aproximadamente 114 ms).
O quadro de dados é composto pelas seguintes partes:
Os dois primeiros bits indicam o início do quadro e têm o valor “1” (isto é verdade para a versão básica do RC5). O terceiro bit é chamado de alternar. Usamos o nome do bit do switch nos gráficos, observando-o na Wikipedia… O nome não é crucial, no entanto. É importante lembrar o significado desse bit!
O bit de alternância muda o seu estado para o oposto (0/1) toda vez que o botão é pressionado.
Graças a isso, pode ser detectado que o mesmo botão é pressionado o tempo todo no controle remoto.
Vou explicar isso mais tarde, mais uma vez, em um exemplo prático!
Então, no quadro de dados, encontramos 5 bits de endereço. Com base neles, os receptores podem reconhecer se os dados são enviados para eles. Na prática, isso permite que use vários controles remotos simultaneamente (por exemplo, para TV e DVD) sem interferir na comunicação um do outro.
Os últimos 6 bits de dados contêm um comando. Cada tecla no controle remoto tem seu próprio número. Graças a isso, a TV pode distinguir entre comandos como “mudar de canal”, “aumentar o volume”, etc. O todo pode parecer complicado, mas tudo será explicado durante o teste na prática!
Receptor e transmissor infravermelho
O PCB do robot tem um layout TSOP2236. Os dois últimos dígitos no nome nos informam que o sistema recebe sinais transmitidos a 36 kHz, que é o exigido pela norma RC5.
É importante não obstruir o receptor de alguma forma (por exemplo, com cabos).
Durante o curso de eletrónica, nós construímos um transmissor IR usando o sistema NE555. No entanto, gerou um sinal constante com uma determinada frequência. Desta vez, precisa de uma solução abrangente que possa enviar várias informações. No conjunto de elementos do curso, há um controle remoto universal para o aparelho de TV. Uma imagem de exemplo é mostrada abaixo. Os controles remotos adicionados podem variar, por exemplo, em forma e botões, mas são certamente compatíveis com o padrão RC5!
Como você verifica se o controle remoto funciona?
Antes de programar, vale a pena garantir que o controle remoto funcione. Noutras palavras, precisa verificar se o LED IV está aceso quando pressiona o botão. Naturalmente, não veremos o infravermelho a olho nu. É suficiente, no entanto, olhar para o controle remoto através de uma câmara digital (uma câmara em um laptop, uma câmara no telefone).
Se o controle remoto funcionar corretamente, as fotos tiradas com uma câmera digital mostrarão que, ao pressionar o botão, o LED “acende em roxo”. O efeito pode variar, claro, dependendo do controle remoto e do telefone, o teste é melhor realizado no escuro.
Instalação da biblioteca RC5
Para descodificar os comandos enviados pelo piloto, usaremos a biblioteca, chamada RC5. Todos os arquivos estão no GitHub, seu autor, user guyc.
Vá para a página da biblioteca e clique no botão Clonar ou Baixar e, de seguida, em Fazer download do ZIP.
Nós descompactamos o arquivo baixado. Nós transferimos a pasta inteira para o diretório da biblioteca, para mim é:
C: \ Users \ Damian \ Documents \ Arduino \ bibliotecas
Depois de reiniciar o ambiente, a biblioteca será instalada e pronta para ação.
Certifique-se de instalar a biblioteca RC5 antes de continuar!
O primeiro teste RC5
Começaremos com o simples uso da biblioteca para verificar sua operação na prática. Primeiro, um esboço básico com informações sobre a biblioteca e a conexão do receptor de infravermelho:
Arduino #define TSOP_PIN 3 #include RC5 rc5(TSOP_PIN); //Informacja o podłączeniu odbiornika TSOP void setup() { Serial.begin(9600); //Start komunikacji przez UART } void loop() { }
A primeira será informado sobre o endereço do dispositivo, um segundo comando e a terceira sobre se o botão está deprimido o tempo todo.
#define TSOP_PIN 3 #include RC5 rc5(TSOP_PIN); //Informacja o podłączeniu odbiornika TSOP //Zmienne dla RC5 byte address; byte command; byte toggle; void setup() { Serial.begin(9600); } void loop() { }
Agora você pode continuar lendo comandos do controle remoto. Para este propósito, usaremos a função pronta rc5.read(). Aqui, no entanto, surge um problema. De acordo com as informações discutidas no curso básico do Arduino, a função pode retornar um valor (usando retorno).
Esta função deve, no entanto, retornar três valores!
O autor da biblioteca resolveu esse problema com a ajuda de indicadores. Este tópico, no entanto, é bastante complexo, especialmente para iniciantes. Portanto, aqui descreverei brevemente como funciona, mas não entrarei em detalhes de programação.
A chamada correta para a função rc5.read() é a seguinte:
rc5.read(&toggle, &address, &command)
Como parâmetros de função, fornecemos três variáveis declaradas anteriormente. Seus nomes são precedidos por & (e comercial), o que significa que, de fato, fornecemos os endereços na memória do Arduino que eles usam como argumentos. Graças a isso, rc5.read() pode “trocar” o conteúdo das variáveis e colocar os dados lidos lá.
No momento não serei capaz de penetrar exatamente como está acontecendo. Deixo
o tópico dos indicadores para a próxima série de artigos do Arduino para avançados.
Além disso, a função inteira retorna um valor de 1 (já tradicional “por retorno”) quando o comando é recebido. Agora podemos adicionar um trecho de código que exibirá os dados recebidos:
#define TSOP_PIN 3 #include RC5 rc5(TSOP_PIN); //Informacja o podłączeniu odbiornika TSOP //Zmienne dla RC5 byte address; byte command; byte toggle; void setup() { Serial.begin(9600); } void loop() { //Jeśli odebrano komendę if (rc5.read(&toggle, &address, &command)) { Serial.print("A:"); Serial.print(address); Serial.print(" K:"); Serial.print(command); Serial.print(" T:"); Serial.println(toggle); } }
A partir de agora, após pressionar o botão no controle remoto, várias informações devem aparecer no monitor da porta serie. Gostaria de lembrar que os quadros são repetidos aproximadamente a cada 114 ms, de modo que até mesmo uma pequena pressão do botão pode levar a alguns comandos.
A imagem abaixo mostra os dados recebidos pelo Arduino após um breve pressão do botão “2”. Como pode ver, o sistema conseguiu pegar 3 quadros de dados naquele momento.
O endereço do dispositivo é 0, porque é assumido que ele trata de aparelhos de TV e o meu controle remoto é usado para controlar a TV. Portanto, o endereço nunca será alterado – sempre haverá 0 (a menos que altere o piloto).
O comando é 2, porque pressionamos o botão descrito como 2. Pressionando analogicamente o botão 8, o valor 8 será enviado, da mesma forma para todas as teclas de 0 a 9. As demais possuem comandos menos intuitivos atribuídos.
É melhor verificar isso na prática. Para mim, por exemplo, a chave para
ligar a TV retorna o valor 12 e o botão “mais silencioso” 33.
Alternar, indica o valor 0, porque o botão foi pressionado uma vez. A segunda pressão do botão mudará o bit para o oposto, que é visível abaixo:
Como você pode ver o endereço e o comando não foi alterado, apenas o bit de alternância nos informa que o botão foi pressionado novamente.
Resumindo, receber uma sequência dos mesmos comandos com um botão de alternância idêntico significa que o botão foi pressionado e mantido pressionado. Receber os mesmos comandos com diferentes valores de alternância significa que o botão foi pressionado e liberado várias vezes.
Controle remoto do veículo
É hora de ir para a parte mais interessante, ou seja, o controle remoto do nosso veículo. Vamos começar com um esboço do programa que contém todas as informações necessárias:
#include #define L_PWM 5 #define L_DIR 4 #define R_PWM 6 #define R_DIR 9 #define PWM_MAX 165 #define BUZZER 10 #define LED 13 #define TSOP_PIN 3 RC5 rc5(TSOP_PIN); //Informacja o podłączeniu odbiornika TSOP byte address; byte command; byte toggle; void setup() { //Konfiguracja pinow od mostka H pinMode(L_DIR, OUTPUT); pinMode(R_DIR, OUTPUT); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); //Konfiguracja pozostalych elementow pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, 0); //Wylaczenie buzzera pinMode(LED, OUTPUT); digitalWrite(LED, 0); //Wylaczenie diody Serial.begin(9600); } void loop() { } void leftMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 0); //Kierunek: do przodu analogWrite(L_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 1); //Kierunek: do tyłu analogWrite(L_PWM, V); //Ustawienie predkosci } } void rightMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 0); //Kierunek: do przodu analogWrite(R_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 1); //Kierunek: do tyłu analogWrite(R_PWM, V); //Ustawienie predkosci } } void stopMotors() { analogWrite(L_PWM, 0); //Wylaczenie silnika lewego analogWrite(R_PWM, 0); //Wylaczenie silnika prawego }
Agora, no loop(), apenas verifique se algum comando foi recebido e, em caso afirmativo, deve executar as operações apropriadas. Eu lembro-lhe que o endereço variável no nosso caso sempre terá o mesmo valor, então não precisamos de usá-lo.
A operação do programa dependerá do valor de uma variável, ou seja, command.
A instrução do switch condicional funciona perfeitamente aqui!
Para começar, vamos adicionar o suporte para a campainha, que será a buzina do nosso veículo. Deixe-me pressionar o botão depois de ligar a TV (estou a receber o endereço 12):
void loop() { if (rc5.read(&toggle, &address, &command)){ switch(command) { case 12: digitalWrite(BUZZER, 1); delay(500); digitalWrite(BUZZER, 0); break; } } }
Agora vale a pena adicionar as instruções responsáveis pelo movimento do veículo. É melhor combiná-los com o layout dos botões no controle remoto. Durante os testes, adotei o seguinte layout de chaves:
- 2 – dirigindo para a frente
- 8 – dirigindo para trás
- 5 – stop
- 4 – vire à esquerda
- 6 – vire à direita
O programa inteiro:
#include #define L_PWM 5 #define L_DIR 4 #define R_PWM 6 #define R_DIR 9 #define PWM_MAX 165 #define BUZZER 10 #define LED 13 #define TSOP_PIN 3 RC5 rc5(TSOP_PIN); //Informacja o podłączeniu odbiornika TSOP byte address; byte command; byte toggle; void setup() { //Konfiguracja pinow od mostka H pinMode(L_DIR, OUTPUT); pinMode(R_DIR, OUTPUT); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); //Konfiguracja pozostalych elementow pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, 0); //Wylaczenie buzzera pinMode(LED, OUTPUT); digitalWrite(LED, 0); //Wylaczenie diody Serial.begin(9600); } void loop() { if (rc5.read(&toggle, &address, &command)){ switch(command) { case 2: //Do przodu leftMotor(40); rightMotor(40); break; case 8: //Do tyłu leftMotor(-40); rightMotor(-40); break; case 5: //STOP stopMotors(); break; case 4: //Obrót w lewo leftMotor(-30); rightMotor(30); break; case 6: //Obrót w prawo leftMotor(30); rightMotor(-30); break; case 12: digitalWrite(BUZZER, 1); delay(500); digitalWrite(BUZZER, 0); break; } } } void leftMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 0); //Kierunek: do przodu analogWrite(L_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 1); //Kierunek: do tyłu analogWrite(L_PWM, V); //Ustawienie predkosci } } void rightMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 0); //Kierunek: do przodu analogWrite(R_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 1); //Kierunek: do tyłu analogWrite(R_PWM, V); //Ustawienie predkosci } } void stopMotors() { analogWrite(L_PWM, 0); //Wylaczenie silnika lewego analogWrite(R_PWM, 0); //Wylaczenie silnika prawego }
O funcionamento do programa na prática é visível no seguinte vídeo:
Claro, como parte do desenvolvimento do programa, vale a pena usar os outros botões e adicionar, por exemplo, as funções de dirigir nas curvas:
Usando um bit de alternância
No final, mostrarei como usar o bit de alternância na prática, o que se torna difícil para algumas pessoas. Vou me concentrar em um exemplo – será fácil adaptá-lo a outras ações do robot! O meu objetivo nessa tarefa é mudar a velocidade na qual o veículo gira.
Pressionar uma vez o botão deve fazer com que o robot gire na velocidade padrão (30%). No entanto, quanto mais tempo o botão é pressionado, o robot deve girar mais rápido.
Começamos com o conteúdo básico do loop(). As únicas mudanças são a remoção de fragmentos de código que não são de interesse para nós agora (dirigindo noutras direções) e adicionando uma variável na qual a velocidade de rotação é mantida:
int predkoscObrotu = 30; void loop() { if (rc5.read(&toggle, &address, &command)){ switch(command) { case 4: //Obrót w lewo leftMotor(-predkoscObrotu); rightMotor(predkoscObrotu); break; } } }
Para detectar se um botão é pressionado todo o tempo que precisa para comparar o valor atual da alternância com a recebida anteriormente. Isso pode ser feito usando uma variável adicional:
int predkoscObrotu = 30; byte togglePoprzedni = 0; void loop() { if (rc5.read(&toggle, &address, &command)){ switch(command) { case 4: //Obrót w lewo leftMotor(-predkoscObrotu); rightMotor(predkoscObrotu); break; } //Jeśli bit toggle jest taki sam jak poprzednio if (toggle == togglePoprzedni) { } else { //Jeśli bit toggle jest taki sam jak poprzednio } //Zapamiętanie poprzedniej wartości toggle togglePoprzedni = toggle; } }
Sob a instrução switch, foi adicionada uma condição que compara as duas variáveis. Para que ele possa funcionar toda vez que recebermos um comando, devemos nos lembrar do valor anterior.
Agora apenas complete a nossa nova condição. Se o bit de alternância for o mesmo, queremos aumentar a velocidade em 1 – também vale a pena verificar se a velocidade de rotação é muito alta. Finalmente, se o bit de alternância for diferente, é suficiente restaurar a velocidade de rotação para o valor inicial. O programa inteiro pode ser assim:
#include #define L_PWM 5 #define L_DIR 4 #define R_PWM 6 #define R_DIR 9 #define PWM_MAX 165 #define BUZZER 10 #define LED 13 #define TSOP_PIN 3 RC5 rc5(TSOP_PIN); //Informacja o podłączeniu odbiornika TSOP byte address; byte command; byte toggle; void setup() { //Konfiguracja pinow od mostka H pinMode(L_DIR, OUTPUT); pinMode(R_DIR, OUTPUT); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); //Konfiguracja pozostalych elementow pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, 0); //Wylaczenie buzzera pinMode(LED, OUTPUT); digitalWrite(LED, 0); //Wylaczenie diody Serial.begin(9600); } int predkoscObrotu = 30; byte togglePoprzedni = 0; void loop() { if (rc5.read(&toggle, &address, &command)){ switch(command) { case 4: //Obrót w lewo leftMotor(-predkoscObrotu); rightMotor(predkoscObrotu); break; } //Jeśli bit toggle jest taki sam jak poprzednio if (toggle == togglePoprzedni) { predkoscObrotu++; //Zwieksz predkosc obrotu o 1 //Jeśli wartość prędkości przekroczy 90 if (predkoscObrotu >= 90) { predkoscObrotu = 30; //To ustawiamy stadnardowe 30 } } else { //Jeśli bit toggle jest różny //Ustaw predkosc na standardową predkoscObrotu = 30; } //Zapamiętanie poprzedniej wartości toggle togglePoprzedni = toggle; } } void leftMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 0); //Kierunek: do przodu analogWrite(L_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 1); //Kierunek: do tyłu analogWrite(L_PWM, V); //Ustawienie predkosci } } void rightMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 0); //Kierunek: do przodu analogWrite(R_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 1); //Kierunek: do tyłu analogWrite(R_PWM, V); //Ustawienie predkosci } } void stopMotors() { analogWrite(L_PWM, 0); //Wylaczenie silnika lewego analogWrite(R_PWM, 0); //Wylaczenie silnika prawego }
De agora em diante, quando segurar o botão “4”, o robot aumentará a velocidade de rotação com cada quadro de dados recebido – ou seja, aproximadamente a cada 114 ms.
A velocidade retornará para 30% quando:
- atingir o limite de velocidade (90%),
- soltar o botão e apertar novamente.
A operação do programa que usa o bit de alternância é visível abaixo:
Sumário
Controle remoto via infravermelho e padrão RC5 são realmente úteis em muitas aplicações. Um pequeno esforço do lado da programação permite a transmissão de códigos diferentes para o robot a uma distância considerável. Encorajo-vos a experimentar com IR!
Na parte seguinte do curso discutirá a construção de robots expansor de porta e um conector para a ligação a modelagem do servo. Estes elementos não são usados durante a construção de robots descritas no curso, mas será certamente útil durante outros robots de expansão isolados!