Controlo Robot Car ATmega + ESP8266 + Android

Olá,

ao fim de varias semanas de programação depois de anunciar neste artigo o Robot Car, considera-se concluída a versão beta de controlo remoto com ESP8266+ATmega+Android.

Neste trabalho para simplificar a programação usou-se o versatil Kit ATmega128, no entanto o trabalho migrará para ATmega8, para reduzir o espaço que o Kit ocupa.

Atmega128-M128

 Uma novidade é o uso do ESP8266 ESP01, conectado a WIFI comunicando com um Smartfone ou Tablet, usando aplicação Blynk.

Numa primeira fase antes de explicar a construção do software vamos ao hardware usado, com poucos euros podemos fazer um carro telecomandado.
Os dispositivos foram pensados de forma a reduzir ao máximo o consumo de corrente alguns dos componentes podem ser alterados depende do gosto e do material que possuem.

COMPONENTES

Resultado Final:

VeiculoFinal

Tudo o que precisa esta enumerado acima, enquanto aguarda pela encomenda do material sugiro que veja este video:

Como montar o KIT Smart Robot Car:

Após a demonstração da montagem do SmartCar, vamos então ao dispositivo de comunicação o ESP8266-ESP01.

Para programar o dispositivo precisamos de usar este programador USB Adapter CH340

USB_esp8266-CH340

com o respectivo shunt aplicado USB_ESP_Programer

Dúvidas sobre este shunt, podem consultar este artigo.

Colocado o shunt no USB Adapter CH340 , vamos precisar do IDE do arduino para programa-lo.

Antes de mais vamos precisar do software  Blynk instalado no arduino, conforme explicado neste video:

O Blynk tem a grande vantagem de possuir botões configuráveis que trabalham com arduino, mas vamos aprender como configura-los para o ESP.

Sei que pode parecer ser um pouco monótono saltar para outras paginas, mas tal como expliquei no inicio se existir algo com exemplificação, não me vou repetir, penso ser mais útil deste modo,assim vamos interligando com outros trabalhos…

Por isso vejam esta paginas com os exemplos do Blynk, naveguem nos menus e verifiquem as alterações existentes e quais a funcionalidades.

Bom, neste trabalho usamos o seguinte codigo:

/*************************************************************
Download latest Blynk library here:
https://github.com/blynkkk/blynk-library/releases/latest
Blynk is a platform with iOS and Android apps to control
Arduino, Raspberry Pi and the likes over the Internet.
You can easily build graphic interfaces for all your
projects by simply dragging and dropping widgets.
Downloads, docs, tutorials: http://www.blynk.cc
Sketch generator: http://examples.blynk.cc
Blynk community: http://community.blynk.cc
Follow us: http://www.fb.com/blynkapp

Blynk library is licensed under MIT license
This example code is in public domain.
/************************************************************
* Created: 04-03-2018
* Author : Norlinux
* http://www.microelectronic.pt
* https://www.facebook.com/MundoDosMicrocontroladores/
* Released under GPLv3.
* Please refer to LICENSE file for licensing information.
* which can be found at http://www.gnu.org/licenses/gpl.txt
*************************************************************/
/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial
#include
#include
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "90f5c415c3f74417b04aaea1d4";
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "MEO-Maquina";
char pass[] = "my-passwd";
BlynkTimer timer;
typedef enum _Qlado {Nenhum, Direito, Esquerdo} Qlado;
Qlado Qual_Lado = Nenhum;
String conteudo = "";
Qlado leStringSerial(){
int Esq=0,Dir=0;
char caractere;
Qlado rtLado=Nenhum;
// Enquanto receber algo pela serial
while(Serial.available() > 0) {
// Lê byte da serial
caractere = Serial.read();
// Ignora caractere de quebra de linha
if(Dir==1){
Esq=0;
conteudo.concat(caractere);
rtLado=Direito;
}
if(Esq==1){
Dir=0;
conteudo.concat(caractere);
rtLado=Esquerdo;
}
if (caractere == 'D'){
// Concatena valores
Dir=1;
}
if (caractere == 'E'){
// Concatena valores
Esq=1;
}
// Aguarda buffer serial ler próximo caractere
delay(10);
}
return rtLado;
}
BLYNK_WRITE(V0)
{
int pinValue = param.asInt();
Serial.print(pinValue);
}
BLYNK_WRITE(V1)
{
int pinValue = param.asInt();
Serial.print(pinValue);
}
BLYNK_WRITE(V2)
{
int pinValue = param.asInt();
Serial.print(pinValue);
}
BLYNK_WRITE(V3)
{
int pinValue = param.asInt();
Serial.print(pinValue);
}
BLYNK_WRITE(V4)
{
int pinValue = param.asInt();
Serial.print(pinValue);
}
BLYNK_WRITE(V5)
{
int pinValue = param.asInt();
Serial.print(pinValue);
}
void SendValue(Qlado Qual_Lado){
switch(Qual_Lado){
case Nenhum:
break;
case Direito:
Blynk.virtualWrite(V8, conteudo);
break;
case Esquerdo:
Blynk.virtualWrite(V7, conteudo);
break;
}
}
void setup()
{
// Debug console
Serial.begin(115200);
Blynk.begin(auth, ssid, pass);
// You can also specify server:
//Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8442);
//Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8442);
}
void loop()
{
Blynk.run();
if (Serial.available() > 0){
Qual_Lado = leStringSerial();
SendValue(Qual_Lado);
conteudo="";
}
}

