Lego 9398 com iluminação e Arduino

Que tal pegar um kit da Lego bem legal e colocar luzes funcionais de farol, lanterna, setas, freios e luz de ré como um veículo real? Pois é bem isso do que se trata este projeto, apenas conseguir reproduzir em escala tudo aquilo que um veículo real faz. A princípio me pareceu fácil, mas confesso que consumiu muitas horas de montagem da eletrônica e outras tantas  de programação para conseguir chegar a este resultado.

O Lego 9398

O Lego 9398 é um utilitário 4×4 para escalar terrenos difíceis. Tem grande curso em sua suspensão, diferencial nos eixos dianteiros e traseiros, motores independentes na tração e controle remoto sem fio. Apesar de ser um brinquedo, ele é muito bem feito e rende boas horas de diversão. Na sua configuração original tem controle remoto por infravermelho que até é interessante para ambientes internos, mas inviável para uso externo devido a luz solar. Você pode pesquisar na internet e encontrará vários vídeos sobre ele.


Conceito principal do projeto

Instalar luzes totalmente funcionais no carro e que ele tenha um sistema de controle remoto por rádio frequência ao invés de infravermelho. Para fazer o projeto ganhar vida vamos utilizar novamente o Arduino e o XBee.


Principais funções do nosso projeto

  1. Acendimento automático dos faróis e lanternas (sensor crepuscular)

  2. Acionamento do farol baixo e das lanternas a qualquer momento pelo controle remoto

  3. Acionamento do farol alto a qualquer momento pelo controle remoto

  4. Setas de direção que funcionam automaticamente ao mudar de direção

  5. Função  pisca-alerta com desarme automático ao voltar a se movimentar

  6. Luz de freio que se acende toda vez que o carro pára

  7. Luz de ré que se acende quando ele vai para trás

  8. Compatibilidade total com o nosso já conhecido Controle Remoto DualShock

Tenho certeza que vocês não esperavam por tudo isso…não é mesmo? 

Primeiro desafio: deslocamento

Eleger uma alternativa para substituir o receptor IR original foi o primeiro desafio, porque ele tem toda a unidade de controle dos motores de tração e direção. Para a tração eu optei por uma ponte H L298N que é barata e faz bem a função de controlar a velocidade e direção dos motores. A L298N ainda permite alimentação direta da bateria de LiPo o que facilita bastante o projeto e prevê a saída de 5V para o Arduino.

Aqui uma dica de extrema importância. Como optei pelo Arduino FIO devido a receber o XBee sem necessidade de shield, temos de tomar cuidado porque o FIO trabalha com 3,3V. Assim coloquei dois diodos retificadores 1N400X em série com positivo para ter uma queda de tensão de 1,4V. Cada diodo tem uma queda de 0,7V resultando em 3,6V na saída que, com a carga de funcionamento, atende bem ao FIO sem riscos.

O ponto negativo é que tive de cortar os conectores originais do fio e por isso o processo não é tão fácil assim de reverter ao original


Segundo desafio: direção

Como já tinha usado este kit antes em outros projetos como o Garden Explorer tendo danificado o “servo motor” do conjunto de direção e aí tive de pensar em uma alternativa para identificar o ponto central da barra de direção, caso contrário não iria conseguir fazer o carro andar em linha reta.

Depois de algumas tentativas frustradas, a melhor solução foi utilizar um potenciômetro de micro servo com engrenagens quebradas. Fiz uma adaptação na barra de direção dianteira e li com um multímetro a posição central. Por fim, foi só ligar o potenciômetro em uma entrada analógica do Arduino e incluir a rotina no código principal.


Terceiro desafio: 4 funções em um mesmo Led

O utilitário tem apenas uma luz de cada lado na parte de trás e eu decidi não mudar o desenho da traseira. Esta decisão foi a razão de muitas horas de pensamento para resolver o seguinte desafio: como fazer um único led ficar apagado, acender fraco, acender forte e piscar em diferentes momentos.

Com certeza vocês devem estar pensando que isso é fácil, mas na prática definir o momento de acender a lanterna, luz de freio e pisca durante a seta é um grande desafio. Depois de definir as prioridades e ajustar a lógica do código tudo deu certo e tenho certeza que foi um dos maiores prazeres que já tive com meus projetos.

