Kelly Controller im Futura Classic
- JMW_Stg
- Beiträge: 28
- Registriert: So 23. Sep 2018, 17:59
- Roller: Futura Classic
- PLZ: 71097
- Kontaktdaten:
Re: Kelly Controller im Futura Classic
@slothorpe, also du könntest dann wenigstens erwähnen, dass er vorher seinen Motor checken soll ob er phase = 60 hat oder nicht. Die Aussage ist mir zu pauschal und jeder weiß dass die Dinger dauernd neu mit den Einzelkomponenten bestückt werden und nicht baugleich sind über die Jahre
-
- Beiträge: 566
- Registriert: Do 4. Jul 2019, 14:14
- Roller: Robo-S, Classico LI, selbst gebautes Lasten-Pedelec
- PLZ: 10717
- Wohnort: Berlin
- Kontaktdaten:
Re: Kelly Controller im Futura Classic
Schau Dir einfach seine Fotos genauer an... er hat den gleichen Controller wie ich früher mit 120Grad, steht deutlich lesbar da!
- jeff-jordan
- Beiträge: 1123
- Registriert: Sa 16. Mai 2020, 08:47
- Roller: Classico LI & Z-Odin
- PLZ: 6...
- Tätigkeit: Dem Inschenjöhr is nix zu schwöhr...
- Kontaktdaten:
Re: Kelly Controller im Futura Classic
Hi zusammen,
bin auch dabei in meinen Classico Li 05/2020 einen Kelly (KLS7218S) einzubauen (weil mein original-Motor definitiv 60° Phasenwinkel hat und damit ein Sabvoton nicht in Frage kommt).
Als erstes galt es da für mich mal das Problem mit der Tacho-Ansteuerung zu lösen.
Das von slothorpe erstellte Programm produzierte beim neuen Tacho jedoch eine "Christbaumbeleuchtung", d.h. es war in der Form erstmal nicht brauchbar.
Also ran mit dem Speicher-Oszi und nochmal selbst alles durchmessen.
Wie's ausschaut ist die Nutzung der einzelnen übertragenen Datenbytes leicht unterschiedlich.
Nach Überarbeitung von slothorpe's Programm läuft die Tacho-Ansteuerung nun einwandfrei und "smooth".
Nebenbei habe ich die Frequenzmessung über eine eingebundene Library rausgeschmissen (lief nicht so zuverlässig wie erhofft) und durch eine eigene Routine zur Frequenzbestimmung auf Basis der Periodendauer ersetzt.
Damit bekommt man auch in einem kurzen Zeitraum von 200ms saubere Ergebnisse.
Ich bin mal so frei und poste hier meine Version der Ansteuerung mit einem Arduiono Nano:
Pinbelegung der Stecker, Hinweise zur Eingangsschaltung etc. findet sich alles im Code dokumentiert
.
@slothorpe: Wenn Du magst, kannst Du das als "branch" in deinem Github hinterlegen...
Cheers,
jeff-jordan
bin auch dabei in meinen Classico Li 05/2020 einen Kelly (KLS7218S) einzubauen (weil mein original-Motor definitiv 60° Phasenwinkel hat und damit ein Sabvoton nicht in Frage kommt).
Als erstes galt es da für mich mal das Problem mit der Tacho-Ansteuerung zu lösen.
Das von slothorpe erstellte Programm produzierte beim neuen Tacho jedoch eine "Christbaumbeleuchtung", d.h. es war in der Form erstmal nicht brauchbar.
Also ran mit dem Speicher-Oszi und nochmal selbst alles durchmessen.
Wie's ausschaut ist die Nutzung der einzelnen übertragenen Datenbytes leicht unterschiedlich.
Nach Überarbeitung von slothorpe's Programm läuft die Tacho-Ansteuerung nun einwandfrei und "smooth".
Nebenbei habe ich die Frequenzmessung über eine eingebundene Library rausgeschmissen (lief nicht so zuverlässig wie erhofft) und durch eine eigene Routine zur Frequenzbestimmung auf Basis der Periodendauer ersetzt.
Damit bekommt man auch in einem kurzen Zeitraum von 200ms saubere Ergebnisse.
Ich bin mal so frei und poste hier meine Version der Ansteuerung mit einem Arduiono Nano:
Code: Alles auswählen
/* Ansteuerung fuer Futura Classico LI 05/2020 Tacho (mit Anzeige des Akku-Füllstands via BMS)
* Hinweis: Die Ansteuerung der Akku-Füllstandsanzeige im Tacho ist NICHT Gegenstand dieses Programmes
*
* Steckerbelegung des Anschlüsse an der Kabelpeitsche des neuen Tachos:
* Draufsicht auf den 9-fach Stecker:
* _
* _____| |_____
* | (1) (2) (3) | (1) gelb = Blinker links (12V), (2) grau = Data Tachoansteuerung (5Vss), (3) schwarz = GND
* | |
* | (4) (5) (6) | (4) grün = Blinker rechts (12V), (5) weiß = Fernlicht (12V), (6) rot = +Vbatt (72V)
* | |
* | (7) (8) (9) | (7) gelb/schwarz = Battery-Management +, (8) blau/schwarz = Battery-Management -, (9) lila = +3,3V
* _______________
*
* Draufsicht auf den 2-fach Stecker:
* _
* ___| |___
* | (1) (2) | (1) nicht beschaltet, (2) braun = +12V (vom Schlüsselschalter/Zündschloss ?)
* ___________
*
* Draufsicht auf die 2-fach Kupplung:
* _
* ___| |___
* | (1) (2) | (1) rot/schwarz = Reset-Taster für Tagestrip-Zähler (+), (2) schwarz = Reset-Taster für Tagestrip-Zähler (-)
* ___________
*
* Um den Tacho "außerhalb des Rollers" betreiben zu können müssen mindestens GND, +Vbatt und +12V angelegt werden. Bei +Vbatt tun es auch 60V.
* Hinweis zur Akku-Füllstandsanzeige: Die Leitungen für das Battery-Management sind direkt mit dem Chogori-Stecker (Pin 4 = BM-, Pin 7 = BM+) für den Akku verbunden.
*
* Das folgende Programm basiert auf dem von Holger Lesch (aka: slothorpe, holger.lesch@gmx.net) bereitgestellten Code für die Kommunikation zwischen Controller & Tacho (Stand 27.10.2019)
* Modifiziert durch Stephan Hans (aka: jeff_jordan, jeff-jordan@nanomail.de), 25.08.2020.
*
* Der erste Versuch "slothorpe's" Version am neuen Classico LI 05/2020 zu betreiben führte zu einer "Christbaum-Anzeige" am Tacho, mit erratischer Symbol- und Geschwindigkeitsanzeige,
* die weder mit der tatsächlichen Geschwindikeit korrelierte, noch auf Frequenzänderungen des Eingangssignals in prognostizierbarer Weise reagierte.
* => Also nochmal ran mit dem Speicheroszilloskop und die Kommunikation selbst durchmessen, dokumentieren und in den Code einfließen lassen.
*
* Das Ergebnis ist eine "aufgeräumte" Version für den Arduino Nano, welche jedoch ein zusätzliches Bauteil (D-Flip-Flop, bspw. CMOS CD4013 oder TTL 74LS74) bedingt.
* Dies ist nicht weiter tragisch, da man ohnehin eine Eingangsschaltung benötigt um a) den Arduino-Nano zu schützen und b) ein "sauberes" Signal zur Messung der Hall-Impulse bereitzustellen.
*
* Änderungen gegenüber Holgers Version:
* Frequenzmessung über Periodendauer, ohne Verwendung einer Bibliothek => führt zu genaueren (und weitestgehend fehlerfreien) Messung der Frequenz, auch im kurzen Zeitintervall (hier max. 200ms).
* Korrektur in der Nutzung der Bytes, die in Richtung Tacho geschickt werden. Betrifft im Wesentlichen die Bytes#2 & #1.
*
* Anmerkungen:
* Die Verwendung von Byte#2 (outbuf[1] = count++) war höchstwahrscheinlich der Grund für die "Christbaum-Anzeige" beim neuen Tacho.
* Beim neuen Tacho konnte kein "Schummelverhalten" bei Geschwindigkeiten unter 10km/h und über 40km/h festgestellt werden.
* Die Anzeige arbeitet ab 4km/h bis 96km/h recht genau (auf Basis der berechneten Hall-Signal Frequenz).
*/
// ----------------------------------------------------------------Programmstart-------------------------------------------------------------------- //
const byte inputPin = 5; // Eingang für die Frequenzmessung. Die Frequenz wird über die Periodendauer ermittelt. Durch einen vorgeschalteten 1/2 Frequenzteiler (Rückgekoppeltes D-Flip-Flop)
// wird sichergestellt, dass ein rechteckiges Eingangssignal mit 50:50 Tastverhältnis vorliegt, es also auch der Frequenzbestimmungsroutine egal sein kann welcher Zyklus ausgewertet wird.
// Hauptsache es kann die Zeit zwischen zwei Flanken ermittelt werden!
double frequenz;
boolean measure_ready = false;
boolean measure_running = false;
long startzeit; // Für das Zwischenspeichern der Systemzeit in Millisekunden beim Aufruf der Frequenzbestimmungsroutine
long timeout; // Systemzeit in Millisekunden bei der die Frequenzbestimmung spätestens (mit Ergebnis Frequenz=0) abgebrochen wird (sonst hängt man Endlos in der Frequenzbestimmung fest).
long now; // Hier wird ein Zeitpunkt während der Frequenzbestimmung festgehalten (in Millisekunden Systemzeit)
long measure_duration; // Hier wird die Dauer bestimmt, wie lange in der Frequenzbestimmungsroutine verweilt wurde (mit oder ohne Ergebnis).
long periodenstart = 0; // Zwischenspeicher der Systemzeit in Mikrosekunden, zum Zeitpunkt der ersten detektierten Flanke des Eingangssignals.
long periodenende = 0; // Zwischenspeicher der Systemzeit in Mikrosekunden, zum Zeitpunkt der zweiten detektierten Flanke des Eingangssignals.
long periodendauer = 0; // Zwischenspeicher für die gemessene Zyklusdauer (in µs).
boolean pinstatus;
int geschwindigkeit16; // Es wird gleich das 16fache der Geschwindigkeit berechnet.
// Der Tacho erwartet die Geschwindigkeit im unteren Nibble vom 8.Byte (outbuf[7])
// und dem 9. Byte (outbuf[8]).
// Die angezeigte Geschwindigkeit ist dabei 16* outbuf[7] plus dem oberen Nibble von outbuf[8].
// Das untere Nibble von outbuf[8] hält dabei "Nachkommastellen", die nicht zur Anzeige, aber zur
// Berechnung der zurückgelegten Strecke herangezogen werden.
// Durch die Berechnung der 16fachen Geschwindigkeit kann man die Bytes #8 und #9 ganz einfach in das
// Byte-Array outbuf[12] übertragen:
// outbuf[8]=geschwindigkeit16; -> hier werden nur die unteren 8-Bit des Integer-Wertes "geschwindigkeit16"
// übernommen, also genau das was der Tacho in Byte#9 erwartet.
// Mit outbuf[7]=geschwindigkeit16>>8; bekommt man dann ganz einfach geschwindigkeit16/256 ->
// = tatsächliche Geschwindigkeit / 16.
const byte ledPin = 13; // Aktivitaetsanzeige
const byte outputPin = 4; // Ausgang Richtung Tacho
const int timePause = 354; // in ms
const int timeLow = 500; // in Mikrosekunden, eine Abweichung von +/- 20% verdaut mein Tacho problemlos ;-).
byte outbuf[12]; // Hält die 12 Byte Datensequenz, die in Richtung Tacho geschickt werden muss.
// -----------------------------------------------------------------Pins initialisieren, Default-Werte in den Datenbuffer laden------------------------------------ //
void setup() {
// put your setup code here, to run once:
pinMode(outputPin, OUTPUT); //
digitalWrite(outputPin,0);
pinMode(ledPin, OUTPUT);
pinMode(inputPin, INPUT_PULLUP);
digitalWrite(ledPin,0);
outbuf[0]=0x08; // 0 km/h Sequenz
outbuf[1]=0x61; // 0x61 original.
outbuf[2]=0x00; // 0x08 -> Handbrake ((P)), Finger weg von den Bits im oberen Nibble -> einzelne Bits bewirken schon mehrere Symbol-Kombinationen & pseudo-Geschwindigkeiten.
outbuf[3]=0x00; // 0x01 -> Motorsymbol, 0x04 -> Tachosymbol/Cruise-Control, 0x10 -> ECU, 0x20 -> Throttle, 0x40 Motorsymbol
outbuf[4]=0x00; // FF, F0, 20 -> Bremssymbol an, Ready aus
outbuf[5]=0x00; // nix
outbuf[6]=0x00; // FF führt dazu, dass die Batterieanzeige langsam herunter läuft (ca. 14 Sekunden pro Prozent)
outbuf[7]=0x00;
outbuf[8]=0x00; // Anzeige ab 0x3d (outbuf[7]=0) mit 4 km/h
outbuf[9]=0x00; // nix
outbuf[10]=0x00; // nix
}
// -------------------------------------------------------------------Routinen für die Datenausgabe----------------------------------------------------------------- //
void writeStart()
{
digitalWrite(outputPin,1);
delayMicroseconds(timeLow * 2);
digitalWrite(outputPin,0);
delayMicroseconds(timeLow); // Fehler im Original korrigiert
}
void write0()
{
digitalWrite(outputPin,0);
delayMicroseconds(timeLow * 2);
digitalWrite(outputPin,1);
delayMicroseconds(timeLow);
}
void write1()
{
digitalWrite(outputPin,0);
delayMicroseconds(timeLow);
digitalWrite(outputPin,1);
delayMicroseconds(timeLow * 2);
}
void writeSequence(byte* seq, int len)
{
int i,j;
byte out;
for (i=0;i<len; i++)
{
out = seq[i];
for (j=0;j<8;j++)
{
if (out & 0x80) write1 (); else write0();
out=out<<1;
}
}
digitalWrite(outputPin,0);
}
// --------------------------------------------------------------Frequenzermittlung via Periodendauer------------------------------------------------------------------- //
void frequenzmessung() // Die Frequenz wird über die Periodendauer bestimmt. Daher muss ein 1:2 Frequenzteiler (Flipflop) vorgeschaltet werden um 50:50 Duty-Cycle zu erreichen
{
measure_ready = false;
measure_running = false;
pinstatus = digitalRead(inputPin);
startzeit = millis();
timeout = startzeit + 200; // Nach spätestens 200ms soll die Messung mit oder ohne Ergebnis abgeschlossen werden.
while ((!measure_ready)) // Wartet auf 2 aufeinanderfolgende Flanken im Eingangssignal... oder ein Timeout
{
if ((digitalRead(inputPin)!=pinstatus) && (!measure_running)) // Es ist egal, ob die positive oder die negative Halbwelle startet, Hauptsache eine Flanke wird detektiert.
{
periodenstart = micros();
measure_running = true;
pinstatus=!pinstatus;
}
if ((digitalRead(inputPin)!=pinstatus) && (measure_running)) // Hier wird die nächste Flanke detektiert.
{
periodenende=micros();
pinstatus=!pinstatus;
measure_ready=true;
measure_running=false;
}
now=millis(); // Zeit bestimmen, wie lange auf 2 Flanken gewartet wurde.
if (now>timeout) // Beim Timeout die Frequenz auf 0 setzen (keine Messung), das measure_ready Flag setzen und damit die While-Schleife beenden.
{
frequenz=0;
measure_ready=true;
}
else if (measure_ready) // Wurde kein Timeout erkannt, konnte die Periodendauer erfolgreich gemessen werden.
{
periodendauer=periodenende-periodenstart; // Bestimmung der Periodendauer
frequenz=1000000/periodendauer; // Umrechnung in die Frequenz
}
} // Ende der while-Schleife
measure_duration=millis()-startzeit; // Merker für die Dauer der Frequenzmessung -> wird benötigt um das Senden der Datenpakete auf den gleichen Rhythmus zu bringen wie zwischen Original-Controller & Tacho (alle 500ms).
}
// ------------------------------------------------------------Hauptprogramm------------------------------------------------------------------------ //
void loop() // Hauptprogramm Schleife
{
int i;
static byte out;
int checksum = 0;
frequenzmessung();
geschwindigkeit16=frequenz*2.991; // 16x tatsächliche Geschwindigkeit; Faktor berechnet sich mit x = 57,6*Radumfang[m]/(halldivider)
// der "halldivider" ist beim original-Motor = 26, beim QS 205 V3 = 16.
// Original-Motor ist hier ein: HE10722900200360047
outbuf[7]=geschwindigkeit16>>8;
outbuf[8]=geschwindigkeit16;
digitalWrite(ledPin,1);
writeStart(); // Sendet das Startbit -> Achtung Tacho, gleich geht's los mit den Bytes :-)
checksum = 0;
// ... hier startet die Ausgabe der 12 Byte in Richtung Tacho
for (i=0;i<11; i++) // ... erst ein mal die ersten 11 Bytes
{
checksum=checksum ^ outbuf[i]; // ... dabei wird die Prüfsumme mit XOR (bitweise) berechnet
writeSequence(&outbuf[i],1);
} // ... die ersten 11 Byte sind fertig übertragen.
out = checksum;
writeSequence(&out,1); // ... und raus mit der Prüfsumme.
// Die komplette Ausgabe der Sequenz für den Tacho dauert 146 ms
digitalWrite(ledPin,0);
delay(timePause-measure_duration); // Im Classico LI 05/2020 erhält der Tacho alle 500ms ein neues Datenpaket.
// Da die Frequenzmessung ihre Zeit beansprucht hat, muss somit nur noch 354ms - Messdauer bis zur nächsten Sequenz gewartet werden.
}
// ------------------------------------------------------Ferdisch----------------------------------------------------------------------- //

