Controlling Turbocharger Solenoid With Arduino

Hi everyone so this is my first project with arduino and programming in general. I am trying to control the vgt solenoid in the turbocharger in my truck which controls how “big” the exhaust is. What i want to do as seen in my code is copy the trucks original signal under most conditions then under some conditions generate my own pwm signal using the pid library so the turbo acts as an exhaust brake. I have attached my code and a schematic of the solenoid driver circuit only. I left out the other sensors and what not for clarity.

The problems im having right now are:

1: With the flyback diode hooked up the wire from the solenoid turns on the arduino and my lcd even if the main 14V line to the regulator is not hooked up

2: My solenoid driver circuit does not seem to be working. I hooked up my voltmeter in series with the wire from the vgt solenoid and only read a max of 0.2 amps when the arduino was in control and I should be seeing 0.4 to 1.4 amps in order for the solenoid to move the veins in the turbo.

Please give me any advice you can, I appreciate it!

#include <PID_v1.h>
#include <PWM.h>
#include <SPI.h>
#include <LiquidCrystal.h>

int32_t frequency = 167; //frequency (in Hz)

const int numReadings= 3;

//exhaust pressure sensor
int ebp_readings[numReadings];      
int ebp_index = 0;                  
float ebp_total = 0;                  
float ebp_average = 0; 
const int ebp = A0;

//throttle position sensor
int tps_readings[numReadings];      
int tps_index = 0;                  
float tps_total = 0;                  
float tps_average = 0; 
const int tps = A1;
unsigned long tps_counter = 0;

//vehicle speed sensor
unsigned long current_time2;
unsigned long previous_time2 = 0;
volatile int vss_counter = 0;
unsigned long vss_freq = 0;
const int vssIn = 2;

//duty cycle in measurement
const int pwmIn = 12;
float totaltime = 6000.0;
float dutycycleIn;
float dutycycleOut;
float dutycycleOutD;
unsigned long durationL;
float durationL_readings[numReadings];      
int durationL_index = 0;                                   
float durationL_average = 0; 

//PWM out pin
const int pwmOut = 9;

// LCD timer
unsigned long current_time1;
unsigned long previous_time1 = 0;


//pid information
double Setpoint, Input, pidOutput;
PID myPID(&Input, &pidOutput, &Setpoint,1,0,0, DIRECT);

// initialize lcd
LiquidCrystal lcd (8, 7, 6, 5, 4, 3);

void setup() {
  pinMode(pwmIn, INPUT);
  pinMode(tps, INPUT);
  pinMode(ebp, INPUT);
  pinMode(pwmOut, OUTPUT);
  pinMode(vssIn, INPUT);
  digitalWrite(vssIn, HIGH);
  
  // duty cycle in measurement
  for (int durationL_thisReading = 0; durationL_thisReading < numReadings; durationL_thisReading++)
    durationL_readings[durationL_thisReading] = 0;
   
  //vehicle speed sensor
  attachInterrupt(0, vspeed, FALLING);
  
  //exhaust pressure sensor
  for (int ebp_thisReading = 0; ebp_thisReading < numReadings; ebp_thisReading++)
    ebp_readings[ebp_thisReading] = 0;

    //throttle position sensor
  for (int tps_thisReading = 0; tps_thisReading < numReadings; tps_thisReading++)
    tps_readings[tps_thisReading] = 0;    
    
   //initialize all timers except for 0, to save time keeping functions
  InitTimersSafe(); 
  //sets the frequency for pwmOut pin 9
  bool success = SetPinFrequencySafe(pwmOut, frequency);  //change to pwmOut after testing
  //if the pin frequency was set successfully, turn pin 13 on
  if(success) {
    pinMode(pwmOut, OUTPUT);
  }

  //initialize PID variables
  Input = ebp_average;
  Setpoint = 356;  //test value = 20 psig
  myPID.SetOutputLimits(38, 115);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);    
  
  // lcd setup
  lcd.begin(16, 2);
  lcd.clear();
  lcd.print("VGT%:");
  lcd.setCursor(0,1);
  lcd.print("EBP:");
  lcd.setCursor(7,1);
  lcd.print("RVGT%:");

}

