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

Controle ativado por voz com Android e NodeMCU

Em meu último tutorial: Quando o IoT encontra a Inteligência Artificial: Automação residencial com Alexa e NodeMCU exploramos como equipamentos ativados por voz como o Amazon Echo-Dot utilizando-se de um serviço da web (como o “Alexa”) podem controlar “dispositivos inteligentes” em nossas casas. Neste novo tutorial faremos o mesmo,  porém  em vez de usar o Alexa desenvolveremos nossa própria App em um smartphone Android controlando, tanto com botões quanto por voz, nossos dispositivos domésticos.

O diagrama de blocos nos dá uma geral do que pretendemos desenvolver:

Block Diagram.jpg

e o filme nos mostra como ficará o projeto final:

1: Lista de materiais (BoM)

Valores em USD, utilizados apenas como referência

  1. NodeMCU ESP8266-12E ($8.79)
  2. Mini BreadBoard ($1.00)
  3. 400-point Experiment Breadboard Breadboard ($ 4.97)
  4. 4-Channel Relay Module ($8.49)
  5. LEDs (vermelho, amarelo, verde e azul) ($1.00)
  6. 4 x Resistor (220 ohm)
  7. Male-Female Dupont Cables ($1.00)
  8. Fonte externa DC de 5V ou bateria

….. e naturalmente um telefone ou tablet Android (qualquer modelo com wifi servirá).

2: Conectando o NodeMCU à rede local de WiFi

Conectaremos o NodeMCU a rede WiFi local,  verificando seu endereço IP. Para isso, usaremos o pequeno programa abaixo, o qual fará parte do projeto final:
#include <ESP8266WiFi.h>
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á ver o IP Address assignado por seu Modem ao NodeMCU:

Serial Monitor Comm Test

Tome nota do endereço (em meu acaso: 10.0.1.3). Necessitaremos deste endereço para a conexão ao aplicativo Android que será desenvolvido mais adiante.

3: Montando o HW

Assembling the HW

Usaremos um módulo de relé de 4 canais para controlar 4 LEDs, simulando 4 dispositivos domésticos. Siga as instruções a seguir e termine as conexões:

Conecte as entradas dos relés com os pinos do NodeMCU como mostrado no diagrama e definido no código como mostrado abaixo:

int relay1 = 14;
int relay2 = 15;
int relay3 = 3;
int relay4 = 1;

Nossos dispositivos inteligentes serão simulados pelos LEDs coloridos:

  • relé 1 ==> LED vermelho
  • relé 2 ==> LED amarelo
  • relé 3 ==> LED verde
  • relé 4 ==> LED azul

Nosso Android App irá enviar um comando em forma de string,  o qual deverá ser interpretado pelo código de maneira a ativar cada um dos relés. Definamos as strings para cada comando:

  • Relé 1:
    • Ligar: “r1on”;
    • Desligar: “r1off”;
  • Relé 2:
    • Ligar: “r2on”;
    • Desligar: “r2off”;
  • Relé 3:
    • Ligar: “r3on”;
    • Desligar: “r3off”;
  • Relé 4:
    • Ligar: “r4on”;
    • Desligar: “r4off”;

Definamos uma variável que irá receber os comandos (command) e a lógica associada a mesma:

String  command ="";

Assim, por exemplo, se o aplicativo Android enviar como um comando: “r1on”,  o relé 1 (relay1) deverá ser ativado:

if (command == "r1on")
{
  digitalWrite(relay1, LOW);
}

Observe que os relés usados no projeto são ativados com um nível baixo (LOW).

Utilizando alsoif, escreveremos os demais comandos (veja o codigo completo ao final).

Também definiremos “comandos de grupo” para ligar (“allon”) e desligar (“alloff”) simultaneamente todos os dispositivos. Por exemplo, para ativar ao mesmo tempo todos os relés, usaremos o comando “allon” como mostrado abaixo:

    if (command == "allon") 
    {
      digitalWrite(relay1,LOW);
      digitalWrite(relay2,LOW);
      digitalWrite(relay3,LOW);
      digitalWrite(relay4,LOW);
    }

