From 0045c0c3904cc8571635b42d8f6a69d5620dec25 Mon Sep 17 00:00:00 2001 From: kirl <18827922006@163.com> Date: Sat, 1 Aug 2026 02:41:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BB=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 + components/actuator/CMakeLists.txt | 2 +- components/actuator/actuator.c | 166 ++++++++++++++++++++++++----- components/actuator/actuator.h | 9 ++ components/rgb/rgb.c | 14 +++ components/rgb/rgb.h | 19 ++++ components/web_ctrl/CMakeLists.txt | 2 +- components/web_ctrl/web_ctrl.c | 51 ++------- components/web_ctrl/web_ctrl.h | 8 ++ main/main.c | 16 ++- 10 files changed, 216 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58846b4..d841ac7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,3 +4,6 @@ cmake_minimum_required(VERSION 3.22) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(edc-mcu) + +# 调试开关: 注释掉以下行以禁用 WiFi / Web 服务器 +add_compile_definitions(EDC_DEBUG_WEB) diff --git a/components/actuator/CMakeLists.txt b/components/actuator/CMakeLists.txt index f1b60f3..ef72145 100644 --- a/components/actuator/CMakeLists.txt +++ b/components/actuator/CMakeLists.txt @@ -1,3 +1,3 @@ idf_component_register(SRCS "actuator.c" INCLUDE_DIRS "." - REQUIRES emm_v5 servo protocol driver freertos) + REQUIRES emm_v5 servo magnet rgb protocol driver freertos) diff --git a/components/actuator/actuator.c b/components/actuator/actuator.c index 8e2e3c6..ee01e62 100644 --- a/components/actuator/actuator.c +++ b/components/actuator/actuator.c @@ -6,6 +6,8 @@ #include "actuator.h" #include "emm_v5.h" #include "servo.h" +#include "magnet.h" +#include "rgb.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" @@ -36,6 +38,19 @@ static inline void _frame_gap(void) vTaskDelay(pdMS_TO_TICKS(ACTUATOR_FRAME_GAP_MS)); } +/* 内部: 带 RGB 闪烁的延时 (周期 ms, 亮灭各半) */ +static void _wait_flash(uint32_t ms, uint8_t r, uint8_t g, uint8_t b, uint32_t period_ms) +{ + uint32_t half = pdMS_TO_TICKS(period_ms / 2); + int cycles = (int)(ms / period_ms); + for (int i = 0; i < cycles; i++) { + rgb_set(r, g, b); + vTaskDelay(half); + rgb_off(); + vTaskDelay(half); + } +} + /* ================================================================ * 初始化 * ================================================================ */ @@ -49,7 +64,7 @@ void actuator_init(void) servo_init(ACTUATOR_SERVO_GPIO); /* 配置舵机限位: 抬起=80°, 放下=160° */ - servo_set_limits(80.0f, 160.0f); + servo_set_limits(0.0f, 95.0f); /* 上电默认归位到抬起状态 */ servo_set_up(); @@ -105,11 +120,8 @@ static void _actuator_move_abs(uint8_t addr, uint8_t dir, uint32_t pulses, * 内部: Y 轴双电机同步运动 * ================================================================ */ -static void _actuator_move_y_sync(uint32_t pulses_y) +static void _actuator_move_y_sync(uint32_t pulses_y, uint16_t vel, uint8_t acc) { - uint16_t vel = ACTUATOR_DEFAULT_VEL; - uint8_t acc = ACTUATOR_DEFAULT_ACC; - /* Y 轴正向 = CCW (dir=1), 双电机同步 */ _actuator_move_abs(ACTUATOR_ADDR_Y1, ACTUATOR_DIR_Y, pulses_y, vel, acc, true); _frame_gap(); @@ -119,9 +131,9 @@ static void _actuator_move_y_sync(uint32_t pulses_y) /* 广播地址触发一次, 两台电机同时开始运动 */ Emm_V5_Synchronous_motion(0); - ESP_LOGD(TAG, "Y 同步(CCW): addr%d+%d -> %lu pulses (%.2fmm)", + ESP_LOGD(TAG, "Y 同步(CCW): addr%d+%d -> %lu pulses (%.2fmm) vel=%d acc=%d", ACTUATOR_ADDR_Y1, ACTUATOR_ADDR_Y2, pulses_y, - (double)pulses_y / (double)ACTUATOR_SCALE_Y); + (double)pulses_y / (double)ACTUATOR_SCALE_Y, vel, acc); } /* ================================================================ @@ -130,28 +142,83 @@ static void _actuator_move_y_sync(uint32_t pulses_y) static void _actuator_move_fragment(const edc_fragment_t *frag) { - uint16_t vel = ACTUATOR_DEFAULT_VEL; - uint8_t acc = ACTUATOR_DEFAULT_ACC; + /* 固定速度/加速度 */ + uint16_t vel_xy = 1500; + uint8_t acc_xy = 200; + uint16_t vel_angle = 50; + uint8_t acc_angle = 100; + + /* source 坐标 → 脉冲数 */ + uint32_t src_pulses_x = (uint32_t)(frag->source.x * s_scale_x); + uint32_t src_pulses_y = (uint32_t)(frag->source.y * s_scale_y); + uint32_t src_pulses_angle = (uint32_t)(frag->source.angle_x10 * s_scale_angle); /* target 坐标 → 脉冲数 */ - uint32_t pulses_x = (uint32_t)(frag->target.x * s_scale_x); - uint32_t pulses_y = (uint32_t)(frag->target.y * s_scale_y); - uint32_t pulses_angle = (uint32_t)(frag->target.angle_x10 * s_scale_angle); + uint32_t dst_pulses_x = (uint32_t)(frag->target.x * s_scale_x); + uint32_t dst_pulses_y = (uint32_t)(frag->target.y * s_scale_y); + uint32_t dst_pulses_angle = (uint32_t)(frag->target.angle_x10 * s_scale_angle); - ESP_LOGI(TAG, "Fragment: X=%lu Y=%lu Angle=%lu (raw x=%u y=%u a=%u)", - pulses_x, pulses_y, pulses_angle, - frag->target.x, frag->target.y, frag->target.angle_x10); + ESP_LOGI(TAG, "Fragment: SRC(X=%lu Y=%lu A=%lu) → DST(X=%lu Y=%lu A=%lu)", + src_pulses_x, src_pulses_y, src_pulses_angle, + dst_pulses_x, dst_pulses_y, dst_pulses_angle); - /* Angle / Z 旋转轴 (独立, CW) */ - _actuator_move_abs(ACTUATOR_ADDR_ANGLE, ACTUATOR_DIR_ANGLE, pulses_angle, vel, acc, false); + /* ---- 1. 磁铁掉电 ---- */ + magnet_set(false); + + /* ---- 2. 运动到 source 位置 ---- */ + /* Angle / Z 旋转轴 */ + _actuator_move_abs(ACTUATOR_ADDR_ANGLE, ACTUATOR_DIR_ANGLE, + src_pulses_angle, vel_angle, acc_angle, false); _frame_gap(); - /* X 轴 (独立, CW) */ - _actuator_move_abs(ACTUATOR_ADDR_X, ACTUATOR_DIR_X, pulses_x, vel, acc, false); + /* X 轴 */ + _actuator_move_abs(ACTUATOR_ADDR_X, ACTUATOR_DIR_X, + src_pulses_x, vel_xy, acc_xy, false); _frame_gap(); /* Y 轴 (双电机同步) */ - _actuator_move_y_sync(pulses_y); + _actuator_move_y_sync(src_pulses_y, vel_xy, acc_xy); + + /* ---- 3. 等待 2s 运动到位 (红灯快闪) ---- */ + _wait_flash(2000, 128, 0, 0, 200); + + /* ---- 4. 舵机放下 ---- */ + servo_set_down(); + _wait_flash(500, 128, 0, 0, 200); + + /* ---- 5. 磁铁吸合 ---- */ + magnet_set(true); + _wait_flash(500, 128, 0, 0, 200); + + /* ---- 6. 舵机抬起 ---- */ + servo_set_up(); + _wait_flash(500, 128, 0, 0, 200); + + /* ---- 7. 运动到 target 位置 ---- */ + _actuator_move_abs(ACTUATOR_ADDR_ANGLE, ACTUATOR_DIR_ANGLE, + dst_pulses_angle, vel_angle, acc_angle, false); + _frame_gap(); + + _actuator_move_abs(ACTUATOR_ADDR_X, ACTUATOR_DIR_X, + dst_pulses_x, vel_xy, acc_xy, false); + _frame_gap(); + + _actuator_move_y_sync(dst_pulses_y, vel_xy, acc_xy); + + /* ---- 8. 等待 2s 运动到位 (红灯快闪) ---- */ + _wait_flash(2000, 128, 0, 0, 200); + + /* ---- 9. 舵机放下 ---- */ + servo_set_down(); + _wait_flash(500, 128, 0, 0, 200); + + /* ---- 10. 磁铁掉电 ---- */ + magnet_set(false); + _wait_flash(500, 128, 0, 0, 200); + + /* ---- 11. 舵机抬起 ---- */ + servo_set_up(); + _wait_flash(500, 128, 0, 0, 200); } /* ================================================================ @@ -180,16 +247,8 @@ int actuator_execute_cycle(const edc_cycle_t *cycle) for (int i = 0; i < cycle->frag_count; i++) { ESP_LOGI(TAG, "-- Fragment %d/%d --", i + 1, cycle->frag_count); _actuator_move_fragment(&cycle->fragments[i]); - - /* 等待当前片段运动完成 (TODO: 改为轮询到位标志) */ - if (i < cycle->frag_count - 1) { - vTaskDelay(pdMS_TO_TICKS(ACTUATOR_FRAG_DELAY_MS)); - } } - /* 最后一个 fragment 也等待 */ - vTaskDelay(pdMS_TO_TICKS(ACTUATOR_FRAG_DELAY_MS)); - ESP_LOGI(TAG, "=== Cycle #%u 完成 ===", cycle->cycle_id); return 0; } @@ -368,3 +427,54 @@ void actuator_motor_stop(uint8_t addr) ESP_LOGD(TAG, "Y 同步: 电机%d 同停", peer); } } + +/* ================================================================ + * 协议回调: 收到 Cycle → 执行 → DONE + * ================================================================ */ + +static edc_cycle_t s_last_cycle; +static bool s_last_cycle_valid; +static portMUX_TYPE s_cycle_lock = portMUX_INITIALIZER_UNLOCKED; + +const edc_cycle_t *actuator_get_last_cycle(void) +{ + return s_last_cycle_valid ? &s_last_cycle : NULL; +} + +bool actuator_is_last_cycle_valid(void) +{ + return s_last_cycle_valid; +} + +void edc_proto_on_cycle(const edc_cycle_t *cycle, edc_channel_t ch) +{ + static uint16_t s_last_exec_id = 0xFFFF; + + /* 保存快照 */ + portENTER_CRITICAL(&s_cycle_lock); + memcpy(&s_last_cycle, cycle, sizeof(edc_cycle_t)); + s_last_cycle_valid = true; + portEXIT_CRITICAL(&s_cycle_lock); + + ESP_LOGI(TAG, "Cycle #%u via %s, %u frags", + cycle->cycle_id, ch == EDC_CH_I2C ? "I2C" : "UART", + cycle->frag_count); + + /* I2C / UART 冗余下发, 同一 cycle_id 只执行一次 */ + if (cycle->cycle_id == s_last_exec_id) { + ESP_LOGD(TAG, "Cycle #%u 重复, 跳过执行", cycle->cycle_id); + return; + } + s_last_exec_id = cycle->cycle_id; + + /* 执行电机运动 (阻塞, 含拾放全流程) */ + ESP_LOGI(TAG, "开始执行 Cycle #%u ...", cycle->cycle_id); + actuator_execute_cycle(cycle); + + /* 全部 Fragment 执行完成后回复 DONE */ + edc_proto_send_done(cycle->cycle_id); + ESP_LOGI(TAG, "Cycle #%u 执行完毕, DONE 已发送", cycle->cycle_id); + + /* 设置 RGB 状态为 DONE, 主循环将持续绿灯慢闪 */ + rgb_set_state(RGB_STATE_DONE); +} diff --git a/components/actuator/actuator.h b/components/actuator/actuator.h index d6ba47d..f36bae0 100644 --- a/components/actuator/actuator.h +++ b/components/actuator/actuator.h @@ -231,7 +231,16 @@ void actuator_motor_mm(uint8_t addr, float distance_mm, uint16_t vel, * @param addr 电机地址 (1~4) */ void actuator_motor_stop(uint8_t addr); +/** + * @brief 获取最近一次执行的 Cycle 快照 (供外部读取, 如 Web 面板) + * @return 有效时返回指针, 否则 NULL + */ +const edc_cycle_t *actuator_get_last_cycle(void); +/** + * @brief 最近 Cycle 是否有效 + */ +bool actuator_is_last_cycle_valid(void); #ifdef __cplusplus } #endif diff --git a/components/rgb/rgb.c b/components/rgb/rgb.c index c0dd7a9..66c2ada 100644 --- a/components/rgb/rgb.c +++ b/components/rgb/rgb.c @@ -221,3 +221,17 @@ void rgb_off(void) { rgb_transmit(0, 0, 0); } + +/* ── 系统状态 ── */ + +static rgb_state_t s_rgb_state = RGB_STATE_IDLE; + +void rgb_set_state(rgb_state_t state) +{ + s_rgb_state = state; +} + +rgb_state_t rgb_get_state(void) +{ + return s_rgb_state; +} diff --git a/components/rgb/rgb.h b/components/rgb/rgb.h index 03ff8f4..0f88e45 100644 --- a/components/rgb/rgb.h +++ b/components/rgb/rgb.h @@ -42,6 +42,25 @@ void rgb_set_hsv(uint16_t h, uint8_t s, uint8_t v); */ void rgb_off(void); +/* ================================================================ + * 系统状态指示 (供 main 循环读取) + * ================================================================ */ + +typedef enum { + RGB_STATE_IDLE = 0, /* 待机: 黄灯慢闪 */ + RGB_STATE_DONE = 1, /* 完成: 绿灯慢闪 */ +} rgb_state_t; + +/** + * @brief 设置系统指示状态 + */ +void rgb_set_state(rgb_state_t state); + +/** + * @brief 获取当前系统指示状态 + */ +rgb_state_t rgb_get_state(void); + #ifdef __cplusplus } #endif diff --git a/components/web_ctrl/CMakeLists.txt b/components/web_ctrl/CMakeLists.txt index d82679a..43e7747 100644 --- a/components/web_ctrl/CMakeLists.txt +++ b/components/web_ctrl/CMakeLists.txt @@ -1,3 +1,3 @@ idf_component_register(SRCS "web_ctrl.c" INCLUDE_DIRS "." - REQUIRES emm_v5 magnet servo esp_http_server esp_wifi nvs_flash protocol actuator) + REQUIRES emm_v5 magnet servo rgb esp_http_server esp_wifi nvs_flash protocol actuator) diff --git a/components/web_ctrl/web_ctrl.c b/components/web_ctrl/web_ctrl.c index 154caa6..c0ef9ca 100644 --- a/components/web_ctrl/web_ctrl.c +++ b/components/web_ctrl/web_ctrl.c @@ -9,6 +9,7 @@ #include "servo.h" #include "protocol.h" #include "actuator.h" +#include "rgb.h" #include #include @@ -22,6 +23,8 @@ #include "esp_netif.h" #include "nvs_flash.h" +#ifdef EDC_DEBUG_WEB + static const char *TAG = "WEB_CTRL"; /* ---- WiFi 配置 ---- */ @@ -791,37 +794,9 @@ static esp_err_t favicon_handler(httpd_req_t *req) } /* ============================================================ - * 协议状态 API + * 协议状态 API (从 actuator 读取最近 Cycle 快照) * ============================================================ */ -/* 最近收到的 Cycle 快照 (由 edc_proto_on_cycle 更新, web 读取) */ -static edc_cycle_t s_web_cycle; -static volatile bool s_web_cycle_valid; -static portMUX_TYPE s_web_lock = portMUX_INITIALIZER_UNLOCKED; - -void edc_proto_on_cycle(const edc_cycle_t *cycle, edc_channel_t ch) -{ - static uint16_t s_last_done_id = 0xFFFF; - - portENTER_CRITICAL(&s_web_lock); - memcpy(&s_web_cycle, cycle, sizeof(edc_cycle_t)); - s_web_cycle_valid = true; - portEXIT_CRITICAL(&s_web_lock); - - ESP_LOGI(TAG, "Cycle #%u via %s, %u frags", - cycle->cycle_id, ch == EDC_CH_I2C ? "I2C" : "UART", - cycle->frag_count); - - /* I2C / UART 冗余下发, 同一 cycle_id 只回一次 DONE */ - if (cycle->cycle_id == s_last_done_id) { - return; - } - s_last_done_id = cycle->cycle_id; - - /* 回复 MPU: Cycle 已收到 (后续实际执行电机后应移到执行完毕处) */ - edc_proto_send_done(cycle->cycle_id); -} - static esp_err_t api_proto_status_handler(httpd_req_t *req) { edc_proto_stat_t stat; @@ -837,18 +812,13 @@ static esp_err_t api_proto_status_handler(httpd_req_t *req) (stat.last_channel == EDC_CH_UART) ? "UART" : "none", stat.done_sent, stat.request_sent); - portENTER_CRITICAL(&s_web_lock); - bool valid = s_web_cycle_valid; - edc_cycle_t cycle; - if (valid) memcpy(&cycle, &s_web_cycle, sizeof(edc_cycle_t)); - portEXIT_CRITICAL(&s_web_lock); - - if (valid) { + const edc_cycle_t *cycle = actuator_get_last_cycle(); + if (cycle) { off += snprintf(json + off, sizeof(json) - off, ",\"cycle\":{\"id\":%u,\"frags\":%u,\"data\":[", - cycle.cycle_id, cycle.frag_count); - for (int i = 0; i < cycle.frag_count; i++) { - edc_fragment_t *f = &cycle.fragments[i]; + cycle->cycle_id, cycle->frag_count); + for (int i = 0; i < cycle->frag_count; i++) { + const edc_fragment_t *f = &cycle->fragments[i]; off += snprintf(json + off, sizeof(json) - off, "%s{\"sx\":%u,\"sy\":%u,\"sa\":%.1f,\"tx\":%u,\"ty\":%u,\"ta\":%.1f}", (i > 0) ? "," : "", @@ -874,7 +844,6 @@ static esp_err_t api_proto_request_handler(httpd_req_t *req) static esp_err_t api_proto_done_handler(httpd_req_t *req) { - edc_cycle_t cycle; edc_proto_stat_t stat; edc_proto_get_stats(&stat); /* 对最近收到的 cycle 发送 DONE */ @@ -1084,3 +1053,5 @@ void web_ctrl_init(void) wifi_init_sta(); http_server_init(); } + +#endif /* EDC_DEBUG_WEB */ diff --git a/components/web_ctrl/web_ctrl.h b/components/web_ctrl/web_ctrl.h index afa77dd..5078862 100644 --- a/components/web_ctrl/web_ctrl.h +++ b/components/web_ctrl/web_ctrl.h @@ -13,12 +13,20 @@ extern "C" { #endif +#ifdef EDC_DEBUG_WEB + /** * @brief 初始化 WiFi (STA 模式) 并启动 HTTP 服务器 * @note 阻塞直到 WiFi 连接成功, 然后启动 HTTP 服务器 */ void web_ctrl_init(void); +#else /* !EDC_DEBUG_WEB */ + +#define web_ctrl_init() ((void)0) + +#endif /* EDC_DEBUG_WEB */ + #ifdef __cplusplus } #endif diff --git a/main/main.c b/main/main.c index a668c06..c41863c 100644 --- a/main/main.c +++ b/main/main.c @@ -58,13 +58,21 @@ void app_main(void) /* 启动 WiFi + HTTP 网页控制服务器 */ web_ctrl_init(); - /* 系统就绪: 绿灯 (低亮) */ - rgb_set(0, 32, 0); + /* 系统就绪: 黄灯 (低亮) */ + rgb_set(32, 32, 0); + rgb_set_state(RGB_STATE_IDLE); ESP_LOGI(TAG, "系统就绪, 等待网页指令..."); while (1) { - /* 心跳指示: 绿灯慢闪 (低亮) */ - rgb_set(0, 32, 0); + /* 根据系统状态选择闪烁颜色 */ + rgb_state_t st = rgb_get_state(); + if (st == RGB_STATE_DONE) { + /* 绿灯慢闪: Cycle 执行完毕 */ + rgb_set(0, 32, 0); + } else { + /* 黄灯慢闪: 待机 */ + rgb_set(32, 32, 0); + } vTaskDelay(pdMS_TO_TICKS(500)); rgb_off(); vTaskDelay(pdMS_TO_TICKS(500));