修复协议解析器, 打通与 MPU 的双向通信
- protocol: 修复 edc_cycle_parse_byte 状态机死代码 (switch 恒 return, 收帧逻辑不可达), 并加 LEN 未就绪保护 - protocol: UART 默认引脚改为 GPIO11/12 (UART0) - sdkconfig.defaults: 控制台切至 USB Serial/JTAG, 避免与协议 UART0 冲突 - web_ctrl: 新增协议状态 API 与 Cycle 快照; on_cycle 回 DONE 并按 cycle_id 去重 - main: 接入协议初始化与接收任务, 更新接线注释 - 实测: MPU 每秒下发 Cycle, I2C/UART 双通道 12/12 收, DONE 12/12 回
This commit is contained in:
1 parent
40ee976236
commit
af4f0edc08
9 files changed
+218
-20
No files matched your search
@@ -7,6 +7,7 @@
|
||||
#include "emm_v5.h"
|
||||
#include "magnet.h"
|
||||
#include "servo.h"
|
||||
#include "protocol.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -175,6 +176,20 @@ button:active{opacity:.7}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card" id="proto-card">
|
||||
<h2><i class="tag amber"></i>通讯协议</h2>
|
||||
<div class="row" style="font-size:.82rem">
|
||||
<span>I2C: <b id="pi2c">-</b></span>
|
||||
<span>UART: <b id="puart">-</b></span>
|
||||
<span>通道: <b id="pch">-</b></span>
|
||||
</div>
|
||||
<div id="pcycle" style="font-size:.78rem;color:var(--sub);margin-top:6px">等待 Cycle...</div>
|
||||
<div class="btn-row" style="margin-top:10px">
|
||||
<button class="btn-go" onclick="var c=document.getElementById('pcid');api('proto/done?id='+(c?c.value:0))">Done</button>
|
||||
<button class="btn-stop" onclick="api('proto/request')">Request</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card log-card">
|
||||
<h2><i class="tag green"></i>运行日志</h2>
|
||||
<div class="log" id="log">等待指令...</div>
|
||||
@@ -197,6 +212,30 @@ fetch('/api/status').then(function(r){return r.json()}).then(function(d){
|
||||
document.getElementById('wifi').innerHTML='<i class="dot"></i>WiFi: '+d.ssid;
|
||||
document.getElementById('ip').textContent='IP: '+d.ip
|
||||
})
|
||||
/* 每秒刷新协议状态 */
|
||||
function pollProto(){
|
||||
fetch('/api/proto/status').then(function(r){return r.json()}).then(function(d){
|
||||
document.getElementById('pi2c').textContent = d.i2c_ok + '/' + (d.i2c_ok+d.i2c_err);
|
||||
document.getElementById('puart').textContent = d.uart_ok + '/' + (d.uart_ok+d.uart_err);
|
||||
document.getElementById('pch').textContent = d.last_ch;
|
||||
if(d.cycle){
|
||||
document.getElementById('pcid').value = d.cycle.id;
|
||||
var html = 'Cycle #'+d.cycle.id + ' ('+d.cycle.frags+' frags)<br>';
|
||||
for(var i=0;i<d.cycle.data.length;i++){
|
||||
var f=d.cycle.data[i];
|
||||
html += 'F'+i+': ('+f.sx+','+f.sy+' '+f.sa+'°) → ('+f.tx+','+f.ty+' '+f.ta+'°)<br>';
|
||||
}
|
||||
document.getElementById('pcycle').innerHTML = html;
|
||||
}
|
||||
}).catch(function(){});
|
||||
}
|
||||
/* 隐藏的 cycle_id 存储 */
|
||||
(function(){
|
||||
var inp=document.createElement('input');
|
||||
inp.type='hidden';inp.id='pcid';inp.value='0';
|
||||
document.getElementById('proto-card').appendChild(inp);
|
||||
})();
|
||||
pollProto();setInterval(pollProto,1000);
|
||||
</script>
|
||||
</body>
|
||||
</html>)raw";
|
||||
@@ -448,6 +487,116 @@ static esp_err_t favicon_handler(httpd_req_t *req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 协议状态 API
|
||||
* ============================================================ */
|
||||
|
||||
/* 最近收到的 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;
|
||||
edc_proto_get_stats(&stat);
|
||||
|
||||
char json[1024];
|
||||
int off = snprintf(json, sizeof(json),
|
||||
"{\"i2c_ok\":%lu,\"i2c_err\":%lu,\"uart_ok\":%lu,\"uart_err\":%lu,"
|
||||
"\"last_ch\":\"%s\",\"done\":%lu,\"req\":%lu",
|
||||
stat.i2c.cycles_ok, stat.i2c.cycles_err,
|
||||
stat.uart.cycles_ok, stat.uart.cycles_err,
|
||||
(stat.last_channel == EDC_CH_I2C) ? "I2C" :
|
||||
(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) {
|
||||
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];
|
||||
off += snprintf(json + off, sizeof(json) - off,
|
||||
"%s{\"sx\":%u,\"sy\":%u,\"sa\":%.1f,\"tx\":%u,\"ty\":%u,\"ta\":%.1f}",
|
||||
(i > 0) ? "," : "",
|
||||
f->source.x, f->source.y, f->source.angle_x10 / 10.0f,
|
||||
f->target.x, f->target.y, f->target.angle_x10 / 10.0f);
|
||||
}
|
||||
off += snprintf(json + off, sizeof(json) - off, "]}");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static esp_err_t api_proto_request_handler(httpd_req_t *req)
|
||||
{
|
||||
edc_proto_send_request();
|
||||
httpd_resp_set_type(req, "text/plain; charset=utf-8");
|
||||
httpd_resp_send(req, "已发送 CYCLE_REQUEST", HTTPD_RESP_USE_STRLEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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 */
|
||||
if (stat.last_channel != EDC_CH_NONE) {
|
||||
char buf[128];
|
||||
size_t len = httpd_req_get_url_query_len(req) + 1;
|
||||
if (len > sizeof(buf)) len = sizeof(buf);
|
||||
uint16_t cid = 0;
|
||||
if (httpd_req_get_url_query_str(req, buf, len) == ESP_OK) {
|
||||
char val[16];
|
||||
if (httpd_query_key_value(buf, "id", val, sizeof(val)) == ESP_OK) {
|
||||
cid = (uint16_t)atoi(val);
|
||||
}
|
||||
}
|
||||
edc_proto_send_done(cid);
|
||||
char resp[64];
|
||||
snprintf(resp, sizeof(resp), "已发送 CYCLE_DONE (id=%u)", cid);
|
||||
httpd_resp_set_type(req, "text/plain; charset=utf-8");
|
||||
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "尚无收到 Cycle");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static void http_server_init(void)
|
||||
{
|
||||
httpd_handle_t server = NULL;
|
||||
@@ -532,6 +681,31 @@ static void http_server_init(void)
|
||||
};
|
||||
httpd_register_uri_handler(server, &uri_favicon);
|
||||
|
||||
/* ---- 协议状态 API ---- */
|
||||
httpd_uri_t uri_proto_status = {
|
||||
.uri = "/api/proto/status",
|
||||
.method = HTTP_GET,
|
||||
.handler = api_proto_status_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(server, &uri_proto_status);
|
||||
|
||||
httpd_uri_t uri_proto_request = {
|
||||
.uri = "/api/proto/request",
|
||||
.method = HTTP_GET,
|
||||
.handler = api_proto_request_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(server, &uri_proto_request);
|
||||
|
||||
httpd_uri_t uri_proto_done = {
|
||||
.uri = "/api/proto/done",
|
||||
.method = HTTP_GET,
|
||||
.handler = api_proto_done_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(server, &uri_proto_done);
|
||||
|
||||
ESP_LOGI(TAG, "HTTP 服务器已启动 (端口 %d)", cfg.server_port);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user