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.
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:
3 Comentários
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
ResponderExcluirTem como disponibilizar os arquivos para imprimir as peças na impressora 3d? 😃
ResponderExcluirParabéns pelas aulas professor. Tem me ajudado muito.
ResponderExcluirProfessor Fernando, como poderia fazer para controlar a velocidade desse servo juntamente com o ângulo desejado usando esp32?