Este código é bem mais simples do que se demonstra.
Para o trabalho foi fundamental colocar duas caixas com o valor do SetPoint do PWM, garantindo que os valores do Robot car sejam coerentes e não entrem em conflito.
O principal são os botões conforme as linhas:
83 BLYNK_WRITE(V0)
88 BLYNK_WRITE(V1)
93 BLYNK_WRITE(V2)
98 BLYNK_WRITE(V3)
103 BLYNK_WRITE(V4)
108 BLYNK_WRITE(V5),

A aparência deste botões ja tinha sido publicada neste trabalho:

Foi no entanto colocado nova função que comunicara com a USART do ATmega, trata-se da função da linha 50 leStringSerial(). Assim que foi enviado um caracter E ou D, sabêmos que esta relacionado com o SetPoint do motor Esquerdo ou Direito.

Fazem o download do programa do arduino para o ESP, se pretenderem testar fazem-no do seguinte modo:
Usar o Proteus como simulador, para isso precisamos de uma porta serie virtual, caso tenham windows 32 bit sugiro este programa VPSE, funciona perfeitamente. Caso tenham Windows 64Bit, aí tudo muda, o que consegui foi um shareware que vai funcionando com algum trabalho Serial Port Monitoring Control.
Este é o esquema dos componentes instalados no Proteus, deixo ficar aqui os files originais para a versão Proteus7.7 SP2.

Robot_ProteusFigura 4.

Entretanto fiz um video que demonstra o seguinte, tendo uma porta serie com com1 e outra com2, usando o RealTerm, podemos simular e ver como varia o PWM de cada motor, então vejam o video:

Vamos ao código:

