Troubleshooting an inaccurate Ignitiontiming on a Scooter

Greetings from Germany!

Firstof thers some pretty bad englisch involved in this post hope we can get along anyway. :smiley:

So lets go!

Im building a Capacitor discharge ignition module for my scooter as is want to change the ignition curve
and have a build in speed/rev limiter.

It is an AC CDI.
basic principle:
Coils on the scooter generator charge a capacitor which is rapitly discharged with a thyristor
causing a high Voltage at the ignition coil which is transformed to a even higher voltage causing a spark
at the spark plug.
the scooter has a vr sensor to determine top dead center (TDC) and revolution time.
the signal is fet into the CDI module which calculates the ingnition time and when to trigger the Thyristor.

to work out everything i got a experimental setup
i conectet the flywheel (with one tooth for vr sensor) an the vr sensor to an electric motor to simulate the engine running.
the vr signal is fet into a zero crossing circut for conditioning into a digital signal
which is fet in to an Teensy 4.0. (for now TDC is as the tooth passes the sensor in reality its some degrees before TDC)
for bluetooth comunication i use a HC06 module.

the following things work just fine:

  • caculating the time for a revolution and sending it to the phone or pc
  • setting the ignitiontime by sending the amount of degrees befor TDC over the phone with the app bluetooth terminal

the speed/rev limiter and the ignition curve can be set aside for now
i only want to have a stable ignitiontiming.

and thats the problem
while mesuring the input signal and the trigger signal with the oscilloscope i noticed the ignition point
jumping around about 100us back and forth which (at this engine speed) translated in to about 3 degrees angular position – target is to get under 1 degree!
i thought maybe it is just the revolution time that jumping around but the percentige or degrees stay stable so i checkt the rev times over the serial monitor-

Exsample times measured:

Serial Monitor

they are stable enough
as the triggersignal is like a pwm signal set HIGH on the trigger point set LOW (pretty acurate 1-3us after TDC) at TDC
i measured the degress before TDC with a little arduino scetch:

float ONCycle;              
float OFFCycle;             
float T; 
float z;                  
int F;  
const int PulseIN = 7;      

void setup() {
  pinMode(PulseIN, INPUT);


void loop() {
  ONCycle = pulseIn(PulseIN, HIGH);
  OFFCycle = pulseIn(PulseIN, LOW);
  T = ONCycle + OFFCycle;
  z = ONCycle / T;
  z = z *360;

as you see the degrees jump around quite a lot:

Serial Monitor

here is the code for the teensy maybe you see an error

#define HWSERIAL Serial1 // Serialport used to comunicate with a HC06 bluetoth moduale

unsigned long starttime; // startime for counting time of a revolution
unsigned long sendezeit; // for sending revolution time in some intervall on the serial 
const int vrsens = 7; // Variable reluctance sonsor imput fet thrue a circut as i need a digital signal
const int transistor = 4; // transistor that is triggert to discharge the capacitor which leads to a spark
float umdrehungszeit; // time for a revolution
float zuendzeitpunkt; // time till the next triggerpoint
float zuendwinkel = 0.973; // coefficent which multiplies with the time for a revolution given in percent set by defalt to 10 deegres before TDC
int logiclevel; // for saving the logiclevel of the inputsignal used to determin TDC and revolution time
int lastlogiclevel; // for compering to logiclevel
int counter; // for counting the edges of the vr inputsignal --> 2 edges per revolution
int motorstatus; // counter to determin if the engine is rumming

const byte numChars = 32; // used for Serial comunication Arduino Forum --> Robin2 Serial Input Basics thanks a lot by the way 
char receivedChars[numChars]; // used for Serial comunication Arduino Forum --> Robin2 Serial Input Basics
boolean newData = false; // used for Serial comunication Arduino Forum --> Robin2 Serial Input Basics

void setup() {
  pinMode(vrsens, INPUT);
  pinMode(transistor, OUTPUT);


void loop() {
  recvWithStartEndMarkers(); // comunication with bluethoth module Arduino Forum --> Robin2 Serial Input Basics
  if (newData == true) { // if i send the ignitionpoint in degrees before TDC over bluethoth checks if data arivet caculates the coefficent and echos it 
      float rxzw = atof(receivedChars); // translation from char array to float
      rxzw = rxzw * 0.00278; // 0.278 is one degree in percent 
      rxzw = 1 - rxzw;
      zuendwinkel = rxzw;
      HWSERIAL.println(zuendwinkel); //echo for checking
      newData = false;
/*  if (motorstatus >= 2){ // if engine is running send revolution time per intervall set by sendezeit
    if (sendezeit<millis()){
      sendezeit = 1000 + millis();
  } */
  logiclevel = digitalRead(vrsens); // saving curent logiclevel
  if (logiclevel != lastlogiclevel){ // locking for an edge
    if (motorstatus == 0){ // if i hookup the circut there will be one edge detectet but the engine is still not running 
      motorstatus = 1;
      } else if (motorstatus == 1){ // after too edges the engine is running
        motorstatus = 2; 
        HWSERIAL.print("Motor an"); // feedback german for "engine running"
        Serial.print("Motor an");
    if (counter == 0){ // after first edge start counting revolutiontime and edges
      starttime = micros();
      counter = counter + 1;
      }else if (counter == 1){ // if theres bin an edge allredy add one
        counter = counter + 1;
        }else{ // if theres bin too one revolution happend --> caculate the revolution time reset counter
          counter = 0;
          umdrehungszeit = micros() - starttime;
    if (logiclevel == HIGH){ // if there is an edge and the logiclevel is high now it is TDC therefor caculate the next trigger point
      zuendzeitpunkt = umdrehungszeit * zuendwinkel; // triggerpoint is revolutiontime times coefficent therefor a fraction of a revolution
      zuendzeitpunkt = zuendzeitpunkt + micros(); // add this time to the current time
   if (zuendzeitpunkt < micros()){ // if the triggertime is smaler than current time trigger transistor
    digitalWrite(transistor, HIGH);
      digitalWrite(transistor, LOW); // writes LOW if theres a new trigger point --> shortly after TDC usaly 1-3 microseconds mesured with the ossiloscope
  lastlogiclevel = logiclevel; // saving last logiclevel for edgedetection

void recvWithStartEndMarkers() { // // comunication with bluethoth module Arduino Forum --> Robin2 Serial Input Basics
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
    while (HWSERIAL.available() > 0 && newData == false) {
        rc =;

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
            } else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
        } else if (rc == startMarker) {
            recvInProgress = true;

Not looked at the software but .. I did have some experience of Autolabs Megajolt .
They use a toothed wheel with a missing tooth to synchronise stuff with the missing tooth being something like 90 deg before TDC so there is space presumably for software to run . Megajolt uses a very simple processor .
They use a lookup table to give the timing - useful method for you later when you want to modify timing with load and rpm as it produces a grid of timing verses rpm verses load .

It did strike me ( and I’m not certain) that the print statements will add a delay to the programs running and this might be critical in your system ?
Unsure of your circuits , but maybe some noise scatter associated with zero crossing detection?
There are a lot of ignition systems out there, some with Arduino , google will find them .