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.

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-programmer

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
                                http://twitter.com/blynk_app

  Blynk library is licensed under MIT license
  This example code is in public domain.
 /************************************************************
 * 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
 *************************************************************/
/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial


#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

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

Codigo ATmel Studio7
/*
 * Atmega128-RobotCar.c
 *
 * 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
 */
#define F_CPU 8000000UL				/* Define CPU Frequency e.g. here its int. 8MHz */
#include <inttypes.h>
#include <avr/io.h>
#include <math.h>
#include <stdio.h>					/* Include standard IO library */
#include <string.h>					/* Include string library */
#include <stdlib.h>					/* 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 uma resposta

Your email address will not be published.

www.000webhost.com