feat: gyro recalibration button in web UI (#32)
Add 'G' CDC command that disarms and re-runs gyro bias calibration. safety_refresh() added to calibration loop (every 40ms) so IWDG does not trip during the 1s blocking re-cal when watchdog is running. GYRO CAL button in ui/index.html sends 'G' and shows status feedback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f867956b43
commit
bd30e2b40d
@ -6,6 +6,7 @@ volatile uint8_t cdc_streaming = 1; /* auto-stream */
|
|||||||
static volatile uint8_t cdc_port_open = 0; /* set when host asserts DTR */
|
static volatile uint8_t cdc_port_open = 0; /* set when host asserts DTR */
|
||||||
volatile uint8_t cdc_arm_request = 0; /* set by A command */
|
volatile uint8_t cdc_arm_request = 0; /* set by A command */
|
||||||
volatile uint8_t cdc_disarm_request = 0; /* set by D command */
|
volatile uint8_t cdc_disarm_request = 0; /* set by D command */
|
||||||
|
volatile uint8_t cdc_recal_request = 0; /* set by G command — gyro recalibration */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PID tuning command buffer.
|
* PID tuning command buffer.
|
||||||
@ -138,6 +139,7 @@ static int8_t CDC_Receive(uint8_t *buf, uint32_t *len) {
|
|||||||
case 'S': cdc_streaming = !cdc_streaming; break;
|
case 'S': cdc_streaming = !cdc_streaming; break;
|
||||||
case 'A': cdc_arm_request = 1; break;
|
case 'A': cdc_arm_request = 1; break;
|
||||||
case 'D': cdc_disarm_request = 1; break;
|
case 'D': cdc_disarm_request = 1; break;
|
||||||
|
case 'G': cdc_recal_request = 1; break; /* gyro recalibration */
|
||||||
case 'R': request_bootloader(); break; /* never returns */
|
case 'R': request_bootloader(); break; /* never returns */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
extern volatile uint8_t cdc_streaming; /* set by S command in CDC RX */
|
extern volatile uint8_t cdc_streaming; /* set by S command in CDC RX */
|
||||||
extern volatile uint8_t cdc_arm_request; /* set by A command */
|
extern volatile uint8_t cdc_arm_request; /* set by A command */
|
||||||
extern volatile uint8_t cdc_disarm_request; /* set by D command */
|
extern volatile uint8_t cdc_disarm_request; /* set by D command */
|
||||||
|
extern volatile uint8_t cdc_recal_request; /* set by G command */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apply a PID tuning command string from the USB terminal.
|
* Apply a PID tuning command string from the USB terminal.
|
||||||
@ -207,6 +208,14 @@ int main(void) {
|
|||||||
balance_disarm(&bal);
|
balance_disarm(&bal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Gyro recalibration — disarm first, then re-sample bias (~1s blocked) */
|
||||||
|
if (cdc_recal_request) {
|
||||||
|
cdc_recal_request = 0;
|
||||||
|
safety_arm_cancel();
|
||||||
|
balance_disarm(&bal);
|
||||||
|
if (imu_ret == 0) mpu6000_calibrate();
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle PID tuning commands from USB (P/I/D/T/M/?) */
|
/* Handle PID tuning commands from USB (P/I/D/T/M/?) */
|
||||||
if (cdc_cmd_ready) {
|
if (cdc_cmd_ready) {
|
||||||
cdc_cmd_ready = 0;
|
cdc_cmd_ready = 0;
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include "mpu6000.h"
|
#include "mpu6000.h"
|
||||||
#include "icm42688.h"
|
#include "icm42688.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "safety.h"
|
||||||
#include "stm32f7xx_hal.h"
|
#include "stm32f7xx_hal.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
@ -67,6 +68,8 @@ void mpu6000_calibrate(void) {
|
|||||||
sum_gy += raw.gy;
|
sum_gy += raw.gy;
|
||||||
sum_gz += raw.gz;
|
sum_gz += raw.gz;
|
||||||
HAL_Delay(1);
|
HAL_Delay(1);
|
||||||
|
/* Refresh IWDG every 40ms — safe during re-cal with watchdog running */
|
||||||
|
if (i % 40 == 39) safety_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
s_bias_gx = (float)sum_gx / GYRO_CAL_SAMPLES;
|
s_bias_gx = (float)sum_gx / GYRO_CAL_SAMPLES;
|
||||||
|
|||||||
@ -42,7 +42,7 @@ void safety_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void safety_refresh(void) {
|
void safety_refresh(void) {
|
||||||
HAL_IWDG_Refresh(&hiwdg);
|
if (hiwdg.Instance) HAL_IWDG_Refresh(&hiwdg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool safety_rc_alive(uint32_t now) {
|
bool safety_rc_alive(uint32_t now) {
|
||||||
|
|||||||
@ -26,6 +26,8 @@
|
|||||||
#arm-btn.armed { background: #ff2222; }
|
#arm-btn.armed { background: #ff2222; }
|
||||||
#dfu-btn { background: #555; display: none; }
|
#dfu-btn { background: #555; display: none; }
|
||||||
#dfu-btn:hover { background: #777; }
|
#dfu-btn:hover { background: #777; }
|
||||||
|
#gyrocal-btn { background: #1a4a6a; display: none; }
|
||||||
|
#gyrocal-btn:hover { background: #2a6a9a; }
|
||||||
#status { margin-top: 8px; font-size: 12px; color: #666; }
|
#status { margin-top: 8px; font-size: 12px; color: #666; }
|
||||||
#state-badge {
|
#state-badge {
|
||||||
display: inline-block; padding: 2px 8px; border-radius: 3px;
|
display: inline-block; padding: 2px 8px; border-radius: 3px;
|
||||||
@ -62,6 +64,7 @@
|
|||||||
<button class="btn" id="arm-btn" onclick="toggleArm()">ARM</button>
|
<button class="btn" id="arm-btn" onclick="toggleArm()">ARM</button>
|
||||||
<button class="btn" id="dfu-btn" onclick="enterDFU()">DFU</button>
|
<button class="btn" id="dfu-btn" onclick="enterDFU()">DFU</button>
|
||||||
<button class="btn" id="yaw-btn" onclick="resetYaw()" style="background:#335533;display:none">YAW RESET</button>
|
<button class="btn" id="yaw-btn" onclick="resetYaw()" style="background:#335533;display:none">YAW RESET</button>
|
||||||
|
<button class="btn" id="gyrocal-btn" onclick="gyroRecal()">GYRO CAL</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="status">WebSerial ready</div>
|
<div id="status">WebSerial ready</div>
|
||||||
</div>
|
</div>
|
||||||
@ -315,6 +318,7 @@ window.toggleSerial = async function() {
|
|||||||
document.getElementById('arm-btn').style.display = 'none';
|
document.getElementById('arm-btn').style.display = 'none';
|
||||||
document.getElementById('dfu-btn').style.display = 'none';
|
document.getElementById('dfu-btn').style.display = 'none';
|
||||||
document.getElementById('yaw-btn').style.display = 'none';
|
document.getElementById('yaw-btn').style.display = 'none';
|
||||||
|
document.getElementById('gyrocal-btn').style.display = 'none';
|
||||||
document.getElementById('status').textContent = 'Disconnected';
|
document.getElementById('status').textContent = 'Disconnected';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -329,6 +333,7 @@ window.toggleSerial = async function() {
|
|||||||
document.getElementById('arm-btn').style.display = 'inline-block';
|
document.getElementById('arm-btn').style.display = 'inline-block';
|
||||||
document.getElementById('dfu-btn').style.display = 'inline-block';
|
document.getElementById('dfu-btn').style.display = 'inline-block';
|
||||||
document.getElementById('yaw-btn').style.display = 'inline-block';
|
document.getElementById('yaw-btn').style.display = 'inline-block';
|
||||||
|
document.getElementById('gyrocal-btn').style.display = 'inline-block';
|
||||||
document.getElementById('status').textContent = 'Connected — streaming';
|
document.getElementById('status').textContent = 'Connected — streaming';
|
||||||
|
|
||||||
readLoop();
|
readLoop();
|
||||||
@ -353,6 +358,11 @@ window.enterDFU = async function() {
|
|||||||
document.getElementById('status').textContent = 'Rebooting to DFU...';
|
document.getElementById('status').textContent = 'Rebooting to DFU...';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.gyroRecal = async function() {
|
||||||
|
await sendCmd('G');
|
||||||
|
document.getElementById('status').textContent = 'Calibrating gyro — hold robot still for 1s...';
|
||||||
|
};
|
||||||
|
|
||||||
async function readLoop() {
|
async function readLoop() {
|
||||||
const decoder = new TextDecoderStream();
|
const decoder = new TextDecoderStream();
|
||||||
port.readable.pipeTo(decoder.writable);
|
port.readable.pipeTo(decoder.writable);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user