A luz de ré foi simples e não exigiu nenhuma habilidade extra para seu funcionamento.


Quarto desafio: Piscar o Led sem usar “delay”

A grande maioria dos iniciantes em Arduino começa piscando um led, normalmente o da placa que fica no pino 13. Para isso alterna entre ligado e desligado utilizando um atraso através do comando Delay. O ponto negativo é a “parada” para a execução do programa durante o tempo definido e isto é inviável para um veículo que está se movimentando por controle remoto. Imagine você chegando na ponta de uma escada e o comando de parar não estar sendo aceito porque o código está aguardando o tempo durante um Delay

Nestas horas é que você entende a importância de um programa livre e da importância de compartilhar conhecimento. Consultando na internet vi alguns códigos que utilizavam o piscar do Led com a variável tempo lendo a função millis


Quinto desafio: Instalação dos Leds

As peças utilizadas pela Lego no modelo para as luzes são translúcidas o que permitiu utilizar Leds de 3mm que se encaixam perfeitamente em seu interior. Como hoje temos Leds de alto brilho deste tamanho e com invólucro incolor fez com que a montagem tivesse um bom acabamento.

Entretanto, passar o nosso “chicote” por entre a carroceria é uma tarefa trabalhosa que me consumiu um sábado inteiro. Para o conjunto ótico dianteiro preparei uma placa de circuito impresso universal onde instalei os leds, resistores e um transistor BC557. O transistor foi utilizado como chaveador para os 4 leds ligados em paralelo e desta forma sem comprometer a porta digital do Arduino.


No centro da capota foi instalado o LDR para ler a luminosidade do ambiente e ser os “olhos” do nosso sensor crepuscular. Também foi instalado em uma placa de circuito impresso universal bem pequena e fixada com uns pingos de cola quente.

Na traseira temos os 3 leds divididos em 2 para lanternas e 1 para luz de ré. Os fios foram passados e se juntaram aos vindos da frente, do LDR e da alimentação vindos da ponte H. Todos eles foram ligados a um conector e depois no Arduino para permitir a retirada do Arduino sem ter de desfazer a instalação elétrica. A ligação do acionamento dos motores do Arduino a ponte H também foi feita através de conectores.

Para facilitar foi colocada uma chave para ligar e desligar o positivo da bateria até a ponte H. Assim tudo fica muito mais simples do que ter de plugar e desplugar o conector da bateria


O produto final

Depois de tanta transpiração esta versão hi-tech do Lego 9398 ficou show! Manteve as características de mobilidade da versão original com toda a funcionalidade da iluminação e facilidade de controle através de rádio frequência.

Estou preparando o vídeo dele que será colocado aqui, mas enquanto não fica pronto você pode curtir um pouco mais através de outras fotos dele.


O código

Estou colocando abaixo o código utilizado no programa para vocês poderem analisar e utilizar. Quero apenas lembrar que, devido possível diferença de plataformas e browsers, alguns caracteres podem ficar diferentes durante o processo de copiar-colar. Assim cuidado com as aspas e outros carácteres especiais que poderão sair diferentes e causar erro na compilação.

/*

*******************************************************

Programa de recepcao de comandos Via XBee – DualShock

Acionador de motores com Ponte H

Autor Magneto – Outubro 2016

*******************************************************

*/

// *** Definicao dos pinos para movimento e direcao ***

const int mB1 = 4; // sentido do Motor B – deslocamento

const int mB2 = 6; // sentido do Motor B – deslocamento

const int mA1 = 8; // sentido do Motor A – de direcao

const int mA2 = 7; // sentido do Motor A – de direcao

const int vMA = 9; // velocidades do Motor A – PWM

const int vMB = 5; // velocidades dos Motor B – PWM

const int sensorCentro = 2; // sensor de centro do motor de direcao

const int ledCentro = 13; // led indicador do centro do motor de direcao

const int pot1 = A0; // potenciometro de direcao

// *** Definicao das variaveis para movimento e direcao ***

int cEnergyMA = 6; // coeficiente de energia dado pelo range do driver do motor A (0 a 255)

