banner

Super Servo Motor com WiFi


Mais um vídeo de motor, pessoal. E usando novamente aquele “motorzinho” que gosto muito, o de vidro de carro. Mas, desta vez, imprimi umas peças em 3D e criei um SUPER SERVO MOTOR com WiFi. Falo isso, pois estou comparando com aqueles servos pequenos de aeromodelo, os quais não têm muita força. Neste nosso projeto de hoje eu utilizei também o ESP32 LoRa. Criei um aplicativo que controla o servo motor por comandos via WiFi. Vou, portanto, de apresentar uma opção de controle de posição usando a ponte H de MOSFET’s e aplicar o controle de posição usando uma conexão TCP/IP com o aplicativo terminal.
O aplicativo, que serve para fazer a comunicação de ESP32 e ESP8266, está disponível neste LINK.







Demonstração






Recursos usados

  • Um ESP WiFi LoRa 32
  • Quatro IRF1404
  • Três BC337
  • Um CD4011
  • Resistores
  • Dois capacitores eletrolíticos 1uF
  • Dois diodo 1N4148
  • Cabo USB para o ESP
  • Fonte 12V para a carga utilizada
  • Carga (usamos um motor de vidro elétrico)
  • Protoboard
  • Fios
  • Potenciômetro de 10k ohms
  • Um celular
  • Aplicativo terminal TCP/IP
  • Peças impressas em 3D






Esquema

Utilizaremos o mesmo circuito apresentado no vídeo sobre ponte H com MOSFET: Super driver de 1 a 200 amperespara motor DC.





Esquema – Blocos






Determinando a posição do eixo

A única alteração no circuito é a introdução de um potenciômetro que será acoplado ao eixo do motor para medir sua posição, assim como nos servos.
Para realizar o acoplamento, utilizamos um conjunto de peças fabricadas na impressora 3D, servindo apenas para acoplar o eixo ao potenciômetro e disponibilizar um ponteiro para observação do controle.






Posicionamento






Código-Fonte: Curva média





Código-Fonte:

#Includes e #Defines

//Bibliotecas para utilização do display OLED
#include <Wire.h>  // Necessário apenas para o Arduino 1.6.5 e posterior
#include "SSD1306.h" // o mesmo que #include "SSD1306Wire.h"

//Biblioteca para WiFi
#include <WiFi.h>

//definições para o display
//Os pinos do OLED estão conectados ao ESP32 pelos seguintes GPIO's:
//OLED_SDA -- GPIO4
//OLED_SCL -- GPIO15
//OLED_RST -- GPIO16

#define SDA    4
#define SCL   15
#define RST   16 //RST pode ser ajustado por software


Objetos, Constantes e variáveis

//Instanciando e ajustando os pinos do objeto "display"
SSD1306  display(0x3c, SDA, SCL, RST);

//definições para o WiFi
const char* ssid     = "NOME_DA_REDE_WIFI";
const char* password = "SENHA_PARA_REDE";
const int porta = 80; //porta na qual o servidor ouvirá requisições

WiFiServer server(porta);

//Define a porta para leitura (ADC)
const int pin_Leitura = 36; //ADC1_0

//Definições dos PWM
//PWM usado como oscilador do dobrador de tensão
const int freq_osc = 1000; //Frequência do PWM
const int canal_osc = 0; // canal do PWM oscilador
const int pin_osc = 21; //GPIO utilizada para o oscilador
const int ciclo = 2048; //ciclo de trabalho de 50%

//PWM usado para controle de valor_pwm
const int freq_vel = 50; //Frequencia do PWM do controle de valor_pwm
const int canal_vel = 1; // canal do PWM controle de valor_pwm
const int pin_EN = 17; //Habilita ou desabilita a ponte (controla a valor_pwm)
const int pin_DIR = 2; //Controla direção

//Resolução dos PWM's
const int resolucao = 12; // Resolução (4096 estados)

//Constante que determinar o valor MÍNIMO da posião (lida no ADC)
const int min_pos = 730;
//Constante que determinar o valor MÁXIMO da posição (lida no ADC)
const int max_pos = 3100;
//constante que representa a largura da zona sem atualização constante
const int zonaInativa = 30; //Em unidade do ADC
//Constante que determinar o valor MÍNIMO do PWM
const int min_pwm = 2000;
//Constante que determinar o valor MÁXIMO do PWM
const int max_pwm = 4095;

// variável para armazenar a posição atual
int posicao = 0;
// Variável usada para guardar a direção
boolean dir = false;
// variável para armazenar a valor_pwm atual
int valor_pwm = 0;
//Valor da posição ALVO a ser alcançada
int alvo = (max_pos + min_pos) / 2; //incialmente 50% do curso


Setup()

void setup()
{
  //Serial.begin(1000000); //para debug

  // Inicia o display
  display.init();
  //Vira a tela verticalmente
  display.flipScreenVertically();
  //Limpa o buffer
  display.clear();
  //ajusta o alinhamento para a esquerda
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  //ajusta a fonte para Dialog 12
  display.setFont(Dialog_plain_12);
  //ajusta a fonte para ArialMT caso não tenha a fonte Dialog 12
  //display.setFont(ArialMT_Plain_10);
  //aguarda um instante
  delay(10);

  //Inicia o WiFi
  WiFi.begin(ssid, password);
  //tenta conectar-se a rede WiFi
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    display.clear(); //limpa o buffer do display
    display.drawString(0, 0, "Conectando em "); //escreve no buffer
    display.drawString(0, 16, String(ssid)); //escreve no buffer
    display.drawString(0, 32, String(millis() / 1000) + "s"); //escreve no buffer
    display.display(); ///mostra no display
  }
  //Avisa que conectou-se
  display.clear(); //limpa o buffer do display
  display.drawString(0, 0, "WiFi conectado."); //escreve no buffer
  display.drawString(0, 16, "Acesso em: "); //escreve no buffer
  display.drawString(0, 32, WiFi.localIP().toString()); //escreve no buffer
  display.drawString(0, 48, "Porta:" + String(porta)); //escreve no buffer
  display.display(); ///mostra no display
  delay(2000);
  //inicia o servidor
  server.begin();

  //Ajusta os pinos de atuação e leitura
  //seta a direção dos GPIO
  pinMode(pin_Leitura, INPUT);
  pinMode(pin_osc, OUTPUT);
  pinMode(pin_DIR, OUTPUT);
  pinMode(pin_EN, OUTPUT);

  // Ajusta o PWM do oscilador do dobrador
  ledcSetup(canal_osc, freq_osc, resolucao);
  ledcAttachPin(pin_osc, canal_osc);
  ledcWrite(canal_osc, ciclo);

  // Ajusta o PWM de controle de valor_pwm
  ledcSetup(canal_vel, freq_vel, resolucao);
  ledcAttachPin(pin_EN, canal_vel);
  ledcWrite(canal_vel, valor_pwm);

  //Ajusta a direção para LOW inicialmente
  digitalWrite(pin_DIR, dir);

  //Criamos uma nova tarefa
  xTaskCreatePinnedToCore
  (
    taskMantemPosicao,   //Função que será executada
    "taskMantemPosicao", //Nome da tarefa
    10000,      //Tamanho da pilha
    NULL,       //Parâmetro da tarefa (no caso não usamos)
    2,          //Prioridade da tarefa
    NULL,       //Caso queria manter uma referência para a tarefa que vai ser criada (no caso não precisamos)
    0           //Número do core que será executada a tarefa (usamos o core 0 para o loop ficar livre com o core 1)
  );
}


Loop()

