ESP32教程30/55——使用Adafruit IO服务通过MQTT在网页上控制舵机 | SunFounder的ESP32物联网学习套件
在本教程中,我们将学习如何使用ESP32微控制器和MQTT协议通过网络控制伺服电机的位置。通过利用Adafruit IO服务,您将能够远程将伺服电机定位到各种角度,如0°、90°或180°。本项目展示了ESP32的功能,它内置了Wi-Fi和蓝牙,使其成为物联网应用的强大工具。

在本项目中,我们将使用Adafruit IO设置一个MQTT代理,创建一个控制伺服电机的仪表板,并将ESP32连接到它。伺服电机的位置可通过仪表板上的滑块进行调整,从而可以从任何支持互联网的设备进行实时控制(视频中5:30处)。
硬件说明
本项目的主要组件包括ESP32微控制器和伺服电机。ESP32是一款功能多样的微控制器,集成了Wi-Fi和蓝牙功能,非常适合物联网应用。它使用MQTT协议与Adafruit IO服务通信,该协议轻量高效,适合通过互联网传输消息。
伺服电机是一种旋转执行器,可以精确控制角位置。它通过接收脉宽调制(PWM)信号来确定其位置。在本项目中,我们将伺服电机连接到ESP32的一个数字引脚上,从而可以远程控制其角度。
数据手册详情
| 制造商 | Parallax |
|---|---|
| 零件编号 | SG90 |
| 逻辑/IO电压 | 3.3 V - 5 V |
| 电源电压 | 4.8 V - 6.0 V |
| 输出电流(每通道) | 最大1 A |
| PWM频率建议 | 50 Hz |
| 输入逻辑阈值 | 0.3 V - 0.7 V |
| 压降 / RDS(on) / 饱和 | 最大0.2 V |
| 热限制 | 工作温度:-10°C至60°C |
| 封装 | 塑料外壳 |
| 备注 / 变体 | 迷你伺服电机,180°旋转 |
- 确保为伺服电机提供正确的电压(4.8 V - 6.0 V)。
- 在ESP32和伺服电机之间使用公共地线。
- 监控PWM信号,避免超出伺服电机的限制。
- 牢固连接伺服电机,防止运行过程中断开。
- 更新Adafruit MQTT库以确保兼容性。
MQTT图表
接线说明

要将伺服电机连接到ESP32,首先将伺服电机的接地线连接到ESP32上的接地引脚。接下来,将伺服电机的电源线(通常为红色)连接到ESP32上的5V引脚。最后,将信号线(通常为黄色或白色)连接到ESP32上的数字引脚25。确保连接牢固,以防止运行过程中出现任何断开。
如果您使用电池为ESP32供电,请确保电池电压在ESP32和伺服电机的可接受范围内。此外,请仔细检查接线是否与代码中使用的引脚定义匹配,以避免任何问题(视频中12:45处)。
代码示例与讲解
在提供的代码中,我们首先包含ESP32和MQTT所需的库。我们定义伺服电机对象,并使用const int servoPin = 25;指定其连接的引脚。默认角度也通过const int defaultServoAngle = 90;设置,这将是ESP32启动时的初始位置。
Servo myServo;
const int servoPin = 25;
const int defaultServoAngle = 90;
int servoAngle = defaultServoAngle;
此代码片段在引脚25上初始化伺服电机,并将其默认角度设置为90°。变量servoAngle将根据从MQTT代理接收到的消息进行更新。
在setup()函数中,我们连接到Wi-Fi并设置MQTT客户端。Adafruit IO的凭据在此定义,包括用户名和密钥:
#define AIO_USERNAME "robojax"
#define AIO_KEY "aio_xmIW58uNNsjJCSOqzZ9QoHyq29wu"
此部分建立与Adafruit IO服务的连接。在实现代码时,请确保将这些值替换为您自己的Adafruit IO凭据。
最后,主循环确保与MQTT服务器的连接保持活动状态,并处理传入的消息。伺服电机的位置根据接收到的角度进行更新:
mqtt.processPackets(500);
int pulseWidth = map(servoAngle, 0, 180, minPulseWidth, maxPulseWidth);
myServo.writeMicroseconds(pulseWidth);
此代码将舵机角度映射到相应的脉冲宽度,并将其发送到舵机电机。processPackets() 函数允许 ESP32 处理传入的 MQTT 消息,确保舵机对通过 Adafruit IO 仪表板发送的命令做出反应。
演示 / 预期效果
一切设置完成后,您应该能够使用您创建的滑块从 Adafruit IO 仪表板控制舵机。当您移动滑块时,舵机将实时调整其角度。确保您的 ESP32 已连接到 Wi-Fi,并且 MQTT 连接稳定。如果舵机没有响应,请检查接线和舵机的电源(视频中 20:15 处)。
请注意舵机的范围限制;发送超出 0° 到 180° 范围的值可能会导致其行为异常。代码中包含检查,通过将角度限制在此范围内来防止此类情况发生。
视频时间戳
- 00:00 开始
- 1:54 项目介绍
- 2:52 MQTT 简介
- 6:50 Adafruit IO 设置
- 9:54 舵机接线
- 11:07 代码讲解
- 18:59 选择 ESP32 开发板和 COM 端口
- 22:10 项目演示
/***********************************************************************
This is Arduino sketch for ESP32 to control Servo motor using MQTT service of Adafruit
www.Robojax.com
Written By Ahamd Shamshiri
on Feb 2nd, 2024
Watch video instruction for this code on YouTube https://youtu.be/T4DhWNg2Rb8
Adafruit MQTT Library ESP32 Adafruit IO SSL/TLS example
this code can be downloaded from https://robojax.com/RJT671
/// ref: https://www.electronicwings.com/esp32/esp32-mqtt-client
*/
#include <ESP32Servo.h>
// Define the servo and the pin it is connected to
Servo myServo;
const int servoPin = 25;
const int defaultServoAngle = 90;
int servoAngle =defaultServoAngle;
// Define the minimum and maximum pulse widths for the servo
const int minPulseWidth = 500; // 0.5 ms
const int maxPulseWidth = 2500; // 2.5 ms
#include <WiFi.h>
#include "WiFiClientSecure.h"
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
/************************* WiFi Access Point *********************************/
#define WLAN_SSID "Barqasaa"
#define WLAN_PASS "wan9&Jang~5"
/************************* Adafruit.io Setup *********************************/
#define AIO_SERVER "io.adafruit.com"
// Using port 8883 for MQTTS
#define AIO_SERVERPORT 8883
// Adafruit IO Account Configuration
// (to obtain these values, visit https://io.adafruit.com and click on Active Key)
// #define AIO_USERNAME "YOUR_ADAFRUIT_IO_USERNAME"
// #define AIO_KEY "YOUR_ADAFRUIT_IO_KEY"
#define AIO_USERNAME "robojax"
#define AIO_KEY "aio_xmIW58uNNsjJCSOqzZ9QoHyq29wu"
/************ Global State (you don't need to change this!) ******************/
// WiFiFlientSecure for SSL/TLS support
WiFiClientSecure client;
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
// io.adafruit.com root CA
const char* adafruitio_root_ca = \
"-----BEGIN CERTIFICATE-----\n"
"MIIEjTCCA3WgAwIBAgIQDQd4KhM/xvmlcpbhMf/ReTANBgkqhkiG9w0BAQsFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n"
"MjAeFw0xNzExMDIxMjIzMzdaFw0yNzExMDIxMjIzMzdaMGAxCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xHzAdBgNVBAMTFkdlb1RydXN0IFRMUyBSU0EgQ0EgRzEwggEiMA0GCSqGSIb3\n"
"DQEBAQUAA4IBDwAwggEKAoIBAQC+F+jsvikKy/65LWEx/TMkCDIuWegh1Ngwvm4Q\n"
"yISgP7oU5d79eoySG3vOhC3w/3jEMuipoH1fBtp7m0tTpsYbAhch4XA7rfuD6whU\n"
"gajeErLVxoiWMPkC/DnUvbgi74BJmdBiuGHQSd7LwsuXpTEGG9fYXcbTVN5SATYq\n"
"DfbexbYxTMwVJWoVb6lrBEgM3gBBqiiAiy800xu1Nq07JdCIQkBsNpFtZbIZhsDS\n"
"fzlGWP4wEmBQ3O67c+ZXkFr2DcrXBEtHam80Gp2SNhou2U5U7UesDL/xgLK6/0d7\n"
"6TnEVMSUVJkZ8VeZr+IUIlvoLrtjLbqugb0T3OYXW+CQU0kBAgMBAAGjggFAMIIB\n"
"PDAdBgNVHQ4EFgQUlE/UXYvkpOKmgP792PkA76O+AlcwHwYDVR0jBBgwFoAUTiJU\n"
"IBiV5uNu5g/6+rkS7QYXjzkwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsG\n"
"AQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMDQGCCsGAQUFBwEB\n"
"BCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEIGA1Ud\n"
"HwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEds\n"
"b2JhbFJvb3RHMi5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEW\n"
"HGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQELBQADggEB\n"
"AIIcBDqC6cWpyGUSXAjjAcYwsK4iiGF7KweG97i1RJz1kwZhRoo6orU1JtBYnjzB\n"
"c4+/sXmnHJk3mlPyL1xuIAt9sMeC7+vreRIF5wFBC0MCN5sbHwhNN1JzKbifNeP5\n"
"ozpZdQFmkCo+neBiKR6HqIA+LMTMCMMuv2khGGuPHmtDze4GmEGZtYLyF8EQpa5Y\n"
"jPuV6k2Cr/N3XxFpT3hRpt/3usU/Zb9wfKPtWpoznZ4/44c1p9rzFcZYrWkj3A+7\n"
"TNBJE0GmP2fhXhP1D/XVfIW/h0yCJGEiV9Glm/uGOa3DXHlmbAcxSyCRraG+ZBkA\n"
"7h4SeM6Y8l/7MBRpPCz6l8Y=\n"
"-----END CERTIFICATE-----\n";
/****************************** Feeds ***************************************/
// Setup a feed called 'test' for publishing and 'test2' for subscription.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Subscribe SERVO = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/servo-angle-1");
/*************************** Sketch Code ************************************/
void setup() {
// Attach the servo to the specified pin and set its pulse width range
myServo.attach(servoPin, minPulseWidth, maxPulseWidth);
// Set the PWM frequency for the servo
myServo.setPeriodHertz(50); // Standard 50Hz servo
Serial.begin(115200);
delay(10);
Serial.println(F("Adafruit IO MQTTS (SSL/TLS) Example"));
// Connect to WiFi access point.
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(WLAN_SSID);
delay(1000);
WiFi.begin(WLAN_SSID, WLAN_PASS);
delay(2000);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Set Adafruit IO's root CA
client.setCACert(adafruitio_root_ca);
// register callback for feed
SERVO.setCallback(servoCallback);
// Setup MQTT subscription for time feed.
mqtt.subscribe(&SERVO);
}
// uint32_t x=0;
void loop() {
// Ensure the connection to the MQTT server is alive (this will make the first
// connection and automatically reconnect when disconnected). See the MQTT_connect
// function definition further below.
MQTT_connect();
Serial.print("Angle: ");
Serial.println(servoAngle);
// wait 0.5 seconds for subscription messages
mqtt.processPackets(500);
//from lesson 17
int pulseWidth = map(servoAngle, 0, 180, minPulseWidth, maxPulseWidth);
myServo.writeMicroseconds(pulseWidth);
delay(15);
// wait a couple seconds to avoid rate limit
//delay(2000);
}
void servoCallback(char* message, uint16_t len) {
char messageBuffer[40];
snprintf(messageBuffer, sizeof(messageBuffer), "Servo status is :: %s, len :: %u", message, len);
Serial.println(messageBuffer);
Serial.println(message);
String inString = message;//sotre the message to String
servoAngle = inString.toInt();//convert the message to Integer
if(servoAngle >180 || servoAngle < 0)
{
servoAngle =0;
}
}
// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
int8_t ret;
// Stop if already connected.
if (mqtt.connected()) {
return;
}
Serial.print("Connecting to MQTT... ");
uint8_t retries = 3;
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
mqtt.disconnect();
delay(5000); // wait 5 seconds
retries--;
if (retries == 0) {
// basically die and wait for WDT to reset me
while (1)
;
}
}
Serial.println("MQTT Connected!");
}
Common Course Links
Common Course Files
|||您可能需要的东西
-
全球速卖通从AliExpress购买SG90伺服电机180或360。s.click.aliexpress.com
资源与参考
-
文档ESP32教程 17/55- SunFunder伺服电机文档docs.sunfounder.com
-
外部SG90 Servo on Amazon USAamzn.to
文件📁
其他文件
-
SG90 Seroo 电机数据表
robojax-servo-SG90_datasheet.pdf0.12 MB