A mesma lógica deverá ser empregada para o comando “alloff”.

Siga o diagrama elétrico acima para concluir a conexão de todo o HW.

Agora faça o download do código completo:

Home_Automation_NodeMCU_Android_Voice_V2_EXT.ino a partir de meu GitHub.

Entre com as credenciais de sua rede local de WiFi:

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

Faça o upload do código em seu NodeMCU e pronto! Você poderá verificar no  Monitor Serial se o programa está em execução. Deixe o programa rodando para que se possa testar o aplicativo Android desenvolvido a partir do próximo passo.

CUIDADO: Quando fizer o upload do código, desligue a alimentação dos relés para não sobrecarregar o NodeMCU.

4: A App Android : “Designer Tab”

 Usaremos a aplicação on-line: MIT App Inventor para o desenvolvimento de nossaApp Android:

MIT V2 0

Componentes Principais da Screen1 (veja foto acima)

  • Entrada do endereço IP
    • TextBox denominado “IP_Address”
  • 8 botões ON / OFF, um para cada relé:
    • Relay_1_ON
    • Relay_2_OFF
    • Etc
  • 2 botões ON / OFF para todos os dispositivos:
    • All_Devices_ON
    • All_Devices_OFF
  • Botão de Entrada de Voz
    • Voice_Input
  • Componentes não visíveis:
    • Web1
    • SpeachRecognizer1
    • TextToSpeach1
  • Outros:
    • Caixa de texto:
      • Speach_To_Text
    • Label:
      • Comm_Status

Ao final o aplicativo deverá ficar assim:

Screenshot_2017-03-29-16-33-37

5: A App Android: Botões

Devemos criar na tab Blocks, 10 botões. Todos seguirão a mesma estrutura que os 2 mostrados na foto.

MIT V2 2

Esses blocos de 2 botões foram criados para:

  • Relay_1_ON. Click
  • Relay_1_OFF.Click

Estas funções são chamadas quando se “clica” em um desses botões. Por exemplo, quando você clica no botão Relay_1_ON, 3 ações serão executadas:

  1. Um comando será enviado no formato: http: / ip_address * / r1on
  2. Um “eco” no mesmo formato é esperado devido a “call Web1.Get”
  3. Uma “mensagem” sonora será lida pela App: no caso: “Relé 1 ligado”

* O IP_Address será o que você digitar na Tela 1. Utilizando-se o default (10.1.0.3), a mensagem real seria: http: /10.0.1.3/r1on

Voce poderá utilizar qualquer mensagem nesta etapa, ou deixá-la vazia (“”) caso não deseje um retorno auditivo.

6: O App Android: Reconhecimento de voz

Os blocos abaixo mostram a construção do código para o reconhecimento de voz de nosso aplicativo:

MIT V2 3

Note que para qualquer comando de voz, o comando enviado será sempre em  minúsculas (uso do bloco de texto downcase). Isso facilitará a decodificação do comando de voz pelo NodeMCU.

Por exemplo, se você deseja “ativar” o relé 1, uma palavra ou sentença deve ser enviada para ser reconhecida pelo código do NodeMCU. Enviaremos para isto um comando de voz em português: “Ligar UM”

if (command == "r1on"  || command == "ligar 1"    || command == "ligar um")
{
  digitalWrite(relay1,LOW);
}

Assim, quando digo “ligar um”, a App irá enviar: http: / 10.0.1.3/ligar um (ou http: / 10.0.1.3/ligar 1) e o NodeMCU colocará seu pino em LOW,  ativando o relé 1.

7: O App Android: Manipulação de erro

The Android App: Error Manipulation

