Robô controlado por voz via WiFi

Em meu último tutorial: Controle ativado por voz com Android e NodeMCU, exploramos como desenvolver nossa própria App em um smartphone Android para controlar localmente (usando botões ou voz) dispositivos domésticos inteligentes. Que tal agora, em vez de dispositivos domésticos controlarmos motores? E melhor ainda, que tal ter esses motores movendo um robô? Pois isso, é exatamente o que desenvolveremos aqui, um robô controlado por voz via WiFi e utilizando como microcontrolador nosso velho amigo, o NodeMCU!

O diagrama de blocos abaixo nos dá uma geral sobre o projeto que desenvolveremos aqui:

WiFi_Robot_Block_Diagram

e o filme nos mostra como ficará o projeto:

Por favor, considere que um de meus motores estava com muito pouco torque. Apesar de o resultado parecer estranho, o projeto funciona a contento. Assim que mudar o motor, atualizarei o vídeo. Obrigado.

1: Lista de materiais (BoM)

Valores em USD, apenas para referência.

  1. NodeMCU ESP8266-12E ($8.79)
  2. 400-point Experiment Breadboard Breadboard ($ 4.97)
  3. H-Bridge L293D ($2.17)
  4. Motor Robot Car Chassis Kit ($13.00)
  5. Male-Female Dupont Cables ($1.00)
  6. Portable Charger 5VDC 3000mAh Power Bank ($10.00)
  7. Bateria 9V DC

….. e qualquer telefone ou tablet Android!

2: A Ponte-H L293D

  L293D
Para “drivar” os motores usaremos o L293D.

De acordo com seu Datasheet, o L293D é um controlador do tipo half-H quádruplo de alta corrente. O L293D foi projetado para fornecer correntes bidirecionais de até 600 mA em tensões variando de 4,5 V a 36 V.

Usaremos o CI L293D diretamente com o NodeMCU, em vez de um Shield, como se vê mais comumente em projetos no mercado.

Pois bem, nosso robô terá duas rodas, acionadas por 2 motores DC:

  • Motor esquerdo (LEFT)
  • Motor direito (RIGHT)

NodeMCU_L293D_Block_Diagram

Ligaremos os motores ao L293D e este ao NodeMCU, como mostra o diagrama em blocos anterior, onde 6 de seus GPIOs comandarão esses motores.

int rightMotor2 = 13;   // D7 - right Motor -
int rightMotor1 = 15;   // D8 - right Motor +
int leftMotor2 = 0;     // D3 - left Motor - 
int leftMotor1 = 2;     // D4 - left Motor +
int eneLeftMotor = 12;  // D6 - enable Motor Left
int eneRightMotor = 14; // D5 - enable Motor Right

Por exemplo, para mover o robô para a frente,  girando o motor esquerdo (LEFT) no sentido apropriado, você deve colocar:

  • HIGH no pino D4 (motor esquerdo +) e
  • LOW no pino D3 (motor esquerdo -)

Para o motor direito (RIGHT) você deve fazer o oposto:

  • HIGH no pino D8 (motor direito +) e
  • LOW no pino D7 (motor esquerdo -)

The H-Bridge L293D

Devido à maneira como meus motores são montados, a combinação acima irá girar ambos motores no mesmo sentido, “empurrando” o robô para a frente.

Robot direction.jpg

O diagrama acima define (ou melhor, convenciona) como o robô deverá se mover. Isso é importante para a definição adequada de suas variáveis.

Para controlar a H-Bridge corretamente, você também deverá trabalhar com os pinos de habilitação (“enable”). No exemplo anterior onde o robô se move para a frente, além dos 4 GPIOs discutidos, o robô só funcionaia se os pinos de habilitação (eneLeftMotor e eneRightMotor) estiverem ambos em HIGH. Você poderá conectar esses pinos do L293D (1 e 9) diretamente a + VCC e esquecê-los. Eu não gosto disso, porque se voce necessita por exemplo, parar rapidamente seu robô, esses pinos deveriam estar em necessariamente em LOW. Além disso, associar os pinos de habilitação a saídas PWM do NodeMCU, permitirá também controlar a velocidade do motor. Em nosso exemplo, usaremos estes pinos apenas com valores digitais tais como HIGH e LOW, o que ajustará a velocidade para MAX e ZERO, respectivamente.

Asim, para se criar uma função que conduza nosso robô para a frente, devemos escrever um código como este:

