Add stall-based pingpong return mode and calibration improvements

- Add MIN_PWM_PERCENT (20%) to ensure motor starts reliably
- Add CURRENT_CALIBRATION factor (0.33) for accurate current readings
- Add stall-based return option for pingpong mode (returns on stall instead of time)
- Update stall detection: 7A threshold, 1000ms confirm time
- Add DISABLE_STALL_DETECT config option
- Keep last speed setting when motor stops (UI improvement)
- Update webserver UI with 'Return on stall only' checkbox
This commit is contained in:
devdesk
2026-02-05 17:50:22 +02:00
parent ba27db4f55
commit 7a6617f9c0
4 changed files with 95 additions and 24 deletions

View File

@@ -52,7 +52,7 @@ void MotorController::setDirection(int dir) {
}
void MotorController::stop() {
_speed = 0;
// Don't reset _speed - keep last speed setting
_direction = 0;
_stalled = false;
_stallStartTime = 0;
@@ -117,8 +117,14 @@ void MotorController::setStallCallback(void (*callback)(float current)) {
}
void MotorController::applyMotorState() {
// Apply minimum PWM when motor is running
int effectiveSpeed = _speed;
if (_speed > 0 && _speed < MIN_PWM_PERCENT) {
effectiveSpeed = MIN_PWM_PERCENT;
}
// Convert percentage to 8-bit PWM value
int pwmValue = map(_speed, 0, 100, 0, 255);
int pwmValue = map(effectiveSpeed, 0, 100, 0, 255);
if (_direction == 0 || _speed == 0) {
// Stop - both PWM outputs off
@@ -151,6 +157,9 @@ float MotorController::readCurrentSense(int pin) {
// Therefore: I_load = V * CURRENT_SENSE_RATIO / R_sense
float current = (voltage * CURRENT_SENSE_RATIO) / SENSE_RESISTOR;
// Apply calibration factor (adjust for real-world measurement differences)
current *= CURRENT_CALIBRATION;
return current;
#else
return 0.0f;
@@ -158,26 +167,31 @@ float MotorController::readCurrentSense(int pin) {
}
// Pingpong mode implementation
void MotorController::startPingpong(int speed, int timeMs, int speedRandomPercent, int timeRandomPercent) {
void MotorController::startPingpong(int speed, int timeMs, int speedRandomPercent, int timeRandomPercent, bool useStallReturn) {
_pingpongBaseSpeed = constrain(speed, 0, 100);
_pingpongBaseTime = constrain(timeMs, 100, 30000);
_pingpongSpeedRandomPercent = constrain(speedRandomPercent, 0, 100);
_pingpongTimeRandomPercent = constrain(timeRandomPercent, 0, 100);
_pingpongUseStallReturn = useStallReturn;
_pingpongDirection = 1;
_pingpongCurrentSpeed = applyRandomness(_pingpongBaseSpeed, _pingpongSpeedRandomPercent);
_pingpongCurrentTime = applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
// Time randomness disabled when using stall return
_pingpongCurrentTime = _pingpongUseStallReturn ? _pingpongBaseTime : applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
_pingpongLastSwitch = millis();
_pingpongActive = true;
_stalled = false; // Reset stall state when starting pingpong
_stallStartTime = 0;
// Apply initial state
_speed = _pingpongCurrentSpeed;
_direction = _pingpongDirection;
applyMotorState();
Serial.printf("Pingpong started: speed=%d%% (base=%d, rand=%d%%), time=%dms (base=%d, rand=%d%%)\n",
Serial.printf("Pingpong started: speed=%d%% (base=%d, rand=%d%%), time=%dms (base=%d, rand=%d%%), stallReturn=%s\n",
_pingpongCurrentSpeed, _pingpongBaseSpeed, _pingpongSpeedRandomPercent,
_pingpongCurrentTime, _pingpongBaseTime, _pingpongTimeRandomPercent);
_pingpongCurrentTime, _pingpongBaseTime, _pingpongTimeRandomPercent,
_pingpongUseStallReturn ? "true" : "false");
}
void MotorController::stopPingpong() {
@@ -206,17 +220,41 @@ int MotorController::getPingpongTimeRandom() {
return _pingpongTimeRandomPercent;
}
bool MotorController::getPingpongStallReturn() {
return _pingpongUseStallReturn;
}
void MotorController::updatePingpong() {
if (!_pingpongActive) return;
bool shouldSwitch = false;
unsigned long now = millis();
if ((now - _pingpongLastSwitch) >= (unsigned long)_pingpongCurrentTime) {
// Time to switch direction
if (_pingpongUseStallReturn) {
// Switch direction only when stall is detected
if (_stalled) {
shouldSwitch = true;
Serial.println("Pingpong: stall detected, switching direction");
}
} else {
// Time-based switching
if ((now - _pingpongLastSwitch) >= (unsigned long)_pingpongCurrentTime) {
shouldSwitch = true;
}
}
if (shouldSwitch) {
// Switch direction
_pingpongDirection = -_pingpongDirection;
// Reset stall state for new direction
_stalled = false;
_stallStartTime = 0;
// Apply randomness for next cycle
_pingpongCurrentSpeed = applyRandomness(_pingpongBaseSpeed, _pingpongSpeedRandomPercent);
_pingpongCurrentTime = applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
// Time randomness disabled when using stall return
_pingpongCurrentTime = _pingpongUseStallReturn ? _pingpongBaseTime : applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
_pingpongLastSwitch = now;
// Apply new state
@@ -224,8 +262,9 @@ void MotorController::updatePingpong() {
_direction = _pingpongDirection;
applyMotorState();
Serial.printf("Pingpong switch: dir=%d, speed=%d%%, next_time=%dms\n",
_pingpongDirection, _pingpongCurrentSpeed, _pingpongCurrentTime);
Serial.printf("Pingpong switch: dir=%d, speed=%d%%, next_time=%dms, stallReturn=%s\n",
_pingpongDirection, _pingpongCurrentSpeed, _pingpongCurrentTime,
_pingpongUseStallReturn ? "true" : "false");
}
}