void loop() {
  WiFiClient client = server.available();   // Aguardando cliente
  if (client)
  {    //Se um cliente está conectado
    String recebido = "";
    while (client.connected())
    {      //fica em loop enquanto o cliente estiver conectado
      if (client.available())
      {        //se existe bytes disponíveis para leitura
        char c = client.read(); //Lê o byte para c
        //Serial.write(c); //escreve na serial o valor de c
        if (c == '\n')
        {          // se c é um caracter de nova linha
          recebido = recebido + ": " + interpretador(recebido);
          client.print(recebido); //retorna resultado da operação
//******************************************************************************************* 
     /*ATUALIZAÇÃO
       Trecho alterado para compatibilidade com a conexão permanente
       do Aplicativo Terminal atualizado
    */ 
          //client.stop(); //Foi comentado para manter a conexão permanente.
          recebido = "";
//*******************************************************************************************          
        }
        else if (c == '\r')//não faz nada, elimina o \r       
        else
        {
          //senão, concatena em recebido
          recebido += c;
        }
      }
//*******************************************************************************************      
     /*ATUALIZAÇÃO
       Trecho introduzido para manter a atualização
       do display agora que o aplicativo funciona com uma 
       conexão permanente
    */
      display.clear(); //limpa o buffer do display
      display.drawString(0, 0, "Posiçao: " + String(map(posicao, min_pos, max_pos, 0, 100)) + "%"); //escreve no buffer
      display.drawString(0, 16, String(ssid)); //escreve no buffer
      display.drawString(0, 32, WiFi.localIP().toString()); //escreve no buffer
      display.drawString(0, 48, "Porta:" + String(porta)); //escreve no buffer
      display.display(); ///mostra no display
//*******************************************************************************************  
    }
    //se o cliente desconectou, fecha a conexão
    client.stop();
    delay(10); //aguarda um momento
  }
  display.clear(); //limpa o buffer do display
  display.drawString(0, 0, "Posiçao: " + String(map(posicao, min_pos, max_pos, 0, 100)) + "%"); //escreve no buffer
  display.drawString(0, 16, String(ssid)); //escreve no buffer
  display.drawString(0, 32, WiFi.localIP().toString()); //escreve no buffer
  display.drawString(0, 48, "Porta:" + String(porta)); //escreve no buffer
  display.display(); ///mostra no display
}


A tarefa taskMantemPosicao()

void taskMantemPosicao(void* pvParameters)
{
  //IMPORTANTE: A tarefa não pode terminar, deve ficar presa em um loop infinito
  while (true)
  {
    float acumulador = 0; //servirá para o calculo da média

    //lê a posição média atual
    for (int i = 0; i < 100; i++)
    {
      acumulador = acumulador + float(analogRead(pin_Leitura));
    }
    posicao = (acumulador / 100.0);


    //determina a DIREÇÃO com base na diferença entre
    //o valor alvo e a posição atual
    dir = (alvo > posicao); //Atenção para a direção do movimento. Teste antes de acoplar

    //determina a valor_pwm com base na diferença entre
    //o valor alvo e a posição atual
    valor_pwm = (((abs(alvo - posicao)) / (max_pos - min_pos)) * (max_pwm - min_pwm)) + min_pwm;

    //cria uma região sem ajuste constante para reduzir vibrações e gasto desnecessário de energia
    if ((abs(alvo - posicao )) <= zonaInativa) {
      valor_pwm = 0;
    }

    //atribui a direção ao pino
    digitalWrite(pin_DIR, dir); // ajusta o pino de direção com o novo valor

    //atribui a valor_pwm ao PWM
    ledcWrite(canal_vel, valor_pwm); //ajusta o PWM para o novo valor

    //Delay de 1ms da tarefa. Pode ser feita também em ticks.
    //Para executar em millis dividimos pela constante portTICK_PERIOD_MS
    TickType_t taskDelay = 1 / portTICK_PERIOD_MS;
    vTaskDelay(taskDelay); //executa o intervalo

    //Serial.println(valor_pwm);//para debug
    //Serial.println((alvo - posicao));//para debug
  }
}


Função interpretador(String comando)

