Ich sitze nun am letzten Sketch für den Fahrschalter und benötige nochmals input von den erfahrenen Programmieren unter euch:
Ausgangssituation: 8 Stufen von EB+ bis Tmax
Wertebereich von 0 bis 1023
Tmax, T+, EB- und EB+ haben Endanschläge, die eine Zuordnung relativ einfach machen. Überall gibt es 1-2 Werte, zwischen denen der Sensor hin- und her springt. (meist 100-200 Punkte auseinander).
Zwischen den 8 Stufen werden durch den AS5040 Sensor jedoch immer wieder die selben Rohwerte ausgegeben. Die Achse dreht sich digital also mehrmals und springt von 1023 wieder auf 0, wieder hoch auf 1023 und wieder auf 0 usw.
So lässt sich nicht perfekt zuordnen, welcher Wert zu welcher Position des Fahrschalters gehört.
Da ich Chatgpt für den Code nutze, dreht er sich seit einiger Zeit auch nurnoch im Kreis mit seinen Lösungsansätzen, weswegen ich jetzt hier nachfrage.
Hier mal der TestCode:
Code: Alles auswählen
#include <Joystick.h>
// ---------- Joystick (nur X) ----------
Joystick_ Joystick;
// ---------- Pins ----------
const int PIN_CS = 10;
const int PIN_CLK = 13;
const int PIN_DO = 12;
// ---------- Medianfilter ----------
const int FILTER_SIZE = 15;
uint16_t filterBuf[FILTER_SIZE];
int filterIdx = 0;
uint16_t medianFilter(uint16_t v) {
filterBuf[filterIdx] = v;
filterIdx = (filterIdx + 1) % FILTER_SIZE;
uint16_t tmp[FILTER_SIZE];
for (int i = 0; i < FILTER_SIZE; i++) tmp[i] = filterBuf[i];
// simple sort for median
for (int i = 0; i < FILTER_SIZE - 1; i++)
for (int j = i + 1; j < FILTER_SIZE; j++)
if (tmp[j] < tmp[i]) {
uint16_t t = tmp[i]; tmp[i] = tmp[j]; tmp[j] = t;
}
return tmp[FILTER_SIZE / 2];
}
// ---------- Positionen ----------
struct Position {
const char* name;
uint16_t minVal;
uint16_t maxVal;
int joyValue;
float xPercent;
bool isDynamic;
uint16_t dynamicValues[5]; // für dynamische Positionen wie T Constant / T-
int numDynamic;
};
// Werte der jeweiligen Positionen
Position positions[] = {
{"Tmax", 161, 161, 1023, 100.0, false, {}, 0},
{"T+", 481, 481, 896, 87.5, false, {}, 0},
{"T Constant", 480, 673, 512, 75.0, true, {544, 609, 673, 545, 480}, 5},
{"T-", 545, 992, 640, 62.5, true, {545, 417, 353, 992}, 4},
{"Neutral", 928, 993, 500, 50.0, false, {}, 0},
{"EB-", 224, 224, 384, 37.5, false, {}, 0},
{"EB Constant",353, 480, 256, 25.0, false, {}, 0},
{"EB+", 608, 672, 0, 0.0, false, {}, 0}
};
// ---------- Klassifizierung ----------
int lastDynamicValue = -1;
int classifyPosition(uint16_t filt) {
// 1. feste Positionen prüfen
for (int i = 0; i < sizeof(positions)/sizeof(positions[0]); i++) {
Position& p = positions[i];
if (!p.isDynamic) {
if (filt >= p.minVal && filt <= p.maxVal) return i;
}
}
// 2. dynamische Positionen prüfen
for (int i = 0; i < sizeof(positions)/sizeof(positions[0]); i++) {
Position& p = positions[i];
if (p.isDynamic) {
// nächstgelegener Wert
int closest = 0;
uint16_t diff = 1024;
for (int j = 0; j < p.numDynamic; j++) {
uint16_t v = p.dynamicValues[j];
uint16_t d = abs(int(filt) - int(v));
if (d < diff) {
diff = d;
closest = j;
}
}
lastDynamicValue = p.dynamicValues[closest];
return i;
}
}
return -1;
}
// ---------- AS5040 lesen ----------
uint16_t readAS5040() {
uint16_t v = 0;
digitalWrite(PIN_CS, LOW);
delayMicroseconds(2);
for (int i = 0; i < 16; i++) {
digitalWrite(PIN_CLK, HIGH);
delayMicroseconds(1);
v <<= 1;
if (digitalRead(PIN_DO)) v |= 1;
digitalWrite(PIN_CLK, LOW);
delayMicroseconds(1);
}
digitalWrite(PIN_CS, HIGH);
delayMicroseconds(2);
return v & 0x03FF;
}
// ---------- Timing ----------
const unsigned long stableDelay = 250;
int lastClass = -1;
int confirmedClass = -1;
int joyValue = 512;
unsigned long classStartTime = 0;
void setup() {
Serial.begin(115200);
delay(200);
Serial.println("=== AS5040 Fahrschalter Position ===");
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_CLK, OUTPUT);
pinMode(PIN_DO, INPUT);
digitalWrite(PIN_CS, HIGH);
digitalWrite(PIN_CLK, LOW);
for (int i = 0; i < FILTER_SIZE; i++) filterBuf[i] = 224;
Joystick.setXAxisRange(0, 1023);
Joystick.begin();
classStartTime = millis();
}
void loop() {
uint16_t raw = readAS5040();
uint16_t filt = medianFilter(raw);
int cls = classifyPosition(filt);
unsigned long now = millis();
if (cls != lastClass) {
lastClass = cls;
classStartTime = now;
}
if (cls != -1 && cls == lastClass && (now - classStartTime >= stableDelay)) {
if (cls != confirmedClass) {
confirmedClass = cls;
joyValue = positions[cls].joyValue;
Joystick.setXAxis(joyValue);
Joystick.sendState();
}
}
// Nur aktuelle Position ausgeben
if (cls >= 0) {
Serial.println(positions[cls].name);
} else {
Serial.println("UNKNOWN");
}
delay(10);
}
Es gibt grundsätzlich zwei Probleme:
Problem 1:
Der Fahrschalter hat in der Tconstant, EBconstant und in der Neutralstellung etwas mechanisches Spiel und folglich ist es möglich in andere Positionsbereiche reinzuspringen, da die Werte sich gleichen.
Mit dem Code oben es gerade so, dass sich alle Positionen bis auf T- jedoch reeeeelativ gut auslesen lassen.
Damit folgt Problem 2:
T- ist von der Federung so ausgelegt wie bei einem Auf-Ab Fahrschalter. Man kann also von Tconstant über zig Werte in den T- Bereich ziehen ohne eine feste Rastung zu haben, bis er irgendwann über den Widerstand zurück in die Neutralstellung fällt.
Möchte man also von Tconstant Leistung zurück nehmen, landet man jedes mal auf einem anderen Positionswert für T-. All diese Werte aber für T- zu deklarieren, beißt sich mit anderen Positionen, die bereits teilweise die selben Werte nutzen, oder durch mechanisches Spiel in diese reinrutschen können.
Mir fehlt schlicht das Know-How für eine codetechnische Lösung der Probleme.
Auch hier befindet sich außen ein kleiner Zauberkasten mit Platine und u.a. zwei 9-Pin D-Sub Anschlüssen (Männlein, Weiblein). Gibt es hier, ähnlich wie beim Schaltbau Fahrschalter, eine Lösungsmöglichkeit?
Ich danke euch für etwaige Denkanstöße und Ideen!