|
| 1 | +// SoftPathCalibrator.cpp |
| 2 | +#include "SoftPathCalibrator.h" |
| 3 | + |
| 4 | +SoftPathCalibrator::SoftPathCalibrator() : |
| 5 | + _platform(SPK_PLATFORM), |
| 6 | + _pin(0), |
| 7 | + _numKeys(0), |
| 8 | + _tol(8), // empfohlener Standardwert |
| 9 | + _debounceAfterZero(false) |
| 10 | +{ |
| 11 | + _keyStr[0] = '\0'; |
| 12 | +} |
| 13 | + |
| 14 | +/* ---------- öffentlich ---------- */ |
| 15 | + |
| 16 | +void SoftPathCalibrator::begin() { |
| 17 | + Serial.begin(115200); |
| 18 | + while (!Serial) { } |
| 19 | + |
| 20 | + Serial.println(F("SoftPath Keypad Kalibrator v1.3")); |
| 21 | + printPlatform(); |
| 22 | + overridePlatform(); |
| 23 | + askPin(); |
| 24 | + askKeys(); |
| 25 | + askTolerance(); |
| 26 | + askDebounce(); |
| 27 | + calibrateKeys(); |
| 28 | + buildKeyString(); |
| 29 | + |
| 30 | + Serial.println(); |
| 31 | + Serial.println(F("Kalibrierung abgeschlossen. Key-String:")); |
| 32 | + Serial.println(_keyStr); |
| 33 | +} |
| 34 | + |
| 35 | +/* ---------- generische I/O-Helfer ---------- */ |
| 36 | + |
| 37 | +void SoftPathCalibrator::printPlatform() { |
| 38 | + Serial.print(F("Erkannte Plattform: ")); |
| 39 | + Serial.println(_platform == 0 ? F("UNO/AVR") : F("ESP32")); |
| 40 | +} |
| 41 | + |
| 42 | +void SoftPathCalibrator::overridePlatform() { |
| 43 | + Serial.println(F("Plattform überschreiben? " |
| 44 | + "'u' = UNO, 'e' = ESP32 (3 s)…")); |
| 45 | + unsigned long t0 = millis(); |
| 46 | + while (millis() - t0 < 3000) { |
| 47 | + if (Serial.available()) { |
| 48 | + char c = Serial.read(); |
| 49 | + if (c == 'u' || c == 'U') { _platform = 0; break; } |
| 50 | + if (c == 'e' || c == 'E') { _platform = 1; break; } |
| 51 | + } |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +void SoftPathCalibrator::readLine(String& out) { |
| 56 | + while (!Serial.available()) { delay(10); } |
| 57 | + out = Serial.readStringUntil('\n'); |
| 58 | + out.trim(); |
| 59 | +} |
| 60 | + |
| 61 | +bool SoftPathCalibrator::askYesNo(const __FlashStringHelper* prompt) { |
| 62 | + Serial.println(prompt); |
| 63 | + String line; readLine(line); |
| 64 | + if (!line.length()) return false; |
| 65 | + char c = line[0]; |
| 66 | + return (c == 'j' || c == 'J' || c == 'y' || c == 'Y'); |
| 67 | +} |
| 68 | + |
| 69 | +/* ---------- Hilfetexte ---------- */ |
| 70 | + |
| 71 | +void SoftPathCalibrator::helpTolerance() { |
| 72 | + Serial.println(F("\nToleranz beschreibt, wie stark der gemessene ADC-Wert\n" |
| 73 | + "vom Sollwert einer Taste abweichen darf, damit sie noch\n" |
| 74 | + "als gedrückt erkannt wird. Ein zu kleiner Wert führt\n" |
| 75 | + "zu Fehlschlägen, ein zu großer kann Mehrfachzuordnungen\n" |
| 76 | + "verursachen.\n" |
| 77 | + "Beispiel bei 10-Bit-ADC und Sollwert 512:\n" |
| 78 | + "Toleranz 8 → gültig 504…520\n" |
| 79 | + "Toleranz 20 → gültig 492…532\n")); |
| 80 | +} |
| 81 | + |
| 82 | +void SoftPathCalibrator::helpDebounce() { |
| 83 | + Serial.println(F("\n„Nach Nullwert entprellen“ sorgt dafür, dass zwischen\n" |
| 84 | + "zwei Tastendrücken erst ein eindeutiger Null-ADC-Wert\n" |
| 85 | + "erkannt werden muss. Dadurch werden Fehlerkennungen\n" |
| 86 | + "bei sehr schnellen oder überschneidenden Drücken\n" |
| 87 | + "vermieden.\n" |
| 88 | + "Aktiviert (j): gedrückt → loslassen (ADC≈0) → nächste Taste\n" |
| 89 | + "Deaktiviert (n): jede Taste sofort gültig.\n")); |
| 90 | +} |
| 91 | + |
| 92 | +/* ---------- Fragen ---------- */ |
| 93 | + |
| 94 | +void SoftPathCalibrator::askPin() { |
| 95 | + if (_platform == 0) { |
| 96 | + Serial.println(F("Analogen Pin eingeben (A0–A5 oder 0–5):")); |
| 97 | + String line; readLine(line); line.toUpperCase(); |
| 98 | + if (line.startsWith("A")) line.remove(0, 1); |
| 99 | + _pin = constrain(line.toInt(), 0, 5); |
| 100 | + } else { |
| 101 | + Serial.println(F("GPIO-Nummer eingeben (0–39):")); |
| 102 | + String line; readLine(line); |
| 103 | + _pin = constrain(line.toInt(), 0, 39); |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +void SoftPathCalibrator::askKeys() { |
| 108 | + Serial.println(F("Anzahl der Tasten (3 / 4 / 6 / 12 / 16):")); |
| 109 | + String line; readLine(line); |
| 110 | + uint8_t n = line.toInt(); |
| 111 | + if (n == 3 || n == 4 || n == 6 || n == 12 || n == 16) _numKeys = n; |
| 112 | + else _numKeys = 4; |
| 113 | +} |
| 114 | + |
| 115 | +void SoftPathCalibrator::askTolerance() { |
| 116 | + for (;;) { |
| 117 | + Serial.println(F("Toleranz 4–60 (Standard 8, 'h' für Hilfe):")); |
| 118 | + String line; readLine(line); |
| 119 | + if (!line.length()) { _tol = 8; break; } // Standard |
| 120 | + if (line[0] == 'h' || line[0] == 'H') { helpTolerance(); continue; } |
| 121 | + uint16_t v = line.toInt(); |
| 122 | + if (v >= 4 && v <= 60) { _tol = v; break; } |
| 123 | + Serial.println(F("Ungültige Eingabe.\n")); |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +void SoftPathCalibrator::askDebounce() { |
| 128 | + for (;;) { |
| 129 | + Serial.println( |
| 130 | + F("Nach Nullwert entprellen? (j/n, 'h' für Hilfe):")); |
| 131 | + String line; readLine(line); |
| 132 | + if (!line.length()) { _debounceAfterZero = false; break; } |
| 133 | + char c = line[0]; |
| 134 | + if (c == 'h' || c == 'H') { helpDebounce(); continue; } |
| 135 | + if (c == 'j' || c == 'J' || c == 'y' || c == 'Y') { |
| 136 | + _debounceAfterZero = true; break; |
| 137 | + } |
| 138 | + if (c == 'n' || c == 'N') { _debounceAfterZero = false; break; } |
| 139 | + Serial.println(F("Bitte 'j' oder 'n' eingeben.\n")); |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +/* ---------- ADC-Helfer ---------- */ |
| 144 | + |
| 145 | +uint16_t SoftPathCalibrator::readAnalogOnce() { |
| 146 | +#if SPK_PLATFORM == 0 |
| 147 | + return analogRead(_pin + A0); |
| 148 | +#else |
| 149 | + return analogRead(_pin); |
| 150 | +#endif |
| 151 | +} |
| 152 | + |
| 153 | +void SoftPathCalibrator::gatherReadings(uint16_t* buf) { |
| 154 | + uint8_t i = 0; |
| 155 | + while (i < 6) { |
| 156 | + uint16_t v = readAnalogOnce(); |
| 157 | + if (v > 0) { |
| 158 | + buf[i++] = v; |
| 159 | + Serial.print('.'); |
| 160 | + } |
| 161 | + delay(8); |
| 162 | + } |
| 163 | + Serial.println(); |
| 164 | +} |
| 165 | + |
| 166 | +uint16_t SoftPathCalibrator::medianOfSix(uint16_t* arr) { |
| 167 | + uint16_t t[6]; |
| 168 | + for (uint8_t i = 0; i < 6; ++i) t[i] = arr[i]; |
| 169 | + for (uint8_t i = 0; i < 5; ++i) |
| 170 | + for (uint8_t j = i + 1; j < 6; ++j) |
| 171 | + if (t[j] < t[i]) { uint16_t x = t[i]; t[i] = t[j]; t[j] = x; } |
| 172 | + return (uint16_t)((t[2] + t[3]) / 2); |
| 173 | +} |
| 174 | + |
| 175 | +/* ---------- ASCII-Layout ---------- */ |
| 176 | + |
| 177 | +void SoftPathCalibrator::drawLayout(uint8_t highlight) { |
| 178 | + uint8_t cols, rows; |
| 179 | + switch (_numKeys) { |
| 180 | + case 3: cols = 3; rows = 1; break; |
| 181 | + case 4: cols = 4; rows = 1; break; |
| 182 | + case 6: cols = 3; rows = 2; break; |
| 183 | + case 12: cols = 3; rows = 4; break; |
| 184 | + default: cols = 4; rows = 4; break; |
| 185 | + } |
| 186 | + |
| 187 | + Serial.println(); |
| 188 | + for (uint8_t r = 0; r < rows; ++r) { |
| 189 | + Serial.print('+'); |
| 190 | + for (uint8_t c = 0; c < cols; ++c) Serial.print(F("----+")); |
| 191 | + Serial.println(); |
| 192 | + |
| 193 | + Serial.print('|'); |
| 194 | + for (uint8_t c = 0; c < cols; ++c) { |
| 195 | + uint8_t idx = r * cols + c; |
| 196 | + char cell[5] = " "; |
| 197 | + if (idx < _numKeys) { |
| 198 | + char d[4]; sprintf(d, "%u", idx + 1); |
| 199 | + if (idx == highlight) cell[0] = '>'; |
| 200 | + memcpy(cell + 1, d, strlen(d)); |
| 201 | + } |
| 202 | + Serial.print(cell); Serial.print('|'); |
| 203 | + } |
| 204 | + Serial.println(); |
| 205 | + } |
| 206 | + Serial.print('+'); |
| 207 | + for (uint8_t c = 0; c < cols; ++c) Serial.print(F("----+")); |
| 208 | + Serial.println("\n"); |
| 209 | +} |
| 210 | + |
| 211 | +/* ---------- Kalibrierung ---------- */ |
| 212 | + |
| 213 | +void SoftPathCalibrator::calibrateKeys() { |
| 214 | + uint16_t buf[6]; |
| 215 | + |
| 216 | + Serial.println(F("=== Kalibrierung ===")); |
| 217 | + for (uint8_t k = 0; k < _numKeys; ++k) { |
| 218 | + bool done = false; |
| 219 | + while (!done) { |
| 220 | + drawLayout(k); |
| 221 | + uint16_t med1 = 0, med2 = 0; |
| 222 | + |
| 223 | + for (uint8_t p = 0; p < 2; ++p) { |
| 224 | + Serial.print(F("Taste ")); Serial.print(k + 1); |
| 225 | + Serial.print(F(", Durchgang ")); Serial.print(p + 1); |
| 226 | + Serial.println(F(": drücken & halten…")); |
| 227 | + while (readAnalogOnce() == 0) { delay(5); } |
| 228 | + gatherReadings(buf); |
| 229 | + (p == 0 ? med1 : med2) = medianOfSix(buf); |
| 230 | + Serial.println(F("Loslassen…")); |
| 231 | + while (readAnalogOnce() > 0) { delay(5); } |
| 232 | + } |
| 233 | + |
| 234 | + uint16_t diff = (med1 > med2) ? (med1 - med2) : (med2 - med1); |
| 235 | + if (diff > _tol) { |
| 236 | + Serial.print(F("Warnung: Abweichung ")); |
| 237 | + Serial.print(diff); |
| 238 | + Serial.print(F(" > Toleranz ")); |
| 239 | + Serial.println(_tol); |
| 240 | + if (askYesNo( |
| 241 | + F("Evtl. falsche Taste? Nochmals versuchen? (j/n):"))) |
| 242 | + continue; |
| 243 | + } |
| 244 | + _values[k] = (uint16_t)((med1 + med2) / 2); |
| 245 | + Serial.print(F(" → gespeicherter Wert: ")); |
| 246 | + Serial.println(_values[k]); |
| 247 | + done = true; |
| 248 | + } |
| 249 | + } |
| 250 | +} |
| 251 | + |
| 252 | +/* ---------- Key-String ---------- */ |
| 253 | + |
| 254 | +void SoftPathCalibrator::buildKeyString() { |
| 255 | + char* p = _keyStr; |
| 256 | + int n = sprintf(p, "SPK1 %u %u %u %u %u", |
| 257 | + _platform, _pin, _numKeys, |
| 258 | + _tol, _debounceAfterZero ? 1 : 0); |
| 259 | + p += n; |
| 260 | + for (uint8_t i = 0; i < _numKeys; ++i) { |
| 261 | + n = sprintf(p, " %u", _values[i]); p += n; |
| 262 | + } |
| 263 | + sprintf(p, "\r\n"); |
| 264 | +} |
0 commit comments