Ducati Cutaway Display

Thank you for the clarification on the code.

The function inside the if statement would then become the trigger from the engine? To test bench the code and lights, I will use a push button. Could I have the if statement read (INPUT, HIGH){ then the above code?

Do the variables inside each if statement function need to be define?

Also, can each case perform several tasks? Turn HEXT, HIGH and HPOWER, LOW or should there be a case for every function of turning on/off led light?

Here is the code I have uploaded to the Arduino.

When I look at the serial print, the value for camshaftDeg is (-1) and does not change. I currently have the entire setup (10 leds) on the bread board to ensure proper lighting and function of code.

const int VCOMP = 11;
const int VSPARK = 10;
const int VPOWER = 9;
const int VEXT = 8;
const int VINT = 7;
const int HCOMP = 6;
const int HSPARK = 5;
const int HPOWER = 4;
const int HEXT = 3;
const int HINT = 2;
const int RBUT = 12;
int offsetMs =0;
int offsetMsLastCycle = 0;
int msPerDegree = 0;
int camshaftDegrees = 0;


void setup() {
  Serial.begin(9600);
pinMode(12, INPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
pinMode(8, OUTPUT);
pinMode(7, OUTPUT);
pinMode(6, OUTPUT);
pinMode(5, OUTPUT);
pinMode(4, OUTPUT);
pinMode(3, OUTPUT);
pinMode(2, OUTPUT);
  
  // if we are at zero degrees, record offset to millis() and calcaulate mS per degree.
if (RBUT == HIGH){
     offsetMs = millis() ;
     msPerDegree = offsetMs - offsetMsLastCycle ;  
     offsetMsLastCycle = offsetMs ;  // save for next cycle
}
 //calculate current position of camshaft in degrees
  camshaftDegrees =( ( millis() - offsetMs ) / msPerDegree);
    
}


void loop() {
 Serial.println(camshaftDegrees);
switch ( camshaftDegrees ) {
   
     case 0 :    digitalWrite( HCOMP, HIGH ) ; break ;
     case 2.5 :  digitalWrite( HSPARK, LOW ) ; break ;
     case 5 :    digitalWrite( HPOWER, HIGH ) ; break ;   
     case 60 :   digitalWrite( HEXT, HIGH ) ; break ; 
     case 90 :   digitalWrite( VINT, LOW ); break;
     case 127.5: digitalWrite( VSPARK, HIGH ); break;
     case 135:   digitalWrite( VCOMP, LOW ); break;
     case 137.5: digitalWrite( VSPARK, LOW ); break;
     case 140:   digitalWrite( VPOWER, HIGH ); break;
     case 175:   digitalWrite( HINT, HIGH ); break;
     case 190:   digitalWrite( HEXT, LOW ); break;
     case 305:   digitalWrite( HINT, LOW ); break;
     case 310:   digitalWrite( VINT, HIGH ); break;
     case 325:   digitalWrite( VEXT, LOW ); break;
     case 352.5: digitalWrite( HSPARK, HIGH ); break;
     
    
}
}

I think this code belongs in loop() rather than setup()

if (RBUT == HIGH){
     offsetMs = millis() ;
     msPerDegree = offsetMs - offsetMsLastCycle ;  
     offsetMsLastCycle = offsetMs ;  // save for next cycle
}
 //calculate current position of camshaft in degrees
  camshaftDegrees =( ( millis() - offsetMs ) / msPerDegree);

There is a problem with this

const int RBUT = 12;

and this

if (RBUT == HIGH)

RBUT will always be HIGH, and I think you intended a digitalRead(RBUT) to mark TDC.

It looks like with the initialized variables and the logic of the code, you will need to press the button twice, at subsequent TDCs to mark the timing of a complete cycle,. I think you are missing a division by 360 to determine msPerDegree.

Cattledog had already spotted the main issues. I've altered the code to get it to work and put some debug messages onto the serial console.
I changed the select statement to trim the decimal part of the degrees away. I hope you don't need a higher accuracy than whole numbers of degrees.
I guess you already understand that the timings are based on the previous cycle so it works best when it has achieved a more or less constant speed, that is for your testing the interval between button presses is more or less constant.
Have fun with it all.

const int VCOMP = 11;
const int VSPARK = 10;
const int VPOWER = 9;
const int VEXT = 8;
const int VINT = 7;
const int HCOMP = 6;
const int HSPARK = 5;
const int HPOWER = 4;
const int HEXT = 3;
const int HINT = 2;
const int RBUT = 12;
unsigned long offsetMs = 0;
unsigned long offsetMsLastCycle = 0;
float msPerDegree = 0;
long camshaftDegrees = 0;
long camshaftDegreesLast = 0 ;
long camshaftRotationCount = 0 ;

void setup() {
  Serial.begin(9600);
  pinMode(12, INPUT);
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(2, OUTPUT);


}


void loop() {

  // if we are at zero degrees, record offset to millis() and calculate mS per degree.
  if ( digitalRead( RBUT ) == HIGH  ) {
    delay (500) ; // includes some button debounce to prevent very short cycles
    camshaftRotationCount ++ ;
    Serial.print( "Camshaft sensor detected. Number of rotations = " ) ;
    Serial.println( camshaftRotationCount ) ;
    offsetMs = millis() ;
    msPerDegree = ( offsetMs - offsetMsLastCycle ) / 360 ;
    Serial.print( "msPerDegree: " ) ;
    Serial.println( msPerDegree ) ;
    offsetMsLastCycle = offsetMs ;  // save for next cycle
  }

  if ( camshaftRotationCount <= 1 ) {
    if ( digitalRead( RBUT ) == HIGH ) {
      Serial.println ( F("Engine speed not yet known") ) ;
    }
    return ;
  }

  //calculate current position of camshaft in degrees - fails if msPerDegree is zero.
  float camshaftDegreesF = ( ( millis() - offsetMs ) / msPerDegree);
  camshaftDegrees = camshaftDegreesF ;
  // Serial.println(camshaftDegrees);


  if (camshaftDegrees != camshaftDegreesLast ) {
    Serial.println(camshaftDegrees);
    switch ( camshaftDegrees ) {

      case 0 :    digitalWrite( HCOMP, HIGH ) ;   Serial.println( F( "HCOMP, HIGH" )) ;  break ;
      case 2 :    digitalWrite( HSPARK, LOW ) ;   Serial.println( F( "HSPARK, LOW" )) ;  break ;  //trimmed 0.5
      case 5 :    digitalWrite( HPOWER, HIGH ) ;  Serial.println( F( "HPOWER, HIGH" )) ; break ;
      case 60 :   digitalWrite( HEXT, HIGH ) ;    Serial.println( F( "HEXT, HIGH" )) ; break ;
      case 90 :   digitalWrite( VINT, LOW );      Serial.println( F( "VINT, LOW" )) ; break ;
      case 127:   digitalWrite( VSPARK, HIGH );   Serial.println( F( "VSPARK, HIGH" )) ; break ;  //trimmed 0.5
      case 135:   digitalWrite( VCOMP, LOW );     Serial.println( F( "VCOMP, LOW" )) ; break ;
      case 137:   digitalWrite( VSPARK, LOW );    Serial.println( F( "VSPARK, LOW" )) ; break ;  //trimmed 0.5
      case 140:   digitalWrite( VPOWER, HIGH );   Serial.println( F( "VPOWER, HIGH" )) ; break ;
      case 175:   digitalWrite( HINT, HIGH );     Serial.println( F( "HINT, HIGH" )) ; break ;
      case 190:   digitalWrite( HEXT, LOW );      Serial.println( F( "HEXT, LOW" )) ; break ;
      case 305:   digitalWrite( HINT, LOW );      Serial.println( F( "HINT, LOW" )) ; break ;
      case 310:   digitalWrite( VINT, HIGH );     Serial.println( F( "VINT, HIGH" )) ; break ;
      case 325:   digitalWrite( VEXT, LOW );      Serial.println( F( "VEXT, LOW" )) ; break ;
      case 352:   digitalWrite( HSPARK, HIGH );   Serial.println( F( "HSPARK, HIGH" )) ; break ;   //trimmed 0.5

    }
    camshaftDegreesLast = camshaftDegrees ;
  }

}

Thank you for the help in the code. I am currently reading through your notes to understand. The test setup seems to function properly and exciting to see the speed difference when the button is pressed consistently every 360 degrees and randomly.

I'm pleased that you have had some success with it.
One thing I would change is this to avoid it missing the first HCOMP / HIGH which I have just noticed :

long camshaftDegreesLast = -1;  // was 0

If you don't want to wait for one complete cycle of the camshaft ( 1 minute at 2 RPM engine speed) before the LEDs function, the code can be fixed to assume a set start up speed by updating the initial value offsetMsLastCycle in setup() to millis() - 60000 (assuming 2 RPM ) and removing the code which tests if the engine speed is not yet known.

I'll be curious to know how you solve the problem of detecting the position of the camshaft.

Thank you for the suggestion. I will try it out.

Another portion of the electrical system involved turning the motor on and off. The motor is a small dc pm motor that draws 600mA (full load) and 12V. I would like the arduino to control the motor as well but not sure if a TIP120 transistor is the best way or 4N35 Phototransistor is better.

Also, I've only connected the arduino via usb. What is needed to connect the arduino to 12v power supply? (The power cord for motor is a basic cell phone charger converting wall outlet of 120V to 12 V at 3000mA)

Currently building all the led holders to fit in the intake and exhaust ports. I figure since the camshaft gear already has a notch cut out to signal the crank sensor, I will attach a small magnet in the groove then machine a holder for a hall sensor. Contactless switches will help with the longevity of the display.

Interesting that a small motor (7.2 watt) can turn all that over at demonstration speeds. I guess though it is not under compression any more and is probably all well oiled.

I'd probably use a small relay module. The phototransistor is not strong enough and, anyway, you don't need any isolation because the grounds would be interconnected. But a TIP120 with a base resistor of 1K would also do.

You've probably said somewhere in the thread what Arduino you are using, but 12 volts should be no problem if connected to the Power Jack or the correct "Raw" or "Vin" on the board (not Vcc or 5V). The Uno and the motor can share the same power supply if the motor is directly fed from the power supply.

Here is a link to the motor being used:
12v Motor

I made a billet adapter to look like the start motor casing the small motor above inside. There are no piston rings on the horizontal cylinder due to the cylinder being cut in half and no compression.

I mocked up the us5881 hall sensor on the bread board and tweaked the code so a magnet will activate the program. Straight forward now that I have the correct sensor. Also mocked up the TIP120 transistor with a 9v bat and small motor to test the system.

My next step for coding is to program the motor and lights to come on for a certain amount of time based upon the position of a potentiometer. I will be coupling the display with a 1950s duncan mechanical parking meter (Duncan Meter). Currently 3d printing a new stainless steel bronze gear to reduce the 2 hour time of meter to only a few minutes. The potentiometer would couple to the black needle that estimates the amount of time left based upon how many coins were inserted. Looking to use one of the analog pins to read the pot, evaluate the value, then run the program for that amount of time.

The overall goal is to have an adult version coin operated machine that shows the internals of a Ducati engine and why they are so unique. Once completed the unit may go to the Arizona Science Center or a automobile museum.

One concern I have not address is turning the lights off. Ideally once the timer has stopped, the motor would stop and the lights would turn off.

Here is a cad model of a simple circuit board I will be making to house all the components. There will be some wire looms to connect to the arduino, leds, and sensors.

I will use the 12volt power supply to power the circuit board and also arduino uno.

Hi,
Your layout looks like you are using 12V with your Pot, you will need to use 5V, as 12V will cause damage to the analog input to the Arduino.

Tom.... :slight_smile:

Very good catch! I will change the voltage link and have the pot become a 2 pin connector (1 for analog pin, 2 for digital 5v pin).

Instead of attempting to use the position of the meter's needle to determine the remaining time, could you not simply detect if the red 'expired' flag is raised. This would require only a simple beam break arrangement, eg a 3mm IR led and IR phototransistor, which could be concealed in the housing.
This would control the lights and the motor.

Thank you for the simple solution to turn the motor on and off. Much easier than trying to estimate where the needle may end up and will require less code.

I am much further along with the display model having many of the LEDs position, Hall effect sensor reading off the cam shaft gear (like OEM) triggering the sequence of code, etc.

Next challenge is having the LEDs trigger at the right time. After many revolutions of the engine and watching the serial print, the average camshaft degrees that register before the code is reset is roughly 29306 (I'm assuming milliseconds as the engine roughly does one complete combustion cycle in ~29-30 seconds). Due to the large amount of numbers being processed, many of the LEDs are not turning on/off since their respective number rarely hits in the serial print (or within the ecu). most of the numbers registering will jump by 5-7 numbers and is not consistent.

So can I specify a range of values for camshaft degrees for the case?

OR

Can I slow down the counting of degrees and be more precise?

On the bench, I was able to slow down the degrees by changing when the magnet swept past the hall sensor yet this was not very accurate to the display.

Playing around with the code:

I changed the equation of msPerDegree to be divided by (originally 360):
3600 - had an error in the code and would not count
36000 - same as above
36 - slowed the counting down considerably to be only 2500 by reset time
3.6 - drastically slowed the counting down to now only 200 by reset time. I believe I have found the answer to precisely trigger the leds within a 360 degree crank cycle. If I am wrong please let me know.

Can you post the code you are currently using ?
I'd have expected a count of 83.3 mS per degree at 2RPM.

I changed the denominator of the equation to suit the motor and 13 seemed to work best. Currently only have the intake, exhaust, and spark working for the LEDs until I can machine pockets for the combustion and power strokes. The code works perfectly.

I will end up using a photo interrupter to switch the motor on and off with the 1950s Duncan parking meter. Once I am at that point, I may chime in again if I don't understand the coding.

const int VCOMP = 11;
const int VSPARK = 10;
const int VPOWER = 9;
const int VEXT = 8;
const int VINT = 12;
const int HCOMP = 6;
const int HSPARK = 5;
const int HPOWER = 4;
const int HEXT = 3;
const int HINT = 7;
const int RHALL = 2;
const int MOTOR = 13;
unsigned long offsetMs = 0;
unsigned long offsetMsLastCycle = 0;
float msPerDegree = 0;
long camshaftDegrees = 0;
long camshaftDegreesLast = 0 ;
long camshaftRotationCount = 0 ;


void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(2, INPUT);


}


void loop() {
digitalWrite(MOTOR, HIGH);
  // if we are at zero degrees, record offset to millis() and calculate mS per degree.
  if ( digitalRead( RHALL ) == LOW  ) {
    delay (500) ; // includes some button debounce to prevent very short cycles
    camshaftRotationCount ++ ;
    Serial.print( "Camshaft sensor detected. Number of rotations = " ) ;
    Serial.println( camshaftRotationCount ) ;
    offsetMs = millis() ;
    msPerDegree = ( offsetMs - offsetMsLastCycle ) / 13 ;
    Serial.print( "msPerDegree: " ) ;
    Serial.println( msPerDegree ) ;
    offsetMsLastCycle = offsetMs ;  // save for next cycle
  }

  if ( camshaftRotationCount <= 1 ) {
    if ( digitalRead( RHALL ) == HIGH ) {
      Serial.println ( F("Engine speed not yet known") ) ;
    }
    return ;
  }

  //calculate current position of camshaft in degrees - fails if msPerDegree is zero.
  float camshaftDegreesF = ( ( millis() - offsetMs ) / msPerDegree);
  camshaftDegrees = camshaftDegreesF ;
  // Serial.println(camshaftDegrees);


  if (camshaftDegrees != camshaftDegreesLast ) {
    Serial.println(camshaftDegrees);
    switch ( camshaftDegrees ) {

      case 2 :    digitalWrite( HINT, HIGH) ;    Serial.println( F( "HINT, HIGH" )) ;  break ;
       
      
      case 15  :   digitalWrite( VEXT, HIGH ) ;  Serial.println( F( "HPOWER, HIGH" )) ; break ;
      
      case 60 :   digitalWrite( HEXT, HIGH ) ;    Serial.println( F( "HEXT, HIGH" )) ; break ;
      case 61 :   digitalWrite( HPOWER, LOW ) ;   Serial.println( F( "HPOWER, LOW")); break;
      case 90 :   digitalWrite( VINT, LOW );      Serial.println( F( "VINT, LOW" )) ; break ;
      case 91 :   digitalWrite( VCOMP, HIGH );    Serial.println( F( "VCOMP, HIGH")); break;
      
      case 240:   digitalWrite( HINT, LOW );     Serial.println( F( "HINT, LOW" )) ; break ;  
      case 260:   digitalWrite( VINT, HIGH );     Serial.println( F( "VINT, HIGH" )) ; break ;
      case 290:   digitalWrite( VEXT, LOW );    Serial.println( F( "VEXT, LOW" )) ; break ;  
      case 340:   digitalWrite( HSPARK, HIGH ) ;   Serial.println( F( "HSPARK, HIGH" )) ; break ;
      case 380:   digitalWrite( HSPARK, LOW );   Serial.println( F( "VPOWER, LOW" )) ; break ;
      
      case 175:   digitalWrite( HINT, LOW );     Serial.println( F( "HINT, HIGH" )) ; break ;
      case 190:   digitalWrite( HEXT, LOW );      Serial.println( F( "HEXT, LOW" )) ; break ;
      case 191:   digitalWrite( VEXT, HIGH);      Serial.println( F( "VEXT, HIGH")); break;
      case 192:   digitalWrite( VPOWER, LOW);     Serial.println( F( "VPOWER, LOW")); break;
      
      case 540:   digitalWrite( VINT, LOW );      Serial.println( F( "VINT, LOW" )) ; break ;
      
      case 306:   digitalWrite( HCOMP, HIGH);     Serial.println( F( "HCOMP, HIGH")); break;
      
      case 640:   digitalWrite( VSPARK, HIGH ); Serial.println( F( "VSPARK, HIGH" )) ; break ;
      case 680:   digitalWrite( VSPARK, LOW );  Serial.println( F( "VSPARK, LOW" )) ; break ;
      case 705:   digitalWrite( HINT, HIGH );   Serial.println( F( "HINT, HIGH" )) ; break ;  
    }
    camshaftDegreesLast = camshaftDegrees ;
  }

}
 msPerDegree = ( offsetMs - offsetMsLastCycle ) / 13 ;

Can you please explain this. Previously all the code was based on a measured time period for one rotation divided by the number of degrees in that rotation.

 msPerDegree = ( offsetMs - offsetMsLastCycle ) / 360 ;

I do not think you are reading the hall sensor properly with

 if ( digitalRead( RHALL ) == LOW  )

and I think that the 13 is coming from the fact that you are measuring amount of time that the magnet is in front of the sensor.

The correct approach is to use a state change procedure with code like this to pick up the time when the sensor first sees the magnet on each rotation. To be most accurate, define a two new byte variables hallSensorReading and lastHallSensorReading and then use this

hallSensorReading = digitalRead(RHALL)
if(hallSenorReading == LOW && lastHallSensorReading ==HIGH)
{
 offsetMs = millis() ;
 msPerDegree = ( offsetMs - offsetMsLastCycle ) / 360 ;
 offsetMsLastCycle = offsetMs ; 
}
 lastHallSensorReading =hallSensorReading ;
 delay (500) ; // includes some button debounce to prevent very short cycles

There is no need to debounce the hall sensor.

Thank you all for the help. I got all 5 leds working on one cylinder to test the program and it works flawlessly. In the middle of drilling the mounting locations on the horizontal cylinder and then the light show should be complete. I may add a few lights to showcase the trans but they would be a constant on.

The next hurdle will be re-gearing the 1950s duncan parking meter to work in a a few minute slot vs 2 hours. Will also need to add a photo interrupter to activate the program. Very excited. I will post a video shortly once the horizontal cylinder is in action.

Here is a short video of the display in action: