reworked Pedal Input

This commit is contained in:
2025-12-03 22:00:56 +01:00
parent a339ceb163
commit a8a3ff2366
4 changed files with 374 additions and 113 deletions

View File

@@ -13,6 +13,12 @@ platform = atmelavr
board = nanoatmega328new
framework = arduino
[env:megaatmega2560]
platform = atmelavr
board = megaatmega2560
framework = arduino
;upload_protocol = custom
;upload_flags =
; -C

View File

@@ -1,151 +1,266 @@
// PedalController.cpp
#include "PedalController.h"
const int minMin = 50; // Minimum allowed raw value for calibration
const int maxMin = 200; // Maximum allowed raw value for calibration
const int minMax = 800; // Minimum allowed maximum value for calibration
const int maxMax = 1000; // Maximum allowed raw value for calibration
const PedalConfig defaultConfig = {
// Basiskonfiguration
static const PedalConfig defaultConfig = {
.minRaw = 0,
.maxRaw = 1023,
.calibrated = false
};
.calibrated = false,
.filterStrength = 8 // 8er Moving Average als Standard
};
PedalController::PedalController(int pin)
: pedalPin(pin)
// Tuning-Konstanten für die Auto-Cal
static const int STABLE_THRESHOLD = 3; // zulässige ADC-Differenz für "stabil"
static const unsigned long STABLE_TIME = 1000; // ms, die der Wert stabil sein muss
static const unsigned long SWEEP_TIME = 3000; // ms für die Sweep-Phase
static const int MIN_RANGE = 50; // minimale Spannweite min/max
static const int SAFETY_MARGIN_LOW = 5; // Puffer unten
static const int SAFETY_MARGIN_HIGH = 5; // Puffer oben
static const uint8_t MIN_FS = 2;
static const uint8_t MAX_FS = 32;
static const uint8_t DEFAULT_FS = 8;
PedalController::PedalController(int analogPin,
int digitalPin,
uint8_t digitalNotPressedLevel)
: analogPin(analogPin),
digitalPin(digitalPin),
useDigital(digitalPin >= 0),
digitalNotPressedLevel(digitalNotPressedLevel ? 1 : 0),
filteredValue(-1),
calState(IDLE),
calStep(0),
stepStartTime(0),
lastChangeTime(0),
lastSample(0),
minSeen(1023),
maxSeen(0)
{
if (useDigital)
{
pinMode(digitalPin, INPUT); // Hall-Module haben normalerweise eigenen Pullup
}
loadDefaults();
pinMode(pedalPin, INPUT);
}
void PedalController::loadDefaults()
// Rohwert lesen und glätten
int PedalController::readRaw()
{
config = defaultConfig;
int raw = analogRead(analogPin);
return applySmoothing(raw);
}
int PedalController::applySmoothing(int rawValue)
int PedalController::applySmoothing(int raw)
{
filteredValue = (filteredValue * (config.filterStrength - 1) + rawValue) / config.filterStrength;
return filteredValue;
uint8_t fs = config.filterStrength;
// Filter deaktiviert? Direkt durchreichen.
if (fs <= 1)
{
filteredValue = raw;
return raw;
}
// Erster Wert: direkt übernehmen
if (filteredValue < 0)
{
filteredValue = raw;
return raw;
}
// 32-Bit Akkumulator, um Überlauf zu vermeiden
int32_t acc = filteredValue * (int32_t)(fs - 1) + raw;
filteredValue = acc / fs;
return (int)filteredValue;
}
CalibrationState PedalController::autoCalibrate(bool reset)
{
static int step = 0;
static unsigned long lastChangeTime = 0;
static int previousValue = 0;
static CalibrationState errorState = IDLE;
unsigned long now = millis();
if (reset)
{
step = 0;
config.minRaw = 1023;
config.maxRaw = 0;
lastChangeTime = millis();
// Konfiguration auf Defaults zurücksetzen
config = defaultConfig;
config.calibrated = false;
errorState = IDLE;
Serial.println("Calibration reset.");
return IDLE;
// interne Zustände für die Kalibrierung
calState = RUNNING;
calStep = 1; // Schritt 1: Pedal losgelassen
stepStartTime = now;
lastChangeTime = now;
filteredValue = -1;
int raw = readRaw();
lastSample = raw;
minSeen = 1023;
maxSeen = 0;
return calState;
}
unsigned long currentTime = millis();
int rawValue = applySmoothing(analogRead(pedalPin));
switch (step)
// Kein Reset angefordert, aber auch keine aktive Kalibrierung
if (calStep == 0)
{
case 0: // Initial state: wait for pedal release
if (rawValue == previousValue && currentTime - lastChangeTime > 2000)
calState = IDLE;
return calState;
}
int raw = readRaw();
int diff = abs(raw - lastSample);
printStatus(raw);
switch (calStep)
{
case 1: // Schritt 1: Pedal losgelassen halten
if (diff > STABLE_THRESHOLD)
{
if (rawValue >= minMin && rawValue <= maxMin)
{
config.minRaw = rawValue;
step++;
lastChangeTime = currentTime;
errorState = RUNNING;
}
else
{
errorState = ERROR;
}
lastSample = raw;
lastChangeTime = now;
}
else if (rawValue != previousValue)
// Wert war lange genug stabil -> als unteres Ende merken
if (now - lastChangeTime > STABLE_TIME)
{
lastChangeTime = currentTime;
minSeen = raw;
config.minRaw = raw;
calStep = 2;
lastSample = raw;
lastChangeTime = now;
}
calState = RUNNING;
break;
case 1: // Wait for pedal full press
if (rawValue == previousValue && currentTime - lastChangeTime > 2000)
case 2: // Schritt 2: Pedal voll durchtreten
if (diff > STABLE_THRESHOLD)
{
if (rawValue >= minMax && rawValue <= maxMax)
{
config.maxRaw = rawValue;
step++;
lastChangeTime = currentTime;
errorState = RUNNING;
}
else
{
errorState = ERROR;
}
lastSample = raw;
lastChangeTime = now;
}
else if (rawValue != previousValue)
// Wert war lange genug stabil -> als erstes oberes Ende merken
if (now - lastChangeTime > STABLE_TIME)
{
lastChangeTime = currentTime;
maxSeen = raw;
config.maxRaw = raw;
calStep = 3;
stepStartTime = now;
}
calState = RUNNING;
break;
case 2: // Validate calibration
if (config.maxRaw > config.minRaw + 100)
{ // Ensure valid range
step++;
case 3: // Schritt 3: Sweep, um echten min/max Bereich zu erfassen
if (raw < minSeen)
minSeen = raw;
if (raw > maxSeen)
maxSeen = raw;
if (now - stepStartTime > SWEEP_TIME)
{
int low = minSeen + SAFETY_MARGIN_LOW;
int high = maxSeen - SAFETY_MARGIN_HIGH;
// Falls Richtung "invertiert" ist, drehen
if (high < low)
{
int tmp = low;
low = high;
high = tmp;
}
// Bereich zu klein, Kalibrierung fehlgeschlagen
if (high - low < MIN_RANGE)
{
config.calibrated = false;
calState = ERROR;
calStep = 0;
break;
}
config.minRaw = low;
config.maxRaw = high;
config.calibrated = true;
errorState = SUCCESS;
calState = SUCCESS;
calStep = 0;
}
else
{
errorState = ERROR;
calState = RUNNING;
}
break;
default:
errorState = IDLE;
step = 0;
// Fallback, falls irgendetwas entgleist
calState = ERROR;
calStep = 0;
break;
}
previousValue = rawValue;
return errorState;
return calState;
}
CalibrationState PedalController::getStatus() const
{
return calState;
}
int PedalController::getPedal()
{
if (!config.calibrated || autoCalibrate(false) != IDLE)
// 1) Digital-Sanity: Wenn DO "nicht gedrückt" meldet → 0
if (useDigital)
{
int d = digitalRead(digitalPin);
if ((uint8_t)d == digitalNotPressedLevel)
{
// Optional: Filter sanft Richtung "unten" ziehen
// aber sicherheitshalber nichts fahren
return 0;
}
}
// 2) Ohne gültige Kalibrierung: kein Pedal
if (!config.calibrated)
{
return 0;
}
int rawValue = applySmoothing(analogRead(pedalPin));
if (rawValue < config.minRaw || rawValue > config.maxRaw)
int raw = readRaw();
int minR = config.minRaw;
int maxR = config.maxRaw;
// Defekter Config-Bereich, zur Sicherheit 0
if (maxR <= minR + 1)
{
return 0; // Out of bounds
return 0;
}
return map(rawValue, config.minRaw, config.maxRaw, 0, 100);
// Clamping
if (raw < minR)
raw = minR;
if (raw > maxR)
raw = maxR;
long span = (long)maxR - (long)minR;
long scaled = (long)(raw - minR) * 100L / span;
// kleine Deadzone unten
if (scaled < 5)
scaled = 0;
if (scaled > 100)
scaled = 100;
return (int)scaled;
}
CalibrationState PedalController::getStatus()
{
return autoCalibrate(false);
}
int PedalController::getRawValue()
{
return analogRead(pedalPin);
}
PedalConfig PedalController::getConfig()
PedalConfig PedalController::getConfig() const
{
return config;
}
@@ -153,4 +268,84 @@ PedalConfig PedalController::getConfig()
void PedalController::setConfig(const PedalConfig &newConfig)
{
config = newConfig;
// FilterStrength sanitizen
if (config.filterStrength < MIN_FS || config.filterStrength > MAX_FS)
{
config.filterStrength = DEFAULT_FS;
}
if (config.maxRaw < config.minRaw)
{
int16_t tmp = config.minRaw;
config.minRaw = config.maxRaw;
config.maxRaw = tmp;
}
filteredValue = -1;
}
void PedalController::loadDefaults()
{
config = defaultConfig;
if (config.filterStrength < MIN_FS || config.filterStrength > MAX_FS)
{
config.filterStrength = DEFAULT_FS;
}
calState = IDLE;
calStep = 0;
filteredValue = -1;
stepStartTime = 0;
lastChangeTime = 0;
lastSample = 0;
minSeen = 1023;
maxSeen = 0;
}
void PedalController::printStatus(int raw)
{
unsigned long now = millis();
if (now - lastStatusPrint < 1000)
return; // nur jede Sekunde
lastStatusPrint = now;
int digitalState = -1;
if (useDigital)
{
digitalState = digitalRead(digitalPin);
}
Serial.print("[CAL] Step=");
Serial.print(calStep);
Serial.print(" RAW=");
Serial.print(raw);
Serial.print(" Filtered=");
Serial.print(filteredValue);
Serial.print(" DO-State=");
if (useDigital)
Serial.print(digitalState);
else
Serial.print("N/A");
Serial.print(" minSeen=");
Serial.print(minSeen);
Serial.print(" maxSeen=");
Serial.print(maxSeen);
Serial.println();
}
int PedalController::debugGetRaw()
{
return readRaw();
}
int PedalController::debugGetDO()
{
if (!useDigital)
return -1;
return digitalRead(digitalPin);
}

View File

@@ -14,33 +14,56 @@ enum CalibrationState {
// Pedal configuration structure
struct PedalConfig {
int minRaw;
int maxRaw;
bool calibrated;
int filterStrength;
int16_t minRaw;
int16_t maxRaw;
bool calibrated;
uint8_t filterStrength; // 1 = kein Filter, >1 = gleitender Mittelwert
};
class PedalController {
private:
int pedalPin;
int filteredValue;
PedalConfig config;
int applySmoothing(int rawValue);
int getRawValue();
public:
PedalController(int pin);
CalibrationState autoCalibrate(bool reset);
CalibrationState getStatus();
// analogPin = ADC-Eingang
// digitalPin = optionaler Digital-Eingang des Hall-Boards (DO), -1 wenn keiner
// digitalNotPressedLevel = 0 (LOW) oder 1 (HIGH), welches Level "nicht gedrückt" bedeutet
explicit PedalController(int analogPin,
int digitalPin = -1,
uint8_t digitalNotPressedLevel = LOW);
// Startet / läuft Auto-Kalibrierung
CalibrationState autoCalibrate(bool reset);
CalibrationState getStatus() const;
// 0100 %, bei nicht kalibriert → 0
int getPedal();
PedalConfig getConfig();
PedalConfig getConfig() const;
void setConfig(const PedalConfig &newConfig);
void loadDefaults();
int debugGetRaw();
int debugGetDO();
private:
int analogPin;
int digitalPin;
bool useDigital;
uint8_t digitalNotPressedLevel; // 0 oder 1
PedalConfig config;
int32_t filteredValue;
// Auto-Cal intern
CalibrationState calState;
uint8_t calStep;
unsigned long stepStartTime;
unsigned long lastChangeTime;
int lastSample;
int minSeen;
int maxSeen;
unsigned long lastStatusPrint;
int readRaw(); // roher ADC-Wert (mit Glättung)
int applySmoothing(int raw);
void printStatus(int raw);
};
#endif // PEDALCONTROLLER_H

View File

@@ -12,16 +12,17 @@
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Pin definitions
const int hallSensor = 2; // Hall sensor input
const int encoderPinA = 3; // Encoder pin A
const int encoderPinB = 4; // Encoder pin B
const int encoderButton = 5; // Encoder button
const int motorPin = 9; // Motor PWM pin
const int pedalPin = A0; // Pedal input pin
const int hallSensor = 2; // Hall sensor input
const int encoderPinA = 3; // Encoder pin A
const int encoderPinB = 4; // Encoder pin B
const int encoderButton = 5; // Encoder button
const int motorPin = 9; // Motor PWM pin
const int pedalPinAnalog = A1; // Pedal analog Input pin
const int pedalPinDigital = 8; // Pedal digital Input pin
// Global objects
MotorController motor(motorPin); // Motor control object
PedalController pedal(pedalPin); // Pedal input object
MotorController motor(motorPin); // Motor control object
PedalController pedal(pedalPinAnalog, pedalPinDigital, HIGH); // Pedal input object
// Combined settings structure
struct Settings
@@ -36,6 +37,9 @@ bool autoCalibrating = false;
bool serialControlEnabled = false;
CalibrationState lastCalibrationState = IDLE;
bool liveSensorOutput = false;
unsigned long lastSensorPrint = 0;
// Function prototypes
void loadSettings();
void saveSettings();
@@ -133,6 +137,26 @@ void loop()
motor.setTargetSpeed(pedalValue);
}
if (liveSensorOutput)
{
unsigned long now = millis();
if (now - lastSensorPrint > 200)
{
lastSensorPrint = now;
int raw = pedal.debugGetRaw();
int pedalValue = pedal.getPedal();
int doState = pedal.debugGetDO();
Serial.print("[SENSOR] RAW=");
Serial.print(raw);
Serial.print(" Pedal%=");
Serial.print(pedalValue);
Serial.print(" DO=");
Serial.println(doState);
}
}
// Update display
// updateDisplay();
@@ -290,6 +314,18 @@ void handleSerialInput()
Serial.println(motor.getSpeed());
}
break;
case 'S':
liveSensorOutput = !liveSensorOutput; // toggeln
if (liveSensorOutput)
{
Serial.println("Sensor-Live-Ausgabe: ON");
}
else
{
Serial.println("Sensor-Live-Ausgabe: OFF");
}
break;
}
Serial.println(input);
}
@@ -306,10 +342,11 @@ void printHelp()
Serial.println("h - Show this help");
Serial.println("+ - Increment PWM");
Serial.println("- - Decrement PWM");
Serial.println("S - Toggle debug Pedal-Input");
}
void configurePWMFrequency()
{
// Configure Timer1 for higher PWM frequency (~4 kHz)
TCCR1B = (1<<CS11); // Set prescaler to 8
TCCR1B = (1 << CS11); // Set prescaler to 8
}