Se ocorrer um erro, o bloco acima escreverá os detalhes referente ao mesmo na última linha do App (Comm_Status). Você somente irá ver-lo se ao criar o App usar a opção Responsive.

8: Testes finais

Final Test

Você poderá criar seu aplicativo passo a passo, como mostrado nas etapas anteriores ou utilizar o fonte de meu projeto (.aia) diretamente no MIT App Inventor:

MIT V2 1

Caso você não tenha experiência no desenvolvimento de aplicativos Android, poderá executar o arquivo .apk diretamente em seu dispositivo Android.

Ambos arquivos .aia e .apk poderão ser baixados de meu GitHub:

Home_Relay_Voice_Ctrl_V2.aia

Home_Relay_Voice_Ctrl_V2.apk

9: 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:

Home-Automation-with-Android-and-NodeMCU

Saludos desde el sur del mundo!

Até o próximo tutorial!

Obrigado

Marcelo

IoT: Sensor de movimento com o NodeMCU e BLYNK

 

Step 1: Principais componentes

  • Resistor (1K, 2.2K and 330ohm)
  • LED
  • Breadboard
  • Cables

Step 2: O HC-SR501 “Passive Infrared (PIR) Motion Sensor”

Este módulo de sensor de movimento utiliza-se do sensor passivo de sinal infravermelho  LHI778 e o do circuito integrado BISS0001 para controlar como o “movimento deve ser detectado”.

O módulo possui sensibilidade ajustável que permite uma faixa de detecção de movimento de 3 a 7 metros.

O módulo também inclui ajustes de atraso de tempo e seleção de gatilho que permitem ajustes finos dentro de sua aplicação.

fkscpzbivo85exc-large

f19wasdivo85ey1-large

 

O link abaixo nos fornece mais detalhes do funcionamanto do sensor:

Arduino HC-SR501 Motion Sensor Tutorial

Step 3: O HW

O HW é muito simples. O sensor tem 3 pinos (+Power, GND e Saída).

É importante lembrar que a saída do HC-SR501 gera um sinal lógico de + 3.3V (ALTO) ou 0V (LOW), o qual É COMPATÍVEL com os níveis de entrada do NodeMCU, que também funciona com o nível 3.3V. Eu mantive a circuito com um conversor de nível para garantir caso seja utilizado algum outro sensor  com saída 5V. 

Assim, você precisará de um divisor de tensão para converter os níveis de sinal (veja o diagrama elétrico acima).

  • Saída do HC-SR501 ==> para a entrada do divisor de tensão
  • NodeMCU Pin D1 (GPIO 5) ==> para o ponto médio do Divisor de Tensão

Também incluíremos um LED no pino D7 (GPIO13), para uma visualização local.

Step 4: Testando o sensor

Primeiramente faremos um programinha simples para testar e calibrar o sensor.

Carregue o código abaixo no Arduino IDE:

/* HC-SR501 Motion Detector */
#define ledPin D7 // Red LED
#define pirPin D1 // Input for HC-SR501

int pirValue; // variable to store read PIR Value

void setup() 
{
  pinMode(ledPin, OUTPUT);
  pinMode(pirPin, INPUT);
  digitalWrite(ledPin, LOW);
}

void loop() 
{
  pirValue = digitalRead(pirPin);
  digitalWrite(ledPin, pirValue);
}

Ao fazer um movimento na frente do sensor, você verá o LED vermelho acender. Você poderá usar o código acima para ajustes no sensor, se necessário.

Se você não sabe como preparar o IDE do arduino para trabalhar com o NodeMCU, por favor de uma olhadinha em meu tutorial:

Do “blink” ao BLYNK, uma viagem pela “Internet das coisas” nas asas do NodeMCU ESP-12E

Step 5: Incluindo o BLYNK