String interpretador( String comando)
{
  //por conveniência, o comando recebido é
  //convertido para maiúsculas
  comando.toUpperCase();

  if (comando == "") //se um comando vazio chegar
  {
    display.clear(); //limpa o buffer do display
    display.drawString(0, 0, "Comando vazio."); //escreve no buffer
    display.display(); ///mostra no display
    delay(3000);
    return "OK";
  }
  else if (comando.indexOf("MSG") != -1) //solicitação de exibição de mensagem no display
  {
    int indice = comando.indexOf("MSG"); //determina a posição do comando MSG
    //Remove a mensagem a ser exibida de string recebida
    String mensagem = (comando.substring(indice + 3, comando.length()));

    display.clear(); //limpa o buffer do display
    display.drawString(0, 27, mensagem); //escreve no buffer
    display.display(); ///mostra no display
    delay(3000); //aguarda para permitir a leitura
    return "OK";
  }
  else if (comando == "LER") //Exibe no display o estado atual do PWM, direção e Posição
  {
    display.clear(); //limpa o buffer do display
    display.drawString(0, 0, "Informações atuais"); //escreve no buffer
    display.drawString(0, 16, "Posição: " + String(posicao)); //escreve no buffer
    display.drawString(0, 32, "PWM: " + String(valor_pwm)); //escreve no buffer
    display.drawString(0, 48, "Direção: " + String(dir)); //escreve no buffer
    display.display(); ///mostra no display
    delay(3000); //aguarda para permitir a leitura
    return "OK";
  }
  else if (comando.indexOf("SET") != -1)//Ajusta para uma nova posição
  {
    int indice = comando.indexOf("SET");//identifica a posição do comando
    //remove a nova posição da string recebida (de 0 a 100%)
    float alvoTemp = (comando.substring(indice + 3, comando.length())).toFloat();
    //Mapeia o valor para um intervalo entre as posições máxima e mínima permitidas
    alvo = ((alvoTemp / 100.0) * float(max_pos - min_pos)) + min_pos;
//*******************************************************************************************  
    /*ATUALIZAÇÃO
       Trecho introduzido para impedir valores 
       fora dos limites max_pos e min_pos
    */
    if (alvo > max_pos) { //Protege o limite superior
      alvo = max_pos;
    }
    if (alvo <= min_pos) {//Protege o limite inferior
      alvo = min_pos;
    }
//*******************************************************************************************  
    return "OK";
  }
  else
  {
    return "ERRO";//se o comando não foi reconhecido, retorna ERRO
  }
}



Usando outro cliente TCP/IP





Aplicativo Terminal


* O comando wait é um comando interno do Aplicativo Terminal



Anexo: Inclusão da fonte Dialog tamanho 12


  • Para uma melhor visualização, incluímos no arquivo de fontes do display uma nova fonte chamada Dialog, com tamanho 12.
  • Caso queira utilizá-la, deixaremos o arquivo OLEDDisplayFonts.h disponível para download. Este arquivo já contem a fonte Dialog tamanho 12 que utilizamos no exemplo e para utilizá-lo:
·         Faça uma cópia de backup do seu arquivo OLEDDisplayFonts.h, da biblioteca OLED usada no exemplo.
·         Copie o arquivo OLEDDisplayFonts.h para o respectivo local na biblioteca.
·         ATENÇÃO PARA NÃO PERDER SEU ARQUIVO DE BACKUP, PARA O CASO DE ALGO SAIR ERRADO.

  • Mais informações sobre a inclusão de fontes na biblioteca OLED, visite o link:




FAÇA O DOWNLOAD DOS ARQUIVOS:








2 comentários:

  1. Show de bola, será que seria capaz de desenha um projeto de uma casa de 4 comodos ? Pq já fiz na mão mas pensa num desenho feio kkkkkk

    ResponderExcluir
  2. Tem como disponibilizar os arquivos para imprimir as peças na impressora 3d? 😃

    ResponderExcluir

Tecnologia do Blogger.