void forwardMotor(void)   
{
  digitalWrite(eneLeftMotor,HIGH);
  digitalWrite(eneRightMotor,HIGH);
    
  digitalWrite(leftMotor1,HIGH);
  digitalWrite(leftMotor2,LOW);
  digitalWrite(rightMotor1,HIGH);
  digitalWrite(rightMotor2,LOW);
}

3: Movendo o robô em outras direções

Na etapa anterior, aprendemos como podemos mover o robô para a frente, pensemos agora como movê-lo em outras direções.
Definamos 5 possíveis comandos:
  1. STOP: Pare
  2. LEFT: Vire à esquerda
  3. RIGHT: Vire à direita
  4. REVERSE: Dê marcha à ré
  5. FORWARD: Vá para a frente

O primeiro comando “STOP” é simples de realizar. Todas as entradas da H-Bridge devem estar em LOW, desta forma os motores não se moverão:

void stopMotor(void)   
{
  digitalWrite(eneLeftMotor,LOW);
  digitalWrite(eneRightMotor,LOW);

  digitalWrite(leftMotor1,LOW);
  digitalWrite(leftMotor2,LOW);
  digitalWrite(rightMotor1,LOW);
  digitalWrite(rightMotor2,LOW);
}

Da mesma forma pensemos em fazer o robô tomar outra direção, digamos que “virar à ESQUERDA”. Considere que o robô está indo para a frente, se queremos virar à esquerda podemos pensar em duas situações:

  1. Parar o motor ESQUERDO, mantendo o motor DIREITO no sentido a frente (Forward): o robô executará uma trajetória de arco para a esquerda
  2. Inverter o sentido do motor ESQUERDO, mantendo o motor DIREITO no sentido a frente (Forward): o robô irá girar sobre seu eixo para a esquerda.

Implementemos o caso 2 acima:

void leftMotor(void)   
{
  digitalWrite(eneLeftMotor,HIGH);
  digitalWrite(eneRightMotor,HIGH); 
  
  digitalWrite(leftMotor1,LOW);
  digitalWrite(leftMotor2,HIGH);
  digitalWrite(rightMotor1,HIGH);
  digitalWrite(rightMotor2,LOW);
}

Os demais comandos seguirão a mesma lógica, como ilustra a tabela abiaxo:

Moving the Robot Around

4: Completando o HW

Completing the HW

Siga o diagrama acima e complete o HW de seu Robô.

Você poderá executar alguns testes simples para verificar que seu código está OK.  Para isto, introduziremos um “botão” para iniciar o seu robô. Vamos instalá-lo na porta D0 do NodeMCU como mostrado no diagrama elétrico anterior.

Você poderá utilizar o programa abaixo para testar os movimentos de seu robô:

// Set Motor Control Pins
int rightMotor2 = 13;    // D7 - right Motor -
int rightMotor1 = 15;    // D8 - right Motor +
int leftMotor2 = 0;    // D3 - left Motor - 
int leftMotor1 = 2;    // D4 - left Motor +
int eneLeftMotor = 12;  // D6 - enable Mortor Left
int eneRightMotor = 14; // D5 - enable Mortor Right

int buttonPin = 16; // D0

void setup()
{
  Serial.begin(115200);

  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(eneLeftMotor, OUTPUT); 
  pinMode(eneRightMotor, OUTPUT); 
  pinMode(leftMotor1, OUTPUT); 
  pinMode(leftMotor2, OUTPUT);  
  pinMode(rightMotor1, OUTPUT);  
  pinMode(rightMotor2, OUTPUT);  

  digitalWrite(eneLeftMotor,LOW);
  digitalWrite(eneRightMotor,LOW);
  digitalWrite(leftMotor1,LOW);
  digitalWrite(leftMotor2,LOW);
  digitalWrite(rightMotor1,LOW);
  digitalWrite(rightMotor2,LOW);
      
  while (digitalRead(buttonPin))  // Wait for button to be pressed to move on 
  {  
  }
}

void loop()
{
  forwardMotor();
  delay (5000);
  stopMotor();
  delay (200);
  leftMotor();
  delay (3000);
  stopMotor();
  delay (200);
  forwardMotor();
  delay (5000);
  stopMotor();
  delay (200);
  rightMotor();
  delay (3000);
  stopMotor();
  delay (200);
  reverseMotor();
  delay (5000);
  stopMotor();
  delay (200);
} 

/* command motor forward */
void forwardMotor(void)   
{
  digitalWrite(eneLeftMotor,HIGH);
  digitalWrite(eneRightMotor,HIGH);
    
  digitalWrite(leftMotor1,HIGH);
  digitalWrite(leftMotor2,LOW);
  digitalWrite(rightMotor1,HIGH);
  digitalWrite(rightMotor2,LOW);
}

