massive Rework
This commit is contained in:
@@ -1,68 +1,62 @@
|
||||
// MotorController.cpp
|
||||
#include "MotorController.h"
|
||||
|
||||
// Default-Konfiguration
|
||||
const MotorConfig defaultConfig = {
|
||||
.minPWM = 0,
|
||||
.maxPWM = 255,
|
||||
.inverted = true,
|
||||
.responseSpeed = 50,
|
||||
.lookupTable = {0, 10, 20, 30, 40, 50, 60, 70, 80, 100} // Percent values for 0% to 100%
|
||||
.responseSpeed = 50, // ca. 50% pro Sekunde
|
||||
.lookupTable = {0, 10, 20, 30, 40, 50, 60, 70, 80, 100} // 0..100% Kennlinie
|
||||
};
|
||||
|
||||
MotorController::MotorController(int motorPin, bool *debugFlag)
|
||||
: motorPWMPin(motorPin), currentSpeed(0), targetSpeed(0), debugFlag(debugFlag)
|
||||
MotorController::MotorController(const MotorSignals &signals)
|
||||
: signals_(signals)
|
||||
{
|
||||
loadDefaults();
|
||||
pinMode(motorPWMPin, OUTPUT);
|
||||
stopMotor();
|
||||
|
||||
// Pin initialisieren, falls sinnvoll
|
||||
if (signals_.pwmPin >= 0)
|
||||
{
|
||||
pinMode(signals_.pwmPin, OUTPUT);
|
||||
analogWrite(signals_.pwmPin, 0);
|
||||
}
|
||||
|
||||
smoothedPromille = 0;
|
||||
lastUpdateTime = millis();
|
||||
lastPWM = 0;
|
||||
lastStatusPrint = 0;
|
||||
lastTargetPercent = 0;
|
||||
lastSpeedPercent = 0;
|
||||
}
|
||||
|
||||
void MotorController::setSignals(const MotorSignals &signals)
|
||||
{
|
||||
signals_ = signals;
|
||||
|
||||
// Bei neuer Verdrahtung ggf. PinMode setzen
|
||||
if (signals_.pwmPin >= 0)
|
||||
{
|
||||
pinMode(signals_.pwmPin, OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
void MotorController::loadDefaults()
|
||||
{
|
||||
config = defaultConfig;
|
||||
}
|
||||
|
||||
void MotorController::setTargetSpeed(int targetSpeedPercent)
|
||||
{
|
||||
|
||||
targetSpeed = constrain(targetSpeedPercent, 0, 100);
|
||||
}
|
||||
|
||||
void MotorController::maintain()
|
||||
{
|
||||
targetSpeed = constrain(targetSpeed, 0, 100); // Ensure targetSpeed is valid
|
||||
int adjustedSpeed = applyDynamicResponse(targetSpeed); // Dynamic adjustment in percentage
|
||||
|
||||
// Interpolate between lookup table values
|
||||
int lowerIndex = map(adjustedSpeed, 0, 100, 0, MotorConfig::lookupTableSize - 1);
|
||||
int upperIndex = min(lowerIndex + 1, MotorConfig::lookupTableSize - 1);
|
||||
int lowerPercent = config.lookupTable[lowerIndex];
|
||||
int upperPercent = config.lookupTable[upperIndex];
|
||||
|
||||
float interpolationFactor = (adjustedSpeed - lowerIndex * (100 / (MotorConfig::lookupTableSize - 1))) / (100.0 / (MotorConfig::lookupTableSize - 1));
|
||||
int interpolatedPWM = map(lowerPercent + interpolationFactor * (upperPercent - lowerPercent), 0, 100, config.minPWM, config.maxPWM);
|
||||
|
||||
setPWM(interpolatedPWM); // Apply final interpolated PWM value
|
||||
currentSpeed = adjustedSpeed; // Update currentSpeed in percentage
|
||||
|
||||
if (debugFlag && *debugFlag)
|
||||
// responseSpeed absichern
|
||||
if (config.responseSpeed <= 0)
|
||||
{
|
||||
Serial.print("[MOTOR] current=");
|
||||
Serial.print(currentSpeed);
|
||||
Serial.print(" target=");
|
||||
Serial.println(targetSpeed);
|
||||
config.responseSpeed = 50; // Fallback
|
||||
}
|
||||
}
|
||||
|
||||
int MotorController::getSpeed() const
|
||||
{
|
||||
return currentSpeed;
|
||||
}
|
||||
|
||||
void MotorController::stopMotor()
|
||||
{
|
||||
setTargetSpeed(0);
|
||||
maintain();
|
||||
// min/max sanity
|
||||
if (config.maxPWM < config.minPWM)
|
||||
{
|
||||
uint8_t tmp = config.minPWM;
|
||||
config.minPWM = config.maxPWM;
|
||||
config.maxPWM = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
MotorConfig MotorController::getConfig() const
|
||||
@@ -73,46 +67,173 @@ MotorConfig MotorController::getConfig() const
|
||||
void MotorController::setConfig(const MotorConfig &newConfig)
|
||||
{
|
||||
config = newConfig;
|
||||
|
||||
// min/max sanity
|
||||
if (config.maxPWM < config.minPWM)
|
||||
{
|
||||
uint8_t tmp = config.minPWM;
|
||||
config.minPWM = config.maxPWM;
|
||||
config.maxPWM = tmp;
|
||||
}
|
||||
|
||||
if (config.responseSpeed <= 0)
|
||||
{
|
||||
config.responseSpeed = 50;
|
||||
}
|
||||
}
|
||||
|
||||
void MotorController::setPWM(uint8_t pwmValue)
|
||||
void MotorController::maintain()
|
||||
{
|
||||
int constrainedPWM = constrain(pwmValue, config.minPWM, config.maxPWM);
|
||||
analogWrite(motorPWMPin, config.inverted ? 255 - constrainedPWM : constrainedPWM);
|
||||
// 1) Target bestimmen
|
||||
int16_t targetPercent = 0;
|
||||
|
||||
if (signals_.targetSpeedPercent)
|
||||
{
|
||||
targetPercent = constrain(*signals_.targetSpeedPercent, 0, 100);
|
||||
}
|
||||
|
||||
// Enable beachten: wenn vorhanden und false -> Ziel = 0
|
||||
if (signals_.enabled && !*signals_.enabled)
|
||||
{
|
||||
targetPercent = 0;
|
||||
}
|
||||
|
||||
lastTargetPercent = targetPercent;
|
||||
|
||||
// 2) Dynamische Rampenbildung
|
||||
int16_t adjustedSpeed = applyDynamicResponse(targetPercent); // 0..100%
|
||||
lastSpeedPercent = adjustedSpeed;
|
||||
|
||||
// 3) Lookup-Interpolation von Prozent -> LUT-Prozent
|
||||
const int16_t n = MotorConfig::lookupTableSize;
|
||||
float pos = (adjustedSpeed / 100.0f) * (n - 1); // 0..(n-1) als float
|
||||
int16_t lowerIndex = (int16_t)pos;
|
||||
if (lowerIndex < 0)
|
||||
lowerIndex = 0;
|
||||
if (lowerIndex > n - 1)
|
||||
lowerIndex = n - 1;
|
||||
|
||||
int16_t upperIndex = lowerIndex + 1;
|
||||
if (upperIndex > n - 1)
|
||||
upperIndex = n - 1;
|
||||
|
||||
float frac = pos - lowerIndex; // 0..1 zwischen den beiden LUT-Punkten
|
||||
|
||||
int16_t lowerPercent = config.lookupTable[lowerIndex];
|
||||
int16_t upperPercent = config.lookupTable[upperIndex];
|
||||
float lutPercent = lowerPercent + frac * (upperPercent - lowerPercent);
|
||||
|
||||
// LUT liefert 0..100% -> auf PWM-Bereich skalieren
|
||||
int16_t pwmLogical = map((int16_t)lutPercent, 0, 100, config.minPWM, config.maxPWM);
|
||||
pwmLogical = constrain(pwmLogical, config.minPWM, config.maxPWM);
|
||||
|
||||
lastPWM = (uint8_t)pwmLogical;
|
||||
|
||||
// 4) PWM ausgeben
|
||||
setPWM(lastPWM);
|
||||
|
||||
// 5) Ausgänge in Signals spiegeln
|
||||
if (signals_.currentSpeedPercent)
|
||||
*signals_.currentSpeedPercent = adjustedSpeed;
|
||||
|
||||
if (signals_.currentPWM)
|
||||
*signals_.currentPWM = lastPWM;
|
||||
|
||||
// 6) Debug-Ausgabe
|
||||
printStatus();
|
||||
}
|
||||
|
||||
int MotorController::applyDynamicResponse(int targetPercent)
|
||||
void MotorController::stopMotor()
|
||||
{
|
||||
// Intern in Promille arbeiten für höhere Genauigkeit
|
||||
static int smoothedPromille = 0;
|
||||
static unsigned long lastUpdateTime = millis();
|
||||
// Hard Stop: Rampe und PWM sofort auf 0 ziehen
|
||||
smoothedPromille = 0;
|
||||
lastSpeedPercent = 0;
|
||||
lastTargetPercent = 0;
|
||||
lastPWM = 0;
|
||||
|
||||
unsigned long currentTime = millis();
|
||||
unsigned long elapsedTime = currentTime - lastUpdateTime;
|
||||
setPWM(0);
|
||||
|
||||
if (signals_.currentSpeedPercent)
|
||||
*signals_.currentSpeedPercent = 0;
|
||||
|
||||
if (signals_.currentPWM)
|
||||
*signals_.currentPWM = 0;
|
||||
}
|
||||
|
||||
int16_t MotorController::applyDynamicResponse(int16_t targetPercent)
|
||||
{
|
||||
uint32_t currentTime = millis();
|
||||
uint32_t elapsedTime = currentTime - lastUpdateTime;
|
||||
|
||||
if (elapsedTime == 0)
|
||||
return smoothedPromille / 10; // Rückgabe in Prozent
|
||||
{
|
||||
return (smoothedPromille + 5) / 10; // Rückgabe in %
|
||||
}
|
||||
|
||||
// Zielwert in Promille berechnen
|
||||
int targetPromille = targetPercent * 10;
|
||||
targetPercent = constrain(targetPercent, 0, 100);
|
||||
int16_t targetPromille = targetPercent * 10;
|
||||
|
||||
// Dynamische Reaktionsgeschwindigkeit in Promille pro Sekunde
|
||||
int maxChangePerMs = config.responseSpeed * 10; // Konfigurierbarer Faktor
|
||||
// responseSpeed = Prozent pro Sekunde → Promille pro Sekunde
|
||||
int16_t maxChangePerSec = config.responseSpeed * 10;
|
||||
|
||||
// Berechne maximale Änderung basierend auf vergangener Zeit
|
||||
int maxDelta = (maxChangePerMs * elapsedTime) / 1000;
|
||||
// maximaler Delta in diesem Zeitintervall
|
||||
int16_t maxDelta = (int16_t)((maxChangePerSec * (int32_t)elapsedTime) / 1000L);
|
||||
|
||||
// Tatsächliche Änderung begrenzen
|
||||
int delta = targetPromille - smoothedPromille;
|
||||
int16_t delta = targetPromille - smoothedPromille;
|
||||
if (delta > maxDelta)
|
||||
delta = maxDelta;
|
||||
else if (delta < -maxDelta)
|
||||
delta = -maxDelta;
|
||||
|
||||
// Smoothed Promille aktualisieren
|
||||
smoothedPromille += delta;
|
||||
lastUpdateTime = currentTime;
|
||||
|
||||
// Rückgabe in Prozent (gerundet)
|
||||
return (smoothedPromille + 5) / 10; // Rundung durch Addition von 5 vor Division
|
||||
return (smoothedPromille + 5) / 10; // runden
|
||||
}
|
||||
|
||||
void MotorController::setPWM(uint8_t pwmLogical)
|
||||
{
|
||||
if (signals_.pwmPin < 0)
|
||||
return;
|
||||
|
||||
uint8_t pwmOut = pwmLogical;
|
||||
|
||||
if (config.inverted)
|
||||
{
|
||||
// Logischer Wert im Bereich [min,max] wird gespiegelt
|
||||
int16_t span = (int16_t)config.maxPWM - (int16_t)config.minPWM;
|
||||
int16_t offset = (int16_t)pwmLogical - (int16_t)config.minPWM;
|
||||
int16_t invOffset = span - offset;
|
||||
int16_t inv = (int16_t)config.minPWM + invOffset;
|
||||
if (inv < 0)
|
||||
inv = 0;
|
||||
if (inv > 255)
|
||||
inv = 255;
|
||||
pwmOut = (uint8_t)inv;
|
||||
}
|
||||
|
||||
analogWrite(signals_.pwmPin, pwmOut);
|
||||
}
|
||||
|
||||
void MotorController::printStatus()
|
||||
{
|
||||
if (!(signals_.debugFlag && *signals_.debugFlag))
|
||||
return;
|
||||
|
||||
uint32_t now = millis();
|
||||
if (now - lastStatusPrint < 1000)
|
||||
return; // nur jede Sekunde
|
||||
|
||||
lastStatusPrint = now;
|
||||
|
||||
Serial.print("[MOTOR] target=");
|
||||
Serial.print(lastTargetPercent);
|
||||
Serial.print("% current=");
|
||||
Serial.print(lastSpeedPercent);
|
||||
Serial.print("% pwm=");
|
||||
Serial.print(lastPWM);
|
||||
Serial.print(" resp=");
|
||||
Serial.print(config.responseSpeed);
|
||||
Serial.print(" inv=");
|
||||
Serial.println(config.inverted ? "Y" : "N");
|
||||
}
|
||||
|
||||
@@ -1,43 +1,67 @@
|
||||
// MotorController.h
|
||||
#ifndef MOTORCONTROLLER_H
|
||||
#define MOTORCONTROLLER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// Motor configuration structure
|
||||
struct MotorConfig
|
||||
{
|
||||
uint8_t minPWM;
|
||||
uint8_t maxPWM;
|
||||
bool inverted;
|
||||
int responseSpeed;
|
||||
static const int lookupTableSize = 10;
|
||||
int16_t responseSpeed; // Prozent pro Sekunde
|
||||
static const int16_t lookupTableSize = 10;
|
||||
uint8_t lookupTable[lookupTableSize];
|
||||
};
|
||||
|
||||
typedef struct MotorSignals
|
||||
{
|
||||
// Pin (by value)
|
||||
int16_t pwmPin; // PWM-Ausgangspin, <0 = deaktiviert
|
||||
|
||||
// Eingänge
|
||||
int16_t *targetSpeedPercent; // Sollwert 0..100%
|
||||
bool *enabled; // optionaler Enable (true = aktiv)
|
||||
|
||||
// Ausgänge
|
||||
int16_t *currentSpeedPercent; // Istwert 0..100%
|
||||
uint8_t *currentPWM; // tatsächlicher PWM-Wert (logisch)
|
||||
|
||||
// Debug
|
||||
bool *debugFlag; // globales Debug-Flag
|
||||
} MotorSignals;
|
||||
|
||||
class MotorController
|
||||
{
|
||||
private:
|
||||
int motorPWMPin;
|
||||
int currentSpeed;
|
||||
int targetSpeed;
|
||||
bool *debugFlag;
|
||||
MotorConfig config;
|
||||
|
||||
int applyDynamicResponse(int targetValue);
|
||||
void setPWM(uint8_t pwmValue);
|
||||
|
||||
public:
|
||||
MotorController(int motorPin, bool *debugFlag = nullptr);
|
||||
explicit MotorController(const MotorSignals &signals);
|
||||
|
||||
void setSignals(const MotorSignals &signals);
|
||||
|
||||
// zyklisch in loop() aufrufen
|
||||
void maintain();
|
||||
|
||||
// Hard Stop
|
||||
void stopMotor();
|
||||
void setTargetSpeed(int targetSpeedPercent);
|
||||
int getSpeed() const;
|
||||
|
||||
MotorConfig getConfig() const;
|
||||
void setConfig(const MotorConfig &newConfig);
|
||||
void loadDefaults();
|
||||
|
||||
private:
|
||||
MotorSignals signals_;
|
||||
MotorConfig config;
|
||||
|
||||
// interner Dynamik-State
|
||||
int16_t smoothedPromille = 0; // 0..1000
|
||||
uint32_t lastUpdateTime = 0;
|
||||
uint8_t lastPWM = 0;
|
||||
uint32_t lastStatusPrint = 0;
|
||||
int16_t lastTargetPercent = 0;
|
||||
int16_t lastSpeedPercent = 0;
|
||||
|
||||
int16_t applyDynamicResponse(int16_t targetPercent);
|
||||
void setPWM(uint8_t pwmLogical);
|
||||
void printStatus();
|
||||
};
|
||||
|
||||
#endif // MOTORCONTROLLER_H
|
||||
|
||||
@@ -11,28 +11,25 @@ static const PedalConfig defaultConfig = {
|
||||
};
|
||||
|
||||
// Tuning-Konstanten für die Auto-Cal
|
||||
static const int STABLE_THRESHOLD = 3; // ADC-Delta, ab dem Stabilität neu bewertet wird
|
||||
static const unsigned long STABLE_TIME = 1000; // ms, die der Wert stabil sein muss
|
||||
static const int NOISE_TOLERANCE = 5; // max. erlaubtes Rauschen im Stabilitätsfenster
|
||||
static const int MIN_RANGE = 50; // minimale ADC-Spannweite für gültigen Pedalweg
|
||||
static const int SAFETY_MARGIN_LOW = 3; // Untere Grenze etwas anheben
|
||||
static const int SAFETY_MARGIN_HIGH = 3; // Obere Grenze etwas absenken
|
||||
static const int MOVE_DELTA = 15; // min. Abstand zur Referenz für "echte Bewegung"
|
||||
static const int NEAR_TOLERANCE = 10; // max. Differenz zw. Wiederholmessungen
|
||||
static const uint8_t MIN_FS = 1; // Filterstärke min.
|
||||
static const uint8_t MAX_FS = 32; // Filterstärke max.
|
||||
static const uint8_t DEFAULT_FS = 3; // Standard-Filterstärke
|
||||
static const int16_t STABLE_THRESHOLD = 3; // ADC-Delta, ab dem Stabilität neu bewertet wird
|
||||
static const uint32_t STABLE_TIME = 1000; // ms, die der Wert stabil sein muss
|
||||
static const int16_t NOISE_TOLERANCE = 5; // max. erlaubtes Rauschen im Stabilitätsfenster
|
||||
static const int16_t MIN_RANGE = 50; // minimale ADC-Spannweite für gültigen Pedalweg
|
||||
static const int16_t SAFETY_MARGIN_LOW = 3; // Untere Grenze etwas anheben
|
||||
static const int16_t SAFETY_MARGIN_HIGH = 3; // Obere Grenze etwas absenken
|
||||
static const int16_t MOVE_DELTA = 15; // min. Abstand zur Referenz für "echte Bewegung"
|
||||
static const int16_t NEAR_TOLERANCE = 10; // max. Differenz zw. Wiederholmessungen
|
||||
static const uint8_t MIN_FS = 1; // Filterstärke min.
|
||||
static const uint8_t MAX_FS = 32; // Filterstärke max.
|
||||
static const uint8_t DEFAULT_FS = 3; // Standard-Filterstärke
|
||||
|
||||
PedalController::PedalController(int analogPin,
|
||||
int digitalPin,
|
||||
uint8_t digitalNotPressedLevel,
|
||||
bool *debugFlag)
|
||||
: analogPin(analogPin),
|
||||
digitalPin(digitalPin),
|
||||
useDigital(digitalPin >= 0),
|
||||
digitalNotPressedLevel(digitalNotPressedLevel ? 1 : 0),
|
||||
debugFlag(debugFlag),
|
||||
PedalController::PedalController(const PedalSignals &signals,
|
||||
uint8_t digitalNotPressedLevel_)
|
||||
: signals_(signals),
|
||||
digitalNotPressedLevel(digitalNotPressedLevel_ ? 1 : 0),
|
||||
config(),
|
||||
filteredValue(-1),
|
||||
lastPedalPercent(0),
|
||||
calState(IDLE),
|
||||
calStep(0),
|
||||
stepStartTime(0),
|
||||
@@ -49,21 +46,25 @@ PedalController::PedalController(int analogPin,
|
||||
step3NearReleased(false),
|
||||
step4NearPressed(false)
|
||||
{
|
||||
if (useDigital)
|
||||
// Digital-Pin als INPUT konfigurieren, falls vorhanden
|
||||
if (signals_.digitalPin >= 0)
|
||||
{
|
||||
pinMode(digitalPin, INPUT); // nur noch Debug
|
||||
pinMode(signals_.digitalPin, INPUT);
|
||||
}
|
||||
|
||||
loadDefaults();
|
||||
}
|
||||
|
||||
// Rohwert lesen (unglättet)
|
||||
int PedalController::readRaw()
|
||||
int16_t PedalController::readRaw()
|
||||
{
|
||||
return analogRead(analogPin);
|
||||
if (signals_.analogPin < 0)
|
||||
return 0;
|
||||
|
||||
return analogRead(signals_.analogPin);
|
||||
}
|
||||
|
||||
int PedalController::applySmoothing(int raw)
|
||||
int16_t PedalController::applySmoothing(int16_t raw)
|
||||
{
|
||||
uint8_t fs = config.filterStrength;
|
||||
|
||||
@@ -74,6 +75,7 @@ int PedalController::applySmoothing(int raw)
|
||||
return raw;
|
||||
}
|
||||
|
||||
// Wenn Wert steigt: sofort übernehmen (reagiert schneller nach oben)
|
||||
if (raw >= filteredValue)
|
||||
{
|
||||
filteredValue = raw;
|
||||
@@ -90,12 +92,46 @@ int PedalController::applySmoothing(int raw)
|
||||
int32_t acc = filteredValue * (int32_t)(fs - 1) + raw;
|
||||
filteredValue = (acc + (fs / 2)) / fs;
|
||||
|
||||
return (int)filteredValue;
|
||||
return (int16_t)filteredValue;
|
||||
}
|
||||
|
||||
int16_t PedalController::mapRawToPercent(int16_t raw)
|
||||
{
|
||||
// Ohne gültige Kalibrierung: 0 %
|
||||
if (!config.calibrated)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t minR = config.minRaw; // gedrückt → niedriger
|
||||
int16_t maxR = config.maxRaw; // losgelassen → höher
|
||||
|
||||
if (maxR <= minR + 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (raw < minR)
|
||||
raw = minR;
|
||||
if (raw > maxR)
|
||||
raw = maxR;
|
||||
|
||||
int32_t span = (int32_t)maxR - (int32_t)minR;
|
||||
// invertiertes Mapping, weil "hoch = losgelassen"
|
||||
int32_t scaled = (int32_t)(maxR - raw) * 100L / span;
|
||||
|
||||
// kleine Deadzone unten (Pedal "nicht gedrückt")
|
||||
if (scaled < 3)
|
||||
scaled = 0;
|
||||
if (scaled > 100)
|
||||
scaled = 100;
|
||||
|
||||
return (int16_t)scaled;
|
||||
}
|
||||
|
||||
CalibrationState PedalController::autoCalibrate(bool reset)
|
||||
{
|
||||
unsigned long now = millis();
|
||||
uint32_t now = millis();
|
||||
|
||||
if (reset)
|
||||
{
|
||||
@@ -133,8 +169,8 @@ CalibrationState PedalController::autoCalibrate(bool reset)
|
||||
return calState;
|
||||
}
|
||||
|
||||
int raw = applySmoothing(readRaw());
|
||||
int diff = abs(raw - lastSample);
|
||||
int16_t raw = applySmoothing(readRaw());
|
||||
int16_t diff = abs(raw - lastSample);
|
||||
|
||||
switch (calStep)
|
||||
{
|
||||
@@ -155,7 +191,7 @@ CalibrationState PedalController::autoCalibrate(bool reset)
|
||||
|
||||
if (now - lastChangeTime > STABLE_TIME)
|
||||
{
|
||||
int noise = maxSeen - minSeen;
|
||||
int16_t noise = maxSeen - minSeen;
|
||||
if (noise > NOISE_TOLERANCE)
|
||||
{
|
||||
Serial.print("CAL: FEHLER - zu viel Rauschen im losgelassenen Zustand (noise=");
|
||||
@@ -327,7 +363,7 @@ CalibrationState PedalController::autoCalibrate(bool reset)
|
||||
case 5:
|
||||
{ // 5. Final loslassen & stabil -> finalisieren
|
||||
// Ziel-Release-Punkt aus den beiden Messungen mitteln
|
||||
int relTarget = (firstReleased + secondReleased) / 2;
|
||||
int16_t relTarget = (firstReleased + secondReleased) / 2;
|
||||
|
||||
// Sind wir schon nah genug an der Release-Position?
|
||||
if (abs(raw - relTarget) > MOVE_DELTA)
|
||||
@@ -348,15 +384,15 @@ CalibrationState PedalController::autoCalibrate(bool reset)
|
||||
|
||||
if (now - lastChangeTime > STABLE_TIME)
|
||||
{
|
||||
int relAvg = relTarget;
|
||||
int pressAvg = (firstPressed + secondPressed) / 2;
|
||||
int16_t relAvg = relTarget;
|
||||
int16_t pressAvg = (firstPressed + secondPressed) / 2;
|
||||
|
||||
int low = pressAvg;
|
||||
int high = relAvg;
|
||||
int16_t low = pressAvg;
|
||||
int16_t high = relAvg;
|
||||
|
||||
if (high < low)
|
||||
{
|
||||
int tmp = low;
|
||||
int16_t tmp = low;
|
||||
low = high;
|
||||
high = tmp;
|
||||
}
|
||||
@@ -414,40 +450,39 @@ CalibrationState PedalController::getStatus() const
|
||||
return calState;
|
||||
}
|
||||
|
||||
int PedalController::getPedal()
|
||||
int16_t PedalController::getPedal()
|
||||
{
|
||||
// Ohne gültige Kalibrierung: kein Pedal
|
||||
if (!config.calibrated)
|
||||
int16_t raw = applySmoothing(readRaw());
|
||||
int16_t percent = mapRawToPercent(raw);
|
||||
lastPedalPercent = percent;
|
||||
return percent;
|
||||
}
|
||||
|
||||
void PedalController::maintain()
|
||||
{
|
||||
int16_t raw = applySmoothing(readRaw());
|
||||
|
||||
// Outputs befüllen
|
||||
if (signals_.rawValue)
|
||||
*signals_.rawValue = raw;
|
||||
|
||||
// digitaler Eingang (falls vorhanden)
|
||||
if (signals_.digitalState)
|
||||
{
|
||||
return 0;
|
||||
if (signals_.digitalPin >= 0)
|
||||
*signals_.digitalState = digitalRead(signals_.digitalPin);
|
||||
else
|
||||
*signals_.digitalState = -1;
|
||||
}
|
||||
|
||||
int raw = applySmoothing(readRaw());
|
||||
int16_t percent = mapRawToPercent(raw);
|
||||
lastPedalPercent = percent;
|
||||
|
||||
int minR = config.minRaw; // gedrückt → niedriger
|
||||
int maxR = config.maxRaw; // losgelassen → höher
|
||||
if (signals_.pedalPercent)
|
||||
*signals_.pedalPercent = percent;
|
||||
|
||||
if (maxR <= minR + 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (raw < minR)
|
||||
raw = minR;
|
||||
if (raw > maxR)
|
||||
raw = maxR;
|
||||
|
||||
long span = (long)maxR - (long)minR;
|
||||
// WICHTIG: invertiertes Mapping, weil "hoch = losgelassen"
|
||||
long scaled = (long)(maxR - raw) * 100L / span;
|
||||
|
||||
// kleine Deadzone unten (Pedal "nicht gedrückt")
|
||||
if (scaled < 3)
|
||||
scaled = 0;
|
||||
if (scaled > 100)
|
||||
scaled = 100;
|
||||
|
||||
return (int)scaled;
|
||||
if (signals_.calibrated)
|
||||
*signals_.calibrated = config.calibrated;
|
||||
}
|
||||
|
||||
PedalConfig PedalController::getConfig() const
|
||||
@@ -503,14 +538,24 @@ void PedalController::loadDefaults()
|
||||
step4NearPressed = false;
|
||||
}
|
||||
|
||||
void PedalController::printStatus(int raw)
|
||||
void PedalController::setSignals(const PedalSignals &signals)
|
||||
{
|
||||
if (!(debugFlag && *debugFlag))
|
||||
signals_ = signals;
|
||||
|
||||
if (signals_.digitalPin >= 0)
|
||||
{
|
||||
pinMode(signals_.digitalPin, INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
void PedalController::printStatus(int16_t raw)
|
||||
{
|
||||
if (!(signals_.debugFlag && *signals_.debugFlag))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long now = millis();
|
||||
uint32_t now = millis();
|
||||
if (now - lastStatusPrint < 1000)
|
||||
return; // nur jede Sekunde
|
||||
lastStatusPrint = now;
|
||||
@@ -523,9 +568,9 @@ void PedalController::printStatus(int raw)
|
||||
Serial.print(filteredValue);
|
||||
|
||||
Serial.print(" DO-State=");
|
||||
if (useDigital)
|
||||
if (signals_.digitalPin >= 0)
|
||||
{
|
||||
Serial.print(digitalRead(digitalPin));
|
||||
Serial.print(digitalRead(signals_.digitalPin));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -543,21 +588,3 @@ void PedalController::printStatus(int raw)
|
||||
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
// Debug-Methoden
|
||||
int PedalController::debugGetRaw()
|
||||
{
|
||||
return readRaw();
|
||||
}
|
||||
|
||||
int PedalController::debugGetSmooth()
|
||||
{
|
||||
return applySmoothing(readRaw());
|
||||
}
|
||||
|
||||
int PedalController::debugGetDO()
|
||||
{
|
||||
if (!useDigital)
|
||||
return -1;
|
||||
return digitalRead(digitalPin);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// Calibration state enumeration
|
||||
enum CalibrationState
|
||||
{
|
||||
IDLE,
|
||||
@@ -13,72 +12,80 @@ enum CalibrationState
|
||||
ERROR
|
||||
};
|
||||
|
||||
// Pedal configuration structure
|
||||
struct PedalConfig
|
||||
{
|
||||
int16_t minRaw;
|
||||
int16_t maxRaw;
|
||||
bool calibrated;
|
||||
uint8_t filterStrength; // 1 = kein Filter, >1 = gleitender Mittelwert
|
||||
bool calibrated;
|
||||
uint8_t filterStrength;
|
||||
};
|
||||
|
||||
typedef struct PedalSignals
|
||||
{
|
||||
// Pins (by value)
|
||||
int16_t analogPin; // ADC-Pin
|
||||
int16_t digitalPin; // -1 falls unbenutzt
|
||||
|
||||
// Eingänge
|
||||
bool *debugFlag; // globales Debug-Flag
|
||||
|
||||
// Ausgänge
|
||||
int16_t *pedalPercent; // 0..100%
|
||||
int16_t *rawValue; // gefilterter Rawwert
|
||||
bool *calibrated; // Status
|
||||
int16_t *digitalState; // DO-Status oder -1
|
||||
} PedalSignals;
|
||||
|
||||
class PedalController
|
||||
{
|
||||
public:
|
||||
explicit PedalController(int analogPin,
|
||||
int digitalPin = -1,
|
||||
uint8_t digitalNotPressedLevel = LOW,
|
||||
bool *debugFlag = nullptr);
|
||||
PedalController(const PedalSignals &signals,
|
||||
uint8_t digitalNotPressedLevel = LOW);
|
||||
|
||||
void setSignals(const PedalSignals &signals);
|
||||
|
||||
// Startet / läuft Auto-Kalibrierung
|
||||
CalibrationState autoCalibrate(bool reset);
|
||||
CalibrationState getStatus() const;
|
||||
|
||||
// 0–100 %, bei nicht kalibriert → 0
|
||||
int getPedal();
|
||||
int16_t getPedal(); // alte API (blocking)
|
||||
void maintain(); // neue Non-Blocking-API
|
||||
|
||||
PedalConfig getConfig() const;
|
||||
void setConfig(const PedalConfig &newConfig);
|
||||
void setConfig(const PedalConfig &cfg);
|
||||
void loadDefaults();
|
||||
|
||||
// Debug-Helfer
|
||||
int debugGetRaw();
|
||||
int debugGetSmooth();
|
||||
int debugGetDO();
|
||||
|
||||
private:
|
||||
int analogPin;
|
||||
int digitalPin;
|
||||
bool useDigital;
|
||||
uint8_t digitalNotPressedLevel; // nur Debug
|
||||
bool *debugFlag;
|
||||
|
||||
PedalSignals signals_;
|
||||
uint8_t digitalNotPressedLevel; // nur Debug / Interpretation
|
||||
|
||||
PedalConfig config;
|
||||
int32_t filteredValue;
|
||||
int16_t lastPedalPercent;
|
||||
|
||||
// Auto-Cal intern
|
||||
CalibrationState calState;
|
||||
uint8_t calStep;
|
||||
unsigned long stepStartTime;
|
||||
unsigned long lastChangeTime;
|
||||
int lastSample;
|
||||
int minSeen;
|
||||
int maxSeen;
|
||||
unsigned long lastStatusPrint;
|
||||
uint8_t calStep;
|
||||
uint32_t stepStartTime;
|
||||
uint32_t lastChangeTime;
|
||||
int16_t lastSample;
|
||||
int16_t minSeen;
|
||||
int16_t maxSeen;
|
||||
uint32_t lastStatusPrint;
|
||||
|
||||
// Vier Referenzpunkte aus dem Ablauf
|
||||
int firstReleased;
|
||||
int firstPressed;
|
||||
int secondReleased;
|
||||
int secondPressed;
|
||||
int16_t firstReleased;
|
||||
int16_t firstPressed;
|
||||
int16_t secondReleased;
|
||||
int16_t secondPressed;
|
||||
|
||||
bool step2MovedEnough;
|
||||
bool step3NearReleased;
|
||||
bool step4NearPressed;
|
||||
|
||||
int readRaw();
|
||||
int applySmoothing(int raw);
|
||||
void printStatus(int raw);
|
||||
int16_t readRaw();
|
||||
int16_t applySmoothing(int16_t raw);
|
||||
int16_t mapRawToPercent(int16_t raw);
|
||||
void printStatus(int16_t raw);
|
||||
};
|
||||
|
||||
#endif // PEDALCONTROLLER_H
|
||||
|
||||
155
src/SerialController.cpp
Normal file
155
src/SerialController.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include <Arduino.h>
|
||||
#include "SerialController.h"
|
||||
|
||||
SerialController::SerialController(const SerialSignals &signals)
|
||||
: signals_(signals)
|
||||
{
|
||||
}
|
||||
|
||||
void SerialController::maintain()
|
||||
{
|
||||
while (Serial.available() > 0)
|
||||
{
|
||||
char c = Serial.read();
|
||||
handleCommand(c);
|
||||
}
|
||||
}
|
||||
|
||||
void SerialController::handleCommand(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
printHelp();
|
||||
break;
|
||||
|
||||
case 'd': // Debug toggeln
|
||||
if (signals_.debugFlag)
|
||||
{
|
||||
*signals_.debugFlag = !*signals_.debugFlag;
|
||||
Serial.print("Debug: ");
|
||||
Serial.println(*signals_.debugFlag ? "ON" : "OFF");
|
||||
}
|
||||
break;
|
||||
|
||||
case '+': // Geschwindigkeit +5%
|
||||
if (signals_.targetSpeedPercent)
|
||||
{
|
||||
if (signals_.serialControlEnabled)
|
||||
*signals_.serialControlEnabled = true;
|
||||
|
||||
int16_t v = *signals_.targetSpeedPercent;
|
||||
v = constrain(v + 5, (int16_t)0, (int16_t)100);
|
||||
*signals_.targetSpeedPercent = v;
|
||||
|
||||
Serial.print("Target +: ");
|
||||
Serial.print(v);
|
||||
Serial.println("%");
|
||||
}
|
||||
break;
|
||||
|
||||
case '-': // Geschwindigkeit -5%
|
||||
if (signals_.targetSpeedPercent)
|
||||
{
|
||||
if (signals_.serialControlEnabled)
|
||||
*signals_.serialControlEnabled = true;
|
||||
|
||||
int16_t v = *signals_.targetSpeedPercent;
|
||||
v = constrain(v - 5, (int16_t)0, (int16_t)100);
|
||||
*signals_.targetSpeedPercent = v;
|
||||
|
||||
Serial.print("Target -: ");
|
||||
Serial.print(v);
|
||||
Serial.println("%");
|
||||
}
|
||||
break;
|
||||
|
||||
// direkte 0–100%-Vorgabe in 10%-Schritten
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
if (signals_.targetSpeedPercent)
|
||||
{
|
||||
if (signals_.serialControlEnabled)
|
||||
*signals_.serialControlEnabled = true;
|
||||
|
||||
int16_t v = (c - '0') * 10;
|
||||
v = constrain(v, (int16_t)0, (int16_t)100);
|
||||
*signals_.targetSpeedPercent = v;
|
||||
|
||||
Serial.print("Target set: ");
|
||||
Serial.print(v);
|
||||
Serial.println("%");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x': // Serielle Steuerung stoppen & Motor freigeben
|
||||
if (signals_.serialControlEnabled)
|
||||
*signals_.serialControlEnabled = false;
|
||||
if (signals_.targetSpeedPercent)
|
||||
*signals_.targetSpeedPercent = 0;
|
||||
|
||||
Serial.println("Serial control disabled, target=0%.");
|
||||
break;
|
||||
|
||||
case 'c': // Auto-Cal anfordern
|
||||
if (signals_.requestAutoCal)
|
||||
{
|
||||
*signals_.requestAutoCal = true;
|
||||
Serial.println("Request: Auto-Calibration.");
|
||||
}
|
||||
break;
|
||||
|
||||
case 's': // Save EEPROM
|
||||
if (signals_.requestSaveEEPROM)
|
||||
{
|
||||
*signals_.requestSaveEEPROM = true;
|
||||
Serial.println("Request: Save settings to EEPROM.");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l': // Load EEPROM
|
||||
if (signals_.requestLoadEEPROM)
|
||||
{
|
||||
*signals_.requestLoadEEPROM = true;
|
||||
Serial.println("Request: Load settings from EEPROM.");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r': // Defaults laden
|
||||
if (signals_.requestLoadDefaults)
|
||||
{
|
||||
*signals_.requestLoadDefaults = true;
|
||||
Serial.println("Request: Load defaults.");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Optional: unerkanntes Kommando anzeigen
|
||||
// Serial.print("Unknown command: ");
|
||||
// Serial.println(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SerialController::printHelp()
|
||||
{
|
||||
Serial.println(F("=== Serial Control Help ==="));
|
||||
Serial.println(F("h - Show this help"));
|
||||
Serial.println(F("d - Toggle debug output"));
|
||||
Serial.println(F("c - Request auto-calibration"));
|
||||
Serial.println(F("s - Request save settings to EEPROM"));
|
||||
Serial.println(F("l - Request load settings from EEPROM"));
|
||||
Serial.println(F("r - Request load defaults"));
|
||||
Serial.println(F("x - Disable serial control, set target=0%"));
|
||||
Serial.println(F("+ - Increase target speed by 5%"));
|
||||
Serial.println(F("- - Decrease target speed by 5%"));
|
||||
Serial.println(F("0-9- Set target speed in 10% steps (0..100%)"));
|
||||
}
|
||||
43
src/SerialController.h
Normal file
43
src/SerialController.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef SERIALCONTROLLER_H
|
||||
#define SERIALCONTROLLER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "MotorController.h"
|
||||
#include "PedalController.h"
|
||||
|
||||
typedef struct SerialSignals
|
||||
{
|
||||
int16_t *targetSpeedPercent; // Motor-Sollwert in %
|
||||
bool *debugFlag; // globales Debug-Flag
|
||||
|
||||
// Steuerflags (werden in main ausgewertet)
|
||||
bool *requestAutoCal;
|
||||
bool *requestSaveEEPROM;
|
||||
bool *requestLoadEEPROM;
|
||||
bool *requestLoadDefaults;
|
||||
|
||||
bool *serialControlEnabled; // schaltet „Serielle Steuerung“ an/aus
|
||||
|
||||
// Optional: Zeiger auf Configs für Dumps
|
||||
MotorConfig *motorConfig;
|
||||
PedalConfig *pedalConfig;
|
||||
} SerialSignals;
|
||||
|
||||
class SerialController
|
||||
{
|
||||
public:
|
||||
SerialController(const SerialSignals &signals);
|
||||
|
||||
// im loop() aufrufen
|
||||
void maintain();
|
||||
|
||||
// einmalig bei Start oder auf Kommando
|
||||
void printHelp();
|
||||
|
||||
private:
|
||||
SerialSignals signals_;
|
||||
|
||||
void handleCommand(char c);
|
||||
};
|
||||
|
||||
#endif
|
||||
352
src/main.cpp
352
src/main.cpp
@@ -2,8 +2,10 @@
|
||||
#include <EEPROM.h>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
|
||||
#include "MotorController.h"
|
||||
#include "PedalController.h"
|
||||
#include "SerialController.h"
|
||||
|
||||
// OLED display setup
|
||||
#define OLED_RESET -1
|
||||
@@ -11,20 +13,36 @@
|
||||
#define SCREEN_HEIGHT 64
|
||||
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 pedalPinAnalog = A0; // Pedal analog Input pin
|
||||
const int pedalPinDigital = 8; // Pedal digital Input pin
|
||||
// Pin definitions (Werte, keine Pointer)
|
||||
const int16_t hallSensorPin = 2; // Hall sensor input
|
||||
const int16_t encoderPinA = 3; // Encoder pin A
|
||||
const int16_t encoderPinB = 4; // Encoder pin B
|
||||
const int16_t encoderButtonPin = 5; // Encoder button
|
||||
const int16_t motorPwmPin = 13; // Motor PWM pin
|
||||
const int16_t pedalPinAnalog = A0; // Pedal analog Input pin
|
||||
const int16_t pedalPinDigital = 8; // Pedal digital Input pin
|
||||
|
||||
// Debug-Flag (gemeinsam für alle Controller)
|
||||
bool debug = false;
|
||||
|
||||
// Global objects
|
||||
MotorController motor(motorPin, &debug);
|
||||
PedalController pedal(pedalPinAnalog, pedalPinDigital, LOW, &debug);
|
||||
// Globale Signale für Motor
|
||||
int16_t gTargetSpeedPercent = 0; // Sollwert 0..100%
|
||||
int16_t gCurrentSpeedPercent = 0; // Istwert 0..100%
|
||||
uint8_t gCurrentPwm = 0; // aktueller PWM-Wert (logisch)
|
||||
bool gMotorEnabled = true;
|
||||
|
||||
// Globale Signale für Pedal
|
||||
int16_t gPedalPercent = 0;
|
||||
int16_t gPedalRaw = 0;
|
||||
bool gPedalCalibrated = false;
|
||||
int16_t gPedalDigitalState = 0;
|
||||
|
||||
// Serial-Steuerflags
|
||||
bool gReqAutoCal = false;
|
||||
bool gReqSaveEEPROM = false;
|
||||
bool gReqLoadEEPROM = false;
|
||||
bool gReqDefaults = false;
|
||||
bool gSerialControl = false;
|
||||
|
||||
// Combined settings structure
|
||||
struct Settings
|
||||
@@ -35,12 +53,42 @@ struct Settings
|
||||
};
|
||||
|
||||
Settings settings;
|
||||
bool autoCalibrating = false;
|
||||
bool serialControlEnabled = false;
|
||||
CalibrationState lastCalibrationState = IDLE;
|
||||
bool autoCalibrating = false;
|
||||
|
||||
bool liveSensorOutput = false;
|
||||
unsigned long lastSensorPrint = 0;
|
||||
// Signals-Instanzen
|
||||
MotorSignals motorSignals{
|
||||
.pwmPin = motorPwmPin,
|
||||
.targetSpeedPercent = &gTargetSpeedPercent,
|
||||
.enabled = &gMotorEnabled,
|
||||
.currentSpeedPercent = &gCurrentSpeedPercent,
|
||||
.currentPWM = &gCurrentPwm,
|
||||
.debugFlag = &debug};
|
||||
|
||||
PedalSignals pedalSignals{
|
||||
.analogPin = pedalPinAnalog,
|
||||
.digitalPin = pedalPinDigital,
|
||||
.debugFlag = &debug,
|
||||
.pedalPercent = &gPedalPercent,
|
||||
.rawValue = &gPedalRaw,
|
||||
.calibrated = &gPedalCalibrated,
|
||||
.digitalState = &gPedalDigitalState};
|
||||
|
||||
SerialSignals serialSignals{
|
||||
.targetSpeedPercent = &gTargetSpeedPercent,
|
||||
.debugFlag = &debug,
|
||||
.requestAutoCal = &gReqAutoCal,
|
||||
.requestSaveEEPROM = &gReqSaveEEPROM,
|
||||
.requestLoadEEPROM = &gReqLoadEEPROM,
|
||||
.requestLoadDefaults = &gReqDefaults,
|
||||
.serialControlEnabled = &gSerialControl,
|
||||
.motorConfig = &settings.motorConfig,
|
||||
.pedalConfig = &settings.pedalConfig};
|
||||
|
||||
// Global objects
|
||||
MotorController motor(motorSignals);
|
||||
PedalController pedal(pedalSignals, LOW);
|
||||
SerialController serialCtrl(serialSignals);
|
||||
|
||||
// Function prototypes
|
||||
void loadSettings();
|
||||
@@ -49,48 +97,82 @@ uint8_t calculateChecksum(const Settings &s);
|
||||
void handleEncoder();
|
||||
void handleEncoderButton();
|
||||
void updateDisplay();
|
||||
void handleSerialInput();
|
||||
void configurePWMFrequency();
|
||||
void stopMotorAtNeedleUp();
|
||||
void printHelp();
|
||||
|
||||
void printStatusLine();
|
||||
|
||||
// ----------------- SETUP -----------------
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Initialize pins
|
||||
pinMode(hallSensor, INPUT);
|
||||
// Pins
|
||||
pinMode(hallSensorPin, INPUT);
|
||||
pinMode(encoderPinA, INPUT_PULLUP);
|
||||
pinMode(encoderPinB, INPUT_PULLUP);
|
||||
pinMode(encoderButton, INPUT_PULLUP);
|
||||
pinMode(encoderButtonPin, INPUT_PULLUP);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// Configure PWM frequency
|
||||
configurePWMFrequency();
|
||||
|
||||
// Attach interrupts for encoder
|
||||
attachInterrupt(digitalPinToInterrupt(encoderPinA), handleEncoder, CHANGE);
|
||||
attachInterrupt(digitalPinToInterrupt(encoderPinB), handleEncoder, CHANGE);
|
||||
attachInterrupt(digitalPinToInterrupt(encoderButton), handleEncoderButton, FALLING);
|
||||
attachInterrupt(digitalPinToInterrupt(encoderButtonPin), handleEncoderButton, FALLING);
|
||||
|
||||
// Initialize serial and load settings
|
||||
Serial.begin(9600);
|
||||
loadSettings();
|
||||
|
||||
// Initialize OLED display
|
||||
// if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
|
||||
// Serial.println(F("SSD1306 allocation failed"));
|
||||
// }
|
||||
// display.clearDisplay();
|
||||
// display.display();
|
||||
// OLED optional
|
||||
// if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
|
||||
// Serial.println(F("SSD1306 allocation failed"));
|
||||
// }
|
||||
// display.clearDisplay();
|
||||
// display.display();
|
||||
|
||||
printHelp();
|
||||
serialCtrl.printHelp();
|
||||
}
|
||||
|
||||
// ----------------- LOOP -----------------
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Handle serial input for debugging and manual control
|
||||
handleSerialInput();
|
||||
motor.maintain();
|
||||
// Alle seriellen Befehle in Signale schreiben lassen
|
||||
serialCtrl.maintain();
|
||||
|
||||
// Serial-Requests auswerten
|
||||
if (gReqDefaults)
|
||||
{
|
||||
gReqDefaults = false;
|
||||
Serial.println("Loading defaults.");
|
||||
motor.loadDefaults();
|
||||
pedal.loadDefaults();
|
||||
}
|
||||
|
||||
if (gReqLoadEEPROM)
|
||||
{
|
||||
gReqLoadEEPROM = false;
|
||||
Serial.println("Loading settings from EEPROM.");
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
if (gReqSaveEEPROM)
|
||||
{
|
||||
gReqSaveEEPROM = false;
|
||||
Serial.println("Saving settings to EEPROM.");
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
if (gReqAutoCal)
|
||||
{
|
||||
gReqAutoCal = false;
|
||||
Serial.println("Starting auto-calibration...");
|
||||
pedal.autoCalibrate(true);
|
||||
autoCalibrating = true;
|
||||
lastCalibrationState = IDLE;
|
||||
gTargetSpeedPercent = 0;
|
||||
gSerialControl = false;
|
||||
}
|
||||
|
||||
if (autoCalibrating)
|
||||
{
|
||||
@@ -105,85 +187,71 @@ void loop()
|
||||
break;
|
||||
case SUCCESS:
|
||||
Serial.println("Auto-calibration completed.");
|
||||
autoCalibrating = false;
|
||||
break;
|
||||
case ERROR:
|
||||
Serial.println("Auto-calibration failed.");
|
||||
autoCalibrating = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
motor.setTargetSpeed(0); // Ensure motor stays off during calibration
|
||||
}
|
||||
else if (serialControlEnabled)
|
||||
{
|
||||
static int lastSpeed = 0;
|
||||
int currSpeed = motor.getSpeed();
|
||||
|
||||
if (lastSpeed > currSpeed)
|
||||
{
|
||||
Serial.print("Decreased Speed to: ");
|
||||
Serial.println(currSpeed);
|
||||
lastSpeed = currSpeed;
|
||||
}
|
||||
else if (lastSpeed < currSpeed)
|
||||
{
|
||||
Serial.print("Increased Speed to: ");
|
||||
Serial.println(currSpeed);
|
||||
lastSpeed = currSpeed;
|
||||
}
|
||||
gTargetSpeedPercent = 0; // während Cal immer 0
|
||||
}
|
||||
else
|
||||
{
|
||||
int pedalValue = pedal.getPedal();
|
||||
motor.setTargetSpeed(pedalValue);
|
||||
}
|
||||
// Nur im Normalbetrieb: Pedal einlesen
|
||||
pedal.maintain();
|
||||
|
||||
if (liveSensorOutput)
|
||||
{
|
||||
unsigned long now = millis();
|
||||
if (now - lastSensorPrint > 200)
|
||||
if (!gSerialControl)
|
||||
{
|
||||
lastSensorPrint = now;
|
||||
|
||||
int raw = pedal.debugGetRaw();
|
||||
int smooth = pedal.debugGetSmooth();
|
||||
int pedalValue = pedal.getPedal();
|
||||
int doState = pedal.debugGetDO();
|
||||
|
||||
Serial.print("[SENSOR] RAW=");
|
||||
Serial.print(raw);
|
||||
Serial.print(" Smooth=");
|
||||
Serial.print(smooth);
|
||||
Serial.print(" Pedal%=");
|
||||
Serial.print(pedalValue);
|
||||
Serial.print(" DO=");
|
||||
Serial.println(doState);
|
||||
// Standard: Pedal → TargetSpeed
|
||||
gTargetSpeedPercent = gPedalPercent;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Serielle Steuerung ist aktiv, Target wird von SerialController manipuliert
|
||||
static int16_t lastSpeed = -1;
|
||||
if (lastSpeed != gCurrentSpeedPercent)
|
||||
{
|
||||
Serial.print("Current Speed: ");
|
||||
Serial.println(gCurrentSpeedPercent);
|
||||
lastSpeed = gCurrentSpeedPercent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update display
|
||||
// updateDisplay();
|
||||
// Motor nachführen
|
||||
motor.maintain();
|
||||
|
||||
delay(10); // Short delay for stability
|
||||
// Optional: Display
|
||||
// updateDisplay();
|
||||
|
||||
// Optional: eine Debug-Zeile mit Kerninfos
|
||||
// printStatusLine();
|
||||
|
||||
delay(10);
|
||||
}
|
||||
|
||||
// ----------------- HILFSFUNKTIONEN -----------------
|
||||
|
||||
void stopMotorAtNeedleUp()
|
||||
{
|
||||
motor.stopMotor();
|
||||
delay(100); // Stabilize
|
||||
delay(100);
|
||||
}
|
||||
|
||||
void loadSettings()
|
||||
{
|
||||
EEPROM.get(0, settings);
|
||||
|
||||
// Validate checksum
|
||||
if (calculateChecksum(settings) != settings.checksum)
|
||||
{
|
||||
Serial.println("Invalid settings checksum, loading defaults.");
|
||||
motor.loadDefaults();
|
||||
pedal.autoCalibrate(true);
|
||||
pedal.loadDefaults();
|
||||
|
||||
settings.motorConfig = motor.getConfig();
|
||||
settings.pedalConfig = pedal.getConfig();
|
||||
@@ -243,7 +311,7 @@ void handleEncoder()
|
||||
|
||||
void handleEncoderButton()
|
||||
{
|
||||
// Handle encoder button press
|
||||
// Encoder-Button-Logik
|
||||
}
|
||||
|
||||
void updateDisplay()
|
||||
@@ -254,110 +322,36 @@ void updateDisplay()
|
||||
|
||||
display.setCursor(0, 0);
|
||||
display.print("Pedal: ");
|
||||
display.println(pedal.getPedal());
|
||||
display.println(gPedalPercent);
|
||||
|
||||
display.setCursor(0, 10);
|
||||
display.print("Target: ");
|
||||
display.println(gTargetSpeedPercent);
|
||||
|
||||
display.setCursor(0, 20);
|
||||
display.print("Speed: ");
|
||||
display.println(gCurrentSpeedPercent);
|
||||
|
||||
display.display();
|
||||
}
|
||||
|
||||
void handleSerialInput()
|
||||
{
|
||||
if (Serial.available())
|
||||
{
|
||||
char input = Serial.read();
|
||||
switch (input)
|
||||
{
|
||||
case 'd':
|
||||
debug = !debug;
|
||||
Serial.print("Debug: ");
|
||||
Serial.println(debug ? "ON" : "OFF");
|
||||
break;
|
||||
case 'c':
|
||||
Serial.println("Starting auto-calibration...");
|
||||
pedal.autoCalibrate(true);
|
||||
autoCalibrating = true;
|
||||
lastCalibrationState = IDLE; // Reset state tracking
|
||||
break;
|
||||
case 'x':
|
||||
Serial.println("Stopping auto-calibration or serial control.");
|
||||
autoCalibrating = false;
|
||||
serialControlEnabled = false;
|
||||
motor.stopMotor();
|
||||
break;
|
||||
case 'r':
|
||||
Serial.println("Loading defaults.");
|
||||
motor.loadDefaults();
|
||||
pedal.loadDefaults();
|
||||
break;
|
||||
case 's':
|
||||
Serial.println("Saving settings to EEPROM.");
|
||||
saveSettings();
|
||||
break;
|
||||
case 'l':
|
||||
Serial.println("Loading settings from EEPROM.");
|
||||
loadSettings();
|
||||
break;
|
||||
case 'h':
|
||||
printHelp();
|
||||
break;
|
||||
case '+':
|
||||
serialControlEnabled = true;
|
||||
motor.setTargetSpeed(motor.getSpeed() + 1);
|
||||
break;
|
||||
case '-':
|
||||
serialControlEnabled = true;
|
||||
motor.setTargetSpeed(motor.getSpeed() - 1);
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
if (serialControlEnabled)
|
||||
{
|
||||
motor.setTargetSpeed((input - 0x30) * 10);
|
||||
Serial.print("Set Speed to: ");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void printHelp()
|
||||
{
|
||||
Serial.println("Available commands:");
|
||||
Serial.println("d - Toggle Calibration debug output");
|
||||
Serial.println("c - Start auto-calibration");
|
||||
Serial.println("x - Stop auto-calibration or serial control");
|
||||
Serial.println("r - Load defaults");
|
||||
Serial.println("s - Save settings to EEPROM");
|
||||
Serial.println("l - Load settings from EEPROM");
|
||||
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
|
||||
}
|
||||
|
||||
void printStatusLine()
|
||||
{
|
||||
if (!debug)
|
||||
return;
|
||||
|
||||
Serial.print("[STAT] Pedal=");
|
||||
Serial.print(gPedalPercent);
|
||||
Serial.print("% Target=");
|
||||
Serial.print(gTargetSpeedPercent);
|
||||
Serial.print("% Speed=");
|
||||
Serial.print(gCurrentSpeedPercent);
|
||||
Serial.print("% PWM=");
|
||||
Serial.println(gCurrentPwm);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user