一.源码下载与准备
访问小智AI的官方源码下载地址,获取最新版本的源码。下载完成后,使用Visual Studio Code(VS Code)打开项目。为了确保项目能够顺利编译和运行,需要在VS Code中安装ESP-IDF开发环境。具体安装步骤可参考官方文档或相关教程。
二.源码修改与功能实现
1.编写代码
在项目目录main/iot/things
下,新建一个名为dog.cc
的文件。该文件将用于实现机器狗的四个舵机的核心控制逻辑。以下是示例代码,供参考:
#include "iot/thing.h"
#include "board.h"
#include "audio_codec.h"
#include <driver/gpio.h>
#include <esp_log.h>
#include "freertos/task.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "driver/mcpwm.h"
#define TAG "Dog"
#define SERVO_FREQ 50 // 50Hz,适用于 SG90 舵机
#define MIN_PULSE_WIDTH 500 // 最小脉宽 (500us) 对应 0° 位置
#define MAX_PULSE_WIDTH 2500 // 最大脉宽 (2500us) 对应 180° 位置
#define DEGREE_TO_PULSE(degree) (MIN_PULSE_WIDTH + (MAX_PULSE_WIDTH - MIN_PULSE_WIDTH) * degree / 180)
#define SERVO_GPIO_1 GPIO_NUM_17 //(gpio_num_t)17 GPIO for servo 1 LF 左前
#define SERVO_GPIO_2 GPIO_NUM_14 //(gpio_num_t)14 GPIO for servo 2 RF 右前
#define SERVO_GPIO_3 GPIO_NUM_18 //(gpio_num_t)18 GPIO for servo 3 LB 左后
#define SERVO_GPIO_4 GPIO_NUM_13 //(gpio_num_t)13 GPIO for servo 4 RB 右后
namespace iot
{
int dog_state_ = 0;
int waitTime = 200;
void set_servo_angle(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, int gpio_num, int angle)
{
uint32_t pulse_width = DEGREE_TO_PULSE(angle); // 将角度转化为脉宽
// 设置指定通道的占空比
if (mcpwm_num == MCPWM_UNIT_0)
{
if (gpio_num == SERVO_GPIO_1)
{
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_GEN_A, (pulse_width / 20000.0) * 100); // 计算占空比
}
else if (gpio_num == SERVO_GPIO_2)
{
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_GEN_B, (pulse_width / 20000.0) * 100);
}
}
else if (mcpwm_num == MCPWM_UNIT_1)
{
if (gpio_num == SERVO_GPIO_3)
{
mcpwm_set_duty(MCPWM_UNIT_1, MCPWM_TIMER_1, MCPWM_GEN_A, (pulse_width / 20000.0) * 100);
}
else if (gpio_num == SERVO_GPIO_4)
{
mcpwm_set_duty(MCPWM_UNIT_1, MCPWM_TIMER_1, MCPWM_GEN_B, (pulse_width / 20000.0) * 100);
}
}
vTaskDelay(50 / portTICK_PERIOD_MS);
// 更新占空比
mcpwm_sync_disable(MCPWM_UNIT_0, MCPWM_TIMER_0);
mcpwm_sync_disable(MCPWM_UNIT_1, MCPWM_TIMER_1);
}
// 站立
void zhanli()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
}
// 前进
void qianjin()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90); // 舵机 1 到 90 度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 135); // 舵机 2 到 135度
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 45); // 舵机 3 到 45 度
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90); // 舵机 4 到 90 度
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 135);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 45);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 45);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 135);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 45);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 135);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
}
// 后退
void houtui()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 45);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 135);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 45);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 135);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 135);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 45);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 135);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 45);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
}
// 左转
void zuozhuan()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 135);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 135);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 45);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 45);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 135);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 135);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 45);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 45);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
}
// 右转
void youzhuan()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 45);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 45);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 135);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 135);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 45);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 45);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 135);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 135);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
}
// 趴下
void paxia()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 25);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 155);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 25);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 155);
vTaskDelay(pdMS_TO_TICKS(waitTime));
}
// 蹲下
void dunxia()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 25);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 155);
vTaskDelay(pdMS_TO_TICKS(waitTime));
}
//点头
void diantou()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 45);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 155);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 90);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 45);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 155);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
dog_state_ = 0;
}
//打招呼
void baishou()
{
// 控制四个舵机分别到不同角度
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_1, 90);
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 165);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_3, 25);
set_servo_angle(MCPWM_UNIT_1, MCPWM_TIMER_1, SERVO_GPIO_4, 155);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 165);
vTaskDelay(pdMS_TO_TICKS(waitTime));
set_servo_angle(MCPWM_UNIT_0, MCPWM_TIMER_0, SERVO_GPIO_2, 90);
dog_state_ = 0;
}
void task1()
{
while (1)
{
switch (dog_state_) {
case 0: zhanli(); break;
case 1: qianjin(); break;
case 2: houtui(); break;
case 3: zuozhuan(); break;
case 4: youzhuan(); break;
case 5: paxia(); break;
case 6: dunxia(); break;
case 7: diantou(); break;
case 8: baishou(); break;
default: break; // 默认处理
}
}
}
class Dog : public Thing
{
private:
void InitializeGpio()
{
// 初始化4个舵机的PWM
const gpio_num_t servo_pins[4] = {SERVO_GPIO_1, SERVO_GPIO_2, SERVO_GPIO_3, SERVO_GPIO_4};
for (int i = 0; i < 4; i++)
{
mcpwm_gpio_init(MCPWM_UNIT_0, static_cast<mcpwm_io_signals_t>(MCPWM0A + i), servo_pins[i]);
}
mcpwm_config_t pwm_config = {
.frequency = 50, // 舵机标准50Hz
.cmpr_a = 0,
.cmpr_b = 0,
.duty_mode = MCPWM_DUTY_MODE_0,
.counter_mode = MCPWM_UP_COUNTER};
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);
// 创建任务
xTaskCreate([](void *arg)
{
task1();
vTaskDelete(NULL); }, "dogControl", 4096 * 4, this, 1, nullptr);
}
public:
Dog() : Thing("Dog", "A test Dog")
{
InitializeGpio();
// 定义设备的属性
properties_.AddBooleanProperty("dog_forward", "小狗是否前进", [this]() -> bool
{ return dog_state_ == 1; });
properties_.AddBooleanProperty("dog_back", "小狗是否后退", [this]() -> bool
{ return dog_state_ == 2; });
properties_.AddBooleanProperty("dog_left", "小狗是否左转", [this]() -> bool
{ return dog_state_ == 3; });
properties_.AddBooleanProperty("dog_right", "小狗是否右转", [this]() -> bool
{ return dog_state_ == 4; });
properties_.AddBooleanProperty("dog_pa", "小狗是否趴下", [this]() -> bool
{ return dog_state_ == 5; });
properties_.AddBooleanProperty("dog_dun", "小狗是否蹲下", [this]() -> bool
{ return dog_state_ == 6; });
properties_.AddBooleanProperty("dog_dian", "小狗是否点头", [this]() -> bool
{ return dog_state_ == 7; });
properties_.AddBooleanProperty("dog_zhao", "小狗是否打招呼", [this]() -> bool
{ return dog_state_ == 8; });
properties_.AddBooleanProperty("dog_stop", "小狗是否站立", [this]() -> bool
{ return dog_state_ == 0; });
// 定义设备可以被远程执行的指令
methods_.AddMethod("DogGoForward", "小狗前进", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 1; });
methods_.AddMethod("DogGoBack", "小狗后退", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 2; });
methods_.AddMethod("DogGoLeft", "小狗左转", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 3; });
methods_.AddMethod("DogGoRight", "小狗右转", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 4; });
methods_.AddMethod("DogGoPa", "小狗趴下", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 5; });
methods_.AddMethod("DogGoDun", "小狗蹲下", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 6; });
methods_.AddMethod("DogGoDian", "小狗点头", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 7; });
methods_.AddMethod("DogGoZhao", "小狗打招呼", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 8; });
methods_.AddMethod("DogGoStop", "小狗站立", ParameterList(), [this](const ParameterList ¶meters)
{ dog_state_ = 0; });
}
};
}
DECLARE_THING(Dog);
2.修改配置文件
在“main/boards/bread-compact-wifi-lcd/compact_wifi_board.cc”文件(我的是1.54彩屏,如果是0.96或1.3寸屏幕sd1306驱动的屏幕,请修改main/boards/bread-compact-wifi/compact_wifi_board.cc文件)中的InitializeIot方法,添加小狗控制代码
thing_manager.AddThing(iot::CreateThing("Dog"));
三.编译与运行
完成代码编写后,使用ESP-IDF工具链进行编译。确保所有依赖项已正确安装,并按照项目结构进行配置。编译成功后,将生成的固件烧录到目标设备上,即可启动桌宠机器狗。
四.功能扩展与优化
根据实际需求,可以进一步扩展机器狗的功能。例如,添加语音识别模块、传感器控制或远程控制功能。通过不断优化代码和硬件配置,打造一个更加智能和互动的桌宠机器狗。