advice needed for arduino as diesel injection brain...

PaulS:
If the uC is to be used, that variable relationship needs to be maintained, or the performance of your engine is going to change. And, probably not for the better.

That's for sure Paul.
The fun part is that we don't do it this way because it will gain power but mostly because others don't do it this way at all.
Will order a Mega tomorrow and gonna have some fun...

Are there any update's about using Arduino with common rail?

Panther95

For sure there are only 2 External Interrupts.
But almost every pin also supports PCINT, whichNick Gammon has a great page on and even shows them as being a little faster:

japie:
Aren't all digital ports usable as interrupts? Or do I need to add some extra function to accomplish that?

Yes, using Pin Change interrupts, as CrossRoads kindly pointed out, I have a page about that:

Not the way you wrote it though. I certainly think you can react in under 20 uS to an external event, if carefully coded. The interrupt processing itself takes about 3 uS to kick in, and then you need to work out which pin caused the interrupt, if using pin change interrupts.

Panther95:
Are there any update's about using Arduino with common rail?

Not working yet, have been a bit busy with other aspects of the machine but did come somewhat closer.
Currently the electronics for the PWM driver for the pump pressure and the alternator are being made and the alternator housing is finished.
Also have some testing to do since my new injectors are slightly different from my previous ones.

My previous project with timer stuff and injector drivers in it can be found at: http://www.minimumrisk.nl/articles.php?lng=nl&pg=270

Last code looks like this:

/*
  Cuprum-2.0 simplified 1-3-4-2 cylinder common rail injection system for Arduino Mega.
  
  Using interrupts for timing injections cycle, a timing disc (alternator) is mounted to camshaft.
  Throttle position is read from potentiometer.
  
  Base rule for the system is that we want all fuel injected at 10 crank degree before TDP:
  - timer 2 gives pulse for cylinder 1
  - wait (180 degrees in uS at current rpm) - injection time - (10 degrees in uS at current rpm)
  
  Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground, output will be 0-1023.
  Attach timing signals to external interrupts: cyl1 pin 3, cyl2 pin 21, cyl3 pin 20 and cyl4 pin 19.
  Attach injector drivers: cyl1 pin 4, cyl2 pin 5, cyl3 pin 6 and cyl4 pin 7.
*/

// setup pin for throttle potentiometer:
int ThrottlePin = A0;
unsigned long ThrottleValue = 0;
unsigned long ThrottleDuration = 0;
// base idle injectiontime is 100uS
unsigned long IdleDuration = 100;
// define throttle calibration values:
// we like to inject from -45 to -10 TDP making 35 degree injection duration max.
// crank duration in uS: (1000000/(rpm/60))/360*35
// at 10K rpm that is 583uS since the throttle has 1023 steps 1023/583=1.75
float ThrottleCalib = 1.75;
// define injection delay
unsigned long InjectionDelay = 0;
// define microsecond timeframe between 2 injects = duration 90 degree camshaft/ 180 degree crankshaft
unsigned long TimeFrame = 0;
unsigned long TimeOld = 0;
unsigned long Delay = 0;
// setup timer pins for interrupts:
int cyl1 = 1;
int cyl2 = 2;
int cyl3 = 3;
int cyl4 = 4;
volatile int state = LOW;
// setup pins for injector drivers:
int inj1 = 4;
int inj2 = 5;
int inj3 = 6;
int inj4 = 7; 

void setup()
{
  // initialize the digital injector pins as an output:
  pinMode(inj1, OUTPUT);     
  pinMode(inj2, OUTPUT);     
  pinMode(inj3, OUTPUT);     
  pinMode(inj4, OUTPUT);
  // attach action when interrupt gets actived:
  pinMode(cyl1, INPUT);
  attachInterrupt(1, inject1, RISING);
  pinMode(cyl2, INPUT);
  attachInterrupt(2, inject2, RISING);
  pinMode(cyl3, INPUT);
  attachInterrupt(3, inject3, RISING);
  pinMode(cyl4, INPUT);
  attachInterrupt(4, inject4, RISING);
}

void loop()
{
  // read the throttle input on analog pin 0:
  ThrottleValue = analogRead(ThrottlePin);
  // see if we are trying to have fun
  if (ThrottleValue > 5)
    {
    // injection delay uS/180*170 to make sure all juice is injected 10 degree before TDP.
    InjectionDelay = TimeFrame/18*17;
    // recalculate throttle value to wanted microseconds:
    ThrottleDuration = ThrottleValue/ThrottleCalib;
    // check the interrupts:
    digitalWrite(cyl1, state);
    digitalWrite(cyl2, state);
    digitalWrite(cyl3, state);
    digitalWrite(cyl4, state);
    }
  // it seems we are idling
  else
    {
    // on idle we want injecting to stop at TDP not before
    InjectionDelay = TimeFrame;
    // if we are running 900rpm we reduce injection time
    if (TimeFrame > 33333)
      {
      // reduce injection time
      IdleDuration=IdleDuration-5;
      ThrottleDuration = IdleDuration;
      }
    // if we are running 800rpm we inject a bit longer
    else if (TimeFrame < 37500)
      {
      // expand injection time
      IdleDuration=IdleDuration+5;
      ThrottleDuration = IdleDuration;
      }
    // check the interrupts:
    digitalWrite(cyl1, state);
    digitalWrite(cyl2, state);
    digitalWrite(cyl3, state);
    digitalWrite(cyl4, state);
    }
}

void inject1()
{
  // find out how many uS 90 degrees camshaft is
  TimeFrame = (micros() - TimeOld);
  TimeOld = TimeFrame;
  Delay = InjectionDelay-ThrottleDuration;
  // open injector, hold it and close
  digitalWrite(inj1, HIGH);
  delayMicroseconds(ThrottleDuration);
  digitalWrite(inj1, LOW);
}

void inject2()
{
  TimeFrame = (micros() - TimeOld);
  TimeOld = TimeFrame;
  Delay = TimeFrame/18*17-ThrottleDuration;
  digitalWrite(inj2, HIGH);
  delayMicroseconds(ThrottleDuration);
  digitalWrite(inj2, LOW);
}

void inject3()
{
  TimeFrame = (micros() - TimeOld);
  TimeOld = TimeFrame;
  Delay = TimeFrame/18*17-ThrottleDuration;
  digitalWrite(inj3, HIGH);
  delayMicroseconds(ThrottleDuration);
  digitalWrite(inj3, LOW);
}

void inject4()
{
  TimeFrame = (micros() - TimeOld);
  TimeOld = TimeFrame;
  Delay = TimeFrame/18*17-ThrottleDuration;
  digitalWrite(inj4, HIGH);
  delayMicroseconds(ThrottleDuration);
  digitalWrite(inj4, LOW);
}

Thanks for the update. I have been looking around on the net for something like this, and this looks very good.
I have a Hobbysport tractor with a friend. A Same Panther with 6 cyl 6ltr engine and stock it has prf1k90.... pumps in it( each cyl its own 9mm pump).
And they can't be made bigger, only good thing about them is that they start with 700bar can go up to 1200bar of injection pressure.
A Inline would be the first chose for many. I think that Common rail could be "easyer" to build, mechanicly than.
Only the hardest part for now is to find injectors that could fit with the least modifycations.

I found the Cuprum befor the arduino, only my electronics knowledge isn't so good.
Are you using a alternator to replace a rotary encoder? bit like this link Simple Rotary Encoder

Hope to see more here.

Hello Panther,

In the past we drove with our electronics system with injectors from a John Deere 8.5 common rail but they are pretty big.
You could find some injector that fits and then change the nozzle to the desired flow or drill the holes to what you want.
In a common rail the holes aren't much smaller then in a conventional system and when using bosh you can also switch nozzles from conventional to CR and visa versa fine when choosing carefully.
Main problem is finding a pump that delivers enough.
I know that a stock 2.0hdi (as fitted on a PSA engine) Siemens pump can deliver up to 160cc without problems according to the specs. and we can "over" rpm the thing to get more juice, but above 5000 pump rpm it will die.

We have two machines based on a 2.0 engine:
On our current conventional Bosch A-pump we use 10mm. plungers at 160cc. and one P-pump with 12mm. plungers at 240cc. Both machines have a different setup, the 160 was meant to scale from lower to upper until pistons start melting and the other we started very high and work our way downwards until critical temps are reached.

I will post results of the project over here but first I have some other rebuilding stuff to do and will setup a test setup on my workbench to gather data about pump capacity/pressure related to injection time, nozzle size and injection duration.
After that some testing of the main program and IF working I will replace the line-pump...

Hello Japie,

I already thought those injectors where from a John Deere. I repair Same and Claas tractors, Claas use also JD. They won't fit my Same.
Next week I have some injectors to try. Have you drilled a nozzle your self? For this year I'm putting pumps from a "bigger" engine( still 9mm only with bit more delivery) and the nozzles need to be 0.295mm. So we talked about drilling them.
For you to look for a bigger pump would be hard I think, for me would be trucks a good source. Or maybe this could be an idea? http://www.albertazziracing.com/foto-foto1-2009.html
Fun to see some one else doing this, especially when he's doing better :smiley:

Panther95

Drilling nozzles is only good for creating porcupines.

Over here you can find a small list of bosch nozzles usable on a lot of different injectors, mechanical or electronic all exchangeable:

DSLA is the "dimension" type and the 145 is the spray angle, only thing not listed is the injector angle in the head so you have to see before you buy.
If you have some other model it is still possible they can be exchanged, find some nozzle reseller and try.

Truck pumps have the disadvantage for me that they can't handle high rpm's, the 2.0l engine is good for 9000rpm (even with convential mechanical pump) so that means 4500 for the pump, large pumps like on US Cummins diesel pick up trucks can only rev to 2500 or 3000 so they are off. (and heavy since our class may only weight 600kg including driver that is an issue)
For tiny use (up to 200cc) a pump from 3.0l automotive engine will do, for your goal a truck pump will probably do it's job.

When looking at the pics from the Italians I think they can use more juice, no idea what temps and air flow they have but I don't see enough smoke... :wink:

http://www.google.nl/url?sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=1&ved=0CC4QFjAA&url=http%3A%2F%2Fwww.merlindiesel.com%2Fmedia%2Fboschpumptestplans4web.pdf&ei=nkcEUcXaPKGb0QXsxYGYBg&usg=AFQjCNE1ovvayjAUBz0UuVOi2VzjTzt4OA

He Japie, found this link and if it doesn't work pm me for the pdf. It's a list of CR pumps by bosch number.
If you look for number 0445010046 wich is for a PSA RHY/RHS 2.0 HDi, it looks like it could pump up to 540cc at 1000rpm and 1000bar.

That is great documentation!

Thanks for sharing.

I do have a coding issue I think, since I am not a coder but a mechanic I used an interrupt to drive the injectors directly with timers attached.
I did got a (digital) beating for that and understand why, if a timer connected to an interrupt is still running when a second interrupt is triggered I have trouble right?
Is there a way to let an interrupt trigger a subroutine so it gets freed immediately and the routine keeps running? (like in bash $command &)

Is there a way to let an interrupt trigger a subroutine so it gets freed immediately and the routine keeps running? (like in bash $command &)

I'm not entirely sure what you're asking here, but with regard to running more than one process like a linux box, the arduino isn't going to do it - it's single core, single threaded. You can fake it to some extent (just like a single core linux box does) but you'll have to code it yourself, there is no operating system to help you manage separate threads.

@japie

Your code will most likely work better if you get rid of the delays.

 delayMicroseconds(ThrottleDuration);

You can used the "blink without delay" style with micros() just like you can with millis().

I guess you have a newer code to look at. Here you have an overlap on pin 4.

// setup timer pins for interrupts:
int cyl1 = 1;
int cyl2 = 2;
int cyl3 = 3;
int cyl4 = 4;
volatile int state = LOW;
// setup pins for injector drivers:
int inj1 = 4;
int inj2 = 5;
int inj3 = 6;
int inj4 = 7;

@cyclegadget

Thanks for pin4, didn't see that. (but they where fictional)

@wildbill

I know it is not threaded and that is why I am concerned.
Main issue is that I want a calculated injection moment, the only stable factor is the end of the injection in degrees. (so in time dependable on rpm)
To make live easier when mounting/adjusting I like the distributor to give a pulse at TBP since that is the only position of my engine I can monitor. (with head in place)

So with cylinder 1 at TDP I have a starting point for injecting cylinder 3 (order 1342)
I have a time interval in us between 2 interrupts so I can calculate delays but main thing is that the interrupt triggers actions with delays where the end of that cycle can be after the next interrupt is triggered. (due to revving up)

I don't see a software solution, only thing I can do is mounting the distributor to the throttle cable and let the interrupt trigger injection without delay.

I wrote some code just to give you a place to start. It is not working code but, I think I wrote enough to give you an idea how to remove your delays.

// Variables will change:
int InjectorState1 = LOW;  // the state of the injector1
unsigned long previousMicros = 0;     
      )

void setup() {
      
}

void loop()
{
  
  unsigned long currentmicros = micros();
   
    
 int inject1()
{
  //////////////////////////////////////////////////I don't know what you are doing here
  // find out how many uS 90 degrees camshaft is
  TimeFrame = (micros() - TimeOld);
  TimeOld = TimeFrame;
  Delay = InjectionDelay - ThrottleDuration;
  
  ////////////////////////////////////////////////this below can work without delay
  
   static int done = 0;  // this variable will show when the inject1() function is complete. 0 = not complete, 1 = complete  
   
  // open injector, hold it and close
     if (InjectorState1 == LOW)
   {
      digitalWrite(inj1,HIGH);   //open the injector
      previousmicros = currentmicros;  // save the time the injector was opened
      InjectorState1 = HIGH;   // set the injectorState to HIGH
   } 
    
     // If the injector is open and it is time to close the injector do so.
     if(currentmicros - previousmicros > Delay && InjectorState1 == HIGH) 
     {
      digitalWrite(inj1, LOW);  // close the injector
      InjectorState1 = LOW;    // reset the injectorState to LOW
      done = 1; // injection cycle is complete
     }
     return done;  // return 0 or 1 to show if function has both opened and closed injector.
     
}

Thanks, I understand this way of handling.
Instead of putting waits in the interrupt you used a compare to see if it is time to close, but does this change behavior when 2 interrupts overlap each other?

I attached an image to explain the situation:
Each injection has 3 values: delay_from_tdp, injection_duration and remaining_time_to_next_tdp
When rpm is steady the green time is free time for the uC but when revving up it can happen that the next interrupt trigger is before that free time so it is triggered when the previous interrupt function is still running.
That don't have to be a problem since during injection there is no action, only a wait time so the start of the next cycle/interrupt can only add a couple of cpu cycles to the injection time.
Main thing I am afraid of is that if an interrupt function is not yet finished the next won't start or the first is not closed due to the overlap, that is why I was asking about the bash & solution. (free the interrupt while the injection cycle remains running)

I haven't had much practice using interrupts() so my sketch may have issues. In this sketch, the interrupt sets a flag to true. The function then runs only if the flag is set. The interrupt is done the moment the flag is set. The inject function resets the flag from "true" to "false" so that it can be set again on the next interrupt.

Each injector has it's own flag and will have it's own function.

The code is mostly written for the cylinder1. You will need to review it and see if it can be used for the rest of the cylinders.

// setup pin for throttle potentiometer:
int ThrottlePin = A0;
unsigned long ThrottleValue = 0;
unsigned long ThrottleDuration = 0;
// base idle injectiontime is 100uS
unsigned long IdleDuration = 100;
// define throttle calibration values:
// we like to inject from -45 to -10 TDP making 35 degree injection duration max.
// crank duration in uS: (1000000/(rpm/60))/360*35
// at 10K rpm that is 583uS since the throttle has 1023 steps 1023/583=1.75
float ThrottleCalib = 1.75;
// define injection delay
unsigned long InjectionDelay = 0;
// define microsecond timeframe between 2 injects = duration 90 degree camshaft/ 180 degree crankshaft
unsigned long TimeFrame = 0;
unsigned long TimeOld = 0;
unsigned long Delay = 0;

unsigned long previousMicros = 0;  
unsigned long currentmicros = 0;
int InjectorState1 = LOW;  // the state of the injector1

int cyl1 = 1;
int cyl2 = 2;
int cyl3 = 3;
int cyl4 = 4;
// setup pins for injector drivers:
int inj1 = 4;
int inj2 = 5;
int inj3 = 6;
int inj4 = 7; 

volatile int state1 = LOW; ///////////////////Flags to determine if injection functions should run
volatile int state2 = LOW;
volatile int state3 = LOW;
volatile int state4 = LOW;
volatile int Fuel1  = false;
volatile int Fuel2  = false;
volatile int Fuel3  = false;
volatile int Fuel4  = false;
void setup()
{
  // initialize the digital injector pins as an output:
  pinMode(inj1, OUTPUT);     
  pinMode(inj2, OUTPUT);     
  pinMode(inj3, OUTPUT);     
  pinMode(inj4, OUTPUT);
  // attach action when interrupt gets actived:
  pinMode(cyl1, INPUT);
  attachInterrupt(1, Fire1, RISING);
  pinMode(cyl2, INPUT);
  attachInterrupt(2, Fire2, RISING);
  pinMode(cyl3, INPUT);
  attachInterrupt(3, Fire3, RISING);
  pinMode(cyl4, INPUT);
  attachInterrupt(4, Fire4, RISING);
}

void loop()
{
  currentmicros = micros();
  /////////////////////////////check interupts//////////// 
  digitalWrite(cyl1, state1); // runs function called Fire1
  digitalWrite(cyl2, state2);  // runs function called Fire2
  digitalWrite(cyl3, state3);  // runs function called Fire3
  digitalWrite(cyl4, state4);  // runs function called Fire4

  /////////////////////////if the flag is true start the inject function///////////// 
  if (Fuel1 == true)       // START THE FUNCTION IF FLAG IS SET
  {
    inject1();
  }
}

////////////////////////////Flag setting functions///////////////    
void Fire1()
{
  Fuel1 = true;
}
void Fire2()
{
  Fuel2 = true;
}
void Fire3()
{
  Fuel3 = true;
}
void Fire4()
{
  Fuel4 = true;
}

//////////////////////////////////////injection function for cyl one
int inject1()
{
  Fuel1 = false; //reset the flag that was caused by the interrupt
  // find out how many uS 90 degrees camshaft is
  TimeFrame = (micros() - TimeOld);
  TimeOld = TimeFrame;
  Delay = InjectionDelay - ThrottleDuration;
  static int done = 0;  // this variable will show when the inject1() function is complete. 0 = not complete, 1 = complete  
  // open injector, hold it and close
  if (InjectorState1 == LOW)
  {
    digitalWrite(inj1,HIGH);   //open the injector
    previousMicros = currentmicros;  // save the time the injector was opened
    InjectorState1 = HIGH;   // set the injectorState to HIGH
    done = 0;                // injection cycle is NOT complete
  } 

  // If the injector is open and it is time to close the injector do so.
  if(currentmicros - previousMicros > Delay && InjectorState1 == HIGH) 
  {
    digitalWrite(inj1, LOW);  // close the injector
    InjectorState1 = LOW;    // reset the injectorState to LOW
    done = 1;              // injection cycle is complete
  }
  return done;  // return 0 or 1 to show if function has both opened and closed injector.
}

OK cyclegadget, that is clear:
The interrupt sets a flag, inside a loop the flag is detected and an injection subroutine for that flag is called.
One advantage of that approach is that I have to concentrate to make the loop as fast as possible since reading ADC and doing calculations in that loop could disturb injection start. (so it is more clear how many cpu cycles things use)

It does however raise another question (which also exist with any other approach), when the inject1 routine is called from within the loop what happens with the loop itself?
If if(currentmicros - previousMicros > Delay && InjectorState1 == HIGH) is running will the loop be stalled until Delay matches the micros calculation or will it keep running during every free cpu cycle?

If if(currentmicros - previousMicros > Delay && InjectorState1 == HIGH) is running will the loop be stalled until Delay matches the micros calculation or will it keep running during every free cpu cycle?

It would have been better if the variable called "Delay" would be the words "on-time-needed" . Basically, it is like you look at your wrist watch noting the time as you turn the injector turn on, next you walk off to do other things and when the time has elapsed, you stop long enough to turn the injector off then, you go back to doing other things.