/* command motor backward */
void reverseMotor(void)   
{
  digitalWrite(eneLeftMotor,HIGH);
  digitalWrite(eneRightMotor,HIGH);
  
  digitalWrite(leftMotor1,LOW);
  digitalWrite(leftMotor2,HIGH);
  digitalWrite(rightMotor1,LOW);
  digitalWrite(rightMotor2,HIGH);
}

/* command motor turn left */
void leftMotor(void)   
{
  digitalWrite(eneLeftMotor,HIGH);
  digitalWrite(eneRightMotor,HIGH); 
  
  digitalWrite(leftMotor1,LOW);
  digitalWrite(leftMotor2,HIGH);
  digitalWrite(rightMotor1,HIGH);
  digitalWrite(rightMotor2,LOW);
}

/* command motor turn right */
void rightMotor(void)   
{
  digitalWrite(eneLeftMotor,HIGH);
  digitalWrite(eneRightMotor,HIGH);
  
  digitalWrite(leftMotor1,HIGH);
  digitalWrite(leftMotor2,LOW);
  digitalWrite(rightMotor1,LOW);
  digitalWrite(rightMotor2,HIGH);
}

/* command motor stop */
void stopMotor(void)   
{
  digitalWrite(eneLeftMotor,LOW);
  digitalWrite(eneRightMotor,LOW);

  digitalWrite(leftMotor1,LOW);
  digitalWrite(leftMotor2,LOW);
  digitalWrite(rightMotor1,LOW);
  digitalWrite(rightMotor2,LOW);
}

Quando você pressionar o botão, o robô começará a fazer os movimentos definidos no loop (). Os movimentos continuarão até que você pressione “Reset” no NodeMCU. Pressionando o botão “verde” novamente, o ciclo se repetirá.

O código acima poderá ser baixado de meu GitHub:

WiFi_Robot_NodeMCU_Android_Voice_Motor_tests

5: Montando o corpo do robô

body Assembly

  1. Use o Kit: Chassis, 2 Rodas, 2 Motores DC, 1 roda solta (coaster)
  2. Solde 2 fios de 10 cm (Vermelho e Preto) em cada polo do motor
  3. Fixe os motores ao chassi como mostrado na foto acima
  4. Monte o coaster
  5. Conecte os fios do motor à eletrônica (L293D)
  6. Fixe a bateria de 9V ao chassi
  7. Fixe a bateria de 5V sob o chassi

O robô deverá ficar com essa cara:

Body Final

6: Conectando o NodeMCU ao seu WiFi local

Conectemos o NodeMCU ao WiFi local, verificando seu endereço IP. Para isso, podemos utilizar o programa abaixo:

#include 
WiFiClient client;
WiFiServer server(80);
const char* ssid = "YOUR SSID";
const char* password = "YOUR PASSWORD";

void setup() 
{
  Serial.begin(115200);
  connectWiFi();
  server.begin();
}

void loop() 
{
}

/* connecting WiFi */
void connectWiFi()
{
  Serial.println("Connecting to WIFI");
  WiFi.begin(ssid, password);
  while ((!(WiFi.status() == WL_CONNECTED)))
  {
    delay(300);
    Serial.print("..");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("NodeMCU Local IP is : ");
  Serial.print((WiFi.localIP()));
}

No monitor serial voce poderá observar o endereço IP dinâmico  designado pelo Modem ao seu NodeMCU.

Connecting the NodeMCU to Local WiFi

Tome nota deste endereço de IP. Precisaremos dele para conectar o App Android.

O código acima poderá ser baixado de meu GitHub:

NodeMCU_Connection_Test_EXT

7: Completando o código fonte para o NodeMCU

Para que nosso robô se mova, o NodeMCU deverá receber comandos provenientes do dispositivo Android. Para tal, definamos uma variável para receber estes comandos:

String  command ="";

Por exemplo, se a aplicação Android enviar como um comando: “forward” (a frente), o robô deverá avançar, invocando-se a função forwardMotor(). Veja abaixo:

if (command == "forward")
{
  forwardMotor();
}

O mesmo conceito deverá ser aplicado a todo os demais comandos e funções associadas tal como vimos no item 4:

  1. STOP: Pare
  2. LEFT: Vire à esquerda
  3. RIGHT: Vire à direita
  4. REVERSE: Dê marcha à ré
  5. FORWARD: Vá para a frente

Baixe o código completo: WiFi_Robot_NodeMCU_Android_Voice_EXT de meu GitHub.

Entre com as credenciais de sua rede local:

const char* ssid = "YOUR SSID";
const char* password = "YOUR PASSWORD";

Carregue o código em seu NodeMCU e pronto! Você poderá verificar no Monitor Serial se o programa está em execução. Deixe o programa em execução para podermos testar o o aplicativo a ser desenvolvido na próxima etapa.

8: A App Android : “Designer Tab”

The Android App:

Usaremos o MIT App Inventor para o desenvolvimento de nossa App Android.

Principais componentes da Screen 1 (veja a foto anterior):

  • User Interface:
    • Input of IP Address
      • TextBox named “IP_Address”
    • 5 Command Buttons:
      • forward
      • reverse
      • right
      • left
      • stop
    • Voice Input Button
      • Voice_Input
  • Non Visible Components:
    • Web1
    • SpeachRecognizer1
    • TextToSpeach1
  • Other:
    • Text Box:
      • Speach_To_Text
    • Label:
      • Comm_Status

                  Screenshot_2017-04-02-14-36-34

9: A App Android: Botões

The Android App: Buttons

Na Tab “Blocks”, deveremos criar 5 Botões, um para cada comando. Todos eles seguirão a estrutura dos blocos acima.

Estes blocos serão chamados quando o botão “forward” (“botão com a seta para cima”) é pressionado.

Quando você clicar no botão, 3 ações serão executadas:

  1. Um comando será enviado no formato: http: / ip_address * / forward
  2. Um “eco” no mesmo formato é esperado
  3. Uma “mensagem” audível será executada pela App: no caso: “forward”

* O IP_Address será a que você digitar no seu App. Por exemplo, se o IP address é 10.1.0.3, a mensagem completa seria: http: /10.0.1.3/forward

Você poderá utilizar qualquer mensagem, ou até mesmo deixar a mensagem vazia.

10: A App Android: Reconhecimento de voz

The Android App: Voice Recognition

Os blocos acima mostram o código de reconhecimento de voz para o nosso aplicativo.

Note que, para qualquer comando de voz inserido, o resultado será um comando em letras minúsculas. Isso facilitará a descodificação do comando pelo NodeMCU.

Por exemplo, se você deseja mover o robô para a frente, uma palavra ou sentença deverá ser enviada para ser interpretada pelo NodeMCU. No exemplo abaixo, o código reconheceria qualquer uma das palavras: “forward”, “frente” ou “a frente “.

if (command == "forward" || command == "frente" || command == "a frente")  
{
  forwardMotor();
}

Assim, por exemplo quando digo “frente”, a App enviará : http: / 10.0.1.10/frente e o NodeMCU tomará o que é recebido após o “/”,  a palavra “frente” que será interpretada como um comando do tipo “forward”. Com este comando, a função forwardMotor () será invocada.

11: A App Android: Manipulação de erros

The Android App: Error Manipulation

Se ocorrer um erro, por exemplo, quando você diz um comando de voz não reconhecido pelo NodeMCU, o bloco acima irá gravar os detalhes do erro na última linha (Comm_Status) do App, conforme mostrado abaixo:

Screenshot_2017-04-02-14-36-50

12: Teste final

Final Test

Você poderá criar seu aplicativo passo a passo, como mostrado nas etapas anteriores ou fazer o upload do meu projeto completo (.aia) no MIT App Inventor, modificando-o de acordo a suas necessidades. Além disso, caso você não tenha experiência no desenvolvimento de aplicativos para Android, poderá executar o arquivo .apk diretamente em seu dispositivo Android.

Ambos, os arquivos .aia e .apk podem ser baixados de meu GitHub:

WiFi_Voice_Robot_Ctrl_V2.aia

WiFi_Voice_Robot_Ctrl_V2.apk

O video abaixo mostra alguns testes de motores utilizando-se do App:

Conclusão

Como sempre, espero que este projecto possa ajudar outros a encontrarem o seu caminho no emocionante mundo da electrónica, robótica e do IoT!

Verifique o depositário no GitHub para obter os arquivos atualizados:

WiFi-Voice-Controlled-Robot

Saludos desde el sur del mundo!

Até o próximo tutorial!

Obrigado

Marcelo

Anúncios

Autor: Marcelo Rovai

Engineer, writer and forever student. Passionate to share knowledge of electronics with focus on IoT and robotics.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s