/*
* Atmega128-RobotCar.c
*
* Created: 04-03-2018
* Author : Norlinux
* http://www.microelectronic.pt
* http://maquina.96.lt
* https://www.facebook.com/MundoDosMicrocontroladores/
* Released under GPLv3.
* Please refer to LICENSE file for licensing information.
* which can be found at http://www.gnu.org/licenses/gpl.txt
*/
#define F_CPU 8000000UL /* Define CPU Frequency e.g. here its int. 8MHz */
#include
#include <avr/io.h>
#include
#include /* Include standard IO library */
#include /* Include string library */
#include /* Include standard library */
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "SimpleUART.h" /* Include UART header file */
int main(void)
{
//Inicialização de comunicação USART
USART_Init(Baud_115200);
//USART_Init(Baud_9600);
//Inicialização das configurações dos Motores
Motor_Init();
//Inicialização da estrutura dos motores
motor_dc motor={0,0,0,0,Frente,Frente,0,0};
//Vector com os valores de SetPoint Pre-defenidos 8 bits
int Duty[7]={-Maximo,-Medio,-Minimo,0,Minimo,Medio,Maximo}; // este vector tem como zero o Duty[3]
int* Motor_Duty = &Duty[Range];
// Variaveis para uso geral
int IncUpDown=0,IncL=0,IncR=0,ValorSP_Esq=0,ValorSP_Dir=0,Flag_Change=0,STOP=0;
// Variavel de envio carateres pela transmissão USART
char msg[6];
while (1)
{
if (STOP==Sim) // Motor Parado
{
motor.Start=Nao;
Stop_Engine();
}
else // Motor em funcionamento
{
motor.Start=Sim;
motor_control_dir(&motor);
motor_control_esq(&motor);
}
if (Flag)
{
switch (NumberReceived)
{
case espUP: //1 botão Frente
if (STOP==Sim)
{break;}
else{
motor.sentido_dir=Frente;
motor.sentido_esq=Frente;
Speed_UP(&IncUpDown,Range);
ValorSP_Esq=Motor_Duty[IncUpDown];
ValorSP_Dir=ValorSP_Esq;
_delay_us(Range);
sprintf(msg,"E%i",ValorSP_Esq);
send_message(msg);
motor.Set_P_dir=ValorSP_Esq;
motor.Set_P_esq=ValorSP_Dir;
Flag=0; // Flag que permite saber qual o estado do buffer USART
Flag_Change=Nao; // Flag de mudança de direcção / Encravamento
break;}
case espDOWN: //2 Botão Traz
if (STOP==Sim)
{break;}
else{
Speed_Down(&IncUpDown,Range);
motor.sentido_dir=Traz;
motor.sentido_esq=Traz;
ValorSP_Dir=Motor_Duty[IncUpDown];
ValorSP_Esq=ValorSP_Dir;
_delay_us(Range);
sprintf(msg,"D%i",ValorSP_Dir);
send_message(msg);
motor.Set_P_dir=ValorSP_Dir;
motor.Set_P_esq=ValorSP_Esq;
Flag=Nao;
Flag_Change=Nao;
break;}
case espRIGHT: // 3 Botão Direita
if (STOP==Sim)
{break;}
else
{
if (motor.sentido_dir==Frente && motor.sentido_esq==Frente)
{
if (Flag_Change==Nao)
{
IncR=IncUpDown;
Speed_Down(&IncR,Range);
Flag_Change=Sim;
}
else
{
Speed_Down(&IncR,Range);
}
ValorSP_Dir=Motor_Duty[IncR];
}
else {
if (Flag_Change==Nao)
{
IncR=IncUpDown;
Speed_UP(&IncR,Range);
Flag_Change=Sim;
}
else
{
Speed_UP(&IncR,Range);
}
ValorSP_Dir=Motor_Duty[IncR];
}
_delay_us(Range);
sprintf(msg,"D%i",ValorSP_Dir);
send_message(msg);
motor.Set_P_dir=ValorSP_Dir;
Flag=Nao;
break;}
case espLEFT: // 4 Esquerda
if (STOP==Sim)
{break;}
else
{
if (motor.sentido_dir==Frente && motor.sentido_esq==Frente)
{
if (Flag_Change==Nao)
{
IncL=IncUpDown;
Speed_Down(&IncL,Range);
Flag_Change=Sim;
}
else
{
Speed_Down(&IncL,Range);
}
ValorSP_Esq=Motor_Duty[IncL];
}
else {
if (Flag_Change==Nao)
{
IncL=IncUpDown;
Speed_UP(&IncL,Range);
Flag_Change=Sim;
}
else
{
Speed_UP(&IncL,Range);
}
ValorSP_Dir=Motor_Duty[IncL];
}
_delay_us(Range);
sprintf(msg,"E%i",ValorSP_Esq);
send_message(msg);
motor.Set_P_esq=ValorSP_Esq;
Flag=0;
break;}
case espON: //5 Arranque/Liga
motor.Start=Sim;
Start_Engine(Frente);
Flag=Nao;
STOP=Nao;
break;
case espOFF: // 0 Paragem / Desliga
STOP=Sim;
Flag=Nao;
break;
}
}
}
}

Vamos dar inicio á compreensão do código:
Para conseguirmos comunicar entre ESP8266 | ATmega128, utiliza-se a função  USART_Init(Baud_115200) ,
De seguida inicia-se a função Motor_Init(), correspondente á configuração dos Port’s do ATmega128 onde irá estar ligado os motores do carro, conforme a Figura4.
A estrutura do tipo motor_dc, é fundamental para guardar e transferir informação referente ao SetPoint,Sentido,Valor do PWM, etc… Funciona como ponteiro que é actualizado na função dentro do main motor_control_dir(&motor); motor_control_esq(&motor);
O vector Duty[7], possui os valores de Set Point pré-definidos de escala entre 0-255, verificou-se que o motor pára com o OCR abaixo dos 170, por essa razão a gama é de 170-255.

Agora um pormenor interessante, este SmartCar tem 3 Velocidades, que poderão ser alteradas conforme o interesse do utilizador.
O facto de ter 3 Velocidades veio a complicar um pouco a mudança de direcção e rotação, para um utilizador comum poderá gerar alguma dificuldade no controlo do veículo, com alguma prática torna-se-á muito interessante.

Falta falar na estrutura ESP_Result, que retorna o valor do botão que for seleccionado no comando do Android+Blynk.

Deixo aqui uma pequena demonstração do funcionamento do SmartCar, com um utilizador novato com apenas 10 minutos de condução do veículo .

Voilá, espero que tenham gostado do trabalho, em breve altera-se para ATmega8 tudo muito compacto, para o carro ter menos peso e ser mais veloz 🙂

 

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 )

Foto do Google

Você está comentando utilizando sua conta Google. 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 )

Conectando a %s