int cEnergyMB = 8; // coeficiente de energia dado pelo range do driver do motor B (0 a 255)

int cEnergyCentro = 50; // coeficiente de energia de retorno ao centro

int scaleJoy2 = 0; // escala de deslocamento

int scaleJoy3 = 0; // escala de direcao

byte trimJoy2 = 98; // trimagem do joystick 2 conforme programa de teste do TX

byte trimJoy3 = 157; // trimagem do joystick 3 conforme programa de teste do TX

byte motorCentral = 0; // posicao central do motor de direcao

int valorDirecao = 0; // valor do potenciometro da direcao

// *** Definicao dos pinos para luzes ***

const int frenteEsquerda = 12; // seta da frente do lado esquerdo

const int frenteDireita = A5; // seta da frente do lado direito

const int farois = 3; // farois

const int atrasEsquerda = 10; // luz traseira do lado esquerdo

const int atrasDireita = 11; // luz traseira do lado direito

const int re = A3; // luz de re

const int ldr = A4; // sensor crepuscular

// *** Definicao das variaveis para luzes ***

int valorLDR = 0; // valor do LDR

int gatilhoLDR = 250; // ponto onde o sensor crepuscular eh ativado

int gatilhoLDR2 = 90; // ponto onde o sensor crepuscular eh ativado

byte luzesAuto = 0; // sensor crepuscular ativado

byte lanterna = 0; // luzes de lanterna e farol baixo

byte farolAlto = 0; // farol alto

byte farolBaixo = 0; // farol baixo

byte luzRe = 0; // luz de re

byte piscaAlerta = 0; // pisca alerta

byte freio = 0; // freio para as luzes traseiras

byte piscaDireito = 0; // pisca do lado direito

byte piscaEsquerdo = 0; // pisca do lado esquerdo

byte flipFarol = 0; // alterna entre farol alto e baixo ou desligado

byte valorBaixo = 50; // PWM para farol baixo e lanterna

byte ctrlSeta = 0; // controle da ativacao das setas

byte ctrlFreio = 0; // controle da ativacao do freio

byte ctrlFarolAlto = 0; // controle da ativacao do farol Alto

byte ctrlFarolBaixo = 0; // controle da ativacao do farol Baixo

// *** Definicao de outras variaveis ***

int incomingByte; // Variavel de entrada de dados pela Serial

int valorFrente = LOW; // ultimo valor das setas frontais

int valorAtras = 0; // ultimo valor das setas de tras

long previousMillis = 0; // will store last time LED was updated

long interval = 100; // interval at which to blink (milliseconds)

void setup() {

// inicializa Serial

Serial.begin(9600);

// Inicializa pinos

pinMode(mB1,OUTPUT);

pinMode(mB2,OUTPUT);

pinMode(mA1,OUTPUT);

pinMode(mA2,OUTPUT);

pinMode(vMA,OUTPUT);

pinMode(vMB,OUTPUT);

pinMode(sensorCentro,INPUT);

pinMode(frenteEsquerda,OUTPUT);

pinMode(frenteDireita,OUTPUT);

pinMode(farois,OUTPUT);

pinMode(atrasEsquerda,OUTPUT);

pinMode(atrasDireita,OUTPUT);

pinMode(re,OUTPUT);

pinMode(ledCentro,OUTPUT);

}

