Kelly Controller im Futura Classic

Für Roller von Futura, zb Hawk, Classico, Elettrico und Roller der Marke Sunra
Antworten
Benutzeravatar
JMW_Stg
Beiträge: 28
Registriert: So 23. Sep 2018, 17:59
Roller: Futura Classic
PLZ: 71097
Kontaktdaten:

Re: Kelly Controller im Futura Classic

Beitrag von JMW_Stg »

@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

slothorpe
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

Beitrag von slothorpe »

Schau Dir einfach seine Fotos genauer an... er hat den gleichen Controller wie ich früher mit 120Grad, steht deutlich lesbar da!

Benutzeravatar
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

Beitrag von jeff-jordan »

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:

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----------------------------------------------------------------------- //
Pinbelegung der Stecker, Hinweise zur Eingangsschaltung etc. findet sich alles im Code dokumentiert :D .

@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 :idea:

slothorpe
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

Beitrag von slothorpe »

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

Antworten

Zurück zu „Futura / Sunra“

Wer ist online?

Mitglieder in diesem Forum: RKK und 14 Gäste