Introduction: Super Servo Motor With WiFi
Today we’re doing another motor video! And again we’ll use that "little engine" that I like a lot. But this time, I printed some 3D parts and created a “Super Servo Motor” with WiFi. I’m also again using the ESP32 LoRa in our project today. I created an application that controls the servo motor by commands via WiFi. Thus, I’ll present a position control option using the MOSFET H bridge and apply the position control using a TCP / IP connection to the terminal application.
Download the APP in this LINK.
Step 1: Demonstration
Step 2: Resources Used
- One ESP WiFi LoRa 32
- Four IRF1404s
- Three BC337s
- One CD4011
- Resistors
- Two 1uF electrolytic capacitors
- Two 1N4148 diodes
- USB cable for ESP
- 12V source for the load used
- Charge (we use an electric glass motor)
- Protoboard
- Wires
- 10k ohms potentiometer
- One cell phone
- TCP / IP Terminal Application
- 3D printed parts
Step 3: Scheme
We’ll use the same circuit presented in the video on H Bridge with MOSFET entitled, “Super driver from 1 to 200 amperes for DC motor.”
Step 4: Scheme - Blocks
Step 5: Determining the Axis Position
The only change in the circuit is the introduction of a potentiometer that will be coupled to the motor shaft to measure its position, just like the servos.
To perform the coupling, we use a set of parts manufactured in the 3D printer. This will serve only to couple the axis to the potentiometer and provide a pointer to observe the control.
Step 6: Positioning
Step 7: Source Code: Mean Curve
Step 8: Source Code:
#Includes and #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
Objects, Constants, and Variables
//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 }
The task taskManualPosition ()
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 } }
Function interpreter (String command)
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 } }
Step 9: Using Another TCP / IP Client
Step 10: Terminal Application
* The wait command is a built-in Terminal Application command
Step 11: Annex: Inclusion of Dialog in Font Size 12
For a better visualization, we included in the source file of the display a new font called Dialog, with size 12.
If you want to use it, we will leave the OLEDDisplayFonts.h file available for download. This file already contains the Dialog font 12 that we used in the example, and to use it:
• Make a backup copy of your OLEDDisplayFonts.h file from the OLED library used in the example.
• Copy the OLEDDisplayFonts.h file to its location in the library.
• MAKE SURE NOT TO LOSE YOUR BACKUP FILE!
For more information about adding fonts to the OLED library, please visit the link: