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