Siga os Passos abaixo::

  • Crie um  New Project.
  • Defina um nome para o projeto (em meu caso: “Motion Detector“)

  • Selecione NodeMCU como HW Model
  • Copie o  AUTH TOKEN para ser adicionado ao seu código do IDE (o melhor é enviar-lo para seu email).
  • Inclua o “Widget”:  Push Notification.

  • Pressione “Play” (o triangulo no canto superior direito)

fyieb1nivo85k5p-large

Naturalmante o appicativo Blynk nesse ponto irá informar que o NodeMCU está off-line. antes de rodar o aplicativo Blynk, o codigo deve ser carregado e iniciado no módulo NodeMCU. Abaixo o código a ser ingressado no Arduino IDE:

/**************************************************************
 * IoT Motion Detector with Blynk
 * Blynk library is licensed under MIT license
 * This example code is in public domain.
 * 
 * Developed by Marcelo Rovai - 30 November 2016
 **************************************************************/
#include <ESP8266WiFi.h>

#define BLYNK_PRINT Serial    // Comment this out to disable prints and save space
#include <BlynkSimpleEsp8266.h>
char auth[] = "YOUR AUTH CODE HERE";

/* WiFi credentials */
char ssid[] = "YOUR SSID";
char pass[] = "YOUR PASSWORD";

/* HC-SR501 Motion Detector */
#define ledPin D7 
#define pirPin D1 // Input for HC-S501
int pirValue; // Place to store read PIR Value

void setup()
{
  Serial.begin(115200);
  delay(10);
  Blynk.begin(auth, ssid, pass);
  pinMode(ledPin, OUTPUT);
  pinMode(pirPin, INPUT);
  digitalWrite(ledPin, LOW);
}

void loop()
{
  getPirValue();
  Blynk.run();
}

/***************************************************
 * Get PIR data
 **************************************************/
void getPirValue(void)
{
  pirValue = digitalRead(pirPin);
  if (pirValue) 
  { 
    Serial.println("==> Motion detected");
    Blynk.notify("T==> Motion detected");  
  }
  digitalWrite(ledPin, pirValue);
}

Assim que o código é carregado e executado, verifique a aplicação BLYNK, a qual também deverá estar funcionando.

Faça um movimento na frente do sensor, você deverá receber uma mensagem em seu celular como a mostrada abaixo:

Abaixo o código completo para ser executado no Arduino IDE (não se esqueça de entrar com seus dados):

Observação sobre o código:

Vale a pena comentar que os testes para este projeto foram executados somente em laboratório. Para projetos envolvendo sistemas caseiros de alarmes o código funcionaria sem problemas, mas se o sensor perceber muitas variações de movimento ao mesmo tempo, poderíamos chegar a ter problemas. Neste caso, uma solução envolvendo “SimpleTimer” seria o aconselhável.

O ideal quando se desenvolve projetos com o Blink é manter-se na função void loop() somente a função Blynk.run(), movendo-se a getPirValue() para dentro de uma função de timer, por exemplo: timer.setInterval(1000L, getPirValue);

 

Step 6: Conclusão

Como sempre, espero que este projeto ajude outras pessoas a encontrar seu caminho no apaixonante mundo da eletrônica 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

LaserCat – IoT com NodeMCU e Blynk

Exploraremos como controlar servos via internet, utilizando-se o NodeMCU ESP12-E e o Blynk.

Este é um projeto muito simples, onde a idéia principal é explorar como controlar servos via internet. Para isso, usaremos uma grande dupla de componentes, o NodeMCU ESP12-E e um aplicativo Blynk.
O projeto foi baseado em um grande tutorial: Smartphone Controlled Cat Laser, desenvolvido usando-se um Arduino Uno conectado a um PC via Serial. Aqui porém, usaremos o NodeMCU, em um formato mais “portátil” utilizando-se WiFi e bateria.

IMPORTANTE: O apontador laser pode ser perigoso se apontado diretamente para os olhos. Proteja os seus olhos, o de animais e outras pessoas. Use com cuidado.

Abaixo, você poderá ver minha gatinha Cleo brincando com o laser:

Step 1: Os componentes principais:

Step 2: O mecanismo “Pan/Tilt” de posicionamento

Vamos construir um mecanismo de “Pan / Tilt” muito simples, montando os servos “um em cima do outro” como mostrado nas fotos abaixo:

  • O “Pan” Servo  é usado “como base”. Mantenha-o na vertical.

  • Fixe o “Tilt” Servo sobre a parte de movimento do “Pan” Servo  (na posição horizontal)
  • Fixe o ponteiro laser na parte movel do “Tilt” Servo como mostrado.

Note que usei algumas peças metálicas, aproveitadas de um brinquedo desmontado, como estrutura de base para o mecanismo “Pan / Tilt”. Você poderá utilizar outros tipos de estrutura, O importante é manter os movimentos horizontal e vertical.

Step 3: O HW

O circuito elétrico é bem simples:

Antes de iniciar as conexões, leve em consideração que apesar do nodeMCU ser alimentado com 5V, ele funciona internamente com 3.3V (assim como seus pinos de I/O). Assim, os Servos não poderão ser alimentados a partir de uma das 3 saídas de 3.3V do NodeMCU. A exceção é o pino “Vin”, que pode tanto fornecer 5V para o breadboard “+ VCC rail”quanto alimentar o NodeMCU quando uma fonte externa é utilizada (como mostrado no diagrama elétrico abaixo).

O módulo laser deve ser alimentado com 5V, porém ele também funciona com 3.3V o  que faz com que a potência do laser seja menor e consequentemente, seu uso mais seguro.

IMPORTANTE: O apontador laser pode ser perigoso se apontado diretamente para os olhos. Proteja os seus olhos, o de animais e outras pessoas. Use com cuidado.

Observando atentamente o diagrama elétrico acima, execute as seguintes ligações:

  • Ligue o cabo de dados do servo Pan (eixo X) ao NodeMCU Pin D1 (note que todos os pinos digitais do NodeMCU são PWM)
  • Ligue os dados do servo Tilt (eixo “Y”) ao NodeMCU Pin D2
  • Ligue o pino “S” do ponteiro laser ao NodeMCU Pin D3
  • Ligue o pino “Vin” de NodeMCU ao “+ 5V Rail” do Breadboard
  • Conecte os pinos GND (comum ou terra) do NodeMCU, Servos e Ponteiro Laser ao “GND Rail” do Breadboard
  • Conecte os cabos de alimentação dos Servos ao  “+ 5V Rail” do Breadboard

 

Step 4: O codigo

Para o controle dos servos, usaremos a biblioteca do IDE: Servo.h, a qual gerará sinais PWM posicionando-os facilmente com entradas angulares.

Uma vez que a base do projeto é usar o aplicativo BLYNK, o código torna-se muito simples. Devemos definir 4 variáveis virtuais:

V0: “X position”

V1: “Y position”

V2: “Random or manual” operation

V3: Laser ON/OFF command

BLYNK_WRITE(V0) 
{
  xPotReading = param.asInt();
}
BLYNK_WRITE(V1) 
{
  yPotReading = param.asInt();
}
BLYNK_WRITE(V2) 
{
  randState = param.asInt();
}
BLYNK_WRITE(V4) 
{
  laserState = param.asInt();
}

Se você não sabe como preparar o IDE do Arduino para trabalhar com o NodeMCU, por favor de uma olhadinha em meu tutorial:

Do “blink” ao BLYNK, uma viagem pela “Internet das coisas” nas asas do NodeMCU ESP-12E

Abaixo, o código completo. de uma passada de olhos nele. É muito fácil de entender.

Step 5: O “Blynk”

Step 6: Conclusão

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

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

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

“Saludos desde el sur del mundo!” 😉

Obrigado

Marcelo

 

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

“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