Controlando o robô aspirador “Roomba” com Arduino e Android

Como um dos vencedores do concurso “Robotics 2016” do Instructables, recebi como prêmio da empresa iRobot, um robô aspirador Roomba Create2. Neste post, vou contar um pouco de minhas primeiras aventuras com o garoto!

O Create 2 é uma plataforma robusta e relativamente barata para o desenvolvimento de projetos na área da robótica (pelo menos nos EUA, onde custa cerca de US $ 200). O Create2 é na verdade um Robô Aspirador de pó “Roomba” da série 660 restaurado que permite uma variedade de métodos de programação. No Brasil, mais e mais se torna comum este tipo de eletrodomésticos ( e só esperar a mãe sair de casa e fazer alguma experiências com o rapaz 😉

Para começar, usei um Arduino e um aplicativo Android para poder mover o robô por aí. Neste primeiro tutorial, explorarei como conectar o Arduino com o Roomba via porta serial e como comandar seus motores, LEDs e som. Em projectos futuros, explorarei seus sensores e usarei um Raspberry Pi para conectar o Roomba com a internet.

Abaixo, um rápido vídeo mostrando os meus primeiros resultados programando o Roomba:

Passo 1: Lista de Materiais
BoM

  • iRobot “Roomba”Create2
  • Arduino UNO
  • módulo Bluetooth HC-06
  • Botão (“Push0Button”)
  • Protobard e cabos de conecção.

 

Passo 2: O Roomba Create2

O Roomba é um robô do tipo diferencial, com 2 rodas e um “caster”

Roomba Wheels

 

 

Sua velocidade vai até 500 mm/s e pode ser comandado para ir tanto para a frente como para trás.

 

Para sinalização, podemos contar com 4 displays de sete segmentos e 5 LEDs:Create-Sensors-LEDS

  • Limpar (Clean)
  • Local (Spot)
  • Doca (Dock)
  • Aviso (Warning)
  • Sugeira (Dirt / Debris)

Como sensores internos, temos entre outros:

  • Detector de degrau (Cliff) (4 à frente)
  • Detectores de colisão (2 na frente)
  • Codificadores de giro das rodas

 

Para a programação, deve ser usado o documento de referencia: iRobot® Create® 2 Open Interface (OI) .

O Roomba  possui 3 modos de programação:

  • Modo de segurança (Safe):
    Libera o controle total do Roomba, com a excepção das seguintes condições de segurança:

    • Carregador de batería conectado e ligado.
    • A detecção de uma queda de roda (ocorre quando se “levanta o Roomba do chão”).
    • A detecção de um degrau de escada por exemplo, enquanto se move para a frente (ou movendo-se para trás com um raio de giro pequeno).

Se uma das condições de segurança acima ocorrem enquanto o Roomba está em modo de segurança, Roomba para todos os motores e reverte para o modo passivo.

  • Modo passivo (Passive):
    Ao enviar o comando Iniciar (“Start”) ou qualquer um dos comandos do modo de limpeza (por exemplo, “Spot”, “Clean”, “Seek Dock”), o Roomba entra em modo passivo. Quando o Roomba está em modo passivo, você pode solicitar e receber dados utilizando qualquer um dos comandos de sensores, mas você não pode mudar os parâmetros de comando dos atuadores (motores, som, luzes, saídas digitais, etc.) .
  • Modo completo (Full):
    Libera o controle completo do Roomba, a  todos os seus atuadores e a todas as condições de segurança que são restritos quando o robô está em modo de segurança descritas no modo Safe.

Passo 3: A ligação em série

Para a comunicação entre o Roomba e o Arduino, será utilizada a porta serial de ambos. Por padrão, o Roomba se comunica a 115.200 bauds, mas para uma melhor comunicação com o Arduino, vamos modificar-lo para 19.200 bauds.

Existem 2 maneiras de se definir a taxa de transmissão do Roomba :

  • Durante o  desligamento do Roomba, continue a manter pressionado o botão central POWER/CLEAN, mesmo após a luz se apagar. Após cerca de 10 segundos, o Roomba tocará uma música com tons descendentes. A partir daí, o Roomba irá comunicar-se a 19.200 bauds até que o processador perda a energia da bateria ou a taxa de transmissão seja explicitamente alterada através de programação.
  • Usar o pino 5 no conector Mini-DIN (Baud Rate Change pin) para alterar a taxa de transmissão do Roomba. Depois de ligar o Roomba, espere 2 segundos; em seguida, aplique um pulso de nivel baixo no pin5 três vezes. Cada pulso deve durar entre 50 e 500 milissegundos. O Roomba irá comunicar-se a 19200 bauds até que o processador perca a energia da bateria ou a taxa de transmissão seja explicitamente alterada por SW.

O diagrama abaixo mostra como o Arduino deve ser conectado ao conector Mini-DIN do Roomba:

Serial_Conn

Par se ter acesso ao Mini-DIN se deve remover a parte superior do Create 2 (capa verde), ou simplesmente fazer um furo na mesma.

Passo 4: Inicializando o Roomba

O primeiro passo a ser feito na programação de um Roomba é:

  • “Acordar” o robô
  • Iniciar e definir o modo de operação (Safe ou Full)

Para acordar o Roomba, devemos enviar um pulso baixo para o pino 5 do Mini-DIN (detectar dispositivo de entrada), como mostrado na função abaixo (ddPin é o pin 5 do Arduino conectado ao pin% do Roomba):

void wakeUp (void)
{
  digitalWrite(ddPin, HIGH);
  delay(100);
  digitalWrite(ddPin, LOW);
  delay(500);
  digitalWrite(ddPin, HIGH);
  delay(2000);
}

Para iniciar o Roomba, sempre devem ser enviados 2 códigos : “START” [128] e o modo, no nosso caso “modo de segurança” [131]. Se você quizer o “modo completo”, deve enviar o código [132].

void startSafe()
{ 
  Roomba.write(128); //Start
  Roomba.write(131); //Safe mode
  delay(1000);
}

Passo 5: Ligar os LEDs e visor

Ligando os LEDs

Conforme descrito na introdução, o Roomba possui 5 LEDs:

  • POWER/CLEAN (bicolor vermelho / verde e intensidade controlada)
  • SPOT (Verde, intensidade fixa)
  • DOVK (verde, a intensidade fixa)
  • WARNING / Check (Laranja, intensidade fixa)
  • DIRT (azul, intensidade fixa)

Todos os LEDs podem ser comandados usando o código [139]

Para controlar o LED POWER/CLEAN, você deve enviar dois bytes de dados para o Roomba: “cor” e “intensidade”.

  • Cor:
    • Verde = 0
    • Laranja = 128
    • vermelho = 255
  • Intensidade:
    • Min = 0
    • Max = 255

A função setPowerLED (byte setColor, byte setIntensity) faz isso automaticamente:

void setPowerLED(byte setColor, byte setIntensity)
{
    color = setColor;
    intensity = setIntensity;
     Roomba.write(139);
     Roomba.write((byte)0x00);
     Roomba.write((byte)color);
     Roomba.write((byte)intensity);
}

Por exemplo, para acender o  LED POWER com cor de laranja  e na metade de sua intensidade maxima, você pode chamar a função como abaixo:

setPowerLED (128, 128);

Para acender os restantes 4 LEDs, devem ser utilizadas as funções:

   setDebrisLED (ON);

   setDockLED (ON);

   setSpotLED (ON);

   setWarningLED (ON);

Todas as funções acima tem um código semelhante a este:

void setDebrisLED(bool enable)
{
    debrisLED = enable;
    Roomba.write(139);
    Roomba.write((debrisLED ? 1 : 0) + (spotLED ? 2 : 0) + (dockLED ? 4 : 0) + (warningLED ? 8 : 0));
    Roomba.write((byte)color);
    Roomba.write((byte)intensity);
}

Basicamente, a diferença estará na linha:

     debrisLED = enable;

 

IMG_4305

 

 

a qual deverá ser alterada permitindo (“enabling”) que cada um dos outros LEDs (spotLED, dockLED, warningLED) acenda.

Envio de mensagens a serem mostradas

O Roomba possui quatro Displays de 7 Segmentos que você podem ser usados para enviar mensagens de duas maneiras diferentes:

  • Código [163]: LEDs com dígitos numéricos (“Raw”)
  • Código [164]: LEDs com dígitos ASCII (aproximação de letras e códigos especiais)

Para exibir números é muito facil. Você apenas deve enviar o código [163], seguido dos 4 dígitos a serem  exibidos. A função:

setDigitLEDs (digit1 byte, digit2 byte, digit3 byte, byte digit4)

faz isso para você:

void setDigitLEDs(byte digit1, byte digit2, byte digit3, byte digit4)
{
    Roomba.write(163);
    Roomba.write(digit1);
    Roomba.write(digit2);
    Roomba.write(digit3);
    Roomba.write(digit4);
}

Por exemplo, para exibir “1, 2, 3, 4”, basta chamar a função:

   setDigitLEDs (1, 2, 3, 4);

 

Com o código [164], é possível aproximação de envio de ASCII.

A função: setDigitLEDFromASCII(byte digit, char letter) faz isso para nós:

void setDigitLEDFromASCII(byte digit, char letter)
{
     switch (digit){
     case 1:
           digit1 = letter;
           break;
     case 2:
          digit2 = letter;
          break;
     case 3:
          digit3 = letter;
          break;
      case 4:
          digit4 = letter;
           break;
      }
      Roomba.write(164);
      Roomba.write(digit1);
      Roomba.write(digit2);
      Roomba.write(digit3);
      Roomba.write(digit4);
}

Para simplificar, criei uma nova função que pode ser utilizada para enviar os 4 dígitos ao mesmo tempo:

void writeLEDs (char a, char b, char c, char d)
{
    setDigitLEDFromASCII(1, a);
    setDigitLEDFromASCII(2, b);
    setDigitLEDFromASCII(3, c);
    setDigitLEDFromASCII(4, d);
}

Por exemplo, para exibir “STOP”, você deve chamar a função:

  writeLEDs ( 's', 't', 'o', 'p');

 

IMG_4304

Passo 6: Pilotando o Roomba pela casa

Para sua mobilidade, o Roomba possui 2 motores DC independentes que podem ser programados para rodar a uma velocidade de até 500 mm/s. Existem vários comandos que podem ser usados para dirigir o robô. Os principais são:

  1. Código [137]: Drive ==> devem ser enviados: +/- velocidade em mm/s e +/- Radius em mm
  2. Código [145]: Direct Drive ==> deve ser enviado velocidade à Esquerda/Direita em mm/s (+ para a frente e – para trás)
  3. Código [146]: Drive PWM ==> devem ser enviados +/- dados PWM individualmente para as rodas esquerda e direita.

Abaixo o código para essas 3 opções descritas anteriormente:

void drive(int velocity, int radius)
{
    clamp(velocity, -500, 500); //def max and min velocity in mm/s
    clamp(radius, -2000, 2000); //def max and min radius in mm
 
    Roomba.write(137);
    Roomba.write(velocity >> 8);
    Roomba.write(velocity);
    Roomba.write(radius >> 8);
    Roomba.write(radius);
}

//---------------------------------------------------------------

void driveWheels(int right, int left)
{
    clamp(right, -500, 500);
    clamp(left, -500, 500);
 
    Roomba.write(145);
    Roomba.write(right >> 8);
    Roomba.write(right);
    Roomba.write(left >> 8);
    Roomba.write(left);
 }

//---------------------------------------------------------------
void driveWheelsPWM(int rightPWM, int leftPWM)
{
    clamp(rightPWM, -255, 255);
    clamp(leftPWM, -255, 255);
 
    Roomba.write(146);
    Roomba.write(rightPWM >> 8);
    Roomba.write(rightPWM);
    Roomba.write(leftPWM >> 8);
    Roomba.write(leftPWM);
}

Note que a função “clamp” define os valores máximos e mínimos que são permitidos para como entrada. Essa função é definida no arquivo rombaDefines.h :

  #define clamp(value, min, max) (value < min ? min : value > max ? max : value)

Usando os códigos descritos acima, funções mais simples podem ser criadas para mover o Roomba:

void turnCW(unsigned short velocity, unsigned short degrees)
{
   drive(velocity, -1);
   clamp(velocity, 0, 500);
   delay(6600);
   drive(0,0);
}

//---------------------------------------------------------------
void turnCCW(unsigned short velocity, unsigned short degrees)
{
   drive(velocity, 1); 
   clamp(velocity, 0, 500);
   delay(6600);
   drive(0,0);
}

//---------------------------------------------------------------
void driveStop(void)
{
 drive(0,0);
}

//---------------------------------------------------------------
void driveLeft(int left)
{
 driveWheels(left, 0);
}

//---------------------------------------------------------------
void driveRight(int right)
{
 driveWheels(0, right);
}

Note-se que para se obter um valor de ângulo correto, o argumento da função “delay” deve ser calculada especificamente para uma dada velocidade (o método de tentativa e erro é a melhor opção aqui).

Abaixo alguns exemplos que podem ser utilizados para testar os motores:

  turnCW (40, 180); // girar no sentido horário em 180 graus e parar

  driveWheels (20, -20); // girar sobre seu eixo ("spin") a uma velocidade de 20mm/s

  driveLeft (20); // Virar à esquerda a uma velocidade de 20mm/s

IMG_4307

 

Para testar os motores, é bom adicionar um botão externo (no meu caso ligado ao Arduino pino 12), de modo a que você possa baixar o código para o Arduino, iniciando o Roomba, mas parando a execução até que o botão seja é pressionado.

 

Abaixo, simples exemplo de um código de testes para os motores utilizando-se Arduino (observe que para o codigo ser executado, funções e definições discutidas nos steps anteriores deverão ser utilizadas):

#include "roombaDefines.h"
#include <SoftwareSerial.h>

// Roomba Create2 connection
int rxPin=10;
int txPin=11;
SoftwareSerial Roomba(rxPin,txPin);

//---------------------------------------------
void setup() 
{
   Roomba.begin(19200);
 
   pinMode(ddPin, OUTPUT);
   pinMode(buttonPin, INPUT_PULLUP); // connected to Arduino pin 12 and used for "starting"

   delay(2000);
 
   wakeUp (); // Wake-up Roomba
   startSafe(); // Start Roomba in Safe Mode

   while (digitalRead(buttonPin)) { } // wait button to be pressed to continous run code
 
   turnCW (40, 180); //test Roomba spin clock-wise 180 degrees and stop
}

//---------------------------------------------
void loop() 
{
}

Passo 7: Controlando Roomba via Bluetooth

Para completar a nossa primeira parte do projeto, vamos instalar um módulo Bluetooth (HC-06) para a nosso Arduino. O diagrama abaixo mostra como fazê-lo.

Serial_BT_Conn

Normalmente, o HC-06 é fornecido de fábrica com uma taxa de transmissão de 9600 bauds. É importante que você o altere para 19.200, de maneira a ser compatível com a velocidade de comunicação utilizada pelo Arduino-Roomba. Você pode fazer isso enviando um comando AT para o módulo (AT + BAUD5 onde “5” é o código para 19.200).

Se você tem alguma dúvida sobre como o HC-06 trabalha, por favor dê uma olhada no meu tutorial: Conectando “coisas” através do Bluetooth

Para guiar o Roomba, vamos utilizar um aplicativo genérico para controle de robôs móveis que desenvolvi a partir do MIT AppInventor 2: “MJRoBot BT Remote Control”. O aplicativo pode ser baixado gratuitamente a partir da loja Google.

O aplicativo tem uma interface simples, o que lhe permite enviar comandos para o módulo de BT em ambos, modo texto ou directamente através de botões pré-programados (cada vez que um botão é pressionado, um caracter é enviado):

  • w: Foreward
  • s: Backward
  • d: Right
  • a: Left
  • f: Stop
  • p: ON / OFF (não utilizado nesta primeira parte)
  • m: manual / automatic (utilizado para reiniciar o Roomba caso ele esteje em modo de segurança e encontre um obstáculo como um degrau por exemplo)
  • +: Velocidade Up
  • -: Speed -Down

Você também pode enviar outros comandos como texto, se necessário. Há também uma janela de texto para exibição de mensagens recebidas a partir do módulo de BT. Esta característica é muito importante durante a fase de testes, pode ser usada da mesma forma que o “Serial Monitor” do PC.

A função loop () do código será a responsável pela “escuta” do dispositivo bluetooth e dependendo do comando recebido, tomar uma ação:

void loop() 
{
   checkBTcmd(); // verify if a comand is received from BT remote control
   manualCmd ();
}

A função checkBTcmd () é mostrada abaixo:

void checkBTcmd() // verify if a command is received from BT remote control
{ 
  if (BT1.available()) 
  { 
    command = BT1.read();
    BT1.flush();
   }
}

Uma vez que um comando é recebido, a função manualCmd () irá tomar as medidas apropriadas:

void manualCmd()
{
  switch (command)
  {
     case 'm': 
        startSafe();
        BT1.print("Roomba BT Ctrl OK - Safe mode");
        BT1.println('\n');
        command = 'f';
        playSound (3);
        break;
 
     case 'f': 
        driveStop(); //turn off both motors
        writeLEDs ('s', 't', 'o', 'p');
        state = command;
        break;

     case 'w': 
        drive (motorSpeed, 0); 
        writeLEDs (' ', 'g', 'o', ' ');
        state = command; 
        break;

     case 'd': 
        driveRight(motorSpeed);
        writeLEDs ('r', 'i', 'g', 'h');
        break;

     case 'a': 
        driveLeft(motorSpeed);
        writeLEDs ('l', 'e', 'f', 't');
        break;
 
     case 's': 
        drive (-motorSpeed, 0);
        writeLEDs ('b', 'a', 'c', 'k');
        state = command;
        break;

     case '+': 
        if (state == 'w')
        {
          motorSpeed = motorSpeed + 10;
          if (motorSpeed > MAX_SPEED) 
          { 
             motorSpeed = MAX_SPEED;
           } 
           command = 'w';
         } else {command = state;}
         break;

     case '-': 
 
        if (state == 'w')
        {
          motorSpeed = motorSpeed - 10;
        } 
        if (motorSpeed < MIN_SPEED ) 
        {    
          motorSpeed = MIN_SPEED;
         }
         command = state;
         break;
     }
}

Passo 8: Conclusão

O código Arduino completo e  documentos relacionados podem ser encontrados em meu depositário do GitHub: Roomba_BT_Ctrl.

Observe que nem todos os atuadores e sensores do Roomba foram discutidos neste tutorial. Existem outros motores utilizados especificamente para a limpeza, LEDs utilizados para a programação, botões, sensores, etc.

Várias das funções que criei em meu programa foram baseados na biblioteca Create 2 desenvolvida por Dom Amato. Você pode baixar a biblioteca completa através do link: https://github.com/brinnLabs/Create2.

 

Minha intenção aqui foi deixar as coisas simples, procurando dar o pontapé inicial para o desenvolvimento de projetos baseados no robô aspirador Roomba. No futuro, pretendo publicar outros tutoriais, usando um Raspberry-Pi, conectando o Roomba à internet, ler seus sensores, etc.

Como sempre, espero que este projeto possa ajudar outras pessoas a encontrar o seu caminho no excitante mundo da eletrônica e da robótica!

Não deixe de visitar e seguir minha página: MJRoBot.org no Facebook

Saludos desde o sur do mundo! 😉

Até o próximo post!

Um abraço e obrigado

Marcelo

Controlando um Raspberry Pi robô pela Internet

Este é um grande exemplo do fascinante mundo do IoT, onde o que aprenderemos com o robô, poderá ser utilizado para controlar qualquer coisa pela internet!

Introdução

A idéia deste projeto, é criar um robô totalmente controlado pela internet, usando-se o Raspberry Pi como processador. O robô será controlado usando comandos de baixo nível escritos com shell scripts, os quais serão por sua vez comandados diretamente por uma página web escrita em HTML. O fato de não usarmos linguagens de alto nível como Python por exemplo, fará com que o robô reaja bem rápido aos comandos recebidos pela página (mesmo quando a internet for lenta).

No link abaixo, voce poderá ver como ficará o projeto final:

O projeto será dividido em duas partes, sendo que na parte 1, será onde aprenderemos a:

  • Instalar e usar a biblioteca WiringPi  para controlar os GPIOs RPi
  • Controlar motores usando uma H-Bridge
  • Transformar o RPI em um servidor web
  • Criar uma pagina em linguagem HTML (e Java Script) para controlar o robô através da Internet

Para ir mais longe e fazer com que seu robô tenha uma “visão” real do mundo, você poderá ir para a segunda parte deste tutorial onde você aprenderá:

  • Como gerar stream de vídeo com uma PiCam
  • Como instalar e usar a biblioteca ServoBlaster
  • Como controlar servo motores e construir um mecanismo para posicionamento vertical e horizontal da câmera (mecanismo PAM/TILT)
  • Criar uma página HTML para controlar o posicionamento da câmara através da internet

 

O diagrama de blocos abaixo mostra a idéia geral para asa duas partes do projeto:

full blockdiagram webrobot

Primeira Parte: Controlando luzes e motores via internet

O RPI será definido como um servidor web e receberá comandos de uma página HTML. Esses comandos irão alterar o status dos GPIOs, fazendo com que o RPI controle os motores do robô (direcção e velocidade), além de ligar / desligar um LED (simulado o comando digital de qualquer sistema adicional). Como você pode perceber, o robô é na verdade um caso particular de um projeto de IoT. Você pode na verdade controlar o que quiser e esse tutorial tem a intenção de ser o ponto de partida de novas idéias a serem implementadas no futuro.

No diagrama abaixo, uma visão geral do que desenvolveremos nessa primeira parte:

Full diagram 1

Lista de Materiais

  • RaspberryPi modelo 2 ou 3
  • H-Bridge L293-D
  • DC Motors (2x)
  • Bateria de 9V para os motores
  • Batería de 5V para RPi
  • LEDs e resistores de 30 ohms
  • Protoboard e fios
  • Suporte de acrílico para a motores / eletrônica (pode ser um kit, CDs, etc.)

Instalação da biblioteca WiringPi

WiringPi é uma biblioteca escrita em C utilizada para o acesso direto aos GPIOs do RPi. Ela é muito fácil de usar e simplifica muito qualquer projeto envolvendo RPi e eletrônica (“embebed systems”).

A biblioteca WiringPi inclui um utilitário chamado “GPIO” (wiringpi.com/the-gpio-utility) que pode ser usado para programar e configurar os pinos GPIO. Você pode usar o utilitário para ler e/ou escrever diretamente nos pinos do RPi a partir de comandos do tipo shell scripts. É possível escrever programas inteiros utilizando-se apenas comandos GPIO em um shell-script.

Para instalar WiringPi:

    git clone git: //git.drogon.net/wiringPi

    cd wiringPi

    ./build

Para mostrar na tela a versão do utilitário, use o comando:

    gpio -v

Para ler todos os pinos normalmente acessíveis e apresentar uma tabela com o status dos pinos em diferentes formatos (wiringPi, BCM_GPIO e pinos físicos), você pode usar o comando “gpio readall“, que cria um gráfico de referência cruzada, com os seus modos e valores atuais. Este comando também irá detectar a versão / modelo da RPI eo impresso o diagrama pino apropriado para o seu Pi.

    gpio readall

A tela do monitor abaixo, mostra o resultado após a entrada dos 2 comandos acima.

FWJ8DSAIOO5KJVV.LARGE

Por exemplo, para configurar-se um GPIO utilizando-se o esquema BCM_GPIO como OUTPUT, deve ser utilizar o comando:

    gpio -g mode 10 out

Uma vez que o pino GPIO tenha seu modo definido, você pode definir um estado lógico para o mesmo. No exemplo, o pino passará a um estado lógico HIGH:

    gpio -g write 10 1

GPIO-10 setPara teste, instale o catodo de um LED ao pino GPIO.10,  adicionando-se um resistor de 330ohm entre seu  ânodo e GND. Faça alguns testes para ter certeza que tudo está funcionando.

Além de se poder definir os pinos como saída ou entrada, você também pode definir alguns deles como uma saída do tipo PWM. Este é o caso do pino físico 12 ou GPIO.18.

Para configurar o pino:

    gpio -g mode 18 pwm

ou

    gpio mode 1 pwm       NOTA: ( “1”) é o WPI id para GPIO.18

para configurar um valor PWM:

    gpio pwm 1 XXX            NOTA: [XXX é um valor entre 0 -> 1023]

Exemplo.:

    gpio pwm 1 512               NOTA:  (50% duty-cicle)

Para remover a configuração deste pino particularmante:

    gpio unexport 1

Para remover a configuração de todos os pinos:

    gpio unexportall

Controlando motores com o Raspberry Pi e a biblioteca WiringPi

Neste ponto, a biblioteca WiringPi está instalado e você pode controlar qualquer GPIO diretamente pela linha de comando no monitor de seu RPi. O próximo passo será criar uma lógica para controlar os motores. Para isso, usaremos uma H-Bridge, o L-293-D. Este H-Bridge pode controlar até 2 motores, sendo que para cada um dos motores, são necessárias 3 entradas :

  • H-Bridge block

 

  • Motor Esquerdo: “enable”; “Motor +” e “Motor -“
  • Motor Direito:     “enable”; “Motor +” e “Motor -“

 

 

Em Ambos os motores as entradas “enable” estarão conectadas juntas e serão controladas por GPIO.18. Este pino será o responsável pelo controle de velocidade. Se você não necessitar controlar a velocidade, deixe os pinos em “1” (conectados a +5V), por exemplo.

Convencionaremos que, se queremos que o motor esquerdo vá “para a frente”, deveremos configurar o Motor + (M+) em “1” e Motor – (M-) em “0”. Para que o motor gire em no sentido inverso, faremos o contrário: Motor + (M+) em “0” e Motor – (M-) em “1”. A melhor maneira de realmente definir as entradas correctamente para se controlar o sentido do motor, é para testar-las durante montagem do mesmo.

Vamos atribuir os GPIOs para as entradas da ponte:

  • Motor esquerdo+: GPIO.5
  • Motor esquerdo-: GPIO.6
  • Motor direito +: GPIO.13
  • Motor direito -: GPIO.19

Com base nos pressupostos acima, uma tabela lógica pode ser construída com os possíveis níveis a serem atribuídas aos GPIOs (ver imagem tabela).

motor_table

O próximo passo é a criação de scripts shell para acionar os motores.

Cada script é essencialmente um simples arquivo tipo texto. Quando se tenta executar um arquivo de texto, o sistema operacional buscará pistas para saber se ele é um script ou não, e como lidar com tudo corretamente. Devido a isso, existem algumas orientações que você precisa saber.

  • Cada script deve iniciar  com “#!/bin/bash” (O Hash-Bang Hack}
  • Cada nova linha é um novo comando
  • Linhas de comentário começam com um #
  • Os comandos são rodeados por ()

Quando o OS (shell) analisa um arquivo de texto, a maneira mais direta para identificar o arquivo como um script é fazendo a sua primeira linha ser: #!/bin/bash. Linhas de comentário começam com hashes (#), mas acrescentando o Bang (!) e o shell path depois, forçar o OS a executar o script.

Por exemplo, para criar um shell script para comandar os motores  a irem “para a frente”, e com base na tabela acima, devemos tomar um editor de texto e  criar o arquivo abaixo  (Use o editor que melhor funcione para você Eu estou usando NANO para isso):

     sudo nano forward.cgi

        #!/bin/bash

        gpio -g write 5 1

        gpio -g write 6 0

        gpio -g write 13 1

       gpio -g write 19 0

.

Uma vez que o script é criado, devemos dar-lhe permissão para ser executado:

    sudo chmod 755 forward.cgi

Agora, para executar o script:

    sudo ./forward.cgi

Foram utilizados LEDs para testar os scripts, os motores reais serão adicionados em uma próxima etapa.

IMG_3722

Note que uso .cgi como extensão de arquivo. CGI significa “Common Gateway Interface”. É uma maneira padrão para servidores web interagir com programas executáveis instalados em um servidor que gera páginas web dinamicamente. Tais programas são conhecidos como scripts CGI ou simplesmente CGIs; eles geralmente são escritos em uma linguagem de script, mas podem ser escritos em qualquer linguagem de programação.

Continuando, a mesma idéia deve ser aplicada para as outras combinações da tabela anterior:

    sudo nano stop.cgi

          #!/bin/bash

          gpio -g write 5 0

          gpio -g write 6 0

          gpio -g write 13 0

         gpio -g write 19 0

 .

    sudo nano reverse.cgi

          #!/bin/bash

          gpio -g write 5 0

          gpio -g write 6 1

          gpio -g write 13 0

          gpio -g write 19 1

.

    sudo nano left.cgi

         #!/bin/bash

         gpio -g write 5 0

         gpio -g write 6 1

         gpio -g write 13 1

        gpio -g write 19 0

.

    sudo nano right.cgi

          #!/bin/bash

          gpio -g write 5 1

         gpio -g write 6 0

         gpio -g write 13 0

         gpio -g write 19 1

.

Uma vez criados os scripts, você deverá dar-lhes permissão para serem executados, o mesmo que foi feito com forward.cgi

     sudo chmod 755 stop.cgi

    sudo chmod 755 reverse.cgi

    sudo chmod 755 left.cgi

    sudo chmod 755 right.cgi

 

Agora, execute alguns testes para confirmar que tudo está funcionando:

     ./forward.cgi

    ./left.cgi

    ./reverse.cgi

    ./right.cgi

    ./stop.cgi

Uma boa prática é temos um diretório específico para os programas utilizados e chamar-lo de “bin”. Assim, para guardar os scripts que vamos usar no projeto, deveremos criar um diretório, por exemplo cgi-bin contendo todos os scripts executáveis (ou arquivos binários).

Criemos o diretório “www” sob  “var”, onde a nossa página web será localizado e sob ele, o diretório “cgi-bin” com os scripts:

    sudo mkdir /var/www

    sudo mkdir /var/www/cgi-bin

Agora, movamos todos os arquivos para este novo diretório:

    sudo mv /*.sgi /var/www/cgi-bin

    cd /var/www/cgi-bin

Usando o comando ls, poderemos ver os arquivos criados:

FP916TIIOO5KK1R.LARGE

Ligar / desligar um LED e controlar  a velocidade dos motores (opcional)

Uma vez que vamos utilizar como H-Bridge, oL293-D para o controle motor, devemos decidir se além da direção, também queremos controlar a velocidade.

Duas possibilidades aqui:

  • Velocidade fixa: A ligar pinos 1 e 9 da ponte (enable) em + 5V (velocidade máxima) ou qualquer outro valor proporcional usando um divisor de tensão com 2 resistores
  • Pinos 1 e 9 ligados ao Raspberry Pi GPIO.18 (saída PWM)

Criaremos um grupo de scripts da mesma maneira que fizemos para definir o controle de direção:

     sudo nano nospeed.cgi

           #!/bin/bash

           gpio pwm 1 0

.

    sudo nano lowspeed.cgi

          #!/bin/bash

          gpio pwm 1 250

.

    sudo nano regularspeed.cgi

         #!/bin/bash

         gpio pwm 1 512

.

     sudo noano highspeed.cgi

           #!/bin/bash

           gpio pwm 1 1023

.
Uma vez que os scripts são criados, você deve dar-lhes permissão para serem executados:

    sudo chmod 755 nospeed.cgi

    sudo chmod 755 lowspeed.cgi

    sudo chmod 755 regularspeed.cgi

    sudo chmod 755 highspeed.cgi

 

Agora, é só executar alguns testes para confirmar que tudo está funcionando:

    ./lowspeedcgi

    ./regularspeed.cgi

    ./highspeed.cgi

    ./nospeedcgi

Para testes,  conectaremos um LED ao GPIO.18, o que permitirá ver pela intensidade de seu brilho, que o comando está funcionando.

Por último, criaremos um script extra para controlar uma saída digital, que será utilizada para ligar ou desligar uma lâmpada, por exemplo. Usaremos o GPIO.10 para isso:

     sudo nano llighton.cgi

         #!/bin/bash

         gpio -g write 10 1

.

     sudo nano llightoff.cgi

           #!/bin/bash

           gpio -g write 10 0

.

    sudo chmod 755 lighton.cgi

    sudo chmod 755 lightoff.cgi

 

Uma última coisa antes de passar para outra etapa. Se você reiniciar o RPi, o GPIOs retornarão ao seu estado padrão que é INPUT. Então, temos de alterar o script /etc/rc.local  o qual é executado no início de todo boot.

Pouco antes do último comando no script (exit 0), devemos incluir os comandos de definição de modo dos GPIOs:

     sudo nano /etc/rc.local

            …

           gpio -g mode 5 out

           gpio -g mode 6 out

           gpio -g mode 13 out

           gpio -g mode 19 out

           gpio -g mode 10 out

           gpio mode 1 pwm

           exit 0

É bom ter o comando de PWM como a ultima linha de comando antes do “exit 0”.

FQB7M04IOO5L4SC.LARGE

Agora, sempre que que o RPI é iniciado, ele estará pronto para controlar as saídas projetadas.

Aproveite e faça o reboot do sistema agora:

  sudo reboot

Instalando o WebServer

Instalaremos o lighttpd que é um servidor web muito leve e rápido (pode ser usado em vez do Apache por exemplo). Conforme descrito em sua wikipage “, Lighttpd é um servidor web seguro, rápido e muito flexível, otimizado para ambientes de alto desempenho. Ele tem um baixo consumo de memória em comparação com outros servidores web, além de cuidar de não carregar demasiado a CPU.

Instalemos o  Lighttpd e seus componentes:

     sudo apt-get -y install lighttpd

    sudo lighttpd-enable-mod cgi

    sudo lighttpd-enable-mod fastcgi

Por default, Lighttpd procurará uma página index.html no diretório: / var / www / html. Vamos alterar isso, de modo que o index.html fique armazenado diretamente na pasta: / var / www. Para isso, é preciso editar o arquivo de configuração do Lighttpd:

    sudo nano /etc/lighttpd/lighttpd.conf

alterar:

             server.document-root =“/var/www/html”

para:

             server.document-root =“/var/www”

 

A fim de que esta alteração tenha efeito, é necessário parar e reiniciar o servidor web:

    sudo /etc/init.d/lighttpd stop

    sudo /etc/init.d/lighttpd start

 

Neste ponto, o servidor web está em execução e se a página index.html está localizado em / var / www, podemos acessa-la a partir de qualquer navegador, apenas digitando o endereço IP RPi.

Criaremos uma página simples para efeito de testes.

Primeiro, criemos um subdiretório para armazenar as imagens da pagina:

    mkdir /var/www/images

(Note que para o teste, já me enviei um arquivo .png para o diretório  (/images/robot52.png):

    cd / var / www

    sudo index.html nano

<html>

<head>

</head>

<style>

body {background-color: lightyellow}

h1 {color:blue}

</style>

<body>

<div style="text-align:center">

<h1>MJRoBot RPi Web Robot Control</h1>

<br><br>

<img src="/images/robot52.png">

</body>

</html>

Depois de terminar a edição da página, guardar-la e alterar as permissões:

    sudo chmod 755 index.html

 

Agora, abra o navegador e digite o endereço IP Raspberry Pi, por exemplo no meu caso: 10.0.0.31

O resultado final pode ser visto abaixo:

F7M7EEBIOO5L527.LARGE

Criando uma página HTML para controlar o robô

Vamos pensar em um projeto simples para a nossa página. Que comandos podemos ter?

  • Dois botões para ligar e desligar as luzes ==> irão trabalhar com os scripts: lighton.cgi e lighoff.cgi
  • Cinco botões para controle de direção do motor ==> irão trabalhar com os scripts: forward.cgi, stop.cgi, left.cgi, right.cgi e reverse.cgi
  • Quatro botões para controle de velocidade do motor ==> irão trabalhar com os escripts: nospeed.cgi, lowspeed.cgi, regularspeed.cgi e highspeed.cgi

Usando o arquivo em HTML  index.html que acabamos de criar na última etapa, incluir emos botões que por sua vez chamarão funções para execução dos respectivos scripts.

Por exemplo, vamos criar um botão para acender o LED (GPIO.10):

button {

 color: blue;

 background:lightgrey;

 border: 1px solid #000;

 border-radius: 8px;

 position: center;

}

<button style="height: 50.0px;width: 100.0px;"><img src="/images/lighton.png" style="height: 40.0px;"></button>

botao

O código HTML acima criará um botão redondo com uma “lâmpada iluminada”.

Quando o botão é pressionado, devido o comando “onclick = lighton ()”, a função “lighton()”  é chamada:

function lighton()

    {

        xmlhttp.open("GET","cgi-bin/lighton.cgi",true);

        xmlhttp.send();

    }

E uma vez que a função lighton() é chamado, o script lighton.cgi é executado e “voilá”, o LED acenderá.

O mesmo procedimento deve ser utilizado para todos os demais botões. Há alguns outros comandos HTML que serão utilizados para organizar o look &fill  da pagina.

F56I7P0IOO5L6UM.LARGE

Abaixo o arquivo fonte HTML pode ser visto  (testar a página com a configuração de LEDs utilizados nas etapas anteriores):

Link para a pagina HTML

Montagem do corpo do robô

A primeira coisa é encontrar uma plataforma. Eu decidi aproveitar um kit 4WD com 4 motores de corrente contínua. Percebendo que o 4WD não é fácil de controlar (para fazer curvas) e uma vez que a idéia aqui não é analise de mobilidade, eu montei o robô completo, mas para o teste real eu só usarei as rodas traseiras, adicionando um “Coster” na parte frontal (sobras de um desodorante do tipo “rolon”).

 

Depois que o corpo e os motores estão no lugar, é hora de incluir um protoboard, faça as conexões do motor e testá-lo. Utilize a bateria externa para testar cada um dos motores.

IMG_3563

Os motores irão ser controlado por uma ponte-H L293-D conforme mostrado no diagrama abaixo:

Neste ponto, você também pode fazer testes, controlando os motores usando + 5V e GND nas entradas da ponte, simulando as GPIOs do RPI.

Instalação do RPI

Uma vez que tudo está funcionando corretamante, é hora de adicionar o Raspberry Pi. O circuito completo é mostrado abaixo:

Circuit Motors only

Instalar a bateria RPI 5V entre os motores com menor chassis nível. A RPI está no topo.

Faça todas as conexões de cabos antes de ligar o RPI.

 

Se todas as etapas anteriores foram OK, você poderá controlar o robô, usando o endereço IP RPi.

IMG_3726

Abra seu WebBrowser favorito e viagem segura!

Abaixo, um vídeo onde o robô é testado usando a página da Web:

 

Segunda Parte: Streaming de video e controle remoto (Pan / Tilt) da câmera pela internet

Como discutido na introdução, o robô é quase uma desculpa para desenvolver um projeto de Internet das coisas. o que fizemos aqui é controlar os GPIOs do RPI através da internet.a A partir daí,  podemos controlar praticamente tudo!

Nesta segunda parte, exploraremos a PiCam, descobriremos como fazer streaming de vídeo e também a forma de controlar a posição da câmera usando servos. Controlar uma câmera através da internet pode ter várias utilidades, inclusive para uso em segurança.

O objetivo final, será unir as duas partes do projeto, criando um robô que pode ser não só ser controlado pela internet, mas também transmitir streaming de vídeo.

 

O diagrama de blocos abaixo mostra a idéia da segunda parte do projeto.

WebCam Ctrl

O RPI será programado como um servidor web e irá receber comandos de uma página HTML. Esses comandos irão controlar os GPIOs do RPi, posicionando a PiCam através dos servomotores (Pan / horizontal e Tilt/ vertical).

Lista de Materiais

A lista de materiais anterior, acrescentaremos a PiCam e dois mini-servos de 180 graus. Será necessário também um regulador de voltagen de 6V (7806), para se poder usar a mesma bateria de 9V já instalada para alimentação dos motores.

Streaming vídeo

Instalaremos o streamer de vídeo PiCam baseado no tutorial:

Raspberry Pi camera board video streaming , desenvolvido por Miguel Mota (atualização de  19 de janeiro de 2014).

Primeiro, vamos atualizar o sistema operacional:

    sudo apt-get update

    sudo apt-get upgrade

Instalar a versão dev de libjpeg:

     sudo apt-get install libjpeg62-turbo-dev   Nota: libjpeg62-dev é obsoleto e foi substituído por este)

Instalar cmake:

    sudo apt-get install cmake

Baixar mjpg-streamer com o plugin raspicam:

    git clone https://github.com/jacksonliam/mjpg-streamer.git ~/mjpg-streamer

Alterar o diretório:

     cd ~/mjpg-streamer/mjpg-streamer-experimental

Compilar:

    make clean all

Substituir o velho jpg-streamer:

 

    sudo rm -rf /opt/mjpg-streamer

    sudo mv ~/mjpg-streamer/mjpg-streamer-experimental /opt/mjpg-streamer

    sudo rm -rf ~/mjpg-streamer

Iniciar a transmissão:

    LD_LIBRARY_PATH=/opt/mjpg-streamer/ /opt/mjpg-streamer/mjpg_streamer -i “input_raspicam.so -fps 15 -q 50 -x 640 -y 480” -o “output_http.so -p 9000 -w /opt/mjpg-streamer/www” &

No Monitor você poderá ver as informações sobre o streaming:

MJPG Streamer Version.: 2.0

i: fps.............: 15

i: resolution........: 640 x 480

i: camera parameters..............:

Sharpness 0, Contrast 0, Brightness 50, Saturation 0,

ISO 400, Video Stabilisation No, Exposure compensation 0

Exposure Mode 'auto', AWB Mode 'auto',

Image Effect 'none', Metering Mode 'average',

Colour Effect Enabled No with U = 128, V = 128

Rotation 0, hflip No, flip No

www-folder-path...: /opt/mjpg-streamer/www/

HTTP TCP port.....: 9000

username:password.: disabled

commands..........: enabled

Starting Camera

Encoder Buffer Size 81920

A câmera deverá estar funcionando. Vá para o seu navegador e digite:

http: // O seu endereço IP: 9000 / stream.html

A página de teste como a de abaixo deverá aparecer (com a imagem capturada por sua camera).

Video Streamer test

Note que se você quiser alterar a porta de comunicação, troque o parâmetro “9000” na linha de comando que voce entrou no monitor do RPI,por um que melhor funcione para você. Observe que estamos trabalhando com 15 quadros por segundo (fps) e uma resolução de 640×480. Você também pode alterar esses parâmetros na linha de comando.

Você deverá voltar a entrar com a linha de comando acima para iniciar a transmissão de toda a vez que seu sistema seja reiniciado (reboot), a menos que você inclua o comando no script /etc/rc.local, como mostrado abaixo:

    sudo nano /etc/rc.local

            …

            LD_LIBRARY_PATH=/opt/mjpg-streamer/ /opt/mjpg-streamer/mjpg_streamer -i “input_raspicam.so -fps 15 -q 50 -x 640 -y 480” -o “output_http.so -p 9000 -w /opt/mjpg-streamer/www” &

Considerando que o webserver já esteja instalado, necessitamos introduzir a linha abaixo para que o streaming the video apareça em nossa página:

<iframe src="http://Your IP Adress:9000/javascript_simple.html" frameborder="0" align="middle" width="640" height="480" align="middle" scrolling="no">$

Abaixo, podemos observar a simples página em HTML com a linha acima incluída:

<html>

<head>

</head>

<style>

body {background-color: lightyellow}

h1 {color:blue}

</style>

<body>

<div style="text-align:center">

<h1> MJRoBot RPi Web Robot Control   <img style="height: 100px"src="/images/robot52.png"> </h1>

<br><br>

<iframe src="http://10.0.1.31:9000/javascript_simple.html" frameborder="0" align="middle" width="640" height="480" align="middle" scrolling="no">$

</body>

</html>

Entrando com o IP do RPi no navegador, chegamos ao resultado abaixo:

MJRoBot Video Streamer test

Instalação da biblioteca ServoBlaster

Uma grande biblioteca a ser usada para controlar os servos é a ServoBlaster

Este é um software específico para o Raspberry Pi, que fornece uma interface para controlar múltiplos servos através dos pinos GPIO. Você controla as posições dos servos enviando comandos que variam a largura de pulso de uma saída. O GPIO manterá a largura do pulso até que você envíe um novo comando alterando o valor anterior.

Por default, ServoBlaster está configurado para controlar 8 servos, mas você pode configurá-lo para até 21. Servos normalmente precisam de um pulso ativo de algo entre 0,5 ms e 2,5 ms, onde a largura do pulso controla a posição do servo. O pulso deve ser repetido aproximadamente a cada 20 ms, embora essa frequência não seja crítica. A largura de pulso sim, é fundamental pois é ela é traduzida diretamente para a posição de servo.

Além de controlar servos, ServoBlaster pode ser configurado para gerar larguras de pulsos entre 0 e 100% (duty-cycle), tornando-o adequado para controlar o brilho de até 21 LEDs, por exemplo.

O ServoBlaster cria um device file /dev/servoblaster, no qual você poderá enviar comandos. O formato do comando é:

[Servo-número] = [servo-position]

(Ex .: echo P1-11 = 80% >/dev/servoblaster)

ou

P [header] – [pin] = [servo-position]

(Ex: echo P0 = 80% >/dev/servoblaster)

Primeiramente, clonemos projeto Richardghirst do GitHub:

    cd /

    sudo git clone https://github.com/richardghirst/PiBits

 

Alterar diretório:

   cd PiBits

   cd ServoBlaster

   cd user

 

Liste o conteúdo e verificar se o diretório contém o arquivo “servod.c”

    ls

 

Compilar e instalar o arquivo servod.c :

    sudo make servod

    sudo make install

 

Neste momento, o programa servod ser instalado. Alterar as permissões e executar o programa para poder testar-lo:

    sudo chmod 755 servod

    sudo ./servod

 

Se tudo estiver OK, você deverá ver no monitor, as seguintes informações:

Board model:                     2

GPIO configuration:            P1 (40 pins)

Using hardware:                PWM

Using DMA channel:              14

Idle timeout:             Disabled

Number of servos:                8

Servo cycle time:            20000us

Pulse increment step size:      10us

Minimum width value:            50 (500us)

Maximum width value:           250 (2500us)

Output levels:              Normal

Using P1 pins:               7,11,12,13,15,16,18,22

Servo mapping:

     0 on P1-7           GPIO-4

     1 on P1-11          GPIO-17

     2 on P1-12          GPIO-18

     3 on P1-13          GPIO-27

     4 on P1-15          GPIO-22

     5 on P1-16          GPIO-23

     6 on P1-18          GPIO-24

     7 on P1-22          GPIO-25

Observe o Mapeamento dos servos acima. Temos por default 8 servos configurados, mas por exemplo no caso do controle da camera,  só precisaremos de 2, por isso deveremos restringir os pinos a ser em utilizados. Consideremos apenas:

  • GPIO.17 (P1-11) que será usado para TILT (controle vertical)
  • GPIO.23 (P1-16) que será usado para PAN (controle horizontal)

Para se definir os pinos que serão configurados, entrar com os parâmetros abaixo:

    sudo ./servod –p1pins = 11,16

 

Uma vez executado o comando acima, o monitor deverá agora mostrar em sua parte inferior:

Using P1 pins: 11,16

Servo mapping:

0 on P1-11 GPIO-17

1 on P1-16 GPIO-23

Observe também que se você reiniciar o RPI, a configuração será perdida, por isso é importante incluir o último comando no /etc/rc.local

    sudo nano /etc/rc.local 

            cd /PiBits/ServoBlaster/user

            sudo ./servod –p1pins=11,16

            cd

            …

 

Também é importante a mudança no script abaixo:

    sudo nano /etc/init.d/servoblaster

          …

          case “$1” in
start)

          /usr/local/sbin/servod $OPTS >/dev/null

change to:

          /usr/local/sbin/servod –p1pins=11,16 $OPTS >/dev/null

 

Abaixo o script completo para referência:

#!/bin/sh

### BEGIN INIT INFO

# Provides:          servoblaster

# Required-Start:    hostname $local_fs

# Required-Stop:

# Should-Start:

# Default-Start:     2 3 4 5

# Default-Stop:      0 1 6

# Short-Description: Start/stop servod.

# Description:       This script starts/stops servod.

### END INIT INFO

PATH=/sbin:/usr/sbin:/bin:/usr/bin

. /lib/init/vars.sh

OPTS="--idle-timeout=2000"

STATUSFILE="/tmp/servoblaster-status"

if [ $( id -u ) != 0 ]; then

        echo "ERROR: Must be run as root"

        exit 1

fi

case "$1" in

  start)

        /usr/local/sbin/servod --p1pins=11,16 $OPTS >/dev/null<br>        ;;

  restart|reload|force-reload)

        killall servod

        /usr/local/sbin/servod $OPTS >/dev/null

        ;;

  stop)

        killall servod

        ;;

  status)

        if [ ! -e /dev/servoblaster ]; then

                echo "ERROR: /dev/servoblaster does not exist"

                exit 2

        fi

        rm -f $STATUSFILE

        echo "status $STATUSFILE" > /dev/servoblaster

        sleep 0.2

        if [ ! -e $STATUSFILE ]; then

                echo "ERROR: servod not responding"

                exit 3

        elif grep -q "^OK" $STATUSFILE; then

                echo "OK"

                exit 0

        elif grep "^ERROR:" $STATUSFILE; then

                exit 4

        else

                echo "ERROR: No status from servod"

                exit 5

        fi

        ;;

  *)

        echo "Usage: servoblaster [start|stop|status]" >&2

        exit 6

        ;;

esac

exit 0

Agora reinicie o sistema, para que as alterações podem ser permanentes

    sudo reboot

 

É isso aí. Servo Blaster está instalado. Note que a partir deste momento, ServoBlaster irá reconhecer apenas dois servos:

  • servo 0 ==> p1-11
  • servo 1 ==> p1-16

 

O comando echo pode ser executado em qualquer um dos formatos acima, com o mesmo resultado:

    echo P1-11=40% >/dev/servoblaster

    echo P1-16=60% >/dev/servoblaster

or

    echo 0=40% >/dev/servoblaster

    echo 1=60% >/dev/servoblaster

Montagem do mecanismo de PAN/TILT

Existem vários mecanismos Pan / Tilt no mercado, mas decidi por montar uma forma muito simples, só amarrando os servos e a PiCam como você pode ver nas fotos abaixo:

Instalar o servos ao RPI

RPi&amp;Servos circuit diagram

  • Ligue o cabo de dados dos servos ao RPI, como mostrado acima.
  • Conecte o + V de ambos os servos em uma fonte de tensão separada a do RPI (claro, os GNDs devem estar interligados). Uma vez que você irá instalar o mecanismo de Pan / Tilt no robô, você poderá utilizar a mesma bateria (9V) que foi usado com os motores de corrente contínua. Neste caso, um regulador de tensão de 6V será necessário.

Para testar os servos, use o comando”echo” do Servoblaster . Você poderá usar tanto valores de ângulo quanto de percentual. Teste a melhor faixa para seu mecanismo de Pan / Tilt:

RPi&amp;Servos block diagram4

TILT Servo:

    echo P1-11=20% >/dev/servoblaster (looking down)

    echo P1-11=60% >/dev/servoblaster (looking front)

    echo P1-11=90% >/dev/servoblaster (looking up)

PAN Servo:

    echo P1-16=30% >/dev/servoblaster

    echo P1-16=62% >/dev/servoblaster

    echo P1-16=90% >/dev/servoblaster

 

Podemos facilmente scripts para controlar a posição dos servos. Por exemplo, para criar um shell script para posicionar a câmera “olhando para frente”,  baseado nos valores que você encontrou, devemos criar o  arquivo abaixo:

    sudo nano cam_view_front.cgi

           #!/bin/bash

           echo P1-11=60% >/dev/servoblaster

           echo P1-16=62% >/dev/servoblaster

.

Uma vez que o script é criado, devemos dar-lhe permissão para ser executado:

    sudo chmod 755 cam_view_front.cgi

 

Agora, executar o script:

    ./cam_view_front.cgi

 

Movimente a câmera para qualquer posição usando o comando echo e depois executar o novo script. Você vai ver que a câmera voltará automaticamente à sua posição “vista frontal”.

Continuando, a mesma idéia deve ser aplicada para as outras possíveis posições de câmera.

Para gerar minha página, optei por criar 5 posições intermediárias para TILT e 5 posições intermediárias para PAN. Você também pode optar por usar “sliders” para uma mudança de posição mais contínua. Você decide.

Usando o mesmo princípio como o descrito para o script “cam_view_front.cgi”, vamos criar 10 novos scripts:

  1. leftpan.cgi                 ==> 90%
  2. leftcenterpan.cgi     ==> 76%
  3. centerpan.cgi            ==> 62%
  4. rightcenterpan.cgi  ==> 46%
  5. rightpan.cgi               ==> 30%
  6. downtilt.cgi               ==> 20%
  7. downcentertilt.cgi   ==> 40%
  8. centertilt.cgi              ==> 60%
  9. upcentertilt.cgi         ==> 75%
  10. uptilt.cgi                     ==> 90%

Criando uma página HTML para controlar a câmera

Usando o index.html que criamos para o streaming da camera, vamos incluir os 10 botões que chamam funções para executar os scripts criados anteriormente.

 

PiCamWebPageCtrl

Abaixo, o código HTML completo para a criação da página:

Codigo HTML para pagina index.html

IMG_3754

Integrando a Câmara e os Servos ao Robô.

Agora que temos o nosso RPI streaming vídeo e podendo posicionar a câmera via internet, vamos integrar esta segunda parte do projeto ao robô que foi criado na primeira parte.

O diagrama de blocos do início deste post, mostra como o projeto pode ser integrado e o circuito abaixo mostrar como fazer as conexões:

full circuit

Em seguida, tomemos a página web desenvolvida na segunda parte e adicionemos a parte de botões e funções criadas na parte 1 para o  controle dos motores.

Link para página HTML final

Final Robot ctrl webpage2

O robô completo toma vida!

O último passo é a integração do mecanismo PAN/TILT a base do robô:

O vídeo abaixo explica como funciona o robô completo:

 

Concluíndo….

That’s all folks!

No meu repositório de arquivos do GITHUB, voce encontrará todos os códigos fonte HTML e Shell Scripts utilizados neste projeto.

 https://github.com/Mjrovai/MJRoBot-Web-RPi-Robot

Como sempre, espero que este projeto ajude outras pessoas a encontrar seu caminho no apaixonante mundo da eletrônica e dos robôs!

Não deixe de visitar e seguir minha página: MJRoBot.org no Facebook

Saludos desde el sur del mundo! 😉

Um abraço e até o próximo post!

Obrigado

Marcelo

“Raqueando” o carrinho de controle remoto

Neste projeto, vamos desarmar um carrinho de controle remoto, substituindo sua eletrônica por um microprocessador Arduino controlado por dispositivo Android.

Sempre que passo em uma loja de brinquedos e vejo carrinhos de controle remoto na vitrine, fico doido para levar-los para casa, deve ser porque tinha adoração por este tipo de brinquedo, mas por falta de opções e grana, não cheguei a ter um quando criança. Para compensar e ter uma desculpa para comprar um 😉 vamos desmontar um desses, “raqueando” suas principais partes e substituindo a eletrônica embarcada original por um microcontrolador tipo Arduino facilmente comandado a distância por um dispositivo Android. Com isso, ficará muito fácil adicionar ao carrinho novas funcionalidades, sensores, etc. É uma forma muito barata de se construir um robô para uso didático.

O vídeo abaixo, mostra como ficará o projeto final:

O MJRoBot Autobot em ação

Conhecendo o carrinho

IMG_2430

O primeiro passo será desarmar o carrinho para se ter uma idéia de como ele funciona, o que se aproveitará e o que se deve ser adicionado:

Na parte inferior da carcaça, estão os os dois motores (frontal e traseiro), o compartimento da bateria (recarregável, 5V) e o modulo eletrônico.  Na parte superior da carcaça, está a antena (placa de cobre conectada a um cabo negro) e os 4 LEDs que funcionam como os faróis dianteiros e traseiros. Cada LED está conectado separadamente por um par de fios. Com esse arranjo, se pode:

  • Acender os faróis (acender os dois LEDs frontais)
  • Dar pisca (acender os LEDs esquerdos ou direitos)
  • Sinalizar uma frenagem  (acender os dois LEDs traseiros)

O diagrama em blocos da eletrônica original é:

Original Block Diagram

Em relação a mobilidade, nos projetos anteriores de robôs utilizamos a técnica de “Differential Steering” onde variando-se o sentido de giro das rodas, varia-se a direção do rob0, mas aqui se pode ver que o carrinho, apesar de também possuir dois motores DC , possui uma confirmação diferente de motores:

FullSizeRender 15.jpgO motor frontal, não possui nenhum controle de velocidade ou posição, sendo utilizado apenas para virar o carrinho “à direita”ou “à esquerda”, movendo-se simultaneamente todo o conjunto rodas/eixo frontal (mais ou menos como em um automóvel). Em geral o carrinho sempre se move para a frente em linha reta e o motor está desligado, deixando assim as rodas livres. Um comando para “virar à esquerda” pVersion 2or exemplo, fará o motor girar, fazendo o mecanismo de engrenagens como um todo se mover para a esquerda. Enquanto o motor estiver alimentado o mecanismo ficará nessa posição limite e o carrinho continuará “virando à esquerda”).

Turn_Left

IMG_3511

 

O motor traseiro está acoplado a um sistema redutor de velocidade por engrenagens e fornece torque variável as rodas traseiras (conjunto fixo). Pode-se assim variar a velocidade, mas não a direção (as duas rodas sempre girarão em um mesmo sentido).

Raqueando a eletrônica embarcada

Uma vez que tudo foi identificado, é a hora de remover a eletrônica original, deixando somente os cabos dos motores e bateria. Como não vou voltar a usar a carcaça superior por enquanto, não me preocuparei com os LEDs originais.

No lugar do módulo eletrônico, usaremos um Arduino Uno, que será o responsável pela lógica do processamento, acionando os motores, LEDs e um Buzzer. O controle remoto que antes era conseguido por um sistema receptor de RF de 27MHz será agora substituído pelo modulo HC-06, o qual se comunicará com um dispositivo Android. A bateria original do carrinho será mantida exclusivamente para a alimentação dos motores via a H-Bridge L293D, pois os motores DC além de consumirem bastante corrente geram ruído devido a suas escovas. Pela maneira que o carrinho foi construído, o motor frontal consome bastante energia no momento em que recebe um comando de virar para um dos lados, pois seu eixo será travado e o consumo de corrente será alto. Para o Arduino usaremos uma bateria de 9V (Vin), sendo que o modulo HC-06 será alimentado pela saída de 5V do Arduino).

O novo diagrama de blocos para implementação do circuito é:

Block Diagram

Neste ponto é importante testar os motores individualmente. Usando uma bateria, alimente o motor e observe para que lado lado ele gira. Inverta a bateria e repita o teste anotando as cores dos fios:

No caso de meu carrinho, temos:

Front Motor:

  • Yellow: LEFT
  • White: RIGH

Rear Motor:

  • Green: Backward
  • Blue: Forward

Montagem Final

O circuito (as cores dos fios do motor não são as corretas):

circuit

O HW do novo carrinho à controle remoto está pronto para seus primeiros testes. Agora, falta implementar o SW e o aplicativo Android.

O Aplicativo Android

MJRoBot BT Icon

Para controlar o carrinho, usaremos uma App que desenvolvi utilizando o MIT AppInventor 2: “MJRoBot BT Remote Control”. A app pode ser baixada gratuitamente da loja da Google através do link: MJRoBot BT Remote Control

A app possui uma interface simples, permitindo o envio de comandos ao modulo de BT tanto em modo TEXT, como diretamente via botões pre-programados (cada vez que um botão é pressionado, um caracter é enviado):

Screenshot_2015-12-11-20-24-01w: Forward

s: Backward

d: Right

a: Left

f: Stop

p: ON/OFF

m: Manual / Automatic

+: Speed+

-: Speed-

 

Também existe uma janela de texto para mensagens recebidas do modulo BT. Esta característica é bem importante durante a fase de testes, pois pode ser usada da mesma maneira que o “Serial monitor”.

O Diagrama de blocos para o projeto no MIT appInventor2:

app_Blocks

O Código Arduino

O bloco principal do código é bem simples:

void loop() 
{
   checkBTcmd(); // verify if a command is received from BT remote control
   receiveCmd ();
 
   if (turnOn) manualCmd ();
   else stopRobot ();
}

A primeira coisa a se fazer é verificar se existe algum novo comando BT chegando. Quem checará isso é a função “checkBTcmd()”:

void checkBTcmd() // verify if a command is received from BT remote control
{ 
   if (BT1.available()) 
   { 
      command = BT1.read();
      BT1.flush();
   }
}

A variável “turnOn” é utilizada para ligar ou desligar o carrinho. A função “receiveCmd()” será a encarregada de definir o status dessa variável em função do comando recebido do modulo BT. Observe que pela lógica usada, cada vez que o botão vermelho com o símbolo de “POWER” é pressionado, o caracter “p” será enviado pelo app e a a variável “turnOn”, mudará se valor (de 1 para 0 e vice e versa):

void receiveCmd ()
{
   switch (command)
   {
     case 'p':  
        turnOn = !turnOn;
        command = 0;
        analogWrite(ledStatus, turnOn*128); // Robot ON - Led ON
        beep(outBuz, 1000, 100);
        BT1.print(" COMMAND ON/OFF");
        BT1.println('\n');
        delay(200); //Delay to call attention to mode change
        break;
 
     case 'm': //not used here
        break;
    } 
}

Voltando ao loop principal, enquanto a variável “turnOn” é HIGH (1), a função “manualCmd()” executará um comando, dependendo do caracter recebido:

void manualCmd()
{
   switch (command)
   {
     case 'f': 
        moveStop(); //turn off both motors
        state = command;
        break;

     case 'w': 
        moveForward(); 
        state = command; 
        break;

     case 'd': 
        moveRight();
        break;

     case 'a': 
        moveLeft();
        break;
 
     case 's': 
        moveBackward();
        state = command;
        break;

     case '+': 
        if (state == 'w')
         {
           motorSpeed = motorSpeed + 10;
           if (motorSpeed > MAX_SPEED) motorSpeed = MAX_SPEED;
           command = 'w';
         } else command = state;
         break;

     case '-': 
        if (state == 'w') motorSpeed = motorSpeed - 10; 
        if (motorSpeed < MIN_SPEED ) motorSpeed = MIN_SPEED;
        command = state;
        break;
    }
}

Por exemplo, se o comando “w” é recebido a função específica para mover o carrinho para a frente “moveForward()” é executada.

Em caso “powerOn” esteja em HIGH e a tecla “POWER” seja pressionada, a variável “powerOn”, passará a LOW e a função “stopRobot()” será a executada ao invés de “manualCmd()”. Esta função garante que o motor traseiro esteja parado, os LEDs apagados e variáveis zeradas.

void stopRobot ()
{
   digitalWrite(ledBlue, LOW);
   digitalWrite(ledRed, LOW);
 
   state = 0;
   moveStop(); //turn off both motors
}

Acionando os motores DC via H-Bridge

Os motores frontal e traseiro estão ligados a ponte-H como mostrado abaixo:

H-Bridge

e a cada um dos pinos do Arduino, os quais deverão ser definidos como OUTPUT, será atribuído uma variável:

const int rearMtFw = 4;     // Rear Motor - FW
const int rearMtBw = 7;     // Rear Motor - BW
const int rearMtEne = 6;    // Rear Motor - enable
const int frontMtLeft = 2;  // Front Motor - turn Left
const int frontMtRight = 3; // Front Motor - turn right 
const int frontMtEne = 5;   // Front Motor enable

Por exemplo, se desejamos mover o carrinho para a frente, a função “moveForward()” deverá colocar o pino 4 en HIGH e o pino 7 em LOW, isso fará com que a corrente flua “no sentido horário”, como mostrado no diagrama abaixo:

FW example H-Bridge

O pino 6 é o “enable”, somente quando ele estiver em “HIGH”, a ponte permitirá o fluxo de corrente pelo motor. Como este pino possui característica PWM, a velocidade com que o motor girará, dependerá do valor da variável “motorSpeed”, no pino 6 (valor de 0 255).

A função também deverá garantir que o motor frontal “gire livremente”, e para isso o pino 5, que é o pino de “enable” deverá estar em LOW (o status dos pinos 2 e 3 não importam, uma vez que o enable está el LOW). O LED vermelho, que funciona como “luz de ré”, deverá sempre estar apagado quando o carrinho se move para a frente:

void moveForward() // rear motor FW
{ 
   analogWrite(rearMtEne, motorSpeed);
   digitalWrite(rearMtFw, HIGH);
   digitalWrite(rearMtBw, LOW);
 
   digitalWrite(frontMtEne, LOW);
   digitalWrite(ledRed, LOW);
   delay(5);
}

por analogia, é obvio que para o carrinho “mover para trás”, basta que o motor gire na direção contrária. Para isto, o pino 4 deverá estar em LOW e o pino 7 em HIGH. Note que nesse caso a “luz de ré”, deverá estar acesa. A função neste caso será:

void moveBackward() // rear motor BW
{ 
   analogWrite(rearMtEne, motorSpeed);
   digitalWrite(rearMtFw, LOW);
   digitalWrite(rearMtBw, HIGH);
 
   digitalWrite(frontMtEne, LOW);
   digitalWrite(ledRed, HIGH);
   delay(5);
}

O mesmo raciocínio pode ser utilizado para o motor frontal, somente que nesse caso, não existe controle de velocidade. Colocando o pino 2 (enable) em HIGH habilita o motor a “tentar girar”, para um lado ou para outro dependendo do status dos pinos 2 e 3:

void moveLeft() // front motor left
{ 
   digitalWrite(frontMtEne, HIGH);
   digitalWrite(frontMtLeft, HIGH);
   digitalWrite(frontMtRight, LOW);
   digitalWrite(ledRed, LOW);
   delay(10);
}

//************************************//

void moveRight() // front motor right
{ 
   digitalWrite(frontMtEne, HIGH);
   digitalWrite(frontMtLeft, LOW);
   digitalWrite(frontMtRight, HIGH);
   digitalWrite(ledRed, LOW);
   delay(10);
}

Para parar o carrinho, basta colocar todas as saídas da ponte relativas ao motor traseiro em LOW, o que “travará” o eixo do motor (o motor frontal basta o enable estar em LOW):

void moveStop() //turn off rear motor
{ 
   analogWrite(rearMtEne, LOW);
   digitalWrite(rearMtFw, LOW);
   digitalWrite(rearMtBw, LOW);
 
   digitalWrite(frontMtEne, LOW);
   digitalWrite(ledRed, LOW);
   delay(5);
}

No link abaixo, você poderá encontrar o código completo para o Arduino:

Codigos para o Arduino

Conclusão

IMG_2488

O carrinho está devidamente raqueado e pronto para ganhar o mundo! Agora o céu é o limite! Comece, por exemplo instando sensores para evitar obstáculos, um HC-04 (ultra-som)  funcionará bem, como vimos no caso do “Mars Rover” tupiniquim. Crie um novo comando e envie o “caracter” via texto (ou use o botão Man/Auto: caracter “m”, para o robô executar outras fincões (buzina, por exemplo).

servo frontalOutra idéia interessante é melhorar a “dirigibilidade” do carrinho. O motor DC frontal poderia ser substituído por um servo de 180o que movimentaria o conjunto frontal de maneira linear, obtendo-se assim ângulos de virada variáveis. A foto ao lado é de um exemplo que encontrei a web. Não é exatamente o mecanismo de meu carrinho, mas mas mostra como poderia ficar.

 

That’s all folks!

Como sempre, espero que este projeto ajude outras pessoas a encontrar seu caminho no apaixonante mundo da eletrônica e dos robôs!

Não deixe de visitar e seguir minha página: MJRoBot.org no Facebook

Saludos desde el sur del mundo! 😉

Um abraço e até o próximo post!

Obrigado

O Mars Rover Tupiniquim aterrisa em Londres!

A edição de maio da revista britânica MagPi publicou uma matéria muito legal sobre meu Projeto “Mars Rover Tupiniquin”. Para quem não conhece, a MagPi é a revista oficial do RapsberryPi. Você pode baixar grátis as edições da revista em PDF.

A edição de maio (45) pode ser baixada no link: https://raspberrypi.org/magpi-issues/MagPi45.pdf

Espero que gostem!

(A repórter trocou o meu nome por “Marcio”, mas está valendo! 😉

IMG_0471

IMG_0472

Não deixe de visitar e seguir minha página: MJRoBot.org no Facebook

Saludos desde el sur del mundo! 😉

Um abraço e obrigado

Marcelo

Robô explorador de labirintos, utilizando Inteligência Artificial com Arduino

Esta é a segunda e última parte de um projeto mais complexo, que explora a potencialidade de um robô seguidor de linha. Nesta etapa, aplicaremos conceitos de inteligência artificial na exploração de labirintos, implementando algoritmos que nos ajudarão a encontrar o caminho da saída mais curto e rápido.

Este projeto foi desenvolvido a partir de meu último tutorial: “Rex”, um robô que nunca perde a linha! Depois de se conseguir desenvolver um robô com capacidade para seguir linhas, o próximo passo natural é sem dúvida, dar-lhe algum grau de inteligência. Assim, nessa etapa, o nosso querido “Rex, o Robô” tentará encontrar uma forma de escapar de um “labirinto” tomando o caminho mais curto e o mais rápido possível.
A propósito, Rex odeia o Minotauro….. 😉

A maioria dos labirintos, por mais complexa sua concepção possa parecer, foram essencialmente formados a partir de uma parede contínua com muitos cruzamentos e bifurcações. Se a parede circundante do objectivo final de um labirinto está ligado ao perímetro do labirinto na entrada, o labirinto sempre poderá ser resolvido mantendo-se uma mão em contacto com a parede, não importando os muitos desvios que possam existir. Estes labirintos “simples” são conhecido como “Simply-connected” ou “perfeitos”, ou em outras palavras, que não contêm loops.

Voltando ao nosso projeto, ele será dividido em duas partes:

1. (Primeira passada): O robô encontrará o seu caminho para sair de um labirinto perfeito desconhecido. Não importa onde você o colocar dentro do labirinto, ele sempre encontrará uma “solução de saída”.

2. (Segunda passada): Uma vez que o robô encontrou uma possível solução para sair do labirinto, ele deve otimizar-la, encontrando o caminho mais curto para ir do início ao fim.

O vídeo abaixo, mostra um exemplo do robô encontrando seu caminho para sair do labirinto (chegar “ao final”). Na primeira vez que o robô explora o labirinto, é claro que vai perder muito tempo “pensando” sobre o que fazer em todo e qualquer cruzamento. Para testar as possibilidades, ele tomará vários caminhos errados e becos sem saída, o que faz com que ele escolha caminhos mais longos e execute várias  “marcha-rés” desnecessárias.

Durante esta”1ª passada”, o robô irá acumulando experiências, “tomando notas” sobre os diferentes cruzamentos e eliminando os atalhos ruins. Em sua “segunda passada”, o robô irá direta e rapidamente ao final do labirinto sem qualquer erro ou dúvida. Ao longo deste tutorial,  exploraremos em detalhes como fazê-lo:

LISTA DE MATERIAIS:

A lista de materiais é basicamente a mesmo que usamos com robô seguidor de linha, exceto que foram incluídos 2 Sensores adicionais para uma melhor precisão na detecção da cruzamentos a esquerda e a direita. O preço final do robô, continua barato (cerca de US$85,00):

  • Corpo (pode ser adaptado para as suas necessidades):
    • 2 quadrados de madeira (80x80mm)
    • 3 Grampos de papel
    • 2 rodas de madeira (diâmetro: 50 mm)
    • 1 roda “solta” (Caster)
    • 9 Elásticos
    • Fita 3M “Command”
    • Articulações plásticas para fixação do sensor
  • Protoboard e fiação
  • 2 conjuntos de baterias (4XNi-metal hidreto) –  5V cada conjunto
  • 2 Servos de Rotação Contínua (SM-S4303R)
  • Arduino Nano
  • Módulo Bluetooth  HC-06
  • 5 sensores x Linha (TCRT5000 4CH Infrared Linha Pista Seguidor Módulo Sensor + 1 sensor de Pista independente)
  • 2 sensores ZX03 (baseado no TCRT5000) Reflective Infrared Sensors (saída analógica)
  • 1 LED
  • 1 Botão

ALTERAÇÕES AO CORPO DO ROBÔ

Retirar o conjunto original dos 5 Sensores e fixar os novos sensores reflectivos aos extremos esquerdo e direito da barra de suporte plástica.

É aconselhável manter os 7 sensores o mais alinhado possível.

Instalação e testes dos novos sensores

A nova matriz de 7 sensores, é montado de forma a que os 5 originais sejam utilizados exclusivamente para o controlo PID e detecção da “linha completa” (explicado mais adiante) e que os novos 2 sensores somente para a detecção dos cruzamentos a esquerda e a direita.

Como uma rápida revisão, vamos lembrar como os 5 sensores originais “digitais” trabalham:

Se um sensor está centrado em relação à linha preta, este irá produzir um sinal HIGH. Por outro lado, o espaço entre os sensores devem ser calculados de tal modo a permitir que dois sensores possam cobrir a largura total da linha preta produzindo assim um sinal HIGH simultaneamente em ambos os sensores.

Como os 2 novos sensores “analógicos” trabalham:

Se um dos sensores está centrado em relação à linha preta, o resultado observado na saída do ADC interno do Arduino será geralmente menor que “100” (lembre-se que o ADC produz uma saída que vai de 0 a 1023). Sobre superfícies mais claras, o valor de saída deverá ser maior (500 a 600 sobre papel branco, por exemplo). Estes valores devem ser testado em diferentes situações de luz de superfície e de materiais para se definir a constante LIMIAR (“THRESHOLD”) correta a ser usada (ver o quadro aqui).

Abaixo, o diagrama mostrando os componentes conectados ao Arduino:

Maze Solver Circuit

Olhando o código do Arduino, cada um dos sensores será definido com um nome específico:

// LFSensor more to the Left is "0"
const int lineFollowSensor0 = 12; //Using Digital input
const int lineFollowSensor1 = 18; //Using Analog Pin A4 as Digital input
const int lineFollowSensor2 = 17; //Using Analog Pin A3 as Digital input
const int lineFollowSensor3 = 16; //Using Analog Pin A2 as Digital input
const int lineFollowSensor4 = 19; //Using Analog Pin A5 as Digital input

const int farRightSensorPin = 0; //Analog Pin A0
const int farLeftSensorPin = 1; //Analog Pin A1

 

Recordando, as possíveis combinações de saída para a matriz de 5 sensores original do seguidor de linha são:

  • 0 0 0 0 1
  • 0 0 0 1 1
  • 0 0 0 1 0
  • 0 0 1 1 0
  • 0 0 1 0 0
  • 0 1 1 0 0
  • 0 1 0 0 0
  • 1 1 0 0 0
  • 1 0 0 0 0

Com a adição dos 2 novos sensores, as suas saídas possíveis são:

  • Sensor Esquerdo: Saída Analógica maior ou menor do que o valor definido de THRESHOLD
  • Sensor Direito: Saída Analógica maior ou menor do queo valor definido de THRESHOLD

A fim de armazenar os valores de cada um dos sensores uma variável tipo matriz  (Array) é criada para os sensores digitais originais 5:

int LFSensor[5]={0, 0, 0, 0, 0};

E duas variáveis do tipo inteiras para os 2 novos sensores analógicos:

int farRightSensor = 0;
int farLeftSensor = 0;

As variáveis serão constantemente actualizadas dependendo do estado de cada um dos sensores:

LFSensor[0] = digitalRead(lineFollowSensor0);
 LFSensor[1] = digitalRead(lineFollowSensor1);
 LFSensor[2] = digitalRead(lineFollowSensor2);
 LFSensor[3] = digitalRead(lineFollowSensor3);
 LFSensor[4] = digitalRead(lineFollowSensor4);
 
 farRightSensor = analogRead(farRightSensorPin);
 farLeftSensor = analogRead(farLeftSensorPin);

Possuindo 5 sensores, como se viu no projeto do Robô seguidor de linha, se permite a geração de uma “variável de erro” que ajudará a controlar a posição do robô sobre a linha. Essa variável de erro será mantida e uma nova denominada “mode” será incluída para saber se o robô está seguindo uma linha, sobre uma linha contínua, uma intersecção ou fora da linha.

Esta variável “mode” será usada também com os novos sensores de esquerda e direita. Consideremos que os novos sensores da esquerda e da direita geraram 3 estados possíveis: H (maior do que THRESHOLD), L (menor do que oTHRESHOLD) e X (irrelevante). Para as saídas digitais, manteremos “0”, “1” e também introduziremos o “X”:

Intersection

X 1  1  1  1  1 X ==> mode = CONT_LINE; error = 0;
H 0 X X X X L ==> mode = RIGHT_TURN; error = 0;  (Veja o exemplo na imagem acima)
L X X X X 0 H ==> mode = LEFT_TURN; error = 0;
X 0 0 0 0 0 X ==> mode = NO_LINE; error = 0;
H 0 0 0 0 1 H ==> mode = FOLLOWING_LINE; error = 4;
H 0 0 0 1 1 H ==> mode = FOLLOWING_LINE; error = 3;
H 0 0 0 1 0 H ==> mode = FOLLOWING_LINE; error = 2;
H 0 0 1 1 0 H ==> mode = FOLLOWING_LINE; error = 1;
H 0 0 1 0 0 H ==> mode = FOLLOWING_LINE; error = 0;
H 0 1 1 0 0 H ==> mode = FOLLOWING_LINE; error = -1;
H 0 1 0 0 0 H ==> mode = FOLLOWING_LINE; error = -2
H 1 1 0 0 0 H ==> mode = FOLLOWING_LINE; error = -3;
H 1 0 0 0 0 H ==> mode = FOLLOWING_LINE; error = -4;

Assim, a implementação da lógica acima na função:

void readLFSsensors()

irá retornar as variáveis “mode” e “error” que serão utilizados na lógica do programa.

É importante testar a lógica dos sensores antes de seguir com o projeto. A função de teste abaixo está incluída no código e poderá ser usado para fins de ensaio:

void testSensorLogic(void) 
{
    Serial.print (farLeftSensor);
    Serial.print (" <== LEFT RIGH==> ");
    Serial.print (farRightSensor);
    Serial.print (" mode: ");
    Serial.print (mode);
    Serial.print (" error:");
    Serial.println (error);
}

Resolvendo o labirinto – a regra da mão esquerda

Como discutido na introdução deste tutorial, a maioria dos labirintos são essencialmente formados a partir de uma parede contínua com muitos cruzamentos e desvios.

Pesquisando na Wikipedia, aprendemos que “o seguidor da parede” é o algorítmo mais conhecido para percorrer labirintos. É também conhecido como “regra da mão esquerda” ou a “regra da mão direita”. Se o labirinto é simplesmente conectado, isto é, todos as suas paredes são ligadas entre si, mantendo-se uma mão em contato com uma das paredes do labirinto é garantido que chegará a uma saída. Usaremos aqui a “Regra da mão esquerda”.

Em resumo, a regra da mão esquerda pode ser descrito como:

  • Coloque a mão esquerda na parede.
  • Comece a andar para a frente
  • Em cada cruzamento, e ao longo do labirinto, manter a sua mão esquerda tocando na parede à sua esquerda.
  • Eventualmente, você vai chegar ao final do labirinto. Você provavelmente não vai seguir o caminho mais curto e mais direto, mas você chegará lá.

Portanto, a chave aqui é identificar as intersecções, definindo que medidas tomar com base nas regras acima. Especificamente no nosso tipo de labirinto em 2D , podemos encontrar 8 tipos diferentes de intersecções :

Intersections types

Olhando a imagem acima, podemos perceber que as possíveis ações nos cruzamentos são:

  1. Em um “cruzamento tipo cruz”
    • Vá para a esquerda ou
    • Vá para a direita ou
    • Siga em frente ou
  2. Em um “T”:
    • Vá para a esquerda ou
    • Vá para a direita
  3. Em um “virar somente a direita”:
    • Vá para a direita
  4. Em um “virar somente a esquerda”:
    • Vá para a esquerda
  5. Em um a frente ou esquerda:
    • Vá para a esquerda ou
    • Siga em frente
  6. Em um a frente ou ou a direita:
    • Vá para a direita ou
    • Siga em frente
  7. Em um beco sem saída:
    • Volte
  8. No final do labirinto:
    • Pare

Aplicando-se  a “regra da mão esquerda” a lista acima, será reduzida a apenas uma opção para uma cada uma das possibilidades:

  1. Em um “cruzamento tipo cruz”
    • Vá para a esquerda
  2. Em um “T” (Transversal):
    • Vá para a esquerda
  3. Em um “virar somente a direita”:
    • Vá para a direita
  4. Em um “virar somente a esquerda”:
    • Vá para a esquerda
  5. Em um a frente ou esquerda:
    • Vá para a esquerda
  6. Em um a frente ou ou a direita:
    • Siga em frente
  7. Em um beco sem saída:
    • Volte
  8. No final do labirinto:
    • Pare

Estamos quase lá. Quando o robô atinge um beco sem saída é fácil identificá-lo, porque não existem situações ambíguas (já implementamos essa ação com o Robô seguidor de linha). O problema está quando o robô encontra uma “linha” por exemplo, pois a linha pode ser parte de um cruzamento tipo “cruz” (1) ou de um “T” (2) ou mesmo um “Final” (8). Além disso, quando o robô chega a um “virar à esquerda ou à direita”, esses cruzamentos podem ser os do tipo simples (opções 3 ou 4) ou opções que podem ir para a frente (5 ou 6). Para se descobrir exatamente em que tipo de intersecção está o robô, é necessário incorporar um passo adicional: o robô deve “dar um passinho a frente” ou seja rodar o que chamamos de “extra inch” e assim “ver” o que vem por adiante:

Maze4
Em termos de fluxo, todas as acções possíveis podem ser descritas como:

  • Em um beco sem saída:
    • Volte
  • Em uma linha:
    • Executar uma polegada extra
    • Se há uma linha:
      • É uma “cruz” ==> Ir para ESQUERDA
    • Se não houver nenhuma linha:
      • é um “T” ==> Ir para ESQUERDA
    • Se houver outra linha:
      • É o fim de Maze ==> PARAR
  • Em uma curva à direita:
    • Executar uma polegada extra
    • se há uma linha:
      • É ir a frente ou virar a direita ==> ir direto
    • Se não houver nenhuma linha:
      • é um virar obrigatoriamente a direita==> Ir para DIREITA
  • Em uma curva à esquerda:
    • Executar uma polegada extra
    • se há uma linha:
      • É ir a frente ou virar a esquerda ==> virar a esquerda
    • Se não houver nenhuma linha:
      • é um virar obrigatoriamente a esquerda==> Ir para ESQUERDA

Note-se que de facto, no caso de um “virar à esquerda”, você poderá pular o teste porque o robô tomará à esquerda de qualquer maneira. Deixei a explicação mais genérica somente para claridade. No código real ignorarei este teste.

IMG_3474

 

A foto ao lado , é de um labirinto bem simples que desenhei no chão do meu laboratório, usando fita isolante de 18mm (3/4) que uso para testes (ainda bem que minha mãe não viu!!!! ;-0:

Aplicando o algorítmo “Left Hand on the Wall” ao código do Arduino

Uma vez que já temos a função  readLFSsensors () modificada, para incluir os 2 sensores adicionais, podemos também re-escrever a função “Loop” introduzindo o algoritmo como descrito anteriormente:

void loop() 
{
     readLFSsensors(); 
     switch (mode)
     { 
         case NO_LINE: 
            motorStop();
            goAndTurn (LEFT, 180);
            break;
         case CONT_LINE: 
            runExtraInch();
            readLFSsensors();
            if (mode == CONT_LINE) mazeEnd();
            else goAndTurn (LEFT, 90); 
            break;
         case RIGHT_TURN: 
            runExtraInch();
            readLFSsensors();
            if (mode == NO_LINE) goAndTurn (RIGHT, 90); 
            break; 
         case LEFT_TURN: 
            goAndTurn (LEFT, 90); 
            break; 
         case FOLLOWING_LINE: 
            followingLine();
            break; 
 
     }
}

Algumas funções importantes aparecem aqui.

followingLine() é a mesma utilizada com o robô seguidor de linha, que quando se está apenas seguindo uma linha, deve-se: calcular o PID e controlar os motores, dependendo dos valores dos ganhos da malha de controle usando a função: motorPIDcontrol ();

runExtraInch (): vai empurrar o robô para a frente um pouquinho. Quanto o robô se moverá, dependerá do tempo que você usa na função delay(), antes que mande parar os motores.

void runExtraInch(void)
{
    motorPIDcontrol();
    delay(extraInch);
    motorStop();
}

goAndTurn (direction, angle): Esta função é importante, porque você na verdade não pode virar o robô, tão logo perceba o tipo de intersecção em que está. Lembre-se que projectamos um robô do tipo diferencial que, quando faz curvas, “gira em torno do seu eixo”. Assim, para sair de um cruzamento, girar 90 graus e continuar movendo-se sobre a linha, o centro das rodas deve obrigatoriamente estar alinhado com o centro da intersecção. Uma vez que a linha dos sensores está à frente do eixo das rodas, o robô deve mover-se para a frente para alinhá-los. Os moteres devem funcionar por um tempo “t” dependendo da distância entre a linha dos sensores e o eixo dos motores ( “d”), velocidade e tamanho das rodas. Esta constante de tempo “t” é  no código: adjGoAndTurn, que deve ser ajustada dependendo de seu projeto:

Turn_Explanation

void goAndTurn(int direction, int degrees)
{
    motorPIDcontrol();
    delay(adjGoAndTurn);
    motorTurn(direction, degrees);
}

Neste ponto, o robô já está “resolvendo um labirinto”!  Você acabou de terminar o “Primeiro Passo”. Não importa onde você começar dentro de um labirinto, você chegará ao final.

Abaixo, um vídeo mostrando um teste para esta fase do projeto:

Tomando nota do Caminho

Consideremos o exemplo abaixo:

Maze1

Partindo do ponto escolhido, o robô encontrará 15 Interseções antes de chegar ao final do labirinto:

  1. Esquerda (L)
  2. Back (B)
  3. Esquerda (L)
  4. Esquerda (L)
  5. Esquerda (L)
  6. Back (B)
  7. Reto (S)
  8. Back (B)
  9. Esquerda (L)
  10. Esquerda (L)
  11. Back (B)
  12. Reto (S)
  13. Esquerda (L)
  14. Esquerda (L)
  15. Fim

O que deve ser feito em qualquer um desses cruzamentos é “salvar a decisão tomada” na mesma sequência em que aconteça. Para isso, vamos criar uma nova variável (matriz) que irá armazenar o caminho que o robô tenha tomado:

A variável path[] e 2 índices variáveis, serão utilizados em conjunto para se gravar os passos:

char path[100] = "";
unsigned char pathLength = 0; // the length of the path
int pathIndex = 0; // used to reach an specific array element.

Voltando ao exemplo, uma vez percorrido todo o circuito, as variáveis ficariam:

path = [LBLLLBSBLLBSLL] e  pathLengh = 14

Simplificando (otimizando) o Caminho

Voltemos ao nosso exemplo. Olhando para o primeiro grupo de cruzamentos, percebemos que o primeiro ramo esquerdo é na verdade um “Dead End”, e assim, se o robô em vez de um “lateral-esquerdo-esquerdo” apenas tivesse seguido reto nesse primeiro cruzamento, uma grande quantidade de energia e tempo seriam salvas! Em outras palavras, uma sequência do tipo “LBL”, de facto, seria o mesmo que “S”.

Maze2

Isso é exatamente como o caminho completo pode ser otimizado. Se você analisar todas as possibilidades onde um “U turn” (back) é utilizado, o conjunto de 3 cruzamentos onde o”U-Turn” ( “B”) aparece  (“xBx”) poderá ser reduzido para apenas um.

A descrição acima é apenas um exemplo, abaixo podemos encontrar a lista completa de possibilidades:

LBR = B
LBS = R
RBL = B
SBL R =
SBS = B
LBL = S

Aplicando-se as substituições acima para o caminho completo de nosso exemplo, podemos reduzi-lo a:

path = [LBLLLBSBLLBSLL] ==> LBL = S

path = [SLLBSBLLBSLL] ==> LBS = R

path = [SLRBLLBSLL] ==> RBL = B

path = [SLBLBSLL] ==> LBL = S

path = [SSBSLL] ==> SBS = B

path = [SBLL] ==> SBL = R

path = [RL]

Olhando para o exemplo, é muito claro que se o robô gira para a DIREITA logo no primeiro cruzamento e depois disso, à esquerda, ele chegará ao final do labirinto pelo caminho mais curto!

Maze3

A primeira passada será consolidada na função mazeSolve (). Esta função é, de facto, a função loop () utilizada anteriormente, onde se incorporaram as etapas de armazenamento e otimização do caminho. Quando a primeira passada termina, a variavel path[] conterá o caminho já optimizado.

Uma nova variável é introduzida para sinalizar o final da “passada”:

unsigned int status = 0; // solving = 0; reach end = 1

Abaixo a função completa para a primeira etapa do programa:

void mazeSolve(void)
{
   while (!status)
   {
      readLFSsensors(); 
      switch (mode)
      { 
         case NO_LINE: 
            motorStop();
            goAndTurn (LEFT, 180);
            recIntersection('B');
            break;
         case CONT_LINE: 
            runExtraInch();
            readLFSsensors();
            if (mode != CONT_LINE) {goAndTurn (LEFT, 90); recIntersection('L');} // or it is a "T" or "Cross"). In both cases, goes to LEFT
            else mazeEnd(); 
            break;
         case RIGHT_TURN: 
            runExtraInch();
            readLFSsensors();
            if (mode == NO_LINE) {goAndTurn (RIGHT, 90); recIntersection('R');}
            else recIntersection('S');
            break; 
         case LEFT_TURN: 
            goAndTurn (LEFT, 90); 
            recIntersection('L');
            break; 
         case FOLLOWING_LINE: 
            followingLine();
            break; 
 
       }
   }
}

Aqui uma nova função foi introduzida: recIntersection (direction)

Esta função será a usada para armazenar as decisões tomadas nos cruzamentos e também para chamar outra importante função: simplifyPath(), que irá otimizando “em tempo real” o grupo de 3 cruzamentos envolvendo “U-Turn”, como vimos anteriormente.

void recIntersection(char direction)
{
    path[pathLength] = direction; // Store the intersection in the path variable.
    pathLength ++;
    simplifyPath(); // Simplify the learned path.
}

O crédito para a criação da função simplifyPath () é de Patrick McCabe. Eu apenas a incluí ao meu código (para mais detalhes, visite patrickmccabemakes.com):

void simplifyPath()
{
   if(pathLength < 3 || path[pathLength-2] != 'B') // only simplify the path if the second-to-last turn was a 'B'
   return;
   int totalAngle = 0;
   int i;
   for(i=1;i<=3;i++)
   {
     switch(path[pathLength-i])
     {
        case 'R':
           totalAngle += 90;
           break;
        case 'L':
           totalAngle += 270;
           break;
        case 'B':
           totalAngle += 180;
           break;
      }
    }
    totalAngle = totalAngle % 360;  // Get the angle as a number between 0 and 360 degrees.
    switch(totalAngle) // Replace all of those turns with a single one.
    {
       case 0:
          path[pathLength - 3] = 'S';
          break;
       case 90:
          path[pathLength - 3] = 'R';
          break;
       case 180:
          path[pathLength - 3] = 'B';
          break;
       case 270:
          path[pathLength - 3] = 'L';
          break;
     }
     pathLength -= 2; // The path is now two steps shorter.
}

A segunda passada: resolvendo o labirinto o mais rápido possível!

O programa principal: loop () é bem simples:

void loop() 
{
    ledBlink(1);
    readLFSsensors(); 
    mazeSolve(); // First pass to solve the maze
    ledBlink(2); // end 1st pass
    
    while (digitalRead(buttonPin) && !mode) { }
    pathIndex = 0;
    status = 0;
    mazeOptimization(); //run the maze as fast as possible
    ledBlink(3); // end of 2nd pass. Maze solved!
}

Assim, quando a primeira passada termina, o que devemos fazer é apenas “alimentar” o robô com o caminho otimizado. Ele vai começar a percorrer o labirinto novamente e quando uma intersecção for encontrado, ele não mais tomará decisões, mas simplesmente seguirá o que está armazenado na variável path [].

Para a segunda passada usamos a função mazeOptimization(), que por sua vez, chama a função mazeTurn(path[]) que comandará os movimentos do robô nessa segunda passada:

void mazeOptimization (void)
{
   while (!status)
   {
      readLFSsensors(); 
      switch (mode)
      {
         case FOLLOWING_LINE:
            followingLine();
            break; 
         case CONT_LINE:
            if (pathIndex >= pathLength) mazeEnd (); 
            else {mazeTurn (path[pathIndex]); pathIndex++;}
            break; 
         case LEFT_TURN:
            if (pathIndex >= pathLength) mazeEnd (); 
            else {mazeTurn (path[pathIndex]); pathIndex++;}
            break; 
         case RIGHT_TURN:
            if (pathIndex >= pathLength) mazeEnd (); 
            else {mazeTurn (path[pathIndex]); pathIndex++;}
            break; 
      } 
   } 
}
void mazeTurn (char dir) 
{
   switch(dir)
   {
      case 'L': // Turn Left
         goAndTurn (LEFT, 90); 
         break; 
      case 'R': // Turn Right
         goAndTurn (RIGHT, 90); 
         break; 
      case 'B': // Turn Back
         goAndTurn (RIGHT, 180); 
         break; 
      case 'S': // Go Straight
         runExtraInch(); 
         break;
    }
}

A segunda passada está feita!

O vídeo abaixo mostra o exemplo trabalhado aqui completo, onde Rex encontra seu caminho para livrar-se do Minotauro! 😉

O código Arduino completo para este projeto, poderá ser encontrado no link abaixo:

Codigo para o Arduino

Usando o aplicativo Android para o ajuste

A App Android desenvolvido para o projeto do Robô seguidor de Linha também pode ser usado aqui.  O código Arduino apresentado na última etapa já inclui comunicação com o dispositivo Android, mas se não quiser usar-lo não há problema, porque o código é “transparente”.

Eu usei bastante o dispositivo Android durante o projeto para enviar dados de teste do robô para o dispositivo, utilizando-se o campo de “Mensagem recebida”.

Diversas variáveis devem ser bem definidas, a fim de garantir que o robô gire corretamente. Os mais importantes estão abaixo (os marcados em negrito tive mudar-los várias vezes):

const int power = 250;
const int iniMotorPower = 250;
const int adj = 0;
float adjTurn = 8;
int extraInch = 200;
int adjGoAndTurn = 800;
const int THRESHOLD = 150;
float Kp=50;
float Ki=0;
float Kd=0;

CONCLUSÃO

Esta é a segunda e última parte de um projeto complexo, explorando a potencialidade de um robô seguidor de linha, onde aplicando-se conceitos de inteligência artificial se conseguiu explorar labirintos,  encontrando o caminho da saída mais curto e rápido.

Espero que esse trabalho possa contribuir para que outras pessoas possam aprender mais sobre eletrônica, robôs, Arduino, etc.

Não deixe de visitar e seguir minha página: MJRoBot.org no Facebook

Saludos desde el sur del mundo! 😉

Um abraço e até o próximo post!

Obrigado

Marcelo

O braço robótico – Introdução

O objetivo desse tutorial é desenvolver passo a passo um projeto para o controle e programação de um braço robótico, simulando as funções básicas de um robô industrial.

Especificações:

  • O robô deverá ter duas funções básicas:
    • Programa: gravar posições do braço em 3 dimensões
    • Run: executar o programa de trabalho (ou seja, executar em sequencia as posições gravadas na etapa de programa.
  • O robô executará o programa até que o comando de “abortar” seja utilizado.
  • O projeto deverá controlar robots de 3 ou 4 DOF (“Degrees of Freedom”).
  • O robô deverá ser controlado em modo “local” e “remoto” (via celular)
  • Durante o programa se poderá mudar o controle de “local” a “remoto” e vice-e-versa.
  • Deverá possuir sinalização tanto visual (LEDS e display) quanto sonora
  • Deverá possuir função de alarme acionado remotamente.

O projeto:

O diagrama abaixo mostra o “Bill of Material” que será utilizado no projeto:

Robot Arm Project diagram

Para o projeto, optarei pelo Arduino MEGA para não ter que me preocupar com o número de portas I/Os. O UNO funcionaria sem problemas, mas se deveria utilizar algum método de expansão de portas. Existem várias opções que podem ser facilmente encontradas na internet, como um decodificador 74138 por exemplo (veja o site: “Expandindo os I/Os do Arduino”).

Para a rede Bluetooth utilizarei o HC-06 (o qual explico em detalhes no tutorial “Conectando “coisas” através do Bluetooth”).

MJRoBot Arm Ctrl iconA app Android utilizado para o “modo  remoto”, foi desenvolvida utilizando-se a ferramenta  MIT appinventor2, uma plataforma tão poderosa quanto simples para esse tipo de app baseado no Android. No futuro pretendo explorar a criação de apps desse tipo aqui no Blog. Por enquanto, a app está disponível para ser baixada gratuitamante na loja da Google:  MJRoBot Arduino Arm Robot Control.

Os vídeos abaixo dão uma idéia de como deverá ficar o projeto final:

Primeiro protótipo sendo programado em “modo local”:

O robô programado de maneira remota via rede Bluetooth:

 

Nos próximos posts, detalharei os passos necessários para o desenvolvimento do projeto.

Como sempre, espero que este projeto ajude outras pessoas a encontrar seu caminho no apaixonante mundo da eletrônica, robótica e do IoT!

Não deixe de visitar e seguir minha página: MJRoBot.org no Facebook

“Saludos desde el sur del mundo!” 😉

Um abraço e até o próximo post!

Obrigado

Marcelo

FullSizeRender 23Em tempo: aproveito e deixo aqui um abraço para duas pessoas muito queridas, meu tio Ítalo que já não está aqui entre nós, que além de me ensinar a jogar Xadrez quando garoto, me deixou de presente essas peças que utilizei no vídeo. O outro abraço vai para o meu sogro Mathias, que muito carinhosamente presenteou-me com o tabuleiro e com as peças do jogo que haviam se perdido com o tempo (tudo feito por ele).

 

 

 

 

Conectando “coisas” através do Bluetooth

 

bluetooth-logo

Uma rede sem fio local (e pessoal) porreta é sem dúvida a Bluetooth (BT). Hoje em nosso dia-a-dia é comum encontrar-nos com celulares, aparelhos de som, cameras, etc., entrelaçados com a ajuda da famosa “luzinha azul”

No mundo do IoT e da automação em geral, é muito comum deparar-nos com controles remotos via celulares utilizando tecnologia BT. Isso é devido a 2 componentes básicos mas muito importantes:

  1. Plataforma de desenvolvimento para OS ANDROID
  2. Módulos BT baratos e acessíveis (Como por exemplo o HC-06)

Neste tutorial, vou desenvolver algumas idéias de como controlar as saídas de um Arduíno através de um celular de maneira a mover um Robot, acionar lâmpadas em uma casa, etc.

No mercado é comum encontrar módulos de BT 3.0  “Master-Slave” como o HC-05  e “Slaves” como o HC-06. Já mais recentemente, apareceram os HC-08 e HC-10 que trabalham com tecnologia BT 4.0 ou BLE (“Bluetooth Low Energy”). Os módulos BLE são os únicos que podem ser conectados a um Iphone, pois infelizmente a Apple não fornece suporte a ao BT 3.0.

Para os projetos discutidos aqui, usarei um HC-06 que é bem popular e barato (Bye, bye, Iphone, vamos de Android!). O Módulo é alimentado com 5V o que faz com que ele seja facilmente conectado a um Arduino

HC06 PinsUNO por exemplo, para receber e transmitir informações a outros dispositivos como um PC ou um telefone celular. Seus pinos de transmissão e recepção podem ser conectados diretamente ao UNO, não havendo a necessidade de se utilizar divisores de tensão como vimos no caso do ESP8266.

Na prática, o HC-06 deve ser ligado diretamente aos pinos 0 e 1 do Arduino (Serial):

  • HC06-Tx ao Arduino pin 0 (Rx)
  • HC06-Rx ao Arduino pin 1 (Tx)

Ao se usar a entrada serial por HW do UNO é muito importante lembrar-se que o HC-06 não pode estar fisicamente conectado aos pinos 0 e 1 durante a carga do programa, isso porque o USB também usa essa mesma serial.  Uma maneira simples de se contornar esse probleminha (se seu projeto não utiliza muitos GPIOs do UNO) é usar uma porta serial por SW através da library SoftwareSerial (a mesma que usamos no caso do ESP8266). Em nosso caso aqui, usaremos os pinos 10 e 11 do UNO (Tx, Rx respectivamente).

UNO-HC06

O passo seguinte será escrever um codigo bem simples para se poder testar, programar e inicializar o HC-o6:

Para iniciar, incluir a Library Software Serial, definindo a variável “BT” como a nova porta serial.

#include <SoftwareSerial.h>
SoftwareSerial BT(10, 11); // RX, TX
String command = ""; // Stores response of bluetooth device
 
void setup()
{
 Serial.begin(9600);
 Serial.println("Type AT commands!");
 BT.begin(9600); // HC-06 usually default baud-rate
}

Em seguida vem o corpo principal do código que simplesmente espera por dados vindos do BT e uma vez que eles chegem, os mesmos são escritos no Serial Monitor. Da mesma maneira, se podem enviar comandos AT desde o monitor serial até o módulo HC-06.

void loop()
{
 if (BT.available()) // receive data if available.
 {
  while(BT.available()) // "keep receiving".
  {
   delay(10); //Delay added to make thing stable 
   char c = BT.read(); //Conduct a serial read
   command += c; //build the string.
  } 
  Serial.println(command);
  command = ""; // No repeats
 } 
 if (Serial.available())
 {
  delay(10); 
  BT.write(Serial.read());
 }
}

 

Uma vez carregado o programa, faça alguns testes básicos. Por exemplo, envie “AT“, o módulo deverá responder “OK“. Pergunte a versão do Firmware: “AT+VERSION”, o módulo deverá responser, por exemplo: “linvorV1.8“. Com o HC-06 é possível definir um nome para o módulo por exemplo: “AT+NAMEMJRoBot_BT_HC06“.  Mas diferente de outros módulos, voce não conseguirá saber qual é o nome que está definido para o módulo. Ao se enviar o comando anterior, o HC-06 responderá simplesmente: “OKsetname”

Em geral, o HC-o6 vem de fábrica com o password (ou PIN): 1234. Voce poderá definir um novo com o comando AT: AT+PINxxxx onde ‘xxxx‘ serão 4 números.

OK! Módulo conectado ao UNO e funcionando. Hora de lançar mão do velho e bom celular Android!!!

Existem muitas apps na loja da Google que podem ser utilizadas. Vou sugerir duas delas, por serem as que usarei nos tutoriais deste blog. Essas apps foram desenvolvidas por mim utilizando o MIT Application2 tool (veja projeto aqui) e estão disponíveis sem custo na loja da Google:

  1. MJRoBot II BT Control
  2. MJRoBot BT Digital Analog Voice Control

MJRoBot BT IconO App MJR0Bot II foi desenvolvido para comandar Robots. Ele basicamente envia um caracter para cada comando de direção, modos AUTO/MANUAL, velocidade + e velocidade-. Alem de permitir o envio de mensagens em modo texto.

2015-12-11 20.26.44 copy

 

MJRoBot BT Ctrl app Logo

O App. MJRoBot Digital Analog Voice Control, envia comandos para acionamento digitais (ligar/desligar) tanto por botões como por voz e comandos numéricos para controle de PWMs (0-255).

FullSizeRender 10

 

Faça o Download de qualquer uma das duas Apps, vá ao set-up do celular e procure o modulo BT fazendo a conexão (entre com o PIN 1234 ou algum outro definido por você).  Isso deverá ser feito uma única vez, pois o celular guardará os dados de conexão. Uma vez que o celular e o HC-06 estão conversando, é só usar as APPs.

A partir da segunda vez, ao lançar-se a app, o modulo BT estará desconectado.

FullSizeRender 14

Acione o botão de BT, para que o app informe dos modulos disponíveis:

FullSizeRender 12

Selecione o nome do módulo (No caso é esse com o HC-06 ao final).

O App então mostrará “CONNECTED”, informando que está “pared” com o HC-06

FullSizeRender 13

A partir daí, é só ir acionando os botões do APP e observar no Monitor Serial, o que é que o APP está enviando.

Por exemplo, acionando “ON” e “OFF” sequencialmente para os 4 devices, no caso da APP MJR0Bot BT Digi/Ana/Voice Ctrl, o resultado seria:

dev1on
dev1off
dev2on
dev2off
dev3on
dev3off
dev4on
dev4off 

Agora que já temos um App para o Android e sabemos como funciona um modulo BT, vamos colocar a mão na massa e criar algo de util!

Controlando as saídas do Arduino.

Vamos construir o circuito abaixo:

Picture1

A idéia será usar o App MJRoBot Digi/Ana/Voice Ctrl para acender e apagar os LEDS e também controlar a intensidade dos mesmos.

Estaremos relacionando:

  • Device 1: "dev1on/dev1off" ==> LED Red ==> Pin 3 do UNO
  • Device 2: "dev2on/dev2off" ==> LED Yellow ==> Pin 5 do UNO
  • Device 3: "dev3on/dev3off" ==> LED Green==> Pin 6 do UNO
  • Device 4: "dev4on/dev4off" ==> LED Blue==> Pin 9 do UNO

Ou seja, ao acionar o botão “ON” relacionado ao “Device 1”, a mensagem de texto “dev1on”será enviada ao Arduino. Ao receber essa mensagem, o LED vermelho deverá acender e assim por diante.

Observe que os 4 pinos são pinos com capacidade de gerar PWM. Isso é importante para a utilização dos “sliders” da App, que enviarão valores para controlar a intensidade dos LEDs via PWM

  • Dev A0: "r/ 0-255" ==> LED Red ==> Pin 3 do UNO
  • Dev A1: "y/ 0-255" ==> LED Yellow ==> Pin 5 do UNO
  • Dev A2: "g/ 0-255" ==> LED Green==> Pin 6 do UNO
  • Dev A3: "b/ 0-255" ==> LED Blue==> Pin 9do UNO

No caso dos controles deslizantes (“sliders”), antes dos valores para controle do PWM (0 a 255), um caracter será enviado para que o Arduino saiba de que “slider” estará chegando o comando.

O código:

Setup inicial:

#include <SoftwareSerial.h>
SoftwareSerial BT(10, 11); //TX, RX respectively
String device;
const int dev1 = 3; //PWM
const int dev2 = 5; //PWM
const int dev3 = 6; //PWM
const int dev4 = 9; //PWM
void setup() 
{
 BT.begin(9600);
 Serial.begin(9600);
 
 pinMode(dev1, OUTPUT);
 pinMode(dev2, OUTPUT);
 pinMode(dev3, OUTPUT);
 pinMode(dev4, OUTPUT);
}

Podemos dividir a parte principal do programa em 4 blocos:

  1. Espera dos comandos BT e construção da variável “device”
  2. Acionamento dos LEDS a partir dos botões
  3. Acionamento dos LEDS a partir de comandos de voz
  4. Controle do intensidade dos LEDs via Sliders
void loop() 
{
 while (BT.available()) 
 {
   delay(10); //Delay added to make thing stable 
   char c = BT.read(); //Conduct a serial read
   device += c; //build the string.
 } 
   if (device.length() > 0) 
 {
   Serial.println(device); 
   // Button control:
   if (device == "dev1on") {digitalWrite(dev1, HIGH);}
   else if (device == "dev1off") {digitalWrite(dev1, LOW);}
   else if (device == "dev2on") {digitalWrite(dev2, HIGH);}
   else if (device == "dev2off") {digitalWrite(dev2, LOW);}
   else if (device == "dev3on") {digitalWrite(dev3, HIGH);}
   else if (device == "dev3off") {digitalWrite(dev3, LOW);}
   else if (device == "dev4on") {digitalWrite(dev4, HIGH);}
   else if (device == "dev4off") {digitalWrite(dev4, LOW);}
 
   // Voice control:
   else if (device == "ligar um" || device == "Ligar 1") {digitalWrite(dev1, HIGH);}
   else if (device == "desligar um" || device == "desligar 1") {digitalWrite(dev1, LOW);}
   else if (device == "ligar som" || device == "Ligar som") {digitalWrite(dev2, HIGH);}
   else if (device == "desligar som" || device == "Desligar som") {digitalWrite(dev2, LOW);}
   else if (device == "ligar TV" || device == "Ligar TV") {digitalWrite(dev3, HIGH);}
   else if (device == "desligar TV" || device == "Desligar TV") {digitalWrite(dev3, LOW);}
   else if (device == "ligar quarto" || device == "Ligar quarto") {digitalWrite(dev4, HIGH);}
   else if (device == "desligar quarto" || device == "Desligar quarto") {digitalWrite(dev4, LOW);}
   // Slider control:
   char colour = device[0]; 
   int value = device[2]; 
   Serial.print(" "); 
   Serial.println(value); 
   if ( colour == 'r') 
   { 
     analogWrite(dev1, value); // use value to set PWM for LED brightness 
   } 
     if ( colour == 'y') 
   { 
     analogWrite(dev2, value); // use value to set PWM for LED brightness 
   } 
     if ( colour == 'g') 
  { 
    analogWrite(dev3, value); // use value to set PWM for LED brightness 
  } 
    if ( colour == 'b') 
  { 
    analogWrite(dev4, value); 
  }
  device=""; //Reset the variable
 } 
}

 

Link para o código Arduino

No vídeo abaixo, uma demonstração da porção programa acima (botões & Slider):

O controle de dispositivos IoT por voz é uma tendência nos dias de hoje. Conseguir este tipo de controle com o conjunto Arduino/HC-06/Android é extremamente simples. Os dispositivos Android já possuem essa característica “de fábrica”. No App que desenvolví, apenas acrescentei um botão que faz com que o Android “escute” um comando e o envie em formato texto para o Arduino. O codigo se encarrega de “ler” a string que chega.

Em vermelho, ressalto no código alguns possíveis comandos de voz. Ao receber por  “Ligar TV” por exemplo, o LED verde (correspondente ao device 3) acenderá (em vez do LED podíamos ter uma relé que ligaria a TV).

No vídeo abaixo, uma pequena demonstração do controle por voz:

 

Bom, acredito que já é possível ver o enorme potencial do controle de “coisas” usando BT/Android/Arduino. Em meus próximos posts estarei desenvolvendo Robots que poderão ser controlados remotamente como vimos aqui.

Um abraço e até mais!