From 48dfbeedea0c4439a9e39869fdca6cbdf57f45e5 Mon Sep 17 00:00:00 2001 From: kirl <18827922006@163.com> Date: Fri, 31 Jul 2026 22:23:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=8A=93=E5=8F=96=E6=94=BE?= =?UTF-8?q?=E7=BD=AE=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/actuator/actuator.c | 3 + components/web_ctrl/web_ctrl.c | 466 +++------------------------------ 2 files changed, 38 insertions(+), 431 deletions(-) diff --git a/components/actuator/actuator.c b/components/actuator/actuator.c index 15e4662..0b537df 100644 --- a/components/actuator/actuator.c +++ b/components/actuator/actuator.c @@ -48,6 +48,9 @@ void actuator_init(void) /* 初始化舵机 PWM */ servo_init(ACTUATOR_SERVO_GPIO); + /* 配置舵机限位: 抬起=80°, 放下=160° */ + servo_set_limits(80.0f, 160.0f); + /* 配置 4 路电机: 闭环位置模式 + 使能 */ const uint8_t addrs[] = { ACTUATOR_ADDR_ANGLE, ACTUATOR_ADDR_X, diff --git a/components/web_ctrl/web_ctrl.c b/components/web_ctrl/web_ctrl.c index 198db93..ad0dbd7 100644 --- a/components/web_ctrl/web_ctrl.c +++ b/components/web_ctrl/web_ctrl.c @@ -8,7 +8,6 @@ #include "magnet.h" #include "servo.h" #include "protocol.h" -#include "actuator.h" #include #include @@ -118,65 +117,38 @@ button:active{opacity:.7}
-
-

轴控制 (XYZA)

-
→电机2 (CW) 0~210
-
→电机3+4 (CCW) 0~335
-
→电机1 (±: CW/CCW)
-
°
-
RPM
-
-
- - -
-
-

电机使能

-
+
- - + +

速度模式

-
+
RPM
- - + +

位置模式

-
+
RPM
- - -
-
- -
-

角度直控 (°)

-
-
° (3200=360°)
-
RPM
-
-
-
- - + +
@@ -202,56 +174,6 @@ button:active{opacity:.7}
-
-

位置清零 & 堵转解除

-
-
- - -
-
- -
-

读取参数

-
-
- -
-
- -
-

回零控制

-
-
-
-
RPM
-
ms
-
RPM
-
mA
-
ms
-
-
-
- - -
-
- - -
-
-
@@ -286,41 +208,6 @@ function api(cmd){ l.innerHTML='
请求失败
'+l.innerHTML }) } -function apiEach(ep,id,ext){ - var addrs=v(id).split('|'); - for(var i=0;i'; - h+=' (raw: '+d.val32+' / 0x'+d.val32.toString(16).toUpperCase()+')'; - } - if(d.val16!==undefined && d.angle===undefined){ - h+='
'+d.val16+' (16bit)'; - } - h+='
raw: '+d.hex+''; - document.getElementById('rp_out').innerHTML = h; - }).catch(function(e){ - document.getElementById('rp_out').innerHTML='读取失败' - }); -} fetch('/api/status').then(function(r){return r.json()}).then(function(d){ document.getElementById('wifi').innerHTML='WiFi: '+d.ssid; document.getElementById('ip').textContent='IP: '+d.ip @@ -449,44 +336,38 @@ static int get_query_int(httpd_req_t *req, const char *key, int def) return def; } -static float get_query_float(httpd_req_t *req, const char *key, float def) -{ - char buf[128]; - size_t len = httpd_req_get_url_query_len(req) + 1; - if (len > sizeof(buf)) len = sizeof(buf); - if (httpd_req_get_url_query_str(req, buf, len) == ESP_OK) { - char val[16]; - if (httpd_query_key_value(buf, key, val, sizeof(val)) == ESP_OK) { - return (float)atof(val); - } - } - return def; -} - /* ============================================================ * HTTP 处理器 * ============================================================ */ static esp_err_t root_handler(httpd_req_t *req) { + const char *html = INDEX_HTML; + size_t total = strlen(html); + size_t offset = 0; + httpd_resp_set_type(req, "text/html; charset=utf-8"); httpd_resp_set_hdr(req, "Connection", "close"); - /* 一次性发送, 由 ESP-IDF 内部处理 TCP 缓冲管理, - * 避免手动分块导致的 EAGAIN / ECONNRESET 偶发失败 */ - esp_err_t err = httpd_resp_send(req, INDEX_HTML, HTTPD_RESP_USE_STRLEN); - if (err != ESP_OK) { - /* 客户端断开是正常现象 (刷新页面/关闭标签), 仅警告即可 */ - ESP_LOGW(TAG, "发送 HTML 失败: %d (客户端可能已断开)", err); + while (offset < total) { + size_t chunk = total - offset; + if (chunk > 1024) chunk = 1024; + esp_err_t err = httpd_resp_send_chunk(req, html + offset, (int)chunk); + if (err != ESP_OK) { + ESP_LOGE(TAG, "发送 HTML 失败: %d", err); + return err; + } + offset += chunk; } - return err; + httpd_resp_send_chunk(req, NULL, 0); /* 结束分块 */ + return ESP_OK; } static esp_err_t api_enable_handler(httpd_req_t *req) { int addr = get_query_int(req, "addr", 1); int state = get_query_int(req, "state", 0); - actuator_motor_enable((uint8_t)addr, (bool)state); + Emm_V5_En_Control((uint8_t)addr, (bool)state, false); char resp[64]; snprintf(resp, sizeof(resp), "电机 %d %s", addr, state ? "使能" : "关闭"); httpd_resp_set_type(req, "text/plain; charset=utf-8"); @@ -500,7 +381,7 @@ static esp_err_t api_velocity_handler(httpd_req_t *req) int dir = get_query_int(req, "dir", 0); int vel = get_query_int(req, "vel", 100); int acc = get_query_int(req, "acc", 10); - actuator_motor_vel((uint8_t)addr, (uint8_t)dir, (uint16_t)vel, (uint8_t)acc); + Emm_V5_Vel_Control((uint8_t)addr, (uint8_t)dir, (uint16_t)vel, (uint8_t)acc, false); char resp[64]; snprintf(resp, sizeof(resp), "速度模式: 地址%d %s %dRPM acc=%d", addr, dir ? "CCW" : "CW", vel, acc); @@ -517,8 +398,8 @@ static esp_err_t api_position_handler(httpd_req_t *req) int acc = get_query_int(req, "acc", 10); int clk = get_query_int(req, "clk", 2000); int raf = get_query_int(req, "raf", 0); - actuator_motor_pos((uint8_t)addr, (uint8_t)dir, (uint16_t)vel, (uint8_t)acc, - (uint32_t)clk, (bool)raf); + Emm_V5_Pos_Control((uint8_t)addr, (uint8_t)dir, (uint16_t)vel, (uint8_t)acc, + (uint32_t)clk, (bool)raf, false); char resp[80]; snprintf(resp, sizeof(resp), "位置模式: 地址%d %s %dRPM %d脉冲 %s", addr, dir ? "CCW" : "CW", vel, clk, raf ? "绝对" : "相对"); @@ -530,7 +411,7 @@ static esp_err_t api_position_handler(httpd_req_t *req) static esp_err_t api_stop_handler(httpd_req_t *req) { int addr = get_query_int(req, "addr", 1); - actuator_motor_stop((uint8_t)addr); + Emm_V5_Stop_Now((uint8_t)addr, false); char resp[32]; snprintf(resp, sizeof(resp), "电机 %d 已停止", addr); httpd_resp_set_type(req, "text/plain; charset=utf-8"); @@ -574,9 +455,9 @@ static esp_err_t api_servo_handler(httpd_req_t *req) char val[16]; if (httpd_query_key_value(buf, "pos", val, sizeof(val)) == ESP_OK) { if (strcmp(val, "up") == 0) { - actuator_servo_up(); + servo_set_up(); } else { - actuator_servo_down(); + servo_set_down(); } char resp[32]; snprintf(resp, sizeof(resp), "舵机: %s", val); @@ -586,7 +467,7 @@ static esp_err_t api_servo_handler(httpd_req_t *req) } if (httpd_query_key_value(buf, "angle", val, sizeof(val)) == ESP_OK) { float angle = atof(val); - actuator_servo_set_angle(angle); + servo_set_angle(angle); char resp[32]; snprintf(resp, sizeof(resp), "舵机: %.1f°", (double)angle); httpd_resp_set_type(req, "text/plain; charset=utf-8"); @@ -598,214 +479,6 @@ static esp_err_t api_servo_handler(httpd_req_t *req) return ESP_FAIL; } -/* ---- 轴联动: XYZA 同时驱动 (mm + °) ---- */ -static esp_err_t api_axis_handler(httpd_req_t *req) -{ - float x_mm = get_query_float(req, "x", 0.0f); - float y_mm = get_query_float(req, "y", 0.0f); - float z_deg = get_query_float(req, "zrot", 0.0f); - float s_ang = get_query_float(req, "servo", -1.0f); - int vel = get_query_int(req, "vel", ACTUATOR_DEFAULT_VEL); - int acc = get_query_int(req, "acc", ACTUATOR_DEFAULT_ACC); - - /* 允许 0 值: 绝对模式下 0=回到原点 */ - actuator_motor_mm(ACTUATOR_ADDR_X, x_mm, (uint16_t)vel, (uint8_t)acc, true); - actuator_motor_mm(ACTUATOR_ADDR_Y1, y_mm, (uint16_t)vel, (uint8_t)acc, true); - actuator_motor_angle(ACTUATOR_ADDR_ANGLE, z_deg, (uint16_t)vel, (uint8_t)acc, true); - if (s_ang >= 0.0f) - actuator_servo_set_angle(s_ang); - - char resp[128]; - snprintf(resp, sizeof(resp), - "轴联动: X=%.1fmm Y=%.1fmm Z=%.1f° Servo=%.1f° vel=%d acc=%d", - (double)x_mm, (double)y_mm, (double)z_deg, (double)s_ang, vel, acc); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 直线距离控制: mm → 脉冲 (丝杆导程 8mm) ---- */ -static esp_err_t api_mm_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 2); /* 默认 X 轴 */ - float mm = get_query_float(req, "mm", 0.0f); - int vel = get_query_int(req, "vel", ACTUATOR_DEFAULT_VEL); - int acc = get_query_int(req, "acc", ACTUATOR_DEFAULT_ACC); - int raf = get_query_int(req, "raf", 1); /* 默认绝对运动 */ - - actuator_motor_mm((uint8_t)addr, mm, (uint16_t)vel, (uint8_t)acc, (bool)raf); - - uint32_t clk = ACTUATOR_MM_TO_PULSES(mm); - char resp[96]; - snprintf(resp, sizeof(resp), - "直线模式: 地址%d %.2fmm → %lu脉冲 %s vel=%dRPM acc=%d", - addr, (double)mm, (unsigned long)clk, raf ? "绝对" : "相对", vel, acc); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ============================================================ - * 底层调试 API (直接对接 Emm_V5, 不走 actuator) - * ============================================================ */ - -/* ---- 当前位置清零 ---- */ -static esp_err_t api_reset_pos_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - Emm_V5_Reset_CurPos_To_Zero((uint8_t)addr); - char resp[32]; - snprintf(resp, sizeof(resp), "电机 %d 位置已清零", addr); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 解除堵转保护 ---- */ -static esp_err_t api_reset_clog_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - Emm_V5_Reset_Clog_Pro((uint8_t)addr); - char resp[32]; - snprintf(resp, sizeof(resp), "电机 %d 堵转保护已解除", addr); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 读取系统参数 (按 Emm 固件手册 V1.0.5 帧格式解析) ---- */ -/* 帧格式: {addr, cmd, sign(00/01), data[4]BE, 0x6B} */ -/* 位置类(Emm): val32=0~65535→角度=val32*360/65536 */ -/* 速度类: val16=RPM */ -static esp_err_t api_read_param_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - int param = get_query_int(req, "param", 0); /* EmmSysParams_t */ - - Emm_V5_Read_Sys_Params((uint8_t)addr, (EmmSysParams_t)param); - - uint8_t rx[128]; - uint8_t len = Emm_V5_Receive_Data(rx, sizeof(rx)); - - /* 解析: 跳过 addr(1)+cmd(1)+sign(1), 取 data[4] 大端 */ - uint32_t val32 = 0; - uint16_t val16 = 0; - int sign = 0; - float angle = 0.0f; - - if (len >= 8) { - sign = rx[2]; - val32 = ((uint32_t)rx[3] << 24) | ((uint32_t)rx[4] << 16) | - ((uint32_t)rx[5] << 8) | ((uint32_t)rx[6]); - /* Emm 固件: 0~65535 表示 0~360°, 仅低 16 位有效 */ - uint16_t pos16 = (uint16_t)(val32 & 0xFFFF); - angle = (sign ? -1.0f : 1.0f) * pos16 * 360.0f / 65536.0f; - } - if (len >= 6) { - val16 = ((uint16_t)rx[3] << 8) | ((uint16_t)rx[4]); - } - - char json[512]; - int off = snprintf(json, sizeof(json), - "{\"addr\":%d,\"param\":%d,\"len\":%d,\"sign\":%d" - ",\"val32\":%lu,\"val16\":%u,\"angle\":%.2f", - addr, param, len, sign, - (unsigned long)val32, (unsigned)val16, (double)angle); - off += snprintf(json + off, sizeof(json) - off, ",\"hex\":\""); - for (int i = 0; i < len && off < (int)sizeof(json) - 4; i++) { - off += snprintf(json + off, sizeof(json) - off, "%02X", rx[i]); - } - off += snprintf(json + off, sizeof(json) - off, "\"}"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 角度直控: 输入 ° 自动换算为 Emm 脉冲 (3200脉冲=360°) ---- */ -static esp_err_t api_angle_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - float angle = get_query_float(req, "angle", 0.0f); - int vel = get_query_int(req, "vel", ACTUATOR_DEFAULT_VEL); - int acc = get_query_int(req, "acc", ACTUATOR_DEFAULT_ACC); - int raf = get_query_int(req, "raf", 1); /* 默认绝对运动 */ - - actuator_motor_angle((uint8_t)addr, angle, (uint16_t)vel, (uint8_t)acc, (bool)raf); - - uint32_t clk = EMM_V5_DEG_TO_CLK(angle); - char resp[96]; - snprintf(resp, sizeof(resp), - "角度模式: 地址%d %.1f° → %lu脉冲 %s vel=%dRPM acc=%d", - addr, (double)angle, (unsigned long)clk, raf ? "绝对" : "相对", vel, acc); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 回零: 设置零点 ---- */ -static esp_err_t api_origin_set_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - int svf = get_query_int(req, "svf", 0); /* 0=不存储 1=存储 */ - Emm_V5_Origin_Set_O((uint8_t)addr, (bool)svf); - char resp[48]; - snprintf(resp, sizeof(resp), "电机 %d 零点已设置 (svF=%d)", addr, svf); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 回零: 配置参数 ---- */ -static esp_err_t api_origin_config_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - int svf = get_query_int(req, "svf", 0); - int omode = get_query_int(req, "omode", 0); - int odir = get_query_int(req, "odir", 0); - int ovel = get_query_int(req, "ovel", 200); - int otm = get_query_int(req, "otm", 10000); - int slvel = get_query_int(req, "slvel", 100); - int slma = get_query_int(req, "slma", 500); - int slms = get_query_int(req, "slms", 100); - int potf = get_query_int(req, "potf", 0); - - Emm_V5_Origin_Modify_Params((uint8_t)addr, (bool)svf, - (uint8_t)omode, (uint8_t)odir, (uint16_t)ovel, (uint32_t)otm, - (uint16_t)slvel, (uint16_t)slma, (uint16_t)slms, (bool)potf); - char resp[128]; - snprintf(resp, sizeof(resp), - "电机 %d 回零参数已配置 (mode=%d dir=%d vel=%d tm=%lu svF=%d potF=%d)", - addr, omode, odir, ovel, (unsigned long)otm, svf, potf); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 回零: 触发回零 ---- */ -static esp_err_t api_origin_go_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - int omode = get_query_int(req, "omode", 0); - Emm_V5_Origin_Trigger_Return((uint8_t)addr, (uint8_t)omode, false); - char resp[48]; - snprintf(resp, sizeof(resp), "电机 %d 回零已触发 (mode=%d)", addr, omode); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - -/* ---- 回零: 强制中断 ---- */ -static esp_err_t api_origin_stop_handler(httpd_req_t *req) -{ - int addr = get_query_int(req, "addr", 1); - Emm_V5_Origin_Interrupt((uint8_t)addr); - char resp[32]; - snprintf(resp, sizeof(resp), "电机 %d 回零已中断", addr); - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - return ESP_OK; -} - /* favicon — 204 No Content, 避免浏览器反复请求 */ static esp_err_t favicon_handler(httpd_req_t *req) { @@ -836,21 +509,13 @@ void edc_proto_on_cycle(const edc_cycle_t *cycle, edc_channel_t ch) cycle->cycle_id, ch == EDC_CH_I2C ? "I2C" : "UART", cycle->frag_count); - /* I2C / UART 冗余下发, 同一 cycle_id 只执行一次 */ + /* I2C / UART 冗余下发, 同一 cycle_id 只回一次 DONE */ if (cycle->cycle_id == s_last_done_id) { return; } s_last_done_id = cycle->cycle_id; - /* 通信控制链路暂时断开, 仅 Web 手控生效 */ -#if 0 /* TODO: 恢复通信控制时取消注释 */ - /* 执行 Cycle 运动 */ - if (actuator_execute_cycle(cycle) == 0) { - /* 回复 MPU: Cycle 执行完成 */ - edc_proto_send_done(cycle->cycle_id); - } -#endif - /* 通信断开期间仍回复 DONE 以维持协议握手 (但不执行运动) */ + /* 回复 MPU: Cycle 已收到 (后续实际执行电机后应移到执行完毕处) */ edc_proto_send_done(cycle->cycle_id); } @@ -937,7 +602,7 @@ static void http_server_init(void) httpd_handle_t server = NULL; httpd_config_t cfg = HTTPD_DEFAULT_CONFIG(); cfg.stack_size = 8192; /* 默认 4096 不够, 增大 */ - cfg.max_uri_handlers = 26; + cfg.max_uri_handlers = 16; cfg.max_open_sockets = 4; cfg.lru_purge_enable = true; @@ -1007,67 +672,6 @@ static void http_server_init(void) }; httpd_register_uri_handler(server, &uri_servo); - httpd_uri_t uri_axis = { - .uri = "/api/axis", .method = HTTP_GET, - .handler = api_axis_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_axis); - - httpd_uri_t uri_mm = { - .uri = "/api/mm", .method = HTTP_GET, - .handler = api_mm_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_mm); - - /* ---- 底层调试 API (直接 Emm_V5) ---- */ - httpd_uri_t uri_reset_pos = { - .uri = "/api/reset_pos", .method = HTTP_GET, - .handler = api_reset_pos_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_reset_pos); - - httpd_uri_t uri_reset_clog = { - .uri = "/api/reset_clog", .method = HTTP_GET, - .handler = api_reset_clog_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_reset_clog); - - httpd_uri_t uri_read_param = { - .uri = "/api/read_param", .method = HTTP_GET, - .handler = api_read_param_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_read_param); - - httpd_uri_t uri_angle = { - .uri = "/api/angle", .method = HTTP_GET, - .handler = api_angle_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_angle); - - httpd_uri_t uri_origin_set = { - .uri = "/api/origin_set", .method = HTTP_GET, - .handler = api_origin_set_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_origin_set); - - httpd_uri_t uri_origin_config = { - .uri = "/api/origin_config", .method = HTTP_GET, - .handler = api_origin_config_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_origin_config); - - httpd_uri_t uri_origin_go = { - .uri = "/api/origin_go", .method = HTTP_GET, - .handler = api_origin_go_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_origin_go); - - httpd_uri_t uri_origin_stop = { - .uri = "/api/origin_stop", .method = HTTP_GET, - .handler = api_origin_stop_handler, .user_ctx = NULL - }; - httpd_register_uri_handler(server, &uri_origin_stop); - /* favicon.ico — 返回空, 避免 404 警告 */ httpd_uri_t uri_favicon = { .uri = "/favicon.ico",