void vspeed(){
  ++vss_counter;
}

void loop() {
  
  
  //duty cycle in measurement
  durationL_total= durationL_total - durationL_readings[durationL_index];         
  durationL_readings[durationL_index] = pulseIn(pwmIn, LOW); 
  durationL_total= durationL_total + durationL_readings[durationL_index];         
  durationL_index = durationL_index + 1;                    
  if (durationL_index >= numReadings)              
    durationL_index = 0;  
  
  durationL_average = durationL_total / numReadings; 
  dutycycleIn = ((durationL_average / 6000) * 100);  
  dutycycleOut = map(dutycycleIn, 0, 100, 0, 255);
  
  //exhaust pressure sensor
  ebp_total= ebp_total - ebp_readings[ebp_index];         
  ebp_readings[ebp_index] = analogRead(ebp); 
  ebp_total= ebp_total + ebp_readings[ebp_index];         
  ebp_index = ebp_index + 1;                    
  if (ebp_index >= numReadings)              
    ebp_index = 0;                           
    
  ebp_average = ebp_total / numReadings;    
  
  //throttle position sensor
  tps_total= tps_total - tps_readings[tps_index];         
  tps_readings[tps_index] = analogRead(tps); 
  tps_total= tps_total + tps_readings[tps_index];         
  tps_index = tps_index + 1;                    
  if (tps_index >= numReadings)              
    tps_index = 0;                           
    
  tps_average = tps_total / numReadings; 
  

  //tps counter
  if(tps_average <=85){
    ++tps_counter;
  }
  else{
    tps_counter = 0;
  }
  
  //vehicle speed sensor
  current_time2 = millis();
    
    if(current_time2 - previous_time2 >= 500) {
    vss_freq = vss_counter;
    vss_counter = 0;
    previous_time2 = current_time2;
    }
    
  //Output
  if(tps_counter >= 3000 && vss_freq >= 22){
    Input = ebp_average;
    myPID.Compute();
    pwmWrite(pwmOut, pidOutput); 
    dutycycleOutD = pidOutput;
    
   }
    else{
    pwmWrite(pwmOut, dutycycleOut);
    dutycycleOutD = dutycycleOut;
    
  }
  
  current_time1 = millis();
  
  // LCD Display
  if(current_time1 - previous_time1 > 400){
    previous_time1 = current_time1;
    
  //print vgt output
  lcd.setCursor(5, 0);
  lcd.print("   ");
  lcd.setCursor(5, 0);
  lcd.print(dutycycleOutD * 0.3921, 0);
   
  //print exhaust pressure sensor measurement
  lcd.setCursor(4, 1);
  lcd.print("   ");
  lcd.setCursor(4, 1);
  lcd.print(((ebp_average * 0.00488 * 19) - 13), 0);
  
  lcd.setCursor(13,1);
  lcd.print("  ");
  lcd.setCursor(13,1);
  lcd.print(dutycycleIn, 0);
 
  }
   
}

VGT Solenoid Driver Circuit.dsn (15.6 KB)

Sorry about the previous picture. Here it is in jpeg format.

Here is a schematic that shows my other sensor inputs as well. Please have a look at it and let me know if I should add or change anything in order to protect the arduino and my truck.

Thanks

Alright guys a little update here. I was able to hook everything up to my truck and it worked!. I did add a 2.2K resistor between the mosfet gate and ground to ensure it is closed when not activated. I did notice some things tho. With out having the solenoid hooked up to the arduino everything reads correctly and performs great! The problem I’m having is with the arduino now controlling the solenoid i am getting erratic readings on my input sensors and the arduino will not shut off unless I disconnect the wire coming from the solenoid that goes to the mosfet. Do i need to add some los pass filters to my input sensors? What about the arduino not shutting off unless the power coming from the solenoid going to the mosfet is unplugged?

The solenoid is ran at 167Hz and requires current from 0.4A to 1.4A in order for it to move just for some more info about my project. Also here is the code as of now.

Any input is appreciated!

Thanks!

#include <PID_v1.h>
#include <PWM.h>
#include <SPI.h>
#include <LiquidCrystal.h>

int32_t frequency = 167; //frequency (in Hz)

const int numReadings= 3;

