Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: AI VOX Board Build
name: AI VOX3 Build

on:
push:
Expand All @@ -17,8 +17,7 @@ jobs:
AI_VOX_VERSION: "v1.3.0"
strategy:
matrix:
example: ["ai_vox_board/ai_vox_engine_led_mcp", "ai_vox_board/ai_vox_engine_motordriver_mcp","ai_vox_board/ai_vox_engine_servo_mcp","ai_vox_board/ai_vox_engine_ws2812b_mcp",
"ai_vox3/ai_vox_engine_dht11_servo_led_mcp","ai_vox3/ai_vox_engine_led_mcp","ai_vox3/ai_vox_engine_motor_fan_mcp","ai_vox3/ai_vox_engine_motordriver_mcp",
example: ["ai_vox3/ai_vox_engine_dht11_servo_led_mcp","ai_vox3/ai_vox_engine_led_mcp","ai_vox3/ai_vox_engine_motor_fan_mcp","ai_vox3/ai_vox_engine_motordriver_mcp",
"ai_vox3/ai_vox_engine_servo_mcp","ai_vox3/ai_vox_engine_us04_dht11_photosensitive_mcp","ai_vox3/ai_vox_engine_ws2812b_mcp"]
core_version: ["3.2.0"]
fqbn: ["esp32:esp32:esp32s3"]
Expand Down Expand Up @@ -50,11 +49,11 @@ jobs:
run: |
${ARDUINO_CLI_DIR}/arduino-cli --config-dir ${ARDUINO_CLI_CONFIG_DIR} lib install "lvgl@9.2.2"

- name: install lib FastLED
- name: install lib Adafruit NeoPixel
run: |
${ARDUINO_CLI_DIR}/arduino-cli --config-dir ${ARDUINO_CLI_CONFIG_DIR} lib install "FastLED@3.10.3"
${ARDUINO_CLI_DIR}/arduino-cli --config-dir ${ARDUINO_CLI_CONFIG_DIR} lib install "Adafruit NeoPixel@1.15.2"

- name: install lib DHT_sensor_library
- name: install lib DHT sensor library
run: |
${ARDUINO_CLI_DIR}/arduino-cli --config-dir ${ARDUINO_CLI_CONFIG_DIR} lib install "DHT sensor library@1.4.6"

Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/arduino_esp32_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ jobs:
run: |
${ARDUINO_CLI_DIR}/arduino-cli --config-dir ${ARDUINO_CLI_CONFIG_DIR} lib install "lvgl@9.2.2"
- name: install lib FastLED
run: |
${ARDUINO_CLI_DIR}/arduino-cli --config-dir ${ARDUINO_CLI_CONFIG_DIR} lib install "FastLED@3.10.3"
- name: install lib DHT_sensor_library
run: |
${ARDUINO_CLI_DIR}/arduino-cli --config-dir ${ARDUINO_CLI_CONFIG_DIR} lib install "DHT sensor library@1.4.6"
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/platform_io_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ jobs:
target: [
"esp32_display_lcd",
"esp32_display_oled",
"ai_vox_board_led",
"ai_vox_board_motordriver",
"ai_vox_board_servo",
"ai_vox_board_ws2812b",
"ai_vox3_dht11_servo_led",
"ai_vox3_led",
"ai_vox3_motor_fan",
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# AI VOX engine extend examples

[![Arduino ESP32 Build](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/arduino_esp32_build.yml/badge.svg)](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/arduino_esp32_build.yml)
[![AI VOX3 Build](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/ai_vox3_build.yml/badge.svg)](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/ai_vox3_build.yml)
[![PlatformIO Build](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/platform_io_build.yml/badge.svg)](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/platform_io_build.yml)
[![PlatformIO Full Build](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/platform_io_full_build.yml/badge.svg)](https://github.com/nulllaborg/ai_vox_engine_extend_examples/actions/workflows/platform_io_full_build.yml)

## 概述

Arduino IDE版小智AI 扩展示例
Expand Down
67 changes: 17 additions & 50 deletions ai_vox3/ai_vox_engine_dht11_servo_led_mcp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "network_config_mode_mp3.h"
#include "network_connected_mp3.h"
#include "notification_0_mp3.h"
#include "servo.h"

#ifndef ARDUINO_ESP32S3_DEV
#error "This example only supports ESP32S3-Dev board."
Expand Down Expand Up @@ -90,21 +91,18 @@ std::unique_ptr<Display> g_display;
auto g_observer = std::make_shared<ai_vox::Observer>();
button_handle_t g_button_boot_handle = nullptr;

constexpr uint8_t kServoFrequency = 50;
constexpr uint8_t kServoResolution = 12;
constexpr uint32_t kMinPulse = 500;
constexpr uint32_t kMaxPulse = 2500;
constexpr uint16_t kMaxServoAngle = 180;

constexpr uint32_t kMaxPwmDuty = pow(2, kServoResolution) - 1;
constexpr uint32_t kPwmFactor = 1000 * 1000 / kServoFrequency;

constexpr uint8_t kLcdBacklightChannel = 7;
constexpr uint32_t kDisplayBacklightFrequency = 1000;
constexpr uint8_t kDisplayBacklightResolution = 8;

std::vector<uint16_t> g_servo_angles(kServoPins.size(), 90);

std::vector<std::unique_ptr<em::Servo>> g_servos;

uint8_t g_display_brightness = 255;

DHT g_dht11(kDht11Pin, DHT11);
Expand Down Expand Up @@ -534,47 +532,22 @@ void InitMcpTools() {
);
}

uint32_t CalculateDuty(const uint16_t angle) {
// Map the angle to the pulse width(500-2500μs)
const uint32_t pulse_width = map(angle, 0, kMaxServoAngle, kMinPulse, kMaxPulse);
return static_cast<uint32_t>((pulse_width * kMaxPwmDuty) / kPwmFactor);
}

void InitServos() {
printf("init servos\n");

g_servos.clear();
for (uint8_t i = 0; i < kServoPins.size(); i++) {
if (!ledcAttach(kServoPins[i], kServoFrequency, kServoResolution)) {
printf("Error: Failed to attach servo %" PRIu8 " on pin%" PRIu8 " .\n", i, kServoPins[i]);
continue;
}
auto servo = std::make_unique<em::Servo>(kServoPins[i], 0, kMaxServoAngle, kMinPulse, kMaxPulse);

if (!ledcWrite(kServoPins[i], CalculateDuty(g_servo_angles[i]))) {
printf("Error: Failed to set initial duty for servo %" PRIu8 " .\n", i);
if (!servo->Init()) {
printf("Error: Failed to init servo %" PRIu8 " on pin %" PRIu16 "\n", i, kServoPins[i]);
continue;
}
}
}

bool SetServoAngle(const uint8_t servo_index, const uint16_t angle) {
if (servo_index < 1 || servo_index > kServoPins.size()) {
printf("Error: Invalid servo index: %" PRIu8 ", valid range: 1-%" PRIu8 " .\n", servo_index, kServoPins.size());
return false;
}

if (angle > kMaxServoAngle) {
printf("Error: Invalid servo angle: %" PRIu16 " for index: %" PRIu8 " .\n", angle, servo_index);
return false;
}
servo->Write(g_servo_angles[i]);

if (!ledcWrite(kServoPins[servo_index - 1], CalculateDuty(angle))) {
printf("Error: Failed to write duty for servo %" PRIu8 " .\n", servo_index);
return false;
g_servos.push_back(std::move(servo));
}

g_servo_angles[servo_index - 1] = angle;

return true;
}

} // namespace
Expand Down Expand Up @@ -771,11 +744,10 @@ void loop() {
}

printf("on mcp tool call: self.servo.set_one_servo, index: %" PRId64 ", angle: %" PRId64 "\n", *index_ptr, *angle_ptr);
if (SetServoAngle(static_cast<uint8_t>(*index_ptr), static_cast<uint16_t>(*angle_ptr))) {
engine.SendMcpCallResponse(mcp_tool_call_event->id, true);
} else {
engine.SendMcpCallError(mcp_tool_call_event->id, "Failed to set servo angle for index: " + std::to_string(*index_ptr));
}
g_servos[*index_ptr - 1]->Write(static_cast<uint16_t>(*angle_ptr));
g_servo_angles[*index_ptr - 1] = *angle_ptr;

engine.SendMcpCallResponse(mcp_tool_call_event->id, true);

} else if ("self.servo.set_range_servos" == mcp_tool_call_event->name) {
const auto start_index_ptr = mcp_tool_call_event->param<int64_t>("start_index");
Expand Down Expand Up @@ -820,16 +792,11 @@ void loop() {
*end_index_ptr,
*angle_ptr);

uint8_t index = 0;
for (index = *start_index_ptr; index <= *end_index_ptr; index++) {
if (!SetServoAngle(index, static_cast<uint16_t>(*angle_ptr))) {
engine.SendMcpCallError(mcp_tool_call_event->id, "Failed to set servo angle for index: " + std::to_string(index));
break;
}
}
if (index > *end_index_ptr) {
engine.SendMcpCallResponse(mcp_tool_call_event->id, true);
for (uint8_t index = *start_index_ptr - 1; index < *end_index_ptr; index++) {
g_servos[index]->Write(static_cast<uint16_t>(*angle_ptr));
g_servo_angles[index - 1] = *angle_ptr;
}
engine.SendMcpCallResponse(mcp_tool_call_event->id, true);

} else if ("self.servo.get_index_servo_angle" == mcp_tool_call_event->name) {
const auto index_ptr = mcp_tool_call_event->param<int64_t>("index");
Expand Down
51 changes: 51 additions & 0 deletions ai_vox3/ai_vox_engine_dht11_servo_led_mcp/servo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @file servo.cpp
*/

#include "servo.h"

#include <Arduino.h>
#include <stdio.h>

#include <cmath>

namespace em {
namespace {
constexpr uint8_t kPwmResolution = 12;
constexpr uint8_t kPwmFrequency = 50;
constexpr uint16_t kMaxAngle = 360;
constexpr int16_t kMaxPwmDuty = pow(2, kPwmResolution) - 1;
constexpr double kPwmFactor = 1 / static_cast<double>(kPwmFrequency) * 1000 * 1000;

double Map(const double x, const double min_in, const double max_in, const double min_out, const double max_out) {
if (x <= min_in) {
return min_out;
} else if (x >= max_in) {
return max_out;
}

return (x - min_in) / (max_in - min_in) * (max_out - min_out) + min_out;
}

} // namespace
Servo::Servo(
const uint8_t pin, const uint16_t min_angle, const uint16_t max_angle, const uint32_t min_pulse_width_us, const uint32_t max_pulse_width_us)
: pin_(pin),
min_angle_(min_angle),
max_angle_(std::min(max_angle, kMaxAngle)),
min_pulse_width_us_(min_pulse_width_us),
max_pulse_width_us_(max_pulse_width_us) {
}

bool Servo::Init() {
return ledcAttach(pin_, kPwmFrequency, kPwmResolution);
}

void Servo::Write(const uint16_t angle) {
WriteMicroseconds(Map(angle, min_angle_, max_angle_, min_pulse_width_us_, max_pulse_width_us_));
}

void Servo::WriteMicroseconds(const double us) {
ledcWrite(pin_, us * kMaxPwmDuty / kPwmFactor);
}
} // namespace em
98 changes: 98 additions & 0 deletions ai_vox3/ai_vox_engine_dht11_servo_led_mcp/servo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#pragma once

#ifndef _EM_SERVO_H_
#define _EM_SERVO_H_

/**
* @file servo.h
*/

#include <stdint.h>

namespace em {

/**
* @~Chinese
* @class Servo
* @brief EspServo类用于控制舵机相关操作。该类仅支持ESP32平台。
*/
/**
* @~English
* @class Servo This class is used to control a servo motor and is supported only on the ESP32 platform.
* @brief The Servo class is used for servo control related operations.
*/
class Servo {
public:
/**
* @~Chinese
* @brief 构造函数,初始化伺服电机的引脚和角度范围。
* @param pin 伺服电机连接的GPIO引脚。
* @param min_angle 最小角度,默认为0度。
* @param max_angle 最大角度,默认为180度。
* @param min_pulse_width_us 最小脉冲宽度(微秒),默认为500微秒。
* @param max_pulse_width_us 最大脉冲宽度(微秒),默认为2500微秒。
*/
/**
* @~English
* @brief Constructor, initializes the servo motor's pin and angle range.
* @param pin The GPIO pin connected to the servo motor.
* @param min_angle The minimum angle, default is 0 degrees.
* @param max_angle The maximum angle, default is 180 degrees.
* @param min_pulse_width_us The minimum pulse width (microseconds), default is 500 microseconds.
* @param max_pulse_width_us The maximum pulse width (microseconds), default is 2500 microseconds.
*/
explicit Servo(const uint8_t pin,
const uint16_t min_angle = 0,
const uint16_t max_angle = 180,
const uint32_t min_pulse_width_us = 500,
const uint32_t max_pulse_width_us = 2500);

/**
* @~Chinese
* @brief 初始化。
* @return 如果初始化成功返回true,否则返回false。
*/
/**
* @~English
* @brief Initializes
* @return Returns true if initialization is successful, otherwise returns false.
*/
bool Init();

/**
* @~Chinese
* @brief 将舵机旋转到指定的角度。
* @param angle 要旋转到的角度。
*/
/**
* @~English
* @brief Rotates the servo motor to the specified angle.
* @param angle The angle to rotate to.
*/
void Write(const uint16_t angle);

/**
* @~Chinese
* @brief 以微秒为单位设置舵机的脉冲宽度。
* @param us 脉冲宽度(微秒)。
*/
/**
* @~English
* @brief Sets the pulse width of the servo in microseconds.
* @param us Pulse width (microseconds).
*/
void WriteMicroseconds(const double us);

private:
Servo(const Servo&) = delete;
Servo& operator=(const Servo&) = delete;

const uint8_t pin_;
const uint16_t min_angle_;
const uint16_t max_angle_;
const uint32_t min_pulse_width_us_;
const uint32_t max_pulse_width_us_;
};
} // namespace em

#endif
Loading