@slothorpe: Wenn Du magst, kannst Du das als "branch" in deinem Github hinterlegen...
Cheers,
jeff-jordan
Zuletzt geändert von jeff-jordan am Mo 31. Aug 2020, 17:33, insgesamt 3-mal geändert.
Classico Li 05/2020 11 000+ km & Z-Odin 12/2021 26 000+ km 

-
- Beiträge: 566
- Registriert: Do 4. Jul 2019, 14:14
- Roller: Robo-S, Classico LI, selbst gebautes Lasten-Pedelec
- PLZ: 10717
- Wohnort: Berlin
- Kontaktdaten:
Re: Kelly Controller im Futura Classic
Hallo Jeff-Jordan,
Danke, klasse, auch dass Du die Frequenz-Messung Überarbeitet hast, ich war auch nie 100% zufrieden damit und nutze schon länger in meinem Roller die Register des Sabvoton, aus denen ich die Radrehzahl ableite.
Würde vorschlagen, wir verheiraten die beiden Quellen und legen dass dann bei GitHub ab, das wäre dann sowohl für 2019er wie 2020er Roller.
Gruß
Holger
Danke, klasse, auch dass Du die Frequenz-Messung Überarbeitet hast, ich war auch nie 100% zufrieden damit und nutze schon länger in meinem Roller die Register des Sabvoton, aus denen ich die Radrehzahl ableite.
Würde vorschlagen, wir verheiraten die beiden Quellen und legen dass dann bei GitHub ab, das wäre dann sowohl für 2019er wie 2020er Roller.
Gruß
Holger
Wer ist online?
Mitglieder in diesem Forum: 0 Mitglieder und 9 Gäste