mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-02-12 23:23:47 +00:00
Compare commits
3 Commits
fix_setupu
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d545f746bc | ||
|
|
6be351b5a0 | ||
|
|
d9447ad060 |
@@ -158,6 +158,17 @@ elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV_C3)
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_EDA_TV_PRO)
|
||||
set(MANUFACTURER "lceda-course-examples")
|
||||
set(BOARD_TYPE "eda-tv-pro")
|
||||
elseif(CONFIG_BOARD_TYPE_EDA_ROBOT_PRO)
|
||||
set(MANUFACTURER "lceda-course-examples")
|
||||
set(BOARD_TYPE "eda-robot-pro")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
elseif(CONFIG_BOARD_TYPE_EDA_SUPER_BEAR)
|
||||
set(MANUFACTURER "lceda-course-examples")
|
||||
set(BOARD_TYPE "eda-super-bear")
|
||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_2P4)
|
||||
set(BOARD_TYPE "magiclick-2p4")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
|
||||
@@ -215,6 +215,15 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_LICHUANG_DEV_C3
|
||||
bool "立创·实战派 ESP32-C3"
|
||||
depends on IDF_TARGET_ESP32C3
|
||||
config BOARD_TYPE_EDA_TV_PRO
|
||||
bool "EDA课程案例 EDA-TV-Pro"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_EDA_ROBOT_PRO
|
||||
bool "EDA课程案例 EDA-Robot-Pro"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_EDA_SUPER_BEAR
|
||||
bool "EDA课程案例 EDA-Super-Bear"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_DF_K10
|
||||
bool "DFRobot 行空板 k10"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#define DISPLAY_SPI_CS_PIN GPIO_NUM_9
|
||||
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_HEIGHT 296
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
|
||||
@@ -53,9 +53,9 @@ public:
|
||||
lv_obj_set_style_pad_left(top_bar_, LV_HOR_RES * 0.12, 0); // 左侧填充12%
|
||||
lv_obj_set_style_pad_right(top_bar_, LV_HOR_RES * 0.12, 0); // 右侧填充12%
|
||||
// 表情容器上移适配
|
||||
lv_obj_align(emoji_box_, LV_ALIGN_CENTER, 0, -50); // 向上偏移50
|
||||
lv_obj_align(emoji_box_, LV_ALIGN_CENTER, 0, -30); // 向上偏移30
|
||||
// 消息栏适配
|
||||
lv_obj_align(bottom_bar_, LV_ALIGN_BOTTOM_MID, 0, -40); // 向上偏移40
|
||||
lv_obj_align(bottom_bar_, LV_ALIGN_BOTTOM_MID, 0, -20); // 向上偏移20
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
14
main/boards/lceda-course-examples/eda-robot-pro/README.md
Normal file
14
main/boards/lceda-course-examples/eda-robot-pro/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## EDA-Robot-Pro 机器狗
|
||||
|
||||
### 项目文档
|
||||
- [EDA-Robot-Pro 项目文档](https://wiki.lceda.cn/zh-hans/course-projects/smart-internet/eda-robot/eda-robot-introduce.html)
|
||||
|
||||
### 编译配置
|
||||
Flash大小按ESP32S3模组大小调整
|
||||
例如模组Flash容量为8MB时:
|
||||
|
||||
```
|
||||
Partition Table --->
|
||||
Partition Table (Custom partition table CSV) --->
|
||||
(partitions/v2/8m.csv) Custom partition CSV file
|
||||
```
|
||||
40
main/boards/lceda-course-examples/eda-robot-pro/config.h
Normal file
40
main/boards/lceda-course-examples/eda-robot-pro/config.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
// 音频配置
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
// 使用 Simplex I2S 模式
|
||||
#define AUDIO_I2S_METHOD_SIMPLEX
|
||||
|
||||
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_17
|
||||
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_16
|
||||
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_18
|
||||
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_40
|
||||
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_39
|
||||
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_38
|
||||
|
||||
// 按钮配置
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define TOUCH_BUTTON_GPIO GPIO_NUM_14
|
||||
// OLED显示屏配置 (SSD1306 128x64)
|
||||
#define DISPLAY_SDA_PIN GPIO_NUM_12
|
||||
#define DISPLAY_SCL_PIN GPIO_NUM_13
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
|
||||
// 机器狗舵机配置 - 四条腿
|
||||
#define LEFT_FRONT_LEG_PIN GPIO_NUM_47 // 左前腿
|
||||
#define LEFT_REAR_LEG_PIN GPIO_NUM_21 // 左后腿
|
||||
#define RIGHT_FRONT_LEG_PIN GPIO_NUM_9 // 右前腿
|
||||
#define RIGHT_REAR_LEG_PIN GPIO_NUM_10 // 右后腿
|
||||
|
||||
// EDA机器狗版本
|
||||
#define EDA_ROBOT_PRO_VERSION "1.0.0"
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
12
main/boards/lceda-course-examples/eda-robot-pro/config.json
Normal file
12
main/boards/lceda-course-examples/eda-robot-pro/config.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "eda-robot-pro",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/16m.csv\"",
|
||||
"CONFIG_OLED_SSD1306_128X64=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
EDA机器狗控制器 - MCP协议版本
|
||||
*/
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "application.h"
|
||||
#include "board.h"
|
||||
#include "config.h"
|
||||
#include "eda_dog_movements.h"
|
||||
#include "mcp_server.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "settings.h"
|
||||
|
||||
#define TAG "EDARobotDogController"
|
||||
|
||||
class EDARobotDogController {
|
||||
private:
|
||||
EDARobotDog dog_;
|
||||
TaskHandle_t action_task_handle_ = nullptr;
|
||||
QueueHandle_t action_queue_;
|
||||
bool is_action_in_progress_ = false;
|
||||
|
||||
struct DogActionParams {
|
||||
int action_type;
|
||||
int steps;
|
||||
int speed;
|
||||
int direction;
|
||||
int height;
|
||||
};
|
||||
|
||||
enum ActionType {
|
||||
ACTION_WALK = 1,
|
||||
ACTION_TURN = 2,
|
||||
ACTION_SIT = 3,
|
||||
ACTION_STAND = 4,
|
||||
ACTION_STRETCH = 5,
|
||||
ACTION_SHAKE = 6,
|
||||
ACTION_LIFT_LEFT_FRONT = 7,
|
||||
ACTION_LIFT_LEFT_REAR = 8,
|
||||
ACTION_LIFT_RIGHT_FRONT = 9,
|
||||
ACTION_LIFT_RIGHT_REAR = 10,
|
||||
ACTION_HOME = 11
|
||||
};
|
||||
|
||||
static void ActionTask(void *arg) {
|
||||
EDARobotDogController *controller = static_cast<EDARobotDogController *>(arg);
|
||||
DogActionParams params;
|
||||
controller->dog_.AttachServos();
|
||||
|
||||
while (true) {
|
||||
if (xQueueReceive(controller->action_queue_, ¶ms,
|
||||
pdMS_TO_TICKS(1000)) == pdTRUE) {
|
||||
ESP_LOGI(TAG, "执行动作: %d", params.action_type);
|
||||
controller->is_action_in_progress_ = true;
|
||||
|
||||
switch (params.action_type) {
|
||||
case ACTION_WALK:
|
||||
controller->dog_.Walk(params.steps, params.speed, params.direction);
|
||||
break;
|
||||
case ACTION_TURN:
|
||||
controller->dog_.Turn(params.steps, params.speed, params.direction);
|
||||
break;
|
||||
case ACTION_SIT:
|
||||
controller->dog_.Sit(params.speed);
|
||||
break;
|
||||
case ACTION_STAND:
|
||||
controller->dog_.Stand(params.speed);
|
||||
break;
|
||||
case ACTION_STRETCH:
|
||||
controller->dog_.Stretch(params.speed);
|
||||
break;
|
||||
case ACTION_SHAKE:
|
||||
controller->dog_.Shake(params.speed);
|
||||
break;
|
||||
case ACTION_LIFT_LEFT_FRONT:
|
||||
controller->dog_.LiftLeftFrontLeg(params.speed, params.height);
|
||||
break;
|
||||
case ACTION_LIFT_LEFT_REAR:
|
||||
controller->dog_.LiftLeftRearLeg(params.speed, params.height);
|
||||
break;
|
||||
case ACTION_LIFT_RIGHT_FRONT:
|
||||
controller->dog_.LiftRightFrontLeg(params.speed, params.height);
|
||||
break;
|
||||
case ACTION_LIFT_RIGHT_REAR:
|
||||
controller->dog_.LiftRightRearLeg(params.speed, params.height);
|
||||
break;
|
||||
case ACTION_HOME:
|
||||
controller->dog_.Home();
|
||||
break;
|
||||
}
|
||||
|
||||
if (params.action_type != ACTION_HOME &&
|
||||
params.action_type != ACTION_SIT) {
|
||||
controller->dog_.Home();
|
||||
}
|
||||
controller->is_action_in_progress_ = false;
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StartActionTaskIfNeeded() {
|
||||
if (action_task_handle_ == nullptr) {
|
||||
xTaskCreate(ActionTask, "dog_action", 1024 * 3, this,
|
||||
configMAX_PRIORITIES - 1, &action_task_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueAction(int action_type, int steps, int speed, int direction,
|
||||
int height) {
|
||||
ESP_LOGI(TAG, "动作控制: 类型=%d, 步数=%d, 速度=%d, 方向=%d, 高度=%d",
|
||||
action_type, steps, speed, direction, height);
|
||||
|
||||
DogActionParams params = {action_type, steps, speed, direction, height};
|
||||
xQueueSend(action_queue_, ¶ms, portMAX_DELAY);
|
||||
StartActionTaskIfNeeded();
|
||||
}
|
||||
|
||||
void LoadTrimsFromNVS() {
|
||||
Settings settings("dog_trims", false);
|
||||
|
||||
int left_front_leg = settings.GetInt("left_front_leg", 0);
|
||||
int left_rear_leg = settings.GetInt("left_rear_leg", 0);
|
||||
int right_front_leg = settings.GetInt("right_front_leg", 0);
|
||||
int right_rear_leg = settings.GetInt("right_rear_leg", 0);
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"从NVS加载微调设置: 左前腿=%d, 左后腿=%d, 右前腿=%d, 右后腿=%d",
|
||||
left_front_leg, left_rear_leg, right_front_leg, right_rear_leg);
|
||||
|
||||
dog_.SetTrims(left_front_leg, left_rear_leg, right_front_leg,
|
||||
right_rear_leg);
|
||||
}
|
||||
|
||||
public:
|
||||
EDARobotDogController() {
|
||||
dog_.Init(LEFT_FRONT_LEG_PIN, LEFT_REAR_LEG_PIN, RIGHT_FRONT_LEG_PIN,
|
||||
RIGHT_REAR_LEG_PIN);
|
||||
|
||||
ESP_LOGI(TAG, "EDA机器狗初始化完成");
|
||||
|
||||
LoadTrimsFromNVS();
|
||||
|
||||
action_queue_ = xQueueCreate(10, sizeof(DogActionParams));
|
||||
|
||||
QueueAction(ACTION_HOME, 1, 1000, 0, 0);
|
||||
|
||||
RegisterMcpTools();
|
||||
}
|
||||
|
||||
void RegisterMcpTools() {
|
||||
auto &mcp_server = McpServer::GetInstance();
|
||||
|
||||
ESP_LOGI(TAG, "开始注册MCP工具...");
|
||||
|
||||
// 基础移动动作
|
||||
mcp_server.AddTool(
|
||||
"self.dog.walk",
|
||||
"行走。steps: 行走步数(1-100); speed: "
|
||||
"行走速度(500-2000,数值越小越快); "
|
||||
"direction: 行走方向(-1=后退, 1=前进)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 4, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 2000),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_WALK, steps, speed, direction, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.dog.turn",
|
||||
"转身。steps: 转身步数(1-100); speed: "
|
||||
"转身速度(500-2000,数值越小越快); "
|
||||
"direction: 转身方向(1=左转, -1=右转)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 4, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 2000, 500, 2000),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_TURN, steps, speed, direction, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 姿态动作
|
||||
mcp_server.AddTool("self.dog.sit",
|
||||
"坐下。speed: 坐下速度(500-2000,数值越小越快)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger,
|
||||
1500, 500, 2000)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
QueueAction(ACTION_SIT, 1, speed, 0, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.dog.stand",
|
||||
"站立。speed: 站立速度(500-2000,数值越小越快)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger,
|
||||
1500, 500, 2000)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
QueueAction(ACTION_STAND, 1, speed, 0, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.dog.stretch",
|
||||
"伸展。speed: 伸展速度(500-2000,数值越小越快)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger,
|
||||
2000, 500, 2000)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
QueueAction(ACTION_STRETCH, 1, speed, 0, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.dog.shake",
|
||||
"摇摆。speed: 摇摆速度(500-2000,数值越小越快)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger,
|
||||
1000, 500, 2000)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
QueueAction(ACTION_SHAKE, 1, speed, 0, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 单腿抬起动作
|
||||
mcp_server.AddTool(
|
||||
"self.dog.lift_left_front_leg",
|
||||
"抬起左前腿。speed: 动作速度(500-2000,数值越小越快); height: "
|
||||
"抬起高度(10-90度)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger, 1000, 500, 2000),
|
||||
Property("height", kPropertyTypeInteger, 45, 10, 90)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
int height = properties["height"].value<int>();
|
||||
QueueAction(ACTION_LIFT_LEFT_FRONT, 1, speed, 0, height);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.dog.lift_left_rear_leg",
|
||||
"抬起左后腿。speed: 动作速度(500-2000,数值越小越快); height: "
|
||||
"抬起高度(10-90度)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger, 1000, 500, 2000),
|
||||
Property("height", kPropertyTypeInteger, 45, 10, 90)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
int height = properties["height"].value<int>();
|
||||
QueueAction(ACTION_LIFT_LEFT_REAR, 1, speed, 0, height);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.dog.lift_right_front_leg",
|
||||
"抬起右前腿。speed: 动作速度(500-2000,数值越小越快); height: "
|
||||
"抬起高度(10-90度)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger, 1000, 500, 2000),
|
||||
Property("height", kPropertyTypeInteger, 45, 10, 90)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
int height = properties["height"].value<int>();
|
||||
QueueAction(ACTION_LIFT_RIGHT_FRONT, 1, speed, 0, height);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.dog.lift_right_rear_leg",
|
||||
"抬起右后腿。speed: 动作速度(500-2000,数值越小越快); height: "
|
||||
"抬起高度(10-90度)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger, 1000, 500, 2000),
|
||||
Property("height", kPropertyTypeInteger, 45, 10, 90)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
int height = properties["height"].value<int>();
|
||||
QueueAction(ACTION_LIFT_RIGHT_REAR, 1, speed, 0, height);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 系统工具
|
||||
mcp_server.AddTool("self.dog.stop", "立即停止", PropertyList(),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
if (action_task_handle_ != nullptr) {
|
||||
vTaskDelete(action_task_handle_);
|
||||
action_task_handle_ = nullptr;
|
||||
}
|
||||
is_action_in_progress_ = false;
|
||||
xQueueReset(action_queue_);
|
||||
|
||||
QueueAction(ACTION_HOME, 1, 1000, 0, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.dog.set_trim",
|
||||
"校准单个舵机位置。设置指定舵机的微调参数以调整机器狗的初始站立姿态,设"
|
||||
"置将永久保存。"
|
||||
"servo_type: "
|
||||
"舵机类型(left_front_leg/left_rear_leg/right_front_leg/"
|
||||
"right_rear_leg); "
|
||||
"trim_value: 微调值(-50到50度)",
|
||||
PropertyList(
|
||||
{Property("servo_type", kPropertyTypeString, "left_front_leg"),
|
||||
Property("trim_value", kPropertyTypeInteger, 0, -50, 50)}),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
std::string servo_type =
|
||||
properties["servo_type"].value<std::string>();
|
||||
int trim_value = properties["trim_value"].value<int>();
|
||||
|
||||
ESP_LOGI(TAG, "设置舵机微调: %s = %d度", servo_type.c_str(),
|
||||
trim_value);
|
||||
|
||||
// 获取当前所有微调值
|
||||
Settings settings("dog_trims", true);
|
||||
int left_front_leg = settings.GetInt("left_front_leg", 0);
|
||||
int left_rear_leg = settings.GetInt("left_rear_leg", 0);
|
||||
int right_front_leg = settings.GetInt("right_front_leg", 0);
|
||||
int right_rear_leg = settings.GetInt("right_rear_leg", 0);
|
||||
|
||||
// 更新指定舵机的微调值
|
||||
if (servo_type == "left_front_leg") {
|
||||
left_front_leg = trim_value;
|
||||
settings.SetInt("left_front_leg", left_front_leg);
|
||||
} else if (servo_type == "left_rear_leg") {
|
||||
left_rear_leg = trim_value;
|
||||
settings.SetInt("left_rear_leg", left_rear_leg);
|
||||
} else if (servo_type == "right_front_leg") {
|
||||
right_front_leg = trim_value;
|
||||
settings.SetInt("right_front_leg", right_front_leg);
|
||||
} else if (servo_type == "right_rear_leg") {
|
||||
right_rear_leg = trim_value;
|
||||
settings.SetInt("right_rear_leg", right_rear_leg);
|
||||
} else {
|
||||
return "错误:无效的舵机类型,请使用: left_front_leg, "
|
||||
"left_rear_leg, right_front_leg, right_rear_leg";
|
||||
}
|
||||
|
||||
dog_.SetTrims(left_front_leg, left_rear_leg, right_front_leg,
|
||||
right_rear_leg);
|
||||
|
||||
QueueAction(ACTION_HOME, 1, 500, 0, 0);
|
||||
|
||||
return "舵机 " + servo_type + " 微调设置为 " +
|
||||
std::to_string(trim_value) + " 度,已永久保存";
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.dog.get_trims", "获取当前的舵机微调设置", PropertyList(),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
Settings settings("dog_trims", false);
|
||||
|
||||
int left_front_leg = settings.GetInt("left_front_leg", 0);
|
||||
int left_rear_leg = settings.GetInt("left_rear_leg", 0);
|
||||
int right_front_leg = settings.GetInt("right_front_leg", 0);
|
||||
int right_rear_leg = settings.GetInt("right_rear_leg", 0);
|
||||
|
||||
std::string result =
|
||||
"{\"left_front_leg\":" + std::to_string(left_front_leg) +
|
||||
",\"left_rear_leg\":" + std::to_string(left_rear_leg) +
|
||||
",\"right_front_leg\":" + std::to_string(right_front_leg) +
|
||||
",\"right_rear_leg\":" + std::to_string(right_rear_leg) + "}";
|
||||
|
||||
ESP_LOGI(TAG, "获取微调设置: %s", result.c_str());
|
||||
return result;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.dog.get_status",
|
||||
"获取机器狗状态,返回 moving 或 idle", PropertyList(),
|
||||
[this](const PropertyList &properties) -> ReturnValue {
|
||||
return is_action_in_progress_ ? "moving" : "idle";
|
||||
});
|
||||
|
||||
ESP_LOGI(TAG, "MCP工具注册完成");
|
||||
}
|
||||
|
||||
~EDARobotDogController() {
|
||||
if (action_task_handle_ != nullptr) {
|
||||
vTaskDelete(action_task_handle_);
|
||||
action_task_handle_ = nullptr;
|
||||
}
|
||||
vQueueDelete(action_queue_);
|
||||
}
|
||||
};
|
||||
|
||||
static EDARobotDogController *g_dog_controller = nullptr;
|
||||
|
||||
void InitializeEDARobotDogController() {
|
||||
if (g_dog_controller == nullptr) {
|
||||
g_dog_controller = new EDARobotDogController();
|
||||
ESP_LOGI(TAG, "EDA机器狗控制器已初始化并注册MCP工具");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
#include "eda_dog_movements.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "oscillator.h"
|
||||
|
||||
static const char *TAG = "EDARobotDogMovements";
|
||||
|
||||
#define LEG_HOME_POSITION 90
|
||||
|
||||
EDARobotDog::EDARobotDog() {
|
||||
is_dog_resting_ = false;
|
||||
// 初始化所有舵机管脚为-1(未连接)
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
servo_pins_[i] = -1;
|
||||
servo_trim_[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
EDARobotDog::~EDARobotDog() { DetachServos(); }
|
||||
|
||||
unsigned long IRAM_ATTR millis() {
|
||||
return (unsigned long)(esp_timer_get_time() / 1000ULL);
|
||||
}
|
||||
|
||||
void EDARobotDog::Init(int left_front_leg, int left_rear_leg, int right_front_leg,
|
||||
int right_rear_leg) {
|
||||
servo_pins_[LEFT_FRONT_LEG] = left_front_leg;
|
||||
servo_pins_[LEFT_REAR_LEG] = left_rear_leg;
|
||||
servo_pins_[RIGHT_FRONT_LEG] = right_front_leg;
|
||||
servo_pins_[RIGHT_REAR_LEG] = right_rear_leg;
|
||||
|
||||
AttachServos();
|
||||
is_dog_resting_ = false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- ATTACH & DETACH FUNCTIONS ----------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EDARobotDog::AttachServos() {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].Attach(servo_pins_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EDARobotDog::DetachServos() {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].Detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- OSCILLATORS TRIMS ------------------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EDARobotDog::SetTrims(int left_front_leg, int left_rear_leg,
|
||||
int right_front_leg, int right_rear_leg) {
|
||||
servo_trim_[LEFT_FRONT_LEG] = left_front_leg;
|
||||
servo_trim_[LEFT_REAR_LEG] = left_rear_leg;
|
||||
servo_trim_[RIGHT_FRONT_LEG] = right_front_leg;
|
||||
servo_trim_[RIGHT_REAR_LEG] = right_rear_leg;
|
||||
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetTrim(servo_trim_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- BASIC MOTION FUNCTIONS -------------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EDARobotDog::MoveServos(int time, int servo_target[]) {
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
final_time_ = millis() + time;
|
||||
if (time > 10) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
increment_[i] =
|
||||
(servo_target[i] - servo_[i].GetPosition()) / (time / 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int iteration = 1; millis() < final_time_; iteration++) {
|
||||
partial_time_ = millis() + 10;
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetPosition(servo_[i].GetPosition() + increment_[i]);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetPosition(servo_target[i]);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(time));
|
||||
}
|
||||
|
||||
// final adjustment to the target.
|
||||
bool f = true;
|
||||
int adjustment_count = 0;
|
||||
while (f && adjustment_count < 10) {
|
||||
f = false;
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1 && servo_target[i] != servo_[i].GetPosition()) {
|
||||
f = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetPosition(servo_target[i]);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
adjustment_count++;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void EDARobotDog::MoveSingle(int position, int servo_number) {
|
||||
if (position > 180)
|
||||
position = 90;
|
||||
if (position < 0)
|
||||
position = 90;
|
||||
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
if (servo_number >= 0 && servo_number < SERVO_COUNT &&
|
||||
servo_pins_[servo_number] != -1) {
|
||||
servo_[servo_number].SetPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
void EDARobotDog::OscillateServos(int amplitude[SERVO_COUNT],
|
||||
int offset[SERVO_COUNT], int period,
|
||||
double phase_diff[SERVO_COUNT], float cycle = 1) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetO(offset[i]);
|
||||
servo_[i].SetA(amplitude[i]);
|
||||
servo_[i].SetT(period);
|
||||
servo_[i].SetPh(phase_diff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
double ref = millis();
|
||||
double end_time = period * cycle + ref;
|
||||
|
||||
while (millis() < end_time) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].Refresh();
|
||||
}
|
||||
}
|
||||
vTaskDelay(5);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
void EDARobotDog::Execute(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT],
|
||||
int period, double phase_diff[SERVO_COUNT],
|
||||
float steps = 1.0) {
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
int cycles = (int)steps;
|
||||
|
||||
//-- Execute complete cycles
|
||||
if (cycles >= 1)
|
||||
for (int i = 0; i < cycles; i++)
|
||||
OscillateServos(amplitude, offset, period, phase_diff);
|
||||
|
||||
//-- Execute the final not complete cycle
|
||||
OscillateServos(amplitude, offset, period, phase_diff, (float)steps - cycles);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- HOME = Dog at rest position --------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EDARobotDog::Home() {
|
||||
if (is_dog_resting_ == false) { // Go to rest position only if necessary
|
||||
int homes[SERVO_COUNT] = {LEG_HOME_POSITION, LEG_HOME_POSITION,
|
||||
LEG_HOME_POSITION, LEG_HOME_POSITION};
|
||||
MoveServos(500, homes);
|
||||
is_dog_resting_ = true;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
|
||||
bool EDARobotDog::GetRestState() { return is_dog_resting_; }
|
||||
|
||||
void EDARobotDog::SetRestState(bool state) { is_dog_resting_ = state; }
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- BASIC LEG MOVEMENTS ----------------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
void EDARobotDog::LiftLeftFrontLeg(int period, int height) {
|
||||
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
// 重复3次摇摆动作
|
||||
for (int num = 0; num < 3; num++) {
|
||||
// servo1.write(180); delay(100);
|
||||
current_pos[LEFT_FRONT_LEG] = 0; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
// servo1.write(150); delay(100);
|
||||
current_pos[LEFT_FRONT_LEG] = 30; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
// servo1.write(90);
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
void EDARobotDog::LiftLeftRearLeg(int period, int height) {
|
||||
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
// 重复3次摇摆动作
|
||||
for (int num = 0; num < 3; num++) {
|
||||
// servo1.write(180); delay(100);
|
||||
current_pos[LEFT_REAR_LEG] = 180; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
// servo1.write(150); delay(100);
|
||||
current_pos[LEFT_REAR_LEG] = 150; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
// servo1.write(90);
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
void EDARobotDog::LiftRightFrontLeg(int period, int height) {
|
||||
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
// 重复3次摇摆动作
|
||||
for (int num = 0; num < 3; num++) {
|
||||
// servo1.write(180); delay(100);
|
||||
current_pos[RIGHT_FRONT_LEG] = 180; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
// servo1.write(150); delay(100);
|
||||
current_pos[RIGHT_FRONT_LEG] = 150; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
// servo1.write(90);
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
void EDARobotDog::LiftRightRearLeg(int period, int height) {
|
||||
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
// 重复3次摇摆动作
|
||||
for (int num = 0; num < 3; num++) {
|
||||
// servo1.write(180); delay(100);
|
||||
current_pos[RIGHT_REAR_LEG] = 0; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
// servo1.write(150); delay(100);
|
||||
current_pos[RIGHT_REAR_LEG] = 30; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
// servo1.write(90);
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- DOG GAIT MOVEMENTS -----------------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
void EDARobotDog::Turn(float steps, int period, int dir) {
|
||||
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
for (int step = 0; step < (int)steps; step++) {
|
||||
if (dir == LEFT) {
|
||||
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 140; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 40; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[RIGHT_FRONT_LEG] = 40; // servo4
|
||||
current_pos[LEFT_FRONT_LEG] = 140; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_FRONT_LEG] = 140; // servo4
|
||||
current_pos[LEFT_FRONT_LEG] = 40; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 40; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 140; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_pos[LEFT_REAR_LEG] = 140; // servo2
|
||||
current_pos[RIGHT_REAR_LEG] = 40; // servo3
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 40; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 140; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 140; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 40; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_REAR_LEG] = 40; // servo2
|
||||
current_pos[RIGHT_REAR_LEG] = 140; // servo3
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EDARobotDog::Walk(float steps, int period, int dir) {
|
||||
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
|
||||
for (int step = 0; step < (int)steps; step++) {
|
||||
if (dir == FORWARD) {
|
||||
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 100; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 100; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 60; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 60; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 140; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 140; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 40; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 40; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 80; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 80; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 120; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 120; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 140; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 140; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
} else {
|
||||
|
||||
// 每次只移动指定的舵机,然后延时100ms
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 80; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 80; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 120; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 120; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 40; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 40; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 140; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 140; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 100; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 100; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 60; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 60; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 90; // servo1
|
||||
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 40; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 40; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
|
||||
current_pos[RIGHT_REAR_LEG] = 90; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 90; // servo2
|
||||
MoveServos(100, current_pos);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EDARobotDog::Sit(int period) {
|
||||
|
||||
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_pos[LEFT_REAR_LEG] = 0; // servo2
|
||||
current_pos[RIGHT_REAR_LEG] = 180; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
void EDARobotDog::Stand(int period) {
|
||||
// 站立:所有腿回到中立位置
|
||||
Home();
|
||||
}
|
||||
|
||||
void EDARobotDog::Stretch(int period) {
|
||||
|
||||
|
||||
// 获取当前位置
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_pos[LEFT_FRONT_LEG] = 0; // servo1
|
||||
current_pos[RIGHT_REAR_LEG] = 0; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 180; // servo2
|
||||
current_pos[RIGHT_FRONT_LEG] = 180; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
|
||||
void EDARobotDog::Shake(int period) {
|
||||
// 摇摆:左右摇摆身体,左前腿和右后腿运动方向相反
|
||||
int A[SERVO_COUNT] = {20, 0, 20, 0}; // 只有前腿摇摆
|
||||
int O[SERVO_COUNT] = {0, LEG_HOME_POSITION, 0, LEG_HOME_POSITION};
|
||||
// 左前腿和右前腿反相摇摆
|
||||
double phase_diff[SERVO_COUNT] = {DEG2RAD(180), 0, DEG2RAD(0), 0};
|
||||
|
||||
Execute(A, O, period, phase_diff, 3);
|
||||
}
|
||||
|
||||
void EDARobotDog::EnableServoLimit(int diff_limit) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetLimiter(diff_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EDARobotDog::DisableServoLimit() {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].DisableLimiter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EDARobotDog::Sleep() {
|
||||
|
||||
int current_pos[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_pos[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_pos[i] = LEG_HOME_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
// servo1.write(0); servo3.write(180); servo2.write(180); servo4.write(0);
|
||||
current_pos[LEFT_FRONT_LEG] = 0; // servo1
|
||||
current_pos[RIGHT_REAR_LEG] = 180; // servo3
|
||||
current_pos[LEFT_REAR_LEG] = 180; // servo2
|
||||
current_pos[RIGHT_FRONT_LEG] = 0; // servo4
|
||||
MoveServos(100, current_pos);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef __EDA_DOG_MOVEMENTS_H__
|
||||
#define __EDA_DOG_MOVEMENTS_H__
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "oscillator.h"
|
||||
|
||||
//-- Constants
|
||||
#define FORWARD 1
|
||||
#define BACKWARD -1
|
||||
#define LEFT 1
|
||||
#define RIGHT -1
|
||||
#define SMALL 5
|
||||
#define MEDIUM 15
|
||||
#define BIG 30
|
||||
|
||||
// -- Servo delta limit default. degree / sec
|
||||
#define SERVO_LIMIT_DEFAULT 240
|
||||
|
||||
// -- Servo indexes for easy access
|
||||
#define LEFT_FRONT_LEG 0
|
||||
#define LEFT_REAR_LEG 1
|
||||
#define RIGHT_FRONT_LEG 2
|
||||
#define RIGHT_REAR_LEG 3
|
||||
#define SERVO_COUNT 4
|
||||
|
||||
class EDARobotDog {
|
||||
public:
|
||||
EDARobotDog();
|
||||
~EDARobotDog();
|
||||
|
||||
//-- EDA Dog initialization
|
||||
void Init(int left_front_leg, int left_rear_leg, int right_front_leg, int right_rear_leg);
|
||||
|
||||
//-- Attach & detach functions
|
||||
void AttachServos();
|
||||
void DetachServos();
|
||||
|
||||
//-- Oscillator Trims
|
||||
void SetTrims(int left_front_leg, int left_rear_leg, int right_front_leg, int right_rear_leg);
|
||||
|
||||
//-- Predetermined Motion Functions
|
||||
void MoveServos(int time, int servo_target[]);
|
||||
void MoveSingle(int position, int servo_number);
|
||||
void OscillateServos(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT], int period,
|
||||
double phase_diff[SERVO_COUNT], float cycle);
|
||||
|
||||
//-- HOME = Dog at rest position
|
||||
void Home();
|
||||
bool GetRestState();
|
||||
void SetRestState(bool state);
|
||||
|
||||
//-- Basic leg movements
|
||||
void LiftLeftFrontLeg(int period = 1000, int height = 45); // 抬起左前腿
|
||||
void LiftLeftRearLeg(int period = 1000, int height = 45); // 抬起左后腿
|
||||
void LiftRightFrontLeg(int period = 1000, int height = 45); // 抬起右前腿
|
||||
void LiftRightRearLeg(int period = 1000, int height = 45); // 抬起右后腿
|
||||
|
||||
//-- Dog gait movements
|
||||
void Walk(float steps = 4, int period = 1000, int dir = FORWARD);
|
||||
void Turn(float steps = 4, int period = 2000, int dir = LEFT);
|
||||
void Sit(int period = 1500);
|
||||
void Stand(int period = 1500);
|
||||
void Stretch(int period = 2000);
|
||||
void Shake(int period = 1000);
|
||||
void Sleep(); // 睡觉动作
|
||||
|
||||
// -- Servo limiter
|
||||
void EnableServoLimit(int speed_limit_degree_per_sec = SERVO_LIMIT_DEFAULT);
|
||||
void DisableServoLimit();
|
||||
|
||||
private:
|
||||
Oscillator servo_[SERVO_COUNT];
|
||||
|
||||
int servo_pins_[SERVO_COUNT];
|
||||
int servo_trim_[SERVO_COUNT];
|
||||
|
||||
unsigned long final_time_;
|
||||
unsigned long partial_time_;
|
||||
float increment_[SERVO_COUNT];
|
||||
|
||||
bool is_dog_resting_;
|
||||
|
||||
void Execute(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT], int period,
|
||||
double phase_diff[SERVO_COUNT], float steps);
|
||||
};
|
||||
|
||||
#endif // __EDA_DOG_MOVEMENTS_H__
|
||||
133
main/boards/lceda-course-examples/eda-robot-pro/eda_robot_pro.cc
Normal file
133
main/boards/lceda-course-examples/eda-robot-pro/eda_robot_pro.cc
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "wifi_board.h"
|
||||
#include "codecs/no_audio_codec.h"
|
||||
#include "display/oled_display.h"
|
||||
#include "system_reset.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "mcp_server.h"
|
||||
|
||||
#include <wifi_station.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
|
||||
#define TAG "EDARobotPro"
|
||||
|
||||
extern void InitializeEDARobotDogController();
|
||||
|
||||
class EDARobotPro : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t display_i2c_bus_;
|
||||
esp_lcd_panel_io_handle_t panel_io_ = nullptr;
|
||||
esp_lcd_panel_handle_t panel_ = nullptr;
|
||||
Display* display_ = nullptr;
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
void InitializeDisplayI2c() {
|
||||
i2c_master_bus_config_t bus_config = {
|
||||
.i2c_port = (i2c_port_t)0,
|
||||
.sda_io_num = DISPLAY_SDA_PIN,
|
||||
.scl_io_num = DISPLAY_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &display_i2c_bus_));
|
||||
}
|
||||
|
||||
void InitializeSsd1306Display() {
|
||||
// SSD1306 config
|
||||
esp_lcd_panel_io_i2c_config_t io_config = {
|
||||
.dev_addr = 0x3C,
|
||||
.on_color_trans_done = nullptr,
|
||||
.user_ctx = nullptr,
|
||||
.control_phase_bytes = 1,
|
||||
.dc_bit_offset = 6,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
.flags = {
|
||||
.dc_low_on_data = 0,
|
||||
.disable_control_phase = 0,
|
||||
},
|
||||
.scl_speed_hz = 400 * 1000,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c_v2(display_i2c_bus_, &io_config, &panel_io_));
|
||||
|
||||
ESP_LOGI(TAG, "Install SSD1306 driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = -1;
|
||||
panel_config.bits_per_pixel = 1;
|
||||
|
||||
esp_lcd_panel_ssd1306_config_t ssd1306_config = {
|
||||
.height = static_cast<uint8_t>(DISPLAY_HEIGHT),
|
||||
};
|
||||
panel_config.vendor_config = &ssd1306_config;
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(panel_io_, &panel_config, &panel_));
|
||||
ESP_LOGI(TAG, "SSD1306 driver installed");
|
||||
|
||||
// Reset the display
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_));
|
||||
if (esp_lcd_panel_init(panel_) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize display");
|
||||
display_ = new NoDisplay();
|
||||
return;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_, false));
|
||||
|
||||
// Set the display to on
|
||||
ESP_LOGI(TAG, "Turning display on");
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true));
|
||||
|
||||
display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
}
|
||||
|
||||
|
||||
// EDA机器狗控制器初始化
|
||||
void InitializeEDARobotDogController() {
|
||||
ESP_LOGI(TAG, "初始化EDA机器狗MCP控制器");
|
||||
::InitializeEDARobotDogController();
|
||||
}
|
||||
void InitializeButtons() {
|
||||
|
||||
touch_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
touch_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
EDARobotPro() :
|
||||
boot_button_(BOOT_BUTTON_GPIO),
|
||||
touch_button_(TOUCH_BUTTON_GPIO){
|
||||
InitializeDisplayI2c();
|
||||
InitializeSsd1306Display();
|
||||
InitializeEDARobotDogController();
|
||||
InitializeButtons();
|
||||
}
|
||||
|
||||
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(EDARobotPro);
|
||||
161
main/boards/lceda-course-examples/eda-robot-pro/oscillator.cc
Normal file
161
main/boards/lceda-course-examples/eda-robot-pro/oscillator.cc
Normal file
@@ -0,0 +1,161 @@
|
||||
//--------------------------------------------------------------
|
||||
//-- Oscillator.pde
|
||||
//-- Generate sinusoidal oscillations in the servos
|
||||
//--------------------------------------------------------------
|
||||
//-- (c) Juan Gonzalez-Gomez (Obijuan), Dec 2011
|
||||
//-- (c) txp666 for esp32, 202503
|
||||
//-- GPL license
|
||||
//--------------------------------------------------------------
|
||||
#include "oscillator.h"
|
||||
|
||||
#include <driver/ledc.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
static const char* TAG = "Oscillator";
|
||||
|
||||
extern unsigned long IRAM_ATTR millis();
|
||||
|
||||
static ledc_channel_t next_free_channel = LEDC_CHANNEL_0;
|
||||
|
||||
Oscillator::Oscillator(int trim) {
|
||||
trim_ = trim;
|
||||
diff_limit_ = 0;
|
||||
is_attached_ = false;
|
||||
|
||||
sampling_period_ = 30;
|
||||
period_ = 2000;
|
||||
number_samples_ = period_ / sampling_period_;
|
||||
inc_ = 2 * M_PI / number_samples_;
|
||||
|
||||
amplitude_ = 45;
|
||||
phase_ = 0;
|
||||
phase0_ = 0;
|
||||
offset_ = 0;
|
||||
stop_ = false;
|
||||
rev_ = false;
|
||||
|
||||
pos_ = 90;
|
||||
previous_millis_ = 0;
|
||||
}
|
||||
|
||||
Oscillator::~Oscillator() {
|
||||
Detach();
|
||||
}
|
||||
|
||||
uint32_t Oscillator::AngleToCompare(int angle) {
|
||||
return (angle - SERVO_MIN_DEGREE) * (SERVO_MAX_PULSEWIDTH_US - SERVO_MIN_PULSEWIDTH_US) /
|
||||
(SERVO_MAX_DEGREE - SERVO_MIN_DEGREE) +
|
||||
SERVO_MIN_PULSEWIDTH_US;
|
||||
}
|
||||
|
||||
bool Oscillator::NextSample() {
|
||||
current_millis_ = millis();
|
||||
|
||||
if (current_millis_ - previous_millis_ > sampling_period_) {
|
||||
previous_millis_ = current_millis_;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Oscillator::Attach(int pin, bool rev) {
|
||||
if (is_attached_) {
|
||||
Detach();
|
||||
}
|
||||
|
||||
pin_ = pin;
|
||||
rev_ = rev;
|
||||
|
||||
ledc_timer_config_t ledc_timer = {.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.duty_resolution = LEDC_TIMER_13_BIT,
|
||||
.timer_num = LEDC_TIMER_1,
|
||||
.freq_hz = 50,
|
||||
.clk_cfg = LEDC_AUTO_CLK};
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
static int last_channel = 0;
|
||||
last_channel = (last_channel + 1) % 7 + 1;
|
||||
ledc_channel_ = (ledc_channel_t)last_channel;
|
||||
|
||||
ledc_channel_config_t ledc_channel = {.gpio_num = pin_,
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.channel = ledc_channel_,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.timer_sel = LEDC_TIMER_1,
|
||||
.duty = 0,
|
||||
.hpoint = 0};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
ledc_speed_mode_ = LEDC_LOW_SPEED_MODE;
|
||||
|
||||
// pos_ = 90;
|
||||
// Write(pos_);
|
||||
previous_servo_command_millis_ = millis();
|
||||
|
||||
is_attached_ = true;
|
||||
}
|
||||
|
||||
void Oscillator::Detach() {
|
||||
if (!is_attached_)
|
||||
return;
|
||||
|
||||
ESP_ERROR_CHECK(ledc_stop(ledc_speed_mode_, ledc_channel_, 0));
|
||||
|
||||
is_attached_ = false;
|
||||
}
|
||||
|
||||
void Oscillator::SetT(unsigned int T) {
|
||||
period_ = T;
|
||||
|
||||
number_samples_ = period_ / sampling_period_;
|
||||
inc_ = 2 * M_PI / number_samples_;
|
||||
}
|
||||
|
||||
void Oscillator::SetPosition(int position) {
|
||||
Write(position);
|
||||
}
|
||||
|
||||
void Oscillator::Refresh() {
|
||||
if (NextSample()) {
|
||||
if (!stop_) {
|
||||
int pos = std::round(amplitude_ * std::sin(phase_ + phase0_) + offset_);
|
||||
if (rev_)
|
||||
pos = -pos;
|
||||
Write(pos + 90);
|
||||
}
|
||||
|
||||
phase_ = phase_ + inc_;
|
||||
}
|
||||
}
|
||||
|
||||
void Oscillator::Write(int position) {
|
||||
if (!is_attached_)
|
||||
return;
|
||||
|
||||
long currentMillis = millis();
|
||||
if (diff_limit_ > 0) {
|
||||
int limit = std::max(
|
||||
1, (((int)(currentMillis - previous_servo_command_millis_)) * diff_limit_) / 1000);
|
||||
if (abs(position - pos_) > limit) {
|
||||
pos_ += position < pos_ ? -limit : limit;
|
||||
} else {
|
||||
pos_ = position;
|
||||
}
|
||||
} else {
|
||||
pos_ = position;
|
||||
}
|
||||
previous_servo_command_millis_ = currentMillis;
|
||||
|
||||
int angle = pos_ + trim_;
|
||||
|
||||
angle = std::min(std::max(angle, 0), 180);
|
||||
|
||||
uint32_t duty = (uint32_t)(((angle / 180.0) * 2.0 + 0.5) * 8191 / 20.0);
|
||||
|
||||
ESP_ERROR_CHECK(ledc_set_duty(ledc_speed_mode_, ledc_channel_, duty));
|
||||
ESP_ERROR_CHECK(ledc_update_duty(ledc_speed_mode_, ledc_channel_));
|
||||
}
|
||||
91
main/boards/lceda-course-examples/eda-robot-pro/oscillator.h
Normal file
91
main/boards/lceda-course-examples/eda-robot-pro/oscillator.h
Normal file
@@ -0,0 +1,91 @@
|
||||
//--------------------------------------------------------------
|
||||
//-- Oscillator.pde
|
||||
//-- Generate sinusoidal oscillations in the servos
|
||||
//--------------------------------------------------------------
|
||||
//-- (c) Juan Gonzalez-Gomez (Obijuan), Dec 2011
|
||||
//-- (c) txp666 for esp32, 202503
|
||||
//-- GPL license
|
||||
//--------------------------------------------------------------
|
||||
#ifndef __OSCILLATOR_H__
|
||||
#define __OSCILLATOR_H__
|
||||
|
||||
#include <driver/ledc.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#define M_PI 3.14159265358979323846
|
||||
|
||||
#ifndef DEG2RAD
|
||||
#define DEG2RAD(g) ((g) * M_PI) / 180
|
||||
#endif
|
||||
|
||||
#define SERVO_MIN_PULSEWIDTH_US 500 // 最小脉宽(微秒)
|
||||
#define SERVO_MAX_PULSEWIDTH_US 2500 // 最大脉宽(微秒)
|
||||
#define SERVO_MIN_DEGREE -90 // 最小角度
|
||||
#define SERVO_MAX_DEGREE 90 // 最大角度
|
||||
#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000 // 1MHz, 1us per tick
|
||||
#define SERVO_TIMEBASE_PERIOD 20000 // 20000 ticks, 20ms
|
||||
|
||||
class Oscillator {
|
||||
public:
|
||||
Oscillator(int trim = 0);
|
||||
~Oscillator();
|
||||
void Attach(int pin, bool rev = false);
|
||||
void Detach();
|
||||
|
||||
void SetA(unsigned int amplitude) { amplitude_ = amplitude; };
|
||||
void SetO(int offset) { offset_ = offset; };
|
||||
void SetPh(double Ph) { phase0_ = Ph; };
|
||||
void SetT(unsigned int period);
|
||||
void SetTrim(int trim) { trim_ = trim; };
|
||||
void SetLimiter(int diff_limit) { diff_limit_ = diff_limit; };
|
||||
void DisableLimiter() { diff_limit_ = 0; };
|
||||
int GetTrim() { return trim_; };
|
||||
void SetPosition(int position);
|
||||
void Stop() { stop_ = true; };
|
||||
void Play() { stop_ = false; };
|
||||
void Reset() { phase_ = 0; };
|
||||
void Refresh();
|
||||
int GetPosition() { return pos_; }
|
||||
|
||||
private:
|
||||
bool NextSample();
|
||||
void Write(int position);
|
||||
uint32_t AngleToCompare(int angle);
|
||||
|
||||
private:
|
||||
bool is_attached_;
|
||||
|
||||
//-- Oscillators parameters
|
||||
unsigned int amplitude_; //-- Amplitude (degrees)
|
||||
int offset_; //-- Offset (degrees)
|
||||
unsigned int period_; //-- Period (miliseconds)
|
||||
double phase0_; //-- Phase (radians)
|
||||
|
||||
//-- Internal variables
|
||||
int pos_; //-- Current servo pos
|
||||
int pin_; //-- Pin where the servo is connected
|
||||
int trim_; //-- Calibration offset
|
||||
double phase_; //-- Current phase
|
||||
double inc_; //-- Increment of phase
|
||||
double number_samples_; //-- Number of samples
|
||||
unsigned int sampling_period_; //-- sampling period (ms)
|
||||
|
||||
long previous_millis_;
|
||||
long current_millis_;
|
||||
|
||||
//-- Oscillation mode. If true, the servo is stopped
|
||||
bool stop_;
|
||||
|
||||
//-- Reverse mode
|
||||
bool rev_;
|
||||
|
||||
int diff_limit_;
|
||||
long previous_servo_command_millis_;
|
||||
|
||||
ledc_channel_t ledc_channel_;
|
||||
ledc_mode_t ledc_speed_mode_;
|
||||
};
|
||||
|
||||
#endif // __OSCILLATOR_H__
|
||||
14
main/boards/lceda-course-examples/eda-super-bear/README.md
Normal file
14
main/boards/lceda-course-examples/eda-super-bear/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## EDA-Super-Bear 机器熊
|
||||
|
||||
### 项目文档
|
||||
- [EDA-Super-Bear 项目文档](https://wiki.lceda.cn/zh-hans/course-projects/smart-internet/eda-superbear/eda-superbear-introduce.html)
|
||||
|
||||
### 编译配置
|
||||
Flash大小按ESP32S3模组大小调整
|
||||
例如模组Flash容量为8MB时:
|
||||
|
||||
```
|
||||
Partition Table --->
|
||||
Partition Table (Custom partition table CSV) --->
|
||||
(partitions/v2/8m.csv) Custom partition CSV file
|
||||
```
|
||||
29
main/boards/lceda-course-examples/eda-super-bear/config.h
Normal file
29
main/boards/lceda-course-examples/eda-super-bear/config.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define RIGHT_LEG_PIN GPIO_NUM_11
|
||||
#define RIGHT_FOOT_PIN GPIO_NUM_14
|
||||
#define LEFT_LEG_PIN GPIO_NUM_13
|
||||
#define LEFT_FOOT_PIN GPIO_NUM_12
|
||||
#define LEFT_HAND_PIN GPIO_NUM_21
|
||||
#define RIGHT_HAND_PIN GPIO_NUM_10
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_I2S_METHOD_SIMPLEX
|
||||
|
||||
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_40
|
||||
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_39
|
||||
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_41
|
||||
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_47
|
||||
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_48
|
||||
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_45
|
||||
|
||||
#define TOUCH_BUTTON_GPIO GPIO_NUM_14
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_38
|
||||
|
||||
#define EDA_SUPER_BEAR_VERSION "1.0.0"
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
11
main/boards/lceda-course-examples/eda-super-bear/config.json
Normal file
11
main/boards/lceda-course-examples/eda-super-bear/config.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "eda-super-bear",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/16m.csv\""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "application.h"
|
||||
#include "codecs/no_audio_codec.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "display/display.h"
|
||||
#include "lamp_controller.h"
|
||||
#include "led/single_led.h"
|
||||
#include "mcp_server.h"
|
||||
#include "system_reset.h"
|
||||
#include "wifi_board.h"
|
||||
|
||||
#define TAG "EdaSuperBear"
|
||||
|
||||
extern void InitializeEdaSuperBearController();
|
||||
|
||||
class EdaSuperBear : public WifiBoard {
|
||||
private:
|
||||
Display* display_;
|
||||
Button boot_button_;
|
||||
|
||||
void InitializeDisplay() {
|
||||
display_ = new NoDisplay();
|
||||
ESP_LOGI(TAG, "使用NoDisplay (无物理显示屏)");
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting) {
|
||||
EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
EdaSuperBear() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializeDisplay();
|
||||
InitializeButtons();
|
||||
ESP_LOGI(TAG, "初始化EdaRobot机器人MCP控制器");
|
||||
::InitializeEdaSuperBearController();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK,
|
||||
AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK,
|
||||
AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override { return display_; }
|
||||
};
|
||||
|
||||
DECLARE_BOARD(EdaSuperBear);
|
||||
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
EdaRobot机器人控制器 - MCP协议版本
|
||||
*/
|
||||
|
||||
#include <cJSON.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "application.h"
|
||||
#include "board.h"
|
||||
#include "config.h"
|
||||
#include "mcp_server.h"
|
||||
#include "eda_super_bear_movements.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "settings.h"
|
||||
|
||||
#define TAG "EdaSuperBearController"
|
||||
|
||||
class EdaSuperBearController {
|
||||
private:
|
||||
EdaRobot edarobot_;
|
||||
TaskHandle_t action_task_handle_ = nullptr;
|
||||
QueueHandle_t action_queue_;
|
||||
bool has_hands_ = false;
|
||||
bool is_action_in_progress_ = false;
|
||||
|
||||
struct EdaRobotActionParams {
|
||||
int action_type;
|
||||
int steps;
|
||||
int speed;
|
||||
int direction;
|
||||
int amount;
|
||||
};
|
||||
|
||||
enum ActionType {
|
||||
ACTION_WALK = 1,
|
||||
ACTION_TURN = 2,
|
||||
ACTION_JUMP = 3,
|
||||
ACTION_SWING = 4,
|
||||
ACTION_MOONWALK = 5,
|
||||
ACTION_BEND = 6,
|
||||
ACTION_SHAKE_LEG = 7,
|
||||
ACTION_UPDOWN = 8,
|
||||
ACTION_TIPTOE_SWING = 9,
|
||||
ACTION_JITTER = 10,
|
||||
ACTION_ASCENDING_TURN = 11,
|
||||
ACTION_CRUSAITO = 12,
|
||||
ACTION_FLAPPING = 13,
|
||||
ACTION_HANDS_UP = 14,
|
||||
ACTION_HANDS_DOWN = 15,
|
||||
ACTION_HAND_WAVE = 16,
|
||||
ACTION_HOME = 17
|
||||
};
|
||||
|
||||
static void ActionTask(void* arg) {
|
||||
EdaSuperBearController* controller = static_cast<EdaSuperBearController*>(arg);
|
||||
EdaRobotActionParams params;
|
||||
controller->edarobot_.AttachServos();
|
||||
|
||||
while (true) {
|
||||
if (xQueueReceive(controller->action_queue_, ¶ms, pdMS_TO_TICKS(1000)) == pdTRUE) {
|
||||
ESP_LOGI(TAG, "执行动作: %d", params.action_type);
|
||||
controller->is_action_in_progress_ = true;
|
||||
|
||||
switch (params.action_type) {
|
||||
case ACTION_WALK:
|
||||
controller->edarobot_.Walk(params.steps, params.speed, params.direction,
|
||||
params.amount);
|
||||
break;
|
||||
case ACTION_TURN:
|
||||
controller->edarobot_.Turn(params.steps, params.speed, params.direction,
|
||||
params.amount);
|
||||
break;
|
||||
case ACTION_JUMP:
|
||||
controller->edarobot_.Jump(params.steps, params.speed);
|
||||
break;
|
||||
case ACTION_SWING:
|
||||
controller->edarobot_.Swing(params.steps, params.speed, params.amount);
|
||||
break;
|
||||
case ACTION_MOONWALK:
|
||||
controller->edarobot_.Moonwalker(params.steps, params.speed, params.amount,
|
||||
params.direction);
|
||||
break;
|
||||
case ACTION_BEND:
|
||||
controller->edarobot_.Bend(params.steps, params.speed, params.direction);
|
||||
break;
|
||||
case ACTION_SHAKE_LEG:
|
||||
controller->edarobot_.ShakeLeg(params.steps, params.speed, params.direction);
|
||||
break;
|
||||
case ACTION_UPDOWN:
|
||||
controller->edarobot_.UpDown(params.steps, params.speed, params.amount);
|
||||
break;
|
||||
case ACTION_TIPTOE_SWING:
|
||||
controller->edarobot_.TiptoeSwing(params.steps, params.speed, params.amount);
|
||||
break;
|
||||
case ACTION_JITTER:
|
||||
controller->edarobot_.Jitter(params.steps, params.speed, params.amount);
|
||||
break;
|
||||
case ACTION_ASCENDING_TURN:
|
||||
controller->edarobot_.AscendingTurn(params.steps, params.speed, params.amount);
|
||||
break;
|
||||
case ACTION_CRUSAITO:
|
||||
controller->edarobot_.Crusaito(params.steps, params.speed, params.amount,
|
||||
params.direction);
|
||||
break;
|
||||
case ACTION_FLAPPING:
|
||||
controller->edarobot_.Flapping(params.steps, params.speed, params.amount,
|
||||
params.direction);
|
||||
break;
|
||||
case ACTION_HANDS_UP:
|
||||
if (controller->has_hands_) {
|
||||
controller->edarobot_.HandsUp(params.speed, params.direction);
|
||||
}
|
||||
break;
|
||||
case ACTION_HANDS_DOWN:
|
||||
if (controller->has_hands_) {
|
||||
controller->edarobot_.HandsDown(params.speed, params.direction);
|
||||
}
|
||||
break;
|
||||
case ACTION_HAND_WAVE:
|
||||
if (controller->has_hands_) {
|
||||
controller->edarobot_.HandWave(params.speed, params.direction);
|
||||
}
|
||||
break;
|
||||
case ACTION_HOME:
|
||||
controller->edarobot_.Home(params.direction == 1);
|
||||
break;
|
||||
}
|
||||
if (params.action_type != ACTION_HOME) {
|
||||
controller->edarobot_.Home(params.action_type < ACTION_HANDS_UP);
|
||||
}
|
||||
controller->is_action_in_progress_ = false;
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StartActionTaskIfNeeded() {
|
||||
if (action_task_handle_ == nullptr) {
|
||||
xTaskCreate(ActionTask, "edarobot_action", 1024 * 3, this, configMAX_PRIORITIES - 1,
|
||||
&action_task_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueAction(int action_type, int steps, int speed, int direction, int amount) {
|
||||
// 检查手部动作
|
||||
if ((action_type >= ACTION_HANDS_UP && action_type <= ACTION_HAND_WAVE) && !has_hands_) {
|
||||
ESP_LOGW(TAG, "尝试执行手部动作,但机器人没有配置手部舵机");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "动作控制: 类型=%d, 步数=%d, 速度=%d, 方向=%d, 幅度=%d", action_type, steps,
|
||||
speed, direction, amount);
|
||||
|
||||
EdaRobotActionParams params = {action_type, steps, speed, direction, amount};
|
||||
xQueueSend(action_queue_, ¶ms, portMAX_DELAY);
|
||||
StartActionTaskIfNeeded();
|
||||
}
|
||||
|
||||
void LoadTrimsFromNVS() {
|
||||
Settings settings("edarobot_trims", false);
|
||||
|
||||
int left_leg = settings.GetInt("left_leg", 0);
|
||||
int right_leg = settings.GetInt("right_leg", 0);
|
||||
int left_foot = settings.GetInt("left_foot", 0);
|
||||
int right_foot = settings.GetInt("right_foot", 0);
|
||||
int left_hand = settings.GetInt("left_hand", 0);
|
||||
int right_hand = settings.GetInt("right_hand", 0);
|
||||
|
||||
ESP_LOGI(TAG, "从NVS加载微调设置: 左腿=%d, 右腿=%d, 左脚=%d, 右脚=%d, 左手=%d, 右手=%d",
|
||||
left_leg, right_leg, left_foot, right_foot, left_hand, right_hand);
|
||||
|
||||
edarobot_.SetTrims(left_leg, right_leg, left_foot, right_foot, left_hand, right_hand);
|
||||
}
|
||||
|
||||
public:
|
||||
EdaSuperBearController() {
|
||||
edarobot_.Init(LEFT_LEG_PIN, RIGHT_LEG_PIN, LEFT_FOOT_PIN, RIGHT_FOOT_PIN, LEFT_HAND_PIN,
|
||||
RIGHT_HAND_PIN);
|
||||
|
||||
has_hands_ = (LEFT_HAND_PIN != -1 && RIGHT_HAND_PIN != -1);
|
||||
ESP_LOGI(TAG, "EdaRobot机器人初始化%s手部舵机", has_hands_ ? "带" : "不带");
|
||||
|
||||
LoadTrimsFromNVS();
|
||||
|
||||
action_queue_ = xQueueCreate(10, sizeof(EdaRobotActionParams));
|
||||
|
||||
QueueAction(ACTION_HOME, 1, 1000, 1, 0); // direction=1表示复位手部
|
||||
|
||||
RegisterMcpTools();
|
||||
}
|
||||
|
||||
void RegisterMcpTools() {
|
||||
auto& mcp_server = McpServer::GetInstance();
|
||||
|
||||
ESP_LOGI(TAG, "开始注册MCP工具...");
|
||||
|
||||
// 基础移动动作
|
||||
mcp_server.AddTool("self.edarobot.walk_forward",
|
||||
"行走。steps: 行走步数(1-100); speed: 行走速度(500-1500,数值越小越快); "
|
||||
"direction: 行走方向(-1=后退, 1=前进); arm_swing: 手臂摆动幅度(0-170度)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 3, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("arm_swing", kPropertyTypeInteger, 50, 0, 170),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int arm_swing = properties["arm_swing"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_WALK, steps, speed, direction, arm_swing);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.turn_left",
|
||||
"转身。steps: 转身步数(1-100); speed: 转身速度(500-1500,数值越小越快); "
|
||||
"direction: 转身方向(1=左转, -1=右转); arm_swing: 手臂摆动幅度(0-170度)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 3, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("arm_swing", kPropertyTypeInteger, 50, 0, 170),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int arm_swing = properties["arm_swing"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_TURN, steps, speed, direction, arm_swing);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.jump",
|
||||
"跳跃。steps: 跳跃次数(1-100); speed: 跳跃速度(500-1500,数值越小越快)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 1, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
QueueAction(ACTION_JUMP, steps, speed, 0, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 特殊动作
|
||||
mcp_server.AddTool("self.edarobot.swing",
|
||||
"左右摇摆。steps: 摇摆次数(1-100); speed: "
|
||||
"摇摆速度(500-1500,数值越小越快); amount: 摇摆幅度(0-170度)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 3, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("amount", kPropertyTypeInteger, 30, 0, 170)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int amount = properties["amount"].value<int>();
|
||||
QueueAction(ACTION_SWING, steps, speed, 0, amount);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.moonwalk",
|
||||
"太空步。steps: 太空步步数(1-100); speed: 速度(500-1500,数值越小越快); "
|
||||
"direction: 方向(1=左, -1=右); amount: 幅度(0-170度)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 3, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1),
|
||||
Property("amount", kPropertyTypeInteger, 25, 0, 170)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
int amount = properties["amount"].value<int>();
|
||||
QueueAction(ACTION_MOONWALK, steps, speed, direction, amount);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.bend",
|
||||
"弯曲身体。steps: 弯曲次数(1-100); speed: "
|
||||
"弯曲速度(500-1500,数值越小越快); direction: 弯曲方向(1=左, -1=右)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 1, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_BEND, steps, speed, direction, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.shake_leg",
|
||||
"摇腿。steps: 摇腿次数(1-100); speed: 摇腿速度(500-1500,数值越小越快); "
|
||||
"direction: 腿部选择(1=左腿, -1=右腿)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 1, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_SHAKE_LEG, steps, speed, direction, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.updown",
|
||||
"上下运动。steps: 上下运动次数(1-100); speed: "
|
||||
"运动速度(500-1500,数值越小越快); amount: 运动幅度(0-170度)",
|
||||
PropertyList({Property("steps", kPropertyTypeInteger, 3, 1, 100),
|
||||
Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("amount", kPropertyTypeInteger, 20, 0, 170)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int steps = properties["steps"].value<int>();
|
||||
int speed = properties["speed"].value<int>();
|
||||
int amount = properties["amount"].value<int>();
|
||||
QueueAction(ACTION_UPDOWN, steps, speed, 0, amount);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 手部动作(仅在有手部舵机时可用)
|
||||
if (has_hands_) {
|
||||
mcp_server.AddTool(
|
||||
"self.edarobot.hands_up",
|
||||
"举手。speed: 举手速度(500-1500,数值越小越快); direction: 手部选择(1=左手, "
|
||||
"-1=右手, 0=双手)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_HANDS_UP, 1, speed, direction, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.edarobot.hands_down",
|
||||
"放手。speed: 放手速度(500-1500,数值越小越快); direction: 手部选择(1=左手, "
|
||||
"-1=右手, 0=双手)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_HANDS_DOWN, 1, speed, direction, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.edarobot.hand_wave",
|
||||
"挥手。speed: 挥手速度(500-1500,数值越小越快); direction: 手部选择(1=左手, "
|
||||
"-1=右手, 0=双手)",
|
||||
PropertyList({Property("speed", kPropertyTypeInteger, 1000, 500, 1500),
|
||||
Property("direction", kPropertyTypeInteger, 1, -1, 1)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
int speed = properties["speed"].value<int>();
|
||||
int direction = properties["direction"].value<int>();
|
||||
QueueAction(ACTION_HAND_WAVE, 1, speed, direction, 0);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// 系统工具
|
||||
mcp_server.AddTool("self.edarobot.stop", "立即停止", PropertyList(),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
if (action_task_handle_ != nullptr) {
|
||||
vTaskDelete(action_task_handle_);
|
||||
action_task_handle_ = nullptr;
|
||||
}
|
||||
is_action_in_progress_ = false;
|
||||
xQueueReset(action_queue_);
|
||||
|
||||
QueueAction(ACTION_HOME, 1, 1000, 1, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.edarobot.set_trim",
|
||||
"校准单个舵机位置。设置指定舵机的微调参数以调整EdaRobot的初始站立姿态,设置将永久保存。"
|
||||
"servo_type: 舵机类型(left_leg/right_leg/left_foot/right_foot/left_hand/right_hand); "
|
||||
"trim_value: 微调值(-50到50度)",
|
||||
PropertyList({Property("servo_type", kPropertyTypeString, "left_leg"),
|
||||
Property("trim_value", kPropertyTypeInteger, 0, -50, 50)}),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
std::string servo_type = properties["servo_type"].value<std::string>();
|
||||
int trim_value = properties["trim_value"].value<int>();
|
||||
|
||||
ESP_LOGI(TAG, "设置舵机微调: %s = %d度", servo_type.c_str(), trim_value);
|
||||
|
||||
// 获取当前所有微调值
|
||||
Settings settings("edarobot_trims", true);
|
||||
int left_leg = settings.GetInt("left_leg", 0);
|
||||
int right_leg = settings.GetInt("right_leg", 0);
|
||||
int left_foot = settings.GetInt("left_foot", 0);
|
||||
int right_foot = settings.GetInt("right_foot", 0);
|
||||
int left_hand = settings.GetInt("left_hand", 0);
|
||||
int right_hand = settings.GetInt("right_hand", 0);
|
||||
|
||||
// 更新指定舵机的微调值
|
||||
if (servo_type == "left_leg") {
|
||||
left_leg = trim_value;
|
||||
settings.SetInt("left_leg", left_leg);
|
||||
} else if (servo_type == "right_leg") {
|
||||
right_leg = trim_value;
|
||||
settings.SetInt("right_leg", right_leg);
|
||||
} else if (servo_type == "left_foot") {
|
||||
left_foot = trim_value;
|
||||
settings.SetInt("left_foot", left_foot);
|
||||
} else if (servo_type == "right_foot") {
|
||||
right_foot = trim_value;
|
||||
settings.SetInt("right_foot", right_foot);
|
||||
} else if (servo_type == "left_hand") {
|
||||
if (!has_hands_) {
|
||||
return "错误:机器人没有配置手部舵机";
|
||||
}
|
||||
left_hand = trim_value;
|
||||
settings.SetInt("left_hand", left_hand);
|
||||
} else if (servo_type == "right_hand") {
|
||||
if (!has_hands_) {
|
||||
return "错误:机器人没有配置手部舵机";
|
||||
}
|
||||
right_hand = trim_value;
|
||||
settings.SetInt("right_hand", right_hand);
|
||||
} else {
|
||||
return "错误:无效的舵机类型,请使用: left_leg, right_leg, left_foot, "
|
||||
"right_foot, left_hand, right_hand";
|
||||
}
|
||||
|
||||
edarobot_.SetTrims(left_leg, right_leg, left_foot, right_foot, left_hand, right_hand);
|
||||
|
||||
QueueAction(ACTION_JUMP, 1, 500, 0, 0);
|
||||
|
||||
return "舵机 " + servo_type + " 微调设置为 " + std::to_string(trim_value) +
|
||||
" 度,已永久保存";
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.get_trims", "获取当前的舵机微调设置", PropertyList(),
|
||||
[this](const PropertyList& properties) -> ReturnValue {
|
||||
Settings settings("edarobot_trims", false);
|
||||
|
||||
int left_leg = settings.GetInt("left_leg", 0);
|
||||
int right_leg = settings.GetInt("right_leg", 0);
|
||||
int left_foot = settings.GetInt("left_foot", 0);
|
||||
int right_foot = settings.GetInt("right_foot", 0);
|
||||
int left_hand = settings.GetInt("left_hand", 0);
|
||||
int right_hand = settings.GetInt("right_hand", 0);
|
||||
|
||||
std::string result =
|
||||
"{\"left_leg\":" + std::to_string(left_leg) +
|
||||
",\"right_leg\":" + std::to_string(right_leg) +
|
||||
",\"left_foot\":" + std::to_string(left_foot) +
|
||||
",\"right_foot\":" + std::to_string(right_foot) +
|
||||
",\"left_hand\":" + std::to_string(left_hand) +
|
||||
",\"right_hand\":" + std::to_string(right_hand) + "}";
|
||||
|
||||
ESP_LOGI(TAG, "获取微调设置: %s", result.c_str());
|
||||
return result;
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.edarobot.get_status", "获取机器人状态,返回 moving 或 idle",
|
||||
PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
|
||||
return is_action_in_progress_ ? "moving" : "idle";
|
||||
});
|
||||
|
||||
mcp_server.AddTool("self.battery.get_level", "获取机器人电池电量和充电状态", PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& board = Board::GetInstance();
|
||||
int level = 0;
|
||||
bool charging = false;
|
||||
bool discharging = false;
|
||||
board.GetBatteryLevel(level, charging, discharging);
|
||||
|
||||
std::string status =
|
||||
"{\"level\":" + std::to_string(level) +
|
||||
",\"charging\":" + (charging ? "true" : "false") + "}";
|
||||
return status;
|
||||
});
|
||||
|
||||
ESP_LOGI(TAG, "MCP工具注册完成");
|
||||
}
|
||||
|
||||
~EdaSuperBearController() {
|
||||
if (action_task_handle_ != nullptr) {
|
||||
vTaskDelete(action_task_handle_);
|
||||
action_task_handle_ = nullptr;
|
||||
}
|
||||
vQueueDelete(action_queue_);
|
||||
}
|
||||
};
|
||||
|
||||
static EdaSuperBearController* g_edarobot_controller = nullptr;
|
||||
|
||||
void InitializeEdaSuperBearController() {
|
||||
if (g_edarobot_controller == nullptr) {
|
||||
g_edarobot_controller = new EdaSuperBearController();
|
||||
ESP_LOGI(TAG, "EdaRobot控制器已初始化并注册MCP工具");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,763 @@
|
||||
#include "eda_super_bear_movements.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "oscillator.h"
|
||||
|
||||
static const char* TAG = "EdaSuperBearMovements";
|
||||
|
||||
#define HAND_HOME_POSITION 45
|
||||
|
||||
EdaRobot::EdaRobot() {
|
||||
is_edarobot_resting_ = false;
|
||||
has_hands_ = false;
|
||||
// 初始化所有舵机管脚为-1(未连接)
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
servo_pins_[i] = -1;
|
||||
servo_trim_[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
EdaRobot::~EdaRobot() {
|
||||
DetachServos();
|
||||
}
|
||||
|
||||
unsigned long IRAM_ATTR millis() {
|
||||
return (unsigned long)(esp_timer_get_time() / 1000ULL);
|
||||
}
|
||||
|
||||
void EdaRobot::Init(int left_leg, int right_leg, int left_foot, int right_foot, int left_hand,
|
||||
int right_hand) {
|
||||
servo_pins_[LEFT_LEG] = left_leg;
|
||||
servo_pins_[RIGHT_LEG] = right_leg;
|
||||
servo_pins_[LEFT_FOOT] = left_foot;
|
||||
servo_pins_[RIGHT_FOOT] = right_foot;
|
||||
servo_pins_[LEFT_HAND] = left_hand;
|
||||
servo_pins_[RIGHT_HAND] = right_hand;
|
||||
|
||||
// 检查是否有手部舵机
|
||||
has_hands_ = (left_hand != -1 && right_hand != -1);
|
||||
|
||||
AttachServos();
|
||||
is_edarobot_resting_ = false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- ATTACH & DETACH FUNCTIONS ----------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EdaRobot::AttachServos() {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].Attach(servo_pins_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EdaRobot::DetachServos() {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].Detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- OSCILLATORS TRIMS ------------------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EdaRobot::SetTrims(int left_leg, int right_leg, int left_foot, int right_foot, int left_hand,
|
||||
int right_hand) {
|
||||
servo_trim_[LEFT_LEG] = left_leg;
|
||||
servo_trim_[RIGHT_LEG] = right_leg;
|
||||
servo_trim_[LEFT_FOOT] = left_foot;
|
||||
servo_trim_[RIGHT_FOOT] = right_foot;
|
||||
|
||||
if (has_hands_) {
|
||||
servo_trim_[LEFT_HAND] = left_hand;
|
||||
servo_trim_[RIGHT_HAND] = right_hand;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetTrim(servo_trim_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- BASIC MOTION FUNCTIONS -------------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EdaRobot::MoveServos(int time, int servo_target[]) {
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
final_time_ = millis() + time;
|
||||
if (time > 10) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
increment_[i] = (servo_target[i] - servo_[i].GetPosition()) / (time / 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int iteration = 1; millis() < final_time_; iteration++) {
|
||||
partial_time_ = millis() + 10;
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetPosition(servo_[i].GetPosition() + increment_[i]);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetPosition(servo_target[i]);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(time));
|
||||
}
|
||||
|
||||
// final adjustment to the target.
|
||||
bool f = true;
|
||||
int adjustment_count = 0;
|
||||
while (f && adjustment_count < 10) {
|
||||
f = false;
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1 && servo_target[i] != servo_[i].GetPosition()) {
|
||||
f = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetPosition(servo_target[i]);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
adjustment_count++;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void EdaRobot::MoveSingle(int position, int servo_number) {
|
||||
if (position > 180)
|
||||
position = 90;
|
||||
if (position < 0)
|
||||
position = 90;
|
||||
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
if (servo_number >= 0 && servo_number < SERVO_COUNT && servo_pins_[servo_number] != -1) {
|
||||
servo_[servo_number].SetPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
void EdaRobot::OscillateServos(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT], int period,
|
||||
double phase_diff[SERVO_COUNT], float cycle = 1) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetO(offset[i]);
|
||||
servo_[i].SetA(amplitude[i]);
|
||||
servo_[i].SetT(period);
|
||||
servo_[i].SetPh(phase_diff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
double ref = millis();
|
||||
double end_time = period * cycle + ref;
|
||||
|
||||
while (millis() < end_time) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].Refresh();
|
||||
}
|
||||
}
|
||||
vTaskDelay(5);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
void EdaRobot::Execute(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT], int period,
|
||||
double phase_diff[SERVO_COUNT], float steps = 1.0) {
|
||||
if (GetRestState() == true) {
|
||||
SetRestState(false);
|
||||
}
|
||||
|
||||
int cycles = (int)steps;
|
||||
|
||||
//-- Execute complete cycles
|
||||
if (cycles >= 1)
|
||||
for (int i = 0; i < cycles; i++)
|
||||
OscillateServos(amplitude, offset, period, phase_diff);
|
||||
|
||||
//-- Execute the final not complete cycle
|
||||
OscillateServos(amplitude, offset, period, phase_diff, (float)steps - cycles);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- HOME = EdaRobot at rest position -------------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void EdaRobot::Home(bool hands_down) {
|
||||
if (is_edarobot_resting_ == false) { // Go to rest position only if necessary
|
||||
// 为所有舵机准备初始位置值
|
||||
int homes[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (i == LEFT_HAND || i == RIGHT_HAND) {
|
||||
if (hands_down) {
|
||||
// 如果需要复位手部,设置为默认值
|
||||
if (i == LEFT_HAND) {
|
||||
homes[i] = HAND_HOME_POSITION;
|
||||
} else { // RIGHT_HAND
|
||||
homes[i] = 180 - HAND_HOME_POSITION; // 右手镜像位置
|
||||
}
|
||||
} else {
|
||||
// 如果不需要复位手部,保持当前位置
|
||||
homes[i] = servo_[i].GetPosition();
|
||||
}
|
||||
} else {
|
||||
// 腿部和脚部舵机始终复位
|
||||
homes[i] = 90;
|
||||
}
|
||||
}
|
||||
|
||||
MoveServos(500, homes);
|
||||
is_edarobot_resting_ = true;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
|
||||
bool EdaRobot::GetRestState() {
|
||||
return is_edarobot_resting_;
|
||||
}
|
||||
|
||||
void EdaRobot::SetRestState(bool state) {
|
||||
is_edarobot_resting_ = state;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- PREDETERMINED MOTION SEQUENCES -----------------------------//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//-- EdaRobot movement: Jump
|
||||
//-- Parameters:
|
||||
//-- steps: Number of steps
|
||||
//-- T: Period
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Jump(float steps, int period) {
|
||||
int up[SERVO_COUNT] = {90, 90, 150, 30, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
MoveServos(period, up);
|
||||
int down[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
MoveServos(period, down);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Walking (forward or backward)
|
||||
//-- Parameters:
|
||||
//-- * steps: Number of steps
|
||||
//-- * T : Period
|
||||
//-- * Dir: Direction: FORWARD / BACKWARD
|
||||
//-- * amount: 手部摆动幅度, 0表示不摆动
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Walk(float steps, int period, int dir, int amount) {
|
||||
//-- Oscillator parameters for walking
|
||||
//-- Hip sevos are in phase
|
||||
//-- Feet servos are in phase
|
||||
//-- Hip and feet are 90 degrees out of phase
|
||||
//-- -90 : Walk forward
|
||||
//-- 90 : Walk backward
|
||||
//-- Feet servos also have the same offset (for tiptoe a little bit)
|
||||
int A[SERVO_COUNT] = {30, 30, 30, 30, 0, 0};
|
||||
int O[SERVO_COUNT] = {0, 0, 5, -5, HAND_HOME_POSITION - 90, HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {0, 0, DEG2RAD(dir * -90), DEG2RAD(dir * -90), 0, 0};
|
||||
|
||||
// 如果amount>0且有手部舵机,设置手部振幅和相位
|
||||
if (amount > 0 && has_hands_) {
|
||||
// 手臂振幅使用传入的amount参数
|
||||
A[LEFT_HAND] = amount;
|
||||
A[RIGHT_HAND] = amount;
|
||||
|
||||
// 左手与右腿同相,右手与左腿同相,使得机器人走路时手臂自然摆动
|
||||
phase_diff[LEFT_HAND] = phase_diff[RIGHT_LEG]; // 左手与右腿同相
|
||||
phase_diff[RIGHT_HAND] = phase_diff[LEFT_LEG]; // 右手与左腿同相
|
||||
} else {
|
||||
A[LEFT_HAND] = 0;
|
||||
A[RIGHT_HAND] = 0;
|
||||
}
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Turning (left or right)
|
||||
//-- Parameters:
|
||||
//-- * Steps: Number of steps
|
||||
//-- * T: Period
|
||||
//-- * Dir: Direction: LEFT / RIGHT
|
||||
//-- * amount: 手部摆动幅度, 0表示不摆动
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Turn(float steps, int period, int dir, int amount) {
|
||||
//-- Same coordination than for walking (see EdaRobot::walk)
|
||||
//-- The Amplitudes of the hip's oscillators are not igual
|
||||
//-- When the right hip servo amplitude is higher, the steps taken by
|
||||
//-- the right leg are bigger than the left. So, the robot describes an
|
||||
//-- left arc
|
||||
int A[SERVO_COUNT] = {30, 30, 30, 30, 0, 0};
|
||||
int O[SERVO_COUNT] = {0, 0, 5, -5, HAND_HOME_POSITION - 90, HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {0, 0, DEG2RAD(-90), DEG2RAD(-90), 0, 0};
|
||||
|
||||
if (dir == LEFT) {
|
||||
A[0] = 30; //-- Left hip servo
|
||||
A[1] = 0; //-- Right hip servo
|
||||
} else {
|
||||
A[0] = 0;
|
||||
A[1] = 30;
|
||||
}
|
||||
|
||||
// 如果amount>0且有手部舵机,设置手部振幅和相位
|
||||
if (amount > 0 && has_hands_) {
|
||||
// 手臂振幅使用传入的amount参数
|
||||
A[LEFT_HAND] = amount;
|
||||
A[RIGHT_HAND] = amount;
|
||||
|
||||
// 转向时手臂摆动相位:左手与左腿同相,右手与右腿同相,增强转向效果
|
||||
phase_diff[LEFT_HAND] = phase_diff[LEFT_LEG]; // 左手与左腿同相
|
||||
phase_diff[RIGHT_HAND] = phase_diff[RIGHT_LEG]; // 右手与右腿同相
|
||||
} else {
|
||||
A[LEFT_HAND] = 0;
|
||||
A[RIGHT_HAND] = 0;
|
||||
}
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Lateral bend
|
||||
//-- Parameters:
|
||||
//-- steps: Number of bends
|
||||
//-- T: Period of one bend
|
||||
//-- dir: RIGHT=Right bend LEFT=Left bend
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Bend(int steps, int period, int dir) {
|
||||
// Parameters of all the movements. Default: Left bend
|
||||
int bend1[SERVO_COUNT] = {90, 90, 62, 35, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
int bend2[SERVO_COUNT] = {90, 90, 62, 105, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
int homes[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
|
||||
// Time of one bend, constrained in order to avoid movements too fast.
|
||||
// T=max(T, 600);
|
||||
// Changes in the parameters if right direction is chosen
|
||||
if (dir == -1) {
|
||||
bend1[2] = 180 - 35;
|
||||
bend1[3] = 180 - 60; // Not 65. EdaRobot is unbalanced
|
||||
bend2[2] = 180 - 105;
|
||||
bend2[3] = 180 - 60;
|
||||
}
|
||||
|
||||
// Time of the bend movement. Fixed parameter to avoid falls
|
||||
int T2 = 800;
|
||||
|
||||
// Bend movement
|
||||
for (int i = 0; i < steps; i++) {
|
||||
MoveServos(T2 / 2, bend1);
|
||||
MoveServos(T2 / 2, bend2);
|
||||
vTaskDelay(pdMS_TO_TICKS(period * 0.8));
|
||||
MoveServos(500, homes);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Shake a leg
|
||||
//-- Parameters:
|
||||
//-- steps: Number of shakes
|
||||
//-- T: Period of one shake
|
||||
//-- dir: RIGHT=Right leg LEFT=Left leg
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::ShakeLeg(int steps, int period, int dir) {
|
||||
// This variable change the amount of shakes
|
||||
int numberLegMoves = 2;
|
||||
|
||||
// Parameters of all the movements. Default: Right leg
|
||||
int shake_leg1[SERVO_COUNT] = {90, 90, 58, 35, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
int shake_leg2[SERVO_COUNT] = {90, 90, 58, 120, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
int shake_leg3[SERVO_COUNT] = {90, 90, 58, 60, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
int homes[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
|
||||
// Changes in the parameters if left leg is chosen
|
||||
if (dir == 1) {
|
||||
shake_leg1[2] = 180 - 35;
|
||||
shake_leg1[3] = 180 - 58;
|
||||
shake_leg2[2] = 180 - 120;
|
||||
shake_leg2[3] = 180 - 58;
|
||||
shake_leg3[2] = 180 - 60;
|
||||
shake_leg3[3] = 180 - 58;
|
||||
}
|
||||
|
||||
// Time of the bend movement. Fixed parameter to avoid falls
|
||||
int T2 = 1000;
|
||||
// Time of one shake, constrained in order to avoid movements too fast.
|
||||
period = period - T2;
|
||||
period = std::max(period, 200 * numberLegMoves);
|
||||
|
||||
for (int j = 0; j < steps; j++) {
|
||||
// Bend movement
|
||||
MoveServos(T2 / 2, shake_leg1);
|
||||
MoveServos(T2 / 2, shake_leg2);
|
||||
|
||||
// Shake movement
|
||||
for (int i = 0; i < numberLegMoves; i++) {
|
||||
MoveServos(period / (2 * numberLegMoves), shake_leg3);
|
||||
MoveServos(period / (2 * numberLegMoves), shake_leg2);
|
||||
}
|
||||
MoveServos(500, homes); // Return to home position
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(period));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot movement: up & down
|
||||
//-- Parameters:
|
||||
//-- * steps: Number of jumps
|
||||
//-- * T: Period
|
||||
//-- * h: Jump height: SMALL / MEDIUM / BIG
|
||||
//-- (or a number in degrees 0 - 90)
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::UpDown(float steps, int period, int height) {
|
||||
//-- Both feet are 180 degrees out of phase
|
||||
//-- Feet amplitude and offset are the same
|
||||
//-- Initial phase for the right foot is -90, so that it starts
|
||||
//-- in one extreme position (not in the middle)
|
||||
int A[SERVO_COUNT] = {0, 0, height, height, 0, 0};
|
||||
int O[SERVO_COUNT] = {0, 0, height, -height, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {0, 0, DEG2RAD(-90), DEG2RAD(90), 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot movement: swinging side to side
|
||||
//-- Parameters:
|
||||
//-- steps: Number of steps
|
||||
//-- T : Period
|
||||
//-- h : Amount of swing (from 0 to 50 aprox)
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Swing(float steps, int period, int height) {
|
||||
//-- Both feets are in phase. The offset is half the amplitude
|
||||
//-- It causes the robot to swing from side to side
|
||||
int A[SERVO_COUNT] = {0, 0, height, height, 0, 0};
|
||||
int O[SERVO_COUNT] = {
|
||||
0, 0, height / 2, -height / 2, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {0, 0, DEG2RAD(0), DEG2RAD(0), 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot movement: swinging side to side without touching the floor with the heel
|
||||
//-- Parameters:
|
||||
//-- steps: Number of steps
|
||||
//-- T : Period
|
||||
//-- h : Amount of swing (from 0 to 50 aprox)
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::TiptoeSwing(float steps, int period, int height) {
|
||||
//-- Both feets are in phase. The offset is not half the amplitude in order to tiptoe
|
||||
//-- It causes the robot to swing from side to side
|
||||
int A[SERVO_COUNT] = {0, 0, height, height, 0, 0};
|
||||
int O[SERVO_COUNT] = {0, 0, height, -height, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Jitter
|
||||
//-- Parameters:
|
||||
//-- steps: Number of jitters
|
||||
//-- T: Period of one jitter
|
||||
//-- h: height (Values between 5 - 25)
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Jitter(float steps, int period, int height) {
|
||||
//-- Both feet are 180 degrees out of phase
|
||||
//-- Feet amplitude and offset are the same
|
||||
//-- Initial phase for the right foot is -90, so that it starts
|
||||
//-- in one extreme position (not in the middle)
|
||||
//-- h is constrained to avoid hit the feets
|
||||
height = std::min(25, height);
|
||||
int A[SERVO_COUNT] = {height, height, 0, 0, 0, 0};
|
||||
int O[SERVO_COUNT] = {0, 0, 0, 0, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {DEG2RAD(-90), DEG2RAD(90), 0, 0, 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Ascending & turn (Jitter while up&down)
|
||||
//-- Parameters:
|
||||
//-- steps: Number of bends
|
||||
//-- T: Period of one bend
|
||||
//-- h: height (Values between 5 - 15)
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::AscendingTurn(float steps, int period, int height) {
|
||||
//-- Both feet and legs are 180 degrees out of phase
|
||||
//-- Initial phase for the right foot is -90, so that it starts
|
||||
//-- in one extreme position (not in the middle)
|
||||
//-- h is constrained to avoid hit the feets
|
||||
height = std::min(13, height);
|
||||
int A[SERVO_COUNT] = {height, height, height, height, 0, 0};
|
||||
int O[SERVO_COUNT] = {
|
||||
0, 0, height + 4, -height + 4, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {DEG2RAD(-90), DEG2RAD(90), DEG2RAD(-90), DEG2RAD(90), 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Moonwalker. EdaRobot moves like Michael Jackson
|
||||
//-- Parameters:
|
||||
//-- Steps: Number of steps
|
||||
//-- T: Period
|
||||
//-- h: Height. Typical valures between 15 and 40
|
||||
//-- dir: Direction: LEFT / RIGHT
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Moonwalker(float steps, int period, int height, int dir) {
|
||||
//-- This motion is similar to that of the caterpillar robots: A travelling
|
||||
//-- wave moving from one side to another
|
||||
//-- The two EdaRobot's feet are equivalent to a minimal configuration. It is known
|
||||
//-- that 2 servos can move like a worm if they are 120 degrees out of phase
|
||||
//-- In the example of EdaRobot, the two feet are mirrored so that we have:
|
||||
//-- 180 - 120 = 60 degrees. The actual phase difference given to the oscillators
|
||||
//-- is 60 degrees.
|
||||
//-- Both amplitudes are equal. The offset is half the amplitud plus a little bit of
|
||||
//- offset so that the robot tiptoe lightly
|
||||
|
||||
int A[SERVO_COUNT] = {0, 0, height, height, 0, 0};
|
||||
int O[SERVO_COUNT] = {
|
||||
0, 0, height / 2 + 2, -height / 2 - 2, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
int phi = -dir * 90;
|
||||
double phase_diff[SERVO_COUNT] = {0, 0, DEG2RAD(phi), DEG2RAD(-60 * dir + phi), 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//-- EdaRobot gait: Crusaito. A mixture between moonwalker and walk
|
||||
//-- Parameters:
|
||||
//-- steps: Number of steps
|
||||
//-- T: Period
|
||||
//-- h: height (Values between 20 - 50)
|
||||
//-- dir: Direction: LEFT / RIGHT
|
||||
//-----------------------------------------------------------
|
||||
void EdaRobot::Crusaito(float steps, int period, int height, int dir) {
|
||||
int A[SERVO_COUNT] = {25, 25, height, height, 0, 0};
|
||||
int O[SERVO_COUNT] = {
|
||||
0, 0, height / 2 + 4, -height / 2 - 4, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {90, 90, DEG2RAD(0), DEG2RAD(-60 * dir), 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- EdaRobot gait: Flapping
|
||||
//-- Parameters:
|
||||
//-- steps: Number of steps
|
||||
//-- T: Period
|
||||
//-- h: height (Values between 10 - 30)
|
||||
//-- dir: direction: FOREWARD, BACKWARD
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::Flapping(float steps, int period, int height, int dir) {
|
||||
int A[SERVO_COUNT] = {12, 12, height, height, 0, 0};
|
||||
int O[SERVO_COUNT] = {
|
||||
0, 0, height - 10, -height + 10, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
double phase_diff[SERVO_COUNT] = {
|
||||
DEG2RAD(0), DEG2RAD(180), DEG2RAD(-90 * dir), DEG2RAD(90 * dir), 0, 0};
|
||||
|
||||
//-- Let's oscillate the servos!
|
||||
Execute(A, O, period, phase_diff, steps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- 手部动作: 举手
|
||||
//-- Parameters:
|
||||
//-- period: 动作时间
|
||||
//-- dir: 方向 1=左手, -1=右手, 0=双手
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::HandsUp(int period, int dir) {
|
||||
if (!has_hands_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int initial[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
int target[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
|
||||
if (dir == 0) {
|
||||
target[LEFT_HAND] = 170;
|
||||
target[RIGHT_HAND] = 10;
|
||||
} else if (dir == 1) {
|
||||
target[LEFT_HAND] = 170;
|
||||
target[RIGHT_HAND] = servo_[RIGHT_HAND].GetPosition();
|
||||
} else if (dir == -1) {
|
||||
target[RIGHT_HAND] = 10;
|
||||
target[LEFT_HAND] = servo_[LEFT_HAND].GetPosition();
|
||||
}
|
||||
|
||||
MoveServos(period, target);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- 手部动作: 双手放下
|
||||
//-- Parameters:
|
||||
//-- period: 动作时间
|
||||
//-- dir: 方向 1=左手, -1=右手, 0=双手
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::HandsDown(int period, int dir) {
|
||||
if (!has_hands_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int target[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
|
||||
if (dir == 1) {
|
||||
target[RIGHT_HAND] = servo_[RIGHT_HAND].GetPosition();
|
||||
} else if (dir == -1) {
|
||||
target[LEFT_HAND] = servo_[LEFT_HAND].GetPosition();
|
||||
}
|
||||
|
||||
MoveServos(period, target);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- 手部动作: 挥手
|
||||
//-- Parameters:
|
||||
//-- period: 动作周期
|
||||
//-- dir: 方向 LEFT/RIGHT/BOTH
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::HandWave(int period, int dir) {
|
||||
if (!has_hands_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir == BOTH) {
|
||||
HandWaveBoth(period);
|
||||
return;
|
||||
}
|
||||
|
||||
int servo_index = (dir == LEFT) ? LEFT_HAND : RIGHT_HAND;
|
||||
|
||||
int current_positions[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_positions[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_positions[i] = 90;
|
||||
}
|
||||
}
|
||||
|
||||
int position;
|
||||
if (servo_index == LEFT_HAND) {
|
||||
position = 170;
|
||||
} else {
|
||||
position = 10;
|
||||
}
|
||||
|
||||
current_positions[servo_index] = position;
|
||||
MoveServos(300, current_positions);
|
||||
vTaskDelay(pdMS_TO_TICKS(300));
|
||||
|
||||
// 左右摆动5次
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (servo_index == LEFT_HAND) {
|
||||
current_positions[servo_index] = position - 30;
|
||||
MoveServos(period / 10, current_positions);
|
||||
vTaskDelay(pdMS_TO_TICKS(period / 10));
|
||||
current_positions[servo_index] = position + 30;
|
||||
MoveServos(period / 10, current_positions);
|
||||
} else {
|
||||
current_positions[servo_index] = position + 30;
|
||||
MoveServos(period / 10, current_positions);
|
||||
vTaskDelay(pdMS_TO_TICKS(period / 10));
|
||||
current_positions[servo_index] = position - 30;
|
||||
MoveServos(period / 10, current_positions);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(period / 10));
|
||||
}
|
||||
|
||||
if (servo_index == LEFT_HAND) {
|
||||
current_positions[servo_index] = HAND_HOME_POSITION;
|
||||
} else {
|
||||
current_positions[servo_index] = 180 - HAND_HOME_POSITION;
|
||||
}
|
||||
MoveServos(300, current_positions);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//-- 手部动作: 双手同时挥手
|
||||
//-- Parameters:
|
||||
//-- period: 动作周期
|
||||
//---------------------------------------------------------
|
||||
void EdaRobot::HandWaveBoth(int period) {
|
||||
if (!has_hands_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int current_positions[SERVO_COUNT];
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
current_positions[i] = servo_[i].GetPosition();
|
||||
} else {
|
||||
current_positions[i] = 90;
|
||||
}
|
||||
}
|
||||
|
||||
int left_position = 170;
|
||||
int right_position = 10;
|
||||
|
||||
current_positions[LEFT_HAND] = left_position;
|
||||
current_positions[RIGHT_HAND] = right_position;
|
||||
MoveServos(300, current_positions);
|
||||
|
||||
// 左右摆动5次
|
||||
for (int i = 0; i < 5; i++) {
|
||||
// 波浪向左
|
||||
current_positions[LEFT_HAND] = left_position - 30;
|
||||
current_positions[RIGHT_HAND] = right_position + 30;
|
||||
MoveServos(period / 10, current_positions);
|
||||
|
||||
// 波浪向右
|
||||
current_positions[LEFT_HAND] = left_position + 30;
|
||||
current_positions[RIGHT_HAND] = right_position - 30;
|
||||
MoveServos(period / 10, current_positions);
|
||||
}
|
||||
|
||||
current_positions[LEFT_HAND] = HAND_HOME_POSITION;
|
||||
current_positions[RIGHT_HAND] = 180 - HAND_HOME_POSITION;
|
||||
MoveServos(300, current_positions);
|
||||
}
|
||||
|
||||
void EdaRobot::EnableServoLimit(int diff_limit) {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].SetLimiter(diff_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EdaRobot::DisableServoLimit() {
|
||||
for (int i = 0; i < SERVO_COUNT; i++) {
|
||||
if (servo_pins_[i] != -1) {
|
||||
servo_[i].DisableLimiter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef __EDA_ROBOT_MAX_MOVEMENTS_H__
|
||||
#define __EDA_ROBOT_MAX_MOVEMENTS_H__
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "oscillator.h"
|
||||
|
||||
//-- Constants
|
||||
#define FORWARD 1
|
||||
#define BACKWARD -1
|
||||
#define LEFT 1
|
||||
#define RIGHT -1
|
||||
#define BOTH 0
|
||||
#define SMALL 5
|
||||
#define MEDIUM 15
|
||||
#define BIG 30
|
||||
|
||||
// -- Servo delta limit default. degree / sec
|
||||
#define SERVO_LIMIT_DEFAULT 240
|
||||
|
||||
// -- Servo indexes for easy access
|
||||
#define LEFT_LEG 0
|
||||
#define RIGHT_LEG 1
|
||||
#define LEFT_FOOT 2
|
||||
#define RIGHT_FOOT 3
|
||||
#define LEFT_HAND 4
|
||||
#define RIGHT_HAND 5
|
||||
#define SERVO_COUNT 6
|
||||
|
||||
class EdaRobot {
|
||||
public:
|
||||
EdaRobot();
|
||||
~EdaRobot();
|
||||
|
||||
//-- EdaRobot initialization
|
||||
void Init(int left_leg, int right_leg, int left_foot, int right_foot, int left_hand = -1,
|
||||
int right_hand = -1);
|
||||
//-- Attach & detach functions
|
||||
void AttachServos();
|
||||
void DetachServos();
|
||||
|
||||
//-- Oscillator Trims
|
||||
void SetTrims(int left_leg, int right_leg, int left_foot, int right_foot, int left_hand = 0,
|
||||
int right_hand = 0);
|
||||
|
||||
//-- Predetermined Motion Functions
|
||||
void MoveServos(int time, int servo_target[]);
|
||||
void MoveSingle(int position, int servo_number);
|
||||
void OscillateServos(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT], int period,
|
||||
double phase_diff[SERVO_COUNT], float cycle);
|
||||
|
||||
//-- HOME = EdaRobot at rest position
|
||||
void Home(bool hands_down = true);
|
||||
bool GetRestState();
|
||||
void SetRestState(bool state);
|
||||
|
||||
//-- Predetermined Motion Functions
|
||||
void Jump(float steps = 1, int period = 2000);
|
||||
|
||||
void Walk(float steps = 4, int period = 1000, int dir = FORWARD, int amount = 0);
|
||||
void Turn(float steps = 4, int period = 2000, int dir = LEFT, int amount = 0);
|
||||
void Bend(int steps = 1, int period = 1400, int dir = LEFT);
|
||||
void ShakeLeg(int steps = 1, int period = 2000, int dir = RIGHT);
|
||||
|
||||
void UpDown(float steps = 1, int period = 1000, int height = 20);
|
||||
void Swing(float steps = 1, int period = 1000, int height = 20);
|
||||
void TiptoeSwing(float steps = 1, int period = 900, int height = 20);
|
||||
void Jitter(float steps = 1, int period = 500, int height = 20);
|
||||
void AscendingTurn(float steps = 1, int period = 900, int height = 20);
|
||||
|
||||
void Moonwalker(float steps = 1, int period = 900, int height = 20, int dir = LEFT);
|
||||
void Crusaito(float steps = 1, int period = 900, int height = 20, int dir = FORWARD);
|
||||
void Flapping(float steps = 1, int period = 1000, int height = 20, int dir = FORWARD);
|
||||
|
||||
// -- 手部动作
|
||||
void HandsUp(int period = 1000, int dir = 0); // 双手举起
|
||||
void HandsDown(int period = 1000, int dir = 0); // 双手放下
|
||||
void HandWave(int period = 1000, int dir = LEFT); // 挥手
|
||||
void HandWaveBoth(int period = 1000); // 双手同时挥手
|
||||
|
||||
// -- Servo limiter
|
||||
void EnableServoLimit(int speed_limit_degree_per_sec = SERVO_LIMIT_DEFAULT);
|
||||
void DisableServoLimit();
|
||||
|
||||
private:
|
||||
Oscillator servo_[SERVO_COUNT];
|
||||
|
||||
int servo_pins_[SERVO_COUNT];
|
||||
int servo_trim_[SERVO_COUNT];
|
||||
|
||||
unsigned long final_time_;
|
||||
unsigned long partial_time_;
|
||||
float increment_[SERVO_COUNT];
|
||||
|
||||
bool is_edarobot_resting_;
|
||||
bool has_hands_; // 是否有手部舵机
|
||||
|
||||
void Execute(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT], int period,
|
||||
double phase_diff[SERVO_COUNT], float steps);
|
||||
};
|
||||
|
||||
#endif // __EDA_ROBOT_MAX_MOVEMENTS_H__
|
||||
161
main/boards/lceda-course-examples/eda-super-bear/oscillator.cc
Normal file
161
main/boards/lceda-course-examples/eda-super-bear/oscillator.cc
Normal file
@@ -0,0 +1,161 @@
|
||||
//--------------------------------------------------------------
|
||||
//-- Oscillator.pde
|
||||
//-- Generate sinusoidal oscillations in the servos
|
||||
//--------------------------------------------------------------
|
||||
//-- (c) Juan Gonzalez-Gomez (Obijuan), Dec 2011
|
||||
//-- (c) txp666 for esp32, 202503
|
||||
//-- GPL license
|
||||
//--------------------------------------------------------------
|
||||
#include "oscillator.h"
|
||||
|
||||
#include <driver/ledc.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
static const char* TAG = "Oscillator";
|
||||
|
||||
extern unsigned long IRAM_ATTR millis();
|
||||
|
||||
static ledc_channel_t next_free_channel = LEDC_CHANNEL_0;
|
||||
|
||||
Oscillator::Oscillator(int trim) {
|
||||
trim_ = trim;
|
||||
diff_limit_ = 0;
|
||||
is_attached_ = false;
|
||||
|
||||
sampling_period_ = 30;
|
||||
period_ = 2000;
|
||||
number_samples_ = period_ / sampling_period_;
|
||||
inc_ = 2 * M_PI / number_samples_;
|
||||
|
||||
amplitude_ = 45;
|
||||
phase_ = 0;
|
||||
phase0_ = 0;
|
||||
offset_ = 0;
|
||||
stop_ = false;
|
||||
rev_ = false;
|
||||
|
||||
pos_ = 90;
|
||||
previous_millis_ = 0;
|
||||
}
|
||||
|
||||
Oscillator::~Oscillator() {
|
||||
Detach();
|
||||
}
|
||||
|
||||
uint32_t Oscillator::AngleToCompare(int angle) {
|
||||
return (angle - SERVO_MIN_DEGREE) * (SERVO_MAX_PULSEWIDTH_US - SERVO_MIN_PULSEWIDTH_US) /
|
||||
(SERVO_MAX_DEGREE - SERVO_MIN_DEGREE) +
|
||||
SERVO_MIN_PULSEWIDTH_US;
|
||||
}
|
||||
|
||||
bool Oscillator::NextSample() {
|
||||
current_millis_ = millis();
|
||||
|
||||
if (current_millis_ - previous_millis_ > sampling_period_) {
|
||||
previous_millis_ = current_millis_;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Oscillator::Attach(int pin, bool rev) {
|
||||
if (is_attached_) {
|
||||
Detach();
|
||||
}
|
||||
|
||||
pin_ = pin;
|
||||
rev_ = rev;
|
||||
|
||||
ledc_timer_config_t ledc_timer = {.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.duty_resolution = LEDC_TIMER_13_BIT,
|
||||
.timer_num = LEDC_TIMER_1,
|
||||
.freq_hz = 50,
|
||||
.clk_cfg = LEDC_AUTO_CLK};
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
static int last_channel = 0;
|
||||
last_channel = (last_channel + 1) % 7 + 1;
|
||||
ledc_channel_ = (ledc_channel_t)last_channel;
|
||||
|
||||
ledc_channel_config_t ledc_channel = {.gpio_num = pin_,
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.channel = ledc_channel_,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.timer_sel = LEDC_TIMER_1,
|
||||
.duty = 0,
|
||||
.hpoint = 0};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
ledc_speed_mode_ = LEDC_LOW_SPEED_MODE;
|
||||
|
||||
// pos_ = 90;
|
||||
// Write(pos_);
|
||||
previous_servo_command_millis_ = millis();
|
||||
|
||||
is_attached_ = true;
|
||||
}
|
||||
|
||||
void Oscillator::Detach() {
|
||||
if (!is_attached_)
|
||||
return;
|
||||
|
||||
ESP_ERROR_CHECK(ledc_stop(ledc_speed_mode_, ledc_channel_, 0));
|
||||
|
||||
is_attached_ = false;
|
||||
}
|
||||
|
||||
void Oscillator::SetT(unsigned int T) {
|
||||
period_ = T;
|
||||
|
||||
number_samples_ = period_ / sampling_period_;
|
||||
inc_ = 2 * M_PI / number_samples_;
|
||||
}
|
||||
|
||||
void Oscillator::SetPosition(int position) {
|
||||
Write(position);
|
||||
}
|
||||
|
||||
void Oscillator::Refresh() {
|
||||
if (NextSample()) {
|
||||
if (!stop_) {
|
||||
int pos = std::round(amplitude_ * std::sin(phase_ + phase0_) + offset_);
|
||||
if (rev_)
|
||||
pos = -pos;
|
||||
Write(pos + 90);
|
||||
}
|
||||
|
||||
phase_ = phase_ + inc_;
|
||||
}
|
||||
}
|
||||
|
||||
void Oscillator::Write(int position) {
|
||||
if (!is_attached_)
|
||||
return;
|
||||
|
||||
long currentMillis = millis();
|
||||
if (diff_limit_ > 0) {
|
||||
int limit = std::max(
|
||||
1, (((int)(currentMillis - previous_servo_command_millis_)) * diff_limit_) / 1000);
|
||||
if (abs(position - pos_) > limit) {
|
||||
pos_ += position < pos_ ? -limit : limit;
|
||||
} else {
|
||||
pos_ = position;
|
||||
}
|
||||
} else {
|
||||
pos_ = position;
|
||||
}
|
||||
previous_servo_command_millis_ = currentMillis;
|
||||
|
||||
int angle = pos_ + trim_;
|
||||
|
||||
angle = std::min(std::max(angle, 0), 180);
|
||||
|
||||
uint32_t duty = (uint32_t)(((angle / 180.0) * 2.0 + 0.5) * 8191 / 20.0);
|
||||
|
||||
ESP_ERROR_CHECK(ledc_set_duty(ledc_speed_mode_, ledc_channel_, duty));
|
||||
ESP_ERROR_CHECK(ledc_update_duty(ledc_speed_mode_, ledc_channel_));
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//--------------------------------------------------------------
|
||||
//-- Oscillator.pde
|
||||
//-- Generate sinusoidal oscillations in the servos
|
||||
//--------------------------------------------------------------
|
||||
//-- (c) Juan Gonzalez-Gomez (Obijuan), Dec 2011
|
||||
//-- (c) txp666 for esp32, 202503
|
||||
//-- GPL license
|
||||
//--------------------------------------------------------------
|
||||
#ifndef __OSCILLATOR_H__
|
||||
#define __OSCILLATOR_H__
|
||||
|
||||
#include "driver/ledc.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#define M_PI 3.14159265358979323846
|
||||
|
||||
#ifndef DEG2RAD
|
||||
#define DEG2RAD(g) ((g) * M_PI) / 180
|
||||
#endif
|
||||
|
||||
#define SERVO_MIN_PULSEWIDTH_US 500 // 最小脉宽(微秒)
|
||||
#define SERVO_MAX_PULSEWIDTH_US 2500 // 最大脉宽(微秒)
|
||||
#define SERVO_MIN_DEGREE -90 // 最小角度
|
||||
#define SERVO_MAX_DEGREE 90 // 最大角度
|
||||
#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000 // 1MHz, 1us per tick
|
||||
#define SERVO_TIMEBASE_PERIOD 20000 // 20000 ticks, 20ms
|
||||
|
||||
class Oscillator {
|
||||
public:
|
||||
Oscillator(int trim = 0);
|
||||
~Oscillator();
|
||||
void Attach(int pin, bool rev = false);
|
||||
void Detach();
|
||||
|
||||
void SetA(unsigned int amplitude) { amplitude_ = amplitude; };
|
||||
void SetO(int offset) { offset_ = offset; };
|
||||
void SetPh(double Ph) { phase0_ = Ph; };
|
||||
void SetT(unsigned int period);
|
||||
void SetTrim(int trim) { trim_ = trim; };
|
||||
void SetLimiter(int diff_limit) { diff_limit_ = diff_limit; };
|
||||
void DisableLimiter() { diff_limit_ = 0; };
|
||||
int GetTrim() { return trim_; };
|
||||
void SetPosition(int position);
|
||||
void Stop() { stop_ = true; };
|
||||
void Play() { stop_ = false; };
|
||||
void Reset() { phase_ = 0; };
|
||||
void Refresh();
|
||||
int GetPosition() { return pos_; }
|
||||
|
||||
private:
|
||||
bool NextSample();
|
||||
void Write(int position);
|
||||
uint32_t AngleToCompare(int angle);
|
||||
|
||||
private:
|
||||
bool is_attached_;
|
||||
|
||||
//-- Oscillators parameters
|
||||
unsigned int amplitude_; //-- Amplitude (degrees)
|
||||
int offset_; //-- Offset (degrees)
|
||||
unsigned int period_; //-- Period (miliseconds)
|
||||
double phase0_; //-- Phase (radians)
|
||||
|
||||
//-- Internal variables
|
||||
int pos_; //-- Current servo pos
|
||||
int pin_; //-- Pin where the servo is connected
|
||||
int trim_; //-- Calibration offset
|
||||
double phase_; //-- Current phase
|
||||
double inc_; //-- Increment of phase
|
||||
double number_samples_; //-- Number of samples
|
||||
unsigned int sampling_period_; //-- sampling period (ms)
|
||||
|
||||
long previous_millis_;
|
||||
long current_millis_;
|
||||
|
||||
//-- Oscillation mode. If true, the servo is stopped
|
||||
bool stop_;
|
||||
|
||||
//-- Reverse mode
|
||||
bool rev_;
|
||||
|
||||
int diff_limit_;
|
||||
long previous_servo_command_millis_;
|
||||
|
||||
ledc_channel_t ledc_channel_;
|
||||
ledc_mode_t ledc_speed_mode_;
|
||||
};
|
||||
|
||||
#endif // __OSCILLATOR_H__
|
||||
13
main/boards/lceda-course-examples/eda-tv-pro/README.md
Normal file
13
main/boards/lceda-course-examples/eda-tv-pro/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## EDA-TV-Pro 小电视
|
||||
|
||||
### 项目文档
|
||||
- [EDA-TV-Pro 项目文档](https://wiki.lceda.cn/zh-hans/course-projects/smart-internet/tv-pro/tv-pro-introduce.html)
|
||||
|
||||
### 编译配置
|
||||
该项目采用的开发板Flash容量为8MB,编译时须配置:
|
||||
|
||||
```
|
||||
Partition Table --->
|
||||
Partition Table (Custom partition table CSV) --->
|
||||
(partitions/v2/8m.csv) Custom partition CSV file
|
||||
```
|
||||
35
main/boards/lceda-course-examples/eda-tv-pro/config.h
Normal file
35
main/boards/lceda-course-examples/eda-tv-pro/config.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
#include <driver/gpio.h>
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_I2S_METHOD_SIMPLEX
|
||||
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_4
|
||||
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_5
|
||||
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_6
|
||||
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_7
|
||||
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_15
|
||||
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_16
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define TOUCH_BUTTON_GPIO GPIO_NUM_40
|
||||
#define ASR_BUTTON_GPIO GPIO_NUM_21
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_2
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_38
|
||||
#define DISPLAY_CS_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_MOSI_PIN GPIO_NUM_11
|
||||
#define DISPLAY_CLK_PIN GPIO_NUM_12
|
||||
#define DISPLAY_DC_PIN GPIO_NUM_47
|
||||
#define DISPLAY_RST_PIN GPIO_NUM_48
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 2
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
11
main/boards/lceda-course-examples/eda-tv-pro/config.json
Normal file
11
main/boards/lceda-course-examples/eda-tv-pro/config.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "eda-tv-pro",
|
||||
"sdkconfig_append": [
|
||||
"LCD_ST7789_240X240_7PIN=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
122
main/boards/lceda-course-examples/eda-tv-pro/eda-tv-pro.cc
Normal file
122
main/boards/lceda-course-examples/eda-tv-pro/eda-tv-pro.cc
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "wifi_board.h"
|
||||
#include "codecs/no_audio_codec.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "system_reset.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "led/single_led.h"
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <driver/spi_common.h>
|
||||
|
||||
#define TAG "eda_tv_pro"
|
||||
|
||||
class eda_tv_pro : public WifiBoard {
|
||||
private:
|
||||
|
||||
Button boot_button_;
|
||||
LcdDisplay* display_;
|
||||
|
||||
void InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.sclk_io_num = DISPLAY_CLK_PIN;
|
||||
buscfg.quadwp_io_num = GPIO_NUM_NC;
|
||||
buscfg.quadhd_io_num = GPIO_NUM_NC;
|
||||
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void InitializeLcdDisplay() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = DISPLAY_CS_PIN;
|
||||
io_config.dc_gpio_num = DISPLAY_DC_PIN;
|
||||
io_config.spi_mode = DISPLAY_SPI_MODE;
|
||||
io_config.pclk_hz = 40 * 1000 * 1000;
|
||||
io_config.trans_queue_depth = 10;
|
||||
io_config.lcd_cmd_bits = 8;
|
||||
io_config.lcd_param_bits = 8;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
|
||||
|
||||
// 初始化液晶屏驱动芯片
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
|
||||
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting) {
|
||||
EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
eda_tv_pro() :
|
||||
boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
InitializeButtons();
|
||||
if (DISPLAY_BACKLIGHT_PIN != GPIO_NUM_NC) {
|
||||
GetBacklight()->RestoreBrightness();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
static SingleLed led(BUILTIN_LED_GPIO);
|
||||
return &led;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
#ifdef AUDIO_I2S_METHOD_SIMPLEX
|
||||
static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN);
|
||||
#else
|
||||
static NoAudioCodecDuplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN);
|
||||
#endif
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual Backlight* GetBacklight() override {
|
||||
if (DISPLAY_BACKLIGHT_PIN != GPIO_NUM_NC) {
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
return &backlight;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(eda_tv_pro);
|
||||
Reference in New Issue
Block a user