O ESP32 AC MotorShield é uma placa de extensão que permite a um ESP32 NodeMCU acionar dois motores DC ou um motor de passo. Já vimos como acionar um motor DC utilizando uma ponte H, o que pode exigir muita cablagem quando se utiliza apenas o CI. Para uma aplicação a bordo, como um robô Willy, será necessário acionar vários motores em paralelo. Estão disponíveis protecções para simplificar a montagem.
Hardware
- Computador
- NodeMCU ESP32
- Cabo USB A macho
- Proteção do motor AC ESP32
- Motor DC x2 ou Motor de passo x1
- Fonte de alimentação externa de 9V
Princípio de funcionamento
O ESP32 AC MotorShield utiliza a ponte H dupla SN751044NE. Pode acionar motores em direção e velocidade com uma tensão nominal entre 4,5 e 36V e uma corrente de 1A com uma fonte de tensão externa:
- até dois motores DC ou um motor de passo bipolar
- GPIOs disponíveis
- Barramentos I2C e UART

Diagrama
Compatível com a placa NodeMCU ESP32, o shield é colocado diretamente sobre o microcontrolador. A alimentação eléctrica do motor é ligada ao bloco de terminais VM.
- 0,4 (motor A
- 15, 2 (motor B
- GPIOs disponíveis noutros pinos
No caso de uma blindagem, as ligações são predefinidas. As ligações do motor são detalhadas nos diagramas seguintes.


Nos dois exemplos seguintes, estamos a adicionar uma interface Web que permite que os motores sejam testados em ambos os sentidos de rotação.
Código de gestão para um motor de corrente contínua
Para interagir com o MotorShield ESP32 e acionar motores DC, não utilizamos nenhuma biblioteca em particular. Pode sempre criar a sua própria biblioteca para simplificar o seu código.
#include <WiFi.h>
#include <WebServer.h>
#include <Arduino.h>
#include <analogWrite.h>
#define IN1 34 //sensor
#define OUT1 0 //A+ - Black
#define OUT2 4 //A- - Green
#define OUT3 15 //B+ - Red
#define OUT4 2 //B- - Blue
//Motor param
int Steps = 0;
int Direction = 0;
int speedMotor = 150;
//Wifi
const char *ssid = "****";
const char *password = "*****";
WebServer server(80);
const int led = 2;
int stateMotorA = 0,stateMotorB = 0;
char stateMotorTextA[3][10] = {"STOP","CCW!","CW!"};
char stateMotorTextB[3][10] = {"STOP","CCW!","CW!"};
String sensorValue;
/*********************************************************************************************
* HANDLE FUNCTIONS
*********************************************************************************************/
void handleRoot()
{
String page = "<!DOCTYPE html>";
page += "<html lang='fr'>";
page += "<head>";
page += " <title>ESP32MotorShieldV1</title>";
page += " <meta http-equiv='refresh' content='60' name='viewport' content='width=device-width, initial-scale=1' charset='UTF-8' />";
page += " <link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>";
page += "<script>";
page += "function getData() {";
page += " var xhttp = new XMLHttpRequest();";
page += " xhttp.onreadystatechange = function() {";
page += " if (this.readyState == 4 && this.status == 200) {";
page += " document.getElementById('SensorValue').innerHTML =this.responseText;";
page += " console.log(this.responseText);";
page += " }";
page += " };";
page += " xhttp.open('GET', 'readSensor', true);";
page += " xhttp.send();";
page += "}";
page += "setInterval(function() {getData();}, 2000); // Call a function repetatively with 2s interval";
page += "</script>";
page += "</head>";
page += "<body>";
page += " <div class='w3-card w3-padding-small w3-jumbo w3-center' style='color:#fff; background-color:#3aaa35;'>";
page += " <p>Motor State A: "; page += stateMotorTextA[stateMotorA]; + "</p>";
page += " </div>";
page += " <div class='w3-bar'>";
page += " <a href='/lefta' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>GAUCHE</a>";
page += " <a href='/stopa' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>STOP</a>";
page += " <a href='/righta' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>DROITE</a>";
page += " </div>";
page += " <div class='w3-card w3-padding-small w3-jumbo w3-center' style='color:#fff; background-color:#3aaa35;'>";
page += " <p>Motor State B: "; page += stateMotorTextB[stateMotorB]; + "</p>";
page += " </div>";
page += " <div class='w3-bar'>";
page += " <a href='/leftb' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>GAUCHE</a>";
page += " <a href='/stopb' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>STOP</a>";
page += " <a href='/rightb' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>DROITE</a>";
page += " </div>";
page += " <div class='w3-card w3-padding-small w3-jumbo w3-center' style='color:#fff; background-color:#3aaa35;'>";
page += " <p>Sensor value: <span id='SensorValue'>0</span></p>";
page += " </div>";
page += " <div class='w3-center w3-padding-16'>";
page += " <p>Server hosted on NodeMCU ESP32 - <i>Made by <a href='https://www.aranacorp.com' style='color:#3aaa35;'>AranaCorp</a></i></p>";
page += " ";
page += " </div>";
page += "</body>";
page += "</html>";
server.setContentLength(page.length());
server.send(200, "text/html", page);
}
void handleLeftA(){
stateMotorA = 2;
digitalWrite(led, HIGH);
server.sendHeader("Location","/");
server.send(303);
}
void handleRightA(){
stateMotorA = 1;
digitalWrite(led, LOW);
server.sendHeader("Location","/");
server.send(303);
}
void handleStopA(){
stateMotorA = 0;
digitalWrite(led, HIGH);
server.sendHeader("Location","/");
server.send(303);
}
void handleLeftB(){
stateMotorB = 2;
digitalWrite(led, HIGH);
server.sendHeader("Location","/");
server.send(303);
}
void handleRightB(){
stateMotorB = 1;
digitalWrite(led, LOW);
server.sendHeader("Location","/");
server.send(303);
}
void handleStopB(){
stateMotorB = 0;
digitalWrite(led, HIGH);
server.sendHeader("Location","/");
server.send(303);
}
void handleInput() {
sensorValue = String(analogRead(IN1));
server.send(200, "text/plain", sensorValue); //Send ADC value only to client ajax request
}
void handleNotFound(){
digitalWrite(led, HIGH);
stateMotorA = 0;
stateMotorB = 0;
server.send(404, "text/plain", "404: Not found");
}
/*********************************************************************************************
* MAIN
*********************************************************************************************/
void setup()
{
pinMode(IN1, INPUT);
pinMode(OUT1, OUTPUT);
pinMode(OUT2, OUTPUT);
pinMode(OUT3, OUTPUT);
pinMode(OUT4, OUTPUT);
Serial.begin(115200);
delay(1000);
Serial.println("\n");
pinMode(led, OUTPUT);
digitalWrite(led, HIGH);
WiFi.persistent(false);
WiFi.begin(ssid, password);
Serial.print("Tentative de connexion...");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(100);
}
Serial.println("\n");
Serial.println("Connexion etablie!");
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/lefta", handleLeftA);
server.on("/righta", handleRightA);
server.on("/stopa", handleStopA);
server.on("/leftb", handleLeftB);
server.on("/rightb", handleRightB);
server.on("/stopb", handleStopB);
server.on("/readSensor", handleInput);//To get sensor value update
server.onNotFound(handleNotFound);
server.begin();
Serial.println("Serveur web actif!");
}
void loop()
{
server.handleClient();
//Handle motor A
switch(stateMotorA) {
case 0:
analogWrite(OUT1, 0);
analogWrite(OUT2, 0);
break;
case 1:
analogWrite(OUT1, speedMotor);
analogWrite(OUT2, 0);
break;
case 2:
analogWrite(OUT1, 0);
analogWrite(OUT2, speedMotor);
break;
}
//Handle motor B
switch(stateMotorB) {
case 0:
analogWrite(OUT3, 0);
analogWrite(OUT4, 0);
break;
case 1:
analogWrite(OUT3, speedMotor);
analogWrite(OUT4, 0);
break;
case 2:
analogWrite(OUT3, 0);
analogWrite(OUT4, speedMotor);
break;
}
}


Código de gestão do motor passo-a-passo
Para acionar um motor passo a passo, as bobinas do motor têm de ser activadas numa sequência precisa. Esta sequência é descrita na função step da biblioteca stepper.h.
/*
Nema | Board pin | NodeMCU GPIO | Arduino IDE
black A+ 0 0
green A- 4 4
red B+ 15 15
blue B- 2 2
*/
#include <WiFi.h>
#include <WebServer.h>
#include <Arduino.h>
#include <analogWrite.h>
#include <Stepper.h>
#define IN1 34 //sensor
#define OUT1 0 //A+ - Black
#define OUT2 4 //A- - Green
#define OUT3 15 //B+ - Red
#define OUT4 2 //B- - Blue
//Stepper param
const int stepsPerRevolution = 200;
int speedMotor = 20;
Stepper myStepper(stepsPerRevolution, OUT1, OUT2, OUT3, OUT4);
//Wifi
const char *ssid = "*****";
const char *password = "********";
WebServer server(80);
const int led = 2;
int stateStepper = 0,stateStepperB = 0;
char stateStepperText[3][10] = {"STOP","CCW!","CW!"};
String sensorValue;
/*********************************************************************************************
* HANDLE FUNCTIONS
*********************************************************************************************/
void handleRoot()
{
String page = "<!DOCTYPE html>";
page += "<html lang='fr'>";
page += "<head>";
page += " <title>ESP32MotorShieldV1</title>";
page += " <meta http-equiv='refresh' content='60' name='viewport' content='width=device-width, initial-scale=1' charset='UTF-8' />";
page += " <link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>";
page += "<script>";
page += "function getData() {";
page += " var xhttp = new XMLHttpRequest();";
page += " xhttp.onreadystatechange = function() {";
page += " if (this.readyState == 4 && this.status == 200) {";
page += " document.getElementById('SensorValue').innerHTML =this.responseText;";
page += " console.log(this.responseText);";
page += " }";
page += " };";
page += " xhttp.open('GET', 'readSensor', true);";
page += " xhttp.send();";
page += "}";
page += "setInterval(function() {getData();}, 2000); // Call a function repetatively with 2s interval";
page += "</script>";
page += "</head>";
page += "<body>";
page += " <div class='w3-card w3-padding-small w3-jumbo w3-center' style='color:#fff; background-color:#3aaa35;'>";
page += " <p>Stepper State: "; page += stateStepperText[stateStepper]; + "</p>";
page += " </div>";
page += " <div class='w3-bar'>";
page += " <a href='/left' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>GAUCHE</a>";
page += " <a href='/stop' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>STOP</a>";
page += " <a href='/right' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:33%; height:50%;'>DROITE</a>";
page += " </div>";
page += " <div class='w3-card w3-padding-small w3-jumbo w3-center' style='color:#fff; background-color:#3aaa35;'>";
page += " <p>Sensor value: <span id='SensorValue'>0</span></p>";
page += " </div>";
page += " <div class='w3-center w3-padding-16'>";
page += " <p>Server hosted on NodeMCU ESP32 - <i>Made by <a href='https://www.aranacorp.com' style='color:#3aaa35;'>AranaCorp</a></i></p>";
page += " ";
page += " </div>";
page += "</body>";
page += "</html>";
server.setContentLength(page.length());
server.send(200, "text/html", page);
}
void handleLeft(){
stateStepper = 2;
digitalWrite(led, HIGH);
server.sendHeader("Location","/");
server.send(303);
}
void handleRight(){
stateStepper = 1;
digitalWrite(led, LOW);
server.sendHeader("Location","/");
server.send(303);
}
void handleStop(){
stateStepper = 0;
digitalWrite(led, HIGH);
server.sendHeader("Location","/");
server.send(303);
}
void handleInput() {
sensorValue = String(analogRead(IN1));
server.send(200, "text/plain", sensorValue); //Send ADC value only to client ajax request
}
void handleNotFound(){
digitalWrite(led, HIGH);
stateStepper = 0;
server.send(404, "text/plain", "404: Not found");
}
/*********************************************************************************************
* MAIN
*********************************************************************************************/
void setup()
{
pinMode(IN1, INPUT);
Serial.begin(115200);
delay(1000);
Serial.println("\n");
pinMode(led, OUTPUT);
digitalWrite(led, HIGH);
myStepper.setSpeed(speedMotor);
WiFi.persistent(false);
WiFi.begin(ssid, password);
Serial.print("Tentative de connexion...");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(100);
}
Serial.println("\n");
Serial.println("Connexion etablie!");
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/left", handleLeft);
server.on("/right", handleRight);
server.on("/stop", handleStop);
server.on("/readSensor", handleInput);//To get sensor value update
server.onNotFound(handleNotFound);
server.begin();
Serial.println("Serveur web actif!");
}
void loop()
{
server.handleClient();
//Handle Stepper
switch(stateStepper) {
case 0:
//stop
break;
case 1:
myStepper.step(1);
break;
case 2:
myStepper.step(-1);
break;
}
}


Aplicações
- Controlar um robô de duas rodas como o Willy através de uma ligação WiFi ou Bluetooth