void loop() {

// Lendo os valores dos sensores de direcao e luzes

motorCentral = digitalRead(sensorCentro);

valorDirecao = analogRead(pot1);

valorLDR = analogRead(ldr);

if(motorCentral == 1)

digitalWrite(ledCentro, HIGH);

else digitalWrite(ledCentro, LOW);

if(valorLDR < gatilhoLDR)

luzesAuto = 1;

else luzesAuto = 0;

// Rotinas de setas de direcao

if(piscaAlerta == 1) {

ligaPiscaAlerta();

ctrlSeta = 1;

}

else if(piscaAlerta == 0 && piscaDireito == 1) {

ligaPiscaDireito();

ctrlSeta = 1;

}

else if(piscaAlerta == 0 && piscaEsquerdo == 1) {

ligaPiscaEsquerdo();

ctrlSeta = 1;

}

// Rotina de Freio

if(freio == 1 && ctrlSeta == 0)

acendeFreio();

else if(freio == 0 && lanterna == 0 && ctrlSeta == 0)

apagaFreio();

else if(freio == 0 && lanterna == 1 && ctrlSeta == 0)

apagaFreio1();

// Rotina de Farol Alto

if(farolAlto == 1)

acendeFarolAlto();

else if(farolAlto == 0 && farolBaixo == 0 && luzesAuto == 0)

apagaFarolAlto();

else if(farolAlto == 0 && farolBaixo == 1 && luzesAuto == 1)

apagaFarolAlto1();

else if(farolAlto == 0 && farolBaixo == 0 && luzesAuto == 1)

apagaFarolAlto1();

// Rotina de Farol Baixo

if(farolBaixo == 1 && farolAlto == 0)

acendeFarolBaixo();

else if(farolBaixo == 0 && farolAlto == 0 && luzesAuto == 1)

acendeFarolBaixo();

else if(farolBaixo == 0 && farolAlto == 0 && luzesAuto == 0)

apagaFarolBaixo();

// Rotina de Lanterna

if(lanterna == 1 && ctrlSeta == 0 && freio == 0)

acendelanterna();

else if(lanterna == 0 && ctrlSeta == 0 && freio == 0 && luzesAuto == 1)

acendelanterna();

else if(lanterna == 0 && ctrlSeta == 0 && freio == 0 && luzesAuto == 0)

apagalanterna();

// Rotina luz de re

if(luzRe == 1)

acendeRe();

else if(luzRe == 0)

apagaRe();

// Restabelece os controles ao nivel inicial

ctrlSeta = 0;

luzesAuto = 0;

// Verificando se tem entrada de dados na Serial

if (Serial.available() > 0) {

incomingByte = Serial.read(); // Lendo o ultimo dado recebido

// Identificar J2UpDown – Joystick da Esquerda

if(incomingByte >= 68 && incomingByte <= 127) {

scaleJoy2 = (incomingByte – trimJoy2);

if(scaleJoy2 > 0) {

digitalWrite(mB1,HIGH);

digitalWrite(mB2,LOW);

freio = 0;

luzRe = 1;

piscaAlerta = 0;

}

else if(scaleJoy2 < 0) {

scaleJoy2 = scaleJoy2 * -1;

digitalWrite(mB1,LOW);

digitalWrite(mB2,HIGH);

freio = 0;

luzRe = 0;

piscaAlerta = 0;

}

else if(scaleJoy2 == 0) {

freio = 1;

luzRe = 0;

}

analogWrite(vMB, scaleJoy2 * cEnergyMB);

}

// Identificar J3LeftRight – joystick da Direita

if(incomingByte >= 128 && incomingByte <= 187) {

scaleJoy3 = (incomingByte – trimJoy3);

if(scaleJoy3 > 0) {

digitalWrite(mA1,LOW);

digitalWrite(mA2,HIGH);

piscaDireito = 1;

}

else if(scaleJoy3 < 0) {

scaleJoy3 = scaleJoy3 * -1;

digitalWrite(mA1,HIGH);

digitalWrite(mA2,LOW);

piscaEsquerdo = 1;

}

else if(scaleJoy3 == 0) {

piscaDireito = 0;

piscaEsquerdo = 0;

}

analogWrite(vMA, scaleJoy3 * cEnergyMA);

// retorno automatico para centro da direita – corrigindo para a esquerda

if(scaleJoy3 == 0 && valorDirecao <= 547 && scaleJoy2 != 0 && motorCentral == 0) {

digitalWrite(mA1,HIGH);

digitalWrite(mA2,LOW);

analogWrite(vMA, cEnergyCentro);

}

// retorno automatico para centro da esquerda – corrigindo para a direita

if(scaleJoy3 == 0 && valorDirecao >= 577 && scaleJoy2 !=0 && motorCentral == 0) {

digitalWrite(mA1,LOW);

digitalWrite(mA2,HIGH);

analogWrite(vMA, cEnergyCentro);

}

}

// ligar farol Alto

if(incomingByte == 3)

farolAlto = 1;

// Desligar Farol Alto

if(incomingByte == 4)

farolAlto = 0;

// Ligar lanterna e farol baixo

if(incomingByte == 248) {

lanterna = 1;

farolBaixo = 1;

}

// Desligar lanterna, farol baixo, pisca alerta e alarme

if(incomingByte == 251) {

lanterna = 0;

farolBaixo = 0;

piscaAlerta = 0;

}

// Pisca Alerta

if(incomingByte == 250)

piscaAlerta = 1;

}

// temporizacao das setas

unsigned long currentMillis = millis();

if(currentMillis – previousMillis > interval) {

// save the last time you blinked the LED

previousMillis = currentMillis;

// if the LED is off turn it on and vice-versa:

if (valorFrente == LOW && valorAtras == 0) {

valorFrente = HIGH;

valorAtras = 255;

}

else if(valorFrente == HIGH && valorAtras == 255) {

valorFrente = LOW;

valorAtras = 0;

}

if(piscaEsquerdo == 0 && piscaDireito == 0 && piscaAlerta == 0) {

valorFrente = LOW;

valorAtras = 0;

ligaPiscaEsquerdo();

ligaPiscaDireito();

}

}

//Debug das informacoes do motor

//Serial.println(incomingByte);

//Serial.print(“Sensor de centro da direcao: “);

//Serial.println(motorCentral);

//Serial.print(“Valor do Potenciometro: “);

//Serial.println(valorDirecao);

//Serial.print(“Valor LDR: “);

//Serial.println(valorLDR);

//delay(500);

}

// *** Rotinas para as luzes ***

void ligaPiscaEsquerdo() {

digitalWrite(frenteEsquerda, valorFrente);

analogWrite(atrasEsquerda, valorAtras);

}

void ligaPiscaDireito() {

digitalWrite(frenteDireita, valorFrente);

analogWrite(atrasDireita, valorAtras);

}

void ligaPiscaAlerta() {

digitalWrite(frenteEsquerda, valorFrente);

analogWrite(atrasEsquerda, valorAtras);

digitalWrite(frenteDireita, valorFrente);

analogWrite(atrasDireita, valorAtras);

}

void acendeFreio() {

analogWrite(atrasEsquerda, 255);

analogWrite(atrasDireita, 255);

}

void apagaFreio() {

analogWrite(atrasEsquerda, 0);

analogWrite(atrasDireita, 0);

}

void apagaFreio1() {

analogWrite(atrasEsquerda, valorBaixo);

analogWrite(atrasDireita, valorBaixo);

}

void acendeFarolAlto() {

analogWrite(farois, 255);

}

void apagaFarolAlto() {

analogWrite(farois, 0);

}

void apagaFarolAlto1() {

analogWrite(farois, valorBaixo);

}

void acendeFarolBaixo() {

analogWrite(farois, valorBaixo);

}

void apagaFarolBaixo() {

analogWrite(farois, 0);

}

void acendelanterna() {

analogWrite(atrasEsquerda, valorBaixo);

analogWrite(atrasDireita, valorBaixo);

}

void apagalanterna() {

analogWrite(atrasEsquerda, 0);

analogWrite(atrasDireita, 0);

}

void acendeRe() {

digitalWrite(re, HIGH);

}

void apagaRe() {

digitalWrite(re, LOW);

}

/* Disposicao dos botoes e comandos

j1LeftRight = 8 a 67

j2UpDown = 68 a 127

j3LeftRight = 128 a 187

j4UpDown = 188 a 247

joyBLeft = 1

joyBRight = 2

frontTopLeft = 3

frontBotLeft = 4

frontTopRight = 5

frontBotRight = 6

upperLeft = 248;

upperRight = 249;

upperTop = 250;

upperDown = 251;

Potenciometro de direcao

Esquerda: 720/759

Direita: 345/377

Centro: 548/576

*****************************

** Limites dos Joysticks **

** J1 Left: 8 **

** J1 Center: 36 **

** J1 Right: 67 **

** **

** J2 Up: 68 **

** J2 Center: 98 **

** J2 Down: 127 **

** **

** J3 Left: 128 **

** J3 Center: 157 **

** J3 Right: 187 **

** **

** J4 Up: 188 **

** J4 Center: 217 **

** J4 Dowm: 247 **

*****************************

*/