//exhaust pressure sensor
int ebp_readings[numReadings];      
int ebp_index = 0;                  
float ebp_total = 0;                  
float ebp_average = 0; 
const int ebp = A0;

//throttle position sensor
int tps_readings[numReadings];      
int tps_index = 0;                  
float tps_total = 0;                  
float tps_average = 0; 
const int tps = A1;
unsigned long tps_counter = 0;

//vehicle speed sensor
unsigned long current_time2;
unsigned long previous_time2 = 0;
volatile int vss_counter = 0;
unsigned long vss_freq = 0;
const int vssIn = 2;

//duty cycle in measurement
const int pwmIn = 12;
float totaltime = 6000.0;
float dutycycleIn;
float dutycycleOut;
float dutycycleOutD;
unsigned long durationL;
float durationL_readings[numReadings];      
int durationL_index = 0;     
float durationL_total = 0;
float durationL_average = 0; 

//PWM out pin
const int pwmOut = 9;

//pid information
double Setpoint, Input, pidOutput;
PID myPID(&Input, &pidOutput, &Setpoint,1,0,0, DIRECT);

//exhaust brake status, 0=off, 1=on
int brake_status = 0;

// initialize lcd
LiquidCrystal lcd (8, 7, 6, 5, 4, 3);

// LCD timer
unsigned long current_time1;
unsigned long previous_time1 = 0;

void setup() {
  pinMode(pwmIn, INPUT);
  pinMode(tps, INPUT);
  pinMode(ebp, INPUT);
  pinMode(pwmOut, OUTPUT);
  pinMode(vssIn, INPUT);
  digitalWrite(vssIn, HIGH);
  
  //vehicle speed sensor
  attachInterrupt(0, vspeed, FALLING);
  
  // duty cycle in measurement
  for (int durationL_thisReading = 0; durationL_thisReading < numReadings; durationL_thisReading++)
    durationL_readings[durationL_thisReading] = 0;
  
  //exhaust pressure sensor
  for (int ebp_thisReading = 0; ebp_thisReading < numReadings; ebp_thisReading++)
    ebp_readings[ebp_thisReading] = 0;

    //throttle position sensor
  for (int tps_thisReading = 0; tps_thisReading < numReadings; tps_thisReading++)
    tps_readings[tps_thisReading] = 0;    
    
   //initialize all timers except for 0, to save time keeping functions
  InitTimersSafe(); 
  //sets the frequency for pwmOut pin 9
  bool success = SetPinFrequencySafe(pwmOut, frequency);  //change to pwmOut after testing
  //if the pin frequency was set successfully, turn pin 13 on
  if(success) {
    pinMode(pwmOut, OUTPUT);
  }

  //initialize PID variables
  Input = ebp_average;
  Setpoint = 356;  //test value = 20 psig
  myPID.SetOutputLimits(38, 128);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);    
  
  // lcd setup
  lcd.begin(16, 2);
  lcd.clear();
  lcd.print("VGT%:");
  lcd.setCursor(0,1);
  lcd.print("EBP:");
  lcd.setCursor(9,1);
  lcd.print("VSS:");

}

void vspeed(){
  ++vss_counter;
}

void loop() {
  
  
  //duty cycle in measurement
  durationL_total= durationL_total - durationL_readings[durationL_index];         
  durationL_readings[durationL_index] = pulseIn(pwmIn, LOW); 
  durationL_total= durationL_total + durationL_readings[durationL_index];         
  durationL_index = durationL_index + 1;                    
  if (durationL_index >= numReadings)              
    durationL_index = 0;  
  
  durationL_average = durationL_total / numReadings; 
  dutycycleIn = ((durationL_average / 6000) * 100);  
  dutycycleOut = map(dutycycleIn, 0, 100, 0, 255);
  
  //exhaust pressure sensor
  ebp_total= ebp_total - ebp_readings[ebp_index];         
  ebp_readings[ebp_index] = analogRead(ebp); 
  ebp_total= ebp_total + ebp_readings[ebp_index];         
  ebp_index = ebp_index + 1;                    
  if (ebp_index >= numReadings)              
    ebp_index = 0;                           
    
  ebp_average = ebp_total / numReadings;    
  
  //throttle position sensor
  tps_total= tps_total - tps_readings[tps_index];         
  tps_readings[tps_index] = analogRead(tps); 
  tps_total= tps_total + tps_readings[tps_index];         
  tps_index = tps_index + 1;                    
  if (tps_index >= numReadings)              
    tps_index = 0;                           
    
  tps_average = tps_total / numReadings; 
  

  //tps counter
  if(tps_average <=80){
    ++tps_counter;
  }
  else{
    tps_counter = 0;
  }
  
  //vehicle speed sensor
  current_time2 = millis();
    
    if(current_time2 - previous_time2 >= 1000) {
    vss_freq = vss_counter;
    vss_counter = 0;
    previous_time2 = current_time2;
    }
    
  //Output
  if(tps_counter >= 20 && vss_freq >= 44){
    Input = ebp_average;
    myPID.Compute();
    pwmWrite(pwmOut, pidOutput); 
    dutycycleOutD = pidOutput;
    brake_status = 1;
   }
    else{
    pwmWrite(pwmOut, dutycycleOut);
    dutycycleOutD = dutycycleOut;
    brake_status = 0;
    
  }
  
  current_time1 = millis();
  
  // LCD Display
  if(current_time1 - previous_time1 > 600){
    previous_time1 = current_time1;
    
  //print vgt output
  lcd.setCursor(5, 0);
  lcd.print("   ");
  lcd.setCursor(5, 0);
  lcd.print(dutycycleOutD * 0.3921, 0);
  
  //throttle position sensor
  lcd.setCursor(9,0);
  lcd.print(" ");
  lcd.setCursor(9,0);
  lcd.print(brake_status);
  
  //tps counter
  lcd.setCursor(12,0);
  lcd.print("    ");
  lcd.setCursor(12,0);
  lcd.print(tps_counter);
   
  //print exhaust pressure sensor measurement
  lcd.setCursor(4, 1);
  lcd.print("   ");
  lcd.setCursor(4, 1);
  lcd.print(((ebp_average * 0.00488 * 19) - 12), 0);
  
  lcd.setCursor(13,1);
  lcd.print("   ");
  lcd.setCursor(13,1);
  lcd.print(vss_freq);
 
  }
   
}

I can't help you with your sensors but I can help with the Arduino not shutting down.

It appears you have the solenoid wired to a separate supply. When the 14V is removed from the regulator the Arduino gets power from the solenoid supply. It goes through the solenoid and the flywheel diode and on to the 14V going to the Arduino regulator. The solenoid will easily allow enough current to flow to power the circuit.

The flywheel diode needs to be wired in parallel with the coil. Banded end to positive end of coil.

Thanks for the reply! I cant wire the diode across the solenoid as it is really difficult to get to it on my truck. The wire coming from the solenoid i spliced right before it goes into the truck’s computer. I am unsure it there is a flyback diode across the solenoid itself. If i ran another wire from positive of my battery to the diode on my arduino then powered the regulator from a switched 14v supply would that solve my issue?

Also are some simple rc low pass filters going to help clean up my sensor inputus?

Thanks

  for (int durationL_thisReading = 0; durationL_thisReading < numReadings; durationL_thisReading++)
    durationL_readings[durationL_thisReading] = 0;

  //exhaust pressure sensor
  for (int ebp_thisReading = 0; ebp_thisReading < numReadings; ebp_thisReading++)
    ebp_readings[ebp_thisReading] = 0;

  //throttle position sensor
  for (int tps_thisReading = 0; tps_thisReading < numReadings; tps_thisReading++)
    tps_readings[tps_thisReading] = 0;

Don’t you trust the compiler?

I do trust the compiler but it seems strange to me that without the solenoid hooked up my readings are stable, then once i hooked up the solenoid my readings went erratic. For example my exhaust pressure sensor reading will jump from 3 to -6 and whatever else with the solenoid hooked up. Without it hooked up it would be at a steady 3.

It just seems weird to me that this happens. I am new to electronics and arduino.

Thanks for the input!

You really do want that flyback diode in the right place, across the solenoid terminals or close to it, that's really important to avoid high-voltage spikes hitting your circuitry.

Running a separate wire to have the flyback diode remote from the solenoid will act as a source of interference as you are routing a inductive spike round a large loop.

Unfortunately i cant place the flyback diode right at the solenoid as it is darn near impossible to get to without taking components off of the engine. I did however run a seperate wire from the battery to the flyback diode. This did solve the problem of the arduino staying on when the switched power was off.

I am still having trouble with noise getting into my inputs. This only happens when the solenoid is hooked up tho. Is a simple rc low pass filter the way to go or should i try something else?

Also i have noticed that when the arduino is controlling the solenoid it receives approximately 0.25 amps less then when the truck computer is controlling it for the same dutycycle of 50%. At 50% duty cycle i should be seeing approximately 1.33 amps through the solenoid but when the arduino is in control i only see approximately 1.1 amps. I am using an IRF840 mosfet and 1N4001 flyback diode.

Thanks for all the input guys i really appreciate it!

  1. Do you really mean IRF840? I can't find that part number. If you mean IRF540, then that isn't a logic-level mosfet, which would explain why you are getting less current through the solenoid (and the mosfet is probably getting hot too).

  2. IN4001 is rated at 1A, which is marginal as a flyback diode for a solenoid that carries (by the sound of it) about 2.66A at full power. It's probaby OK because 1A is the rating for maximum average current, but for safety I suggest a diode with greater current-carrying capability - preferably a Schottky diode.

  3. Your erratic readings are probably caused by the current through the mosfet and Arduino ground wire inducing a voltage on the Arduino ground. One possible solution is use a separate ground wire for the mosfet source terminal, and either buffer or optically-isolate the gate drive. This would also get round the problem of using a mosfet that isn't logic-level, by providing a higher gate drive voltage.

Yes it is a IRF840 and it is possible it is not turning completely on. Could someone recommend a mosfet and some sort of isolator?

The solenoid will never be driven with more then 1.5 amps but can draw 3 amps if it is shorted to ground and it is ran at 167 Hz.

Thanks so much!

Does it matter to you whether the solenoid is powered or not powered when the Arduino is not running, e.g. just powered up?

Mot sure if I understand what you mean dc42. If the arduino is not on the truck will not be running so theref ]:Dre the solenoid should not have any current running through it. The solenoid will always have 14V to it when the truck key is on.

Could you also recommend a sufficient flyback diode?

Thanks

For the flyback diode I suggest a 2A or 3A Schottky diode with a reverse voltage rating of at least 100V because transients are common on the battery voltage in automotive systems. Something like http://uk.farnell.com/stmicroelectronics/stps3150rl/schottky-rectifier-3a/dp/1702811.

For the isolated gate drive, you could use the attached schematic. It won’t turn the mosfet off especially quickly, but at the low PWM frequency you are using, I don’t think that will matter. If the mosfet runs hot when you use PWM but not when you turn it on all the time, then we may need to think again. This circuit provides 10V gate drive, so your IRF540 should be OK in it.

PS - this circuit needs an opto isolator with a minimum current transfer ratio of at least 100%, but preferably not one with Darlington output.

Ok guys finally had a bit of time today to work on it. I replaced the mosfet with a tip120 transistor and ran a seperate ground for the solenoid. Doing this did in fact solve the problem of my input readings going all over the place. Yay!

The other problem i am having is that I am still not getting enough amps through the solenoid when the arduino is controlling the solenoid vs when the truck's computer is controlling it for the same dutycycle. I am using a 270 omh resistor on the transistor gate so it should be fully turning on. Any input on this?

Here is the link to the transistor i am using http://www.nteinc.com/specs/200to299/pdf/nte261.pdf

Thanks guys!

mitchedwards: The other problem i am having is that I am still not getting enough amps through the solenoid when the arduino is controlling the solenoid vs when the truck's computer is controlling it for the same dutycycle.

That's because you are using a TIP120 transistor instead of a mosfet.

Thanks for replying dc42. How would using the transistor that i am vs using a mosfet affect the current through the solenoid? The transistor is rated above what i need it for.

If you look at the datasheet for the TIP120, you will find that at 3A collector current it has a voltage drop of up to 2V. A suitably-chosen mosfet will only drop a small fraction of a volt.