回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

小智语音控制ESP32 S3外接2个舵机|极简MCP插件教程 简单

头像 rzyzzxw 2025.08.08 44 0

8.8

【写在前面】

在B站看到很多伙伴做那个AI宠物小狗的,4个舵机,可能做出各种好玩的动作,心中就有种下种子了,当然现在还是不会做,做些准备吧。

所以这个帖子就记录用ESP32 S3控制舵机的学习,从控制一个舵机到两个舵机。

我的学习就是学习读代码,学习改代码,所记都是笔记,营养自己。

35cd3b52acff3fcedb03686424ca910.jpg

材料清单

  • K10小智AI X1
  • ESP32 S3-DevKitC-1开发板(N16R8) X1
  • SG90舵机 X2

步骤1 安装ESP32Servo舵机库

工具-库管理,查找esp32servo,安装舵机库。

image.png

b75f1e60f59b1027de7cb37be47c6e6.png

步骤2 例程学习,为小智自然语言控制舵机做准备

代码
#include <ESP32Servo.h>  // 使用官方推荐库[2,6](@ref)

Servo myServo;            // 创建舵机对象
const int servoPin = 5;   // 信号线接GPIO5(ESP32-S3推荐引脚1-21,35-45,47)[4](@ref)

void setup() {
  // 初始化舵机(SG90需校准脉宽)
  myServo.attach(servoPin, 500, 2400);  // 设置脉冲范围500-2400μs(适配SG90)[3,6](@ref)
  myServo.write(90);                    // 初始位置设为90°
}

void loop() {
  // 0°→180°平滑扫描(步进1°)
  for (int angle = 0; angle <= 180; angle++) {
    myServo.write(angle);  // 设置角度
    delay(15);             // 延时15ms(SG90转动60°约需60ms)[3](@ref)
  }
  // 180°→0°回扫
  for (int angle = 180; angle >= 0; angle--) {
    myServo.write(angle);
    delay(15);
  }
}

1、​​脉宽校准​

attach(servoPin, 500, 2400)显式设置 SG90 的脉冲范围(0°=500μs, 180°=2400μs)。

若角度偏差,调整参数:

0°不到位 → 提高500值(如550)

180°超限 → 降低2400值(如2350)

​2、ESP32-S3 引脚选择​

​推荐 PWM 引脚​​:GPIO1-21, 35-45, 47(避开板载LED的GPIO48)

​禁用引脚​​:GPIO0(板载按钮)、GPIO48(LED)

3、供电方案(⚠️必看)

image.png

​严禁​​使用ESP32的3.3V直接供电(电流不足会导致重启)

大功率动作时,在舵机电源端并联​​100μF电容​​防抖

步骤3 编写程序

程序重点

设定舵机引脚

image.png

设置舵机角度0-108

舵机角度控制工具

image.png

初始化舵机

image.png

代码
#include <WiFi.h>
#include <WebSocketMCP.h>
#include <ESP32Servo.h>  // 使用兼容ESP32的舵机库

#define SERVO_PIN 12     // 舵机控制引脚(GPIO12)

// WiFi配置
const char* ssid = "*****";
const char* password = "*****";

// MCP服务器配置
const char* mcpEndpoint = "ws://api.xiaozhi.me/mcp/?token=*****";


// 创建舵机对象
Servo myServo;

// 创建WebSocketMCP实例
WebSocketMCP mcpClient;

// 连接状态回调函数
void onConnectionStatus(bool connected) {
  if (connected) {
    Serial.println("[MCP] 已连接到服务器");
    registerMcpTools();  // 注册舵机控制工具
  } else {
    Serial.println("[MCP] 与服务器断开连接");
  }
}

// 设置舵机角度(0-180°)
void setServoAngle(int angle) {
  angle = constrain(angle, 0, 180);   // 限制角度范围
  myServo.write(angle);               // 使用库函数设置角度
  Serial.printf("舵机角度设置: %d°\n", angle);
}

// 注册MCP工具
void registerMcpTools() {
  // 舵机角度控制工具
  mcpClient.registerTool(
    "servo_control",
    "控制舵机角度(0-180°)",
    "{\"type\":\"object\",\"properties\":{\"angle\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":180}},\"required\":[\"angle\"]}",
    [](const String& args) {
      DynamicJsonDocument doc(256);
      deserializeJson(doc, args);
      int angle = doc["angle"];  // 解析角度值
      setServoAngle(angle);      // 调用舵机控制函数
      
      // 返回成功响应
      return WebSocketMCP::ToolResponse("{\"success\":true,\"angle\":" + String(angle) + "}");
    }
  );
  Serial.println("[MCP] 舵机控制工具已注册");
}

void setup() {
  Serial.begin(115200);
    
  // 初始化舵机(500-2500μs脉宽范围对应0-180°)
  ESP32PWM::allocateTimer(0);  // 分配定时器资源
  myServo.setPeriodHertz(50);  // 标准舵机频率50Hz
  myServo.attach(SERVO_PIN, 500, 2500);  // 引脚和脉宽范围
  setServoAngle(90);  // 初始设为90度(中间位置)
  
  // 连接WiFi
  Serial.print("连接到WiFi: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("WiFi已连接");
  Serial.println("IP地址: " + WiFi.localIP().toString());

  // 初始化MCP客户端
  mcpClient.begin(mcpEndpoint, onConnectionStatus);
}

void loop() {
  // 处理MCP客户端事件
  mcpClient.loop();
  delay(10);
}

上传代码,测试通过。

小智后台

image.png

以下为笔记存档备用。

进阶优化技巧


​​多舵机控制​​添加更多舵机对象并分配不同引脚(最多支持16个):
Servo servo2;
void setup() {
servo2.attach(6, 500, 2400); // 第二个舵机接GPIO6
}

代码
#include <ESP32Servo.h>  // 使用ESP32专用舵机库

#define SERVO_PIN 5       // 推荐使用GPIO5(ESP32-S3最佳PWM引脚)
Servo myServo;           // 创建舵机对象

void setup() {
  Serial.begin(115200);  // 初始化串口通信
  
  // 分配硬件定时器资源(ESP32Servo必需)
  ESP32PWM::allocateTimer(0);
  ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);
  ESP32PWM::allocateTimer(3);
  
  // 配置SG90舵机参数[1,2](@ref)
  myServo.setPeriodHertz(50);               // 标准50Hz舵机频率
  myServo.attach(SERVO_PIN, 500, 2400);     // 校准脉宽范围(500-2400μs对应0-180°)
  myServo.write(90);                        // 初始位置设为90°
  
  Serial.println("舵机初始化完成");
}

void loop() {
  // 0°→180°平滑扫描(步进1°)
  for (int angle = 0; angle <= 180; angle++) {
    myServo.write(angle);                   // 设置舵机角度
    Serial.print("当前角度: "); Serial.println(angle);
    delay(15);                             // SG90转动60°约需60ms[2](@ref)
  }
  
  // 180°→0°回扫
  for (int angle = 180; angle >= 0; angle--) {
    myServo.write(angle);
    Serial.print("当前角度: "); Serial.println(angle);
    delay(15);
  }
  
  // 返回中间位置并暂停
  myServo.write(90);
  Serial.println("返回中位");
  delay(1000);
}

步骤4 改写代码控制两个舵机

代码
#include <WiFi.h>
#include <WebSocketMCP.h>
#include <ESP32Servo.h>  // 使用兼容ESP32的舵机库

#define SERVO1_PIN 5
#define SERVO2_PIN 6

// WiFi配置
const char* ssid = "******";
const char* password = "******";

// MCP服务器配置
const char* mcpEndpoint = "ws://api.xiaozhi.me/mcp/?token=********";


// 创建舵机对象
Servo myServo1;  // 舵机1
Servo myServo2;  // 舵机2

// 创建WebSocketMCP实例
WebSocketMCP mcpClient;

// 连接状态回调函数
void onConnectionStatus(bool connected) {
  if (connected) {
    Serial.println("[MCP] 已连接到服务器");
    registerMcpTools();  // 注册舵机控制工具
  } else {
    Serial.println("[MCP] 与服务器断开连接");
  }
}

// 设置舵机角度(0-180°)
void setServo1Angle(int angle) {
  angle = constrain(angle, 0, 180);   // 限制角度范围
  myServo1.write(angle);               // 使用库函数设置角度
  Serial.printf("舵机1角度设置: %d°\n", angle);
}
void setServo2Angle(int angle) {
  angle = constrain(angle, 0, 180);   // 限制角度范围
  myServo2.write(angle);               // 使用库函数设置角度
  Serial.printf("舵机2角度设置: %d°\n", angle);
}

// 注册MCP工具
void registerMcpTools() {
  // 舵机1角度控制工具
  mcpClient.registerTool(
    "servo1_control",
    "控制1号舵机小明角度(0-180°)",
    "{\"type\":\"object\",\"properties\":{\"angle\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":180}},\"required\":[\"angle\"]}",
    [](const String& args) {
      DynamicJsonDocument doc(256);
      deserializeJson(doc, args);
      int angle = doc["angle"];  // 解析角度值
      setServo1Angle(angle);      // 调用舵机1控制函数
      
      // 返回成功响应
      return WebSocketMCP::ToolResponse("{\"success\":true,\"angle\":" + String(angle) + "}");
    }
  );
  // 舵机2角度控制工具
  mcpClient.registerTool(
    "servo2_control",
    "控制2号舵机小红角度(0-180°)",
    "{\"type\":\"object\",\"properties\":{\"angle\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":180}},\"required\":[\"angle\"]}",
    [](const String& args) {
      DynamicJsonDocument doc(256);
      deserializeJson(doc, args);
      int angle = doc["angle"];  // 解析角度值
      setServo2Angle(angle);      // 调用舵机2控制函数
      
      // 返回成功响应
      return WebSocketMCP::ToolResponse("{\"success\":true,\"angle\":" + String(angle) + "}");
    }
  );

  Serial.println("[MCP] 双舵机控制工具已注册");
}

void setup() {
  Serial.begin(115200);
    
  // 初始化舵机(500-2500μs脉宽范围对应0-180°)
  ESP32PWM::allocateTimer(0);  // 分配定时器资源
  ESP32PWM::allocateTimer(1);  
  
  myServo1.setPeriodHertz(50);  // 标准舵机频率50Hz
  myServo1.attach(SERVO1_PIN, 500, 2500);  // 引脚和脉宽范围
  setServo1Angle(90);  // 初始设为90度(中间位置)
  
  myServo2.setPeriodHertz(50);  // 标准舵机频率50Hz
  myServo2.attach(SERVO2_PIN, 500, 2500);  // 引脚和脉宽范围
  setServo2Angle(90);  // 初始设为90度(中间位置)
  
  // 连接WiFi
  Serial.print("连接到WiFi: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("WiFi已连接");
  Serial.println("IP地址: " + WiFi.localIP().toString());

  // 初始化MCP客户端
  mcpClient.begin(mcpEndpoint, onConnectionStatus);
}

void loop() {
  // 处理MCP客户端事件
  mcpClient.loop();
  delay(10);
}

上传,测试通过。

image.png

评论

user-avatar