Go Down

Topic: Arduino BoostController (Read 567 times) previous topic - next topic

Siris87

Jan 17, 2015, 07:34 pm Last Edit: Jan 17, 2015, 08:40 pm by Siris87
I'm new to Arduino, and programing in C+, but not programing in general. This is my first project, and am looking for feedback from a more experienced audience. So here goes:

I set out to create a closed loop boost controller for a automotive application, those of you not familiar this the concept this is a rough description, the controller essentially pulses a solenoid valve installed inline on the vacuum/boost hose from the turbocharger to the wastegate. It is intended to "hide" boost from the wastegate to keep it closed, which in turn builds more boost.

My Diagram is attached, and it seems to work on the desk, I have not installed it in a car yet. Mostly I'm curious about the quality of my code, is this the cleanest most efficient way to write it? Does anyone have suggestions on how to improve it?

Code: [Select]

#include <PID_v1.h>
//Define Pins used
const int MapPin = A0; // Map sesnor pin
const int MacPin = 3; // Mac valve pin
const int WBPin = A5; // Wideband pin

//Set desired boost level and define Pid variables
double TargetBoost = 250; // 250kpa is target boost level
double MapKPA, PidPWM;// Initialise other PID variables
const long sampleRate = 15;// Pid sample rate

// Pid tuning parameters
const double Kp = 7; //Pid Proporional Gain. Initial ramp up i.e Spool, Lower if over boost
const double Ki = 20; //Pid Integral Gain. Overall change while near Target Boost, higher value means less change, possible boost spikes
const double Kd = 0; //Pid Derivative Gain. Not Sure what to do with it.....

//Load PID controller
PID myPID(&MapKPA, &PidPWM, &TargetBoost, Kp, Ki, Kd, DIRECT); //Initialize PID with our variables

//Set other global variables
unsigned long LeanTime;
float Lean = 0;
float Cut = 0;
float AF = 0;
float Atmo = 0;

void setup() {
  //Serial.begin(9600); //debug uncomment to debug
  Atmo = analogRead(MapPin) * (315.0 / 1023.0); //Read Atmospheric pressure and convert to KPA
  //Pid Stuffs
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0, 180); //Mac valve lower duty cycle limits 180 Closed, 0 Off
  myPID.SetSampleTime(sampleRate); //PID's sample rate in milliseconds
  TCCR2B = TCCR2B & B11111000 | B00000111; //Set pin 3 and 11 PWM frequency to 31hz
  pinMode(13, OUTPUT);
}


void loop() {
  //BenchTest
  /*if (MapKPA < TargetBoost) {
    MapKPA = (MapKPA + random(10));
  }
  else {
    MapKPA = (MapKPA - (100.0 - PidPWM));
  }*/
 
  MapKPA = analogRead(MapPin) * (315.0 / 1023.0);  //Read map Pin and convert to KPA
  AF = ((analogRead(WBPin) * (5.0 / 1023.0)/0.625) + 10.0);//Read wideband and convert to a/f ratio

  if (AF > 12.1) {
    Lean = Lean + 1; //add to lean counter
  }
  else {
    Lean = 0;//Reset lean counter becuase rich
  }
  if (Lean == 1) {
    LeanTime = millis();//Log initial lean reading time
  }
  if (((Lean >= 2)) && ((unsigned long)(millis() - LeanTime) > 250)) {//Check if lean for more than 250ms (.25 seconds) and lean more than 2 loops
    Cut = 1; //cut boost if too lean for too long
  }
  else {
    Cut = 0; //dont cut boost becuase rich :D
  }

  if ((MapKPA < 100) || (Cut>0)) {//Keep valve from clicking when not in boost or cut boost if too lean
    PidPWM = 0;//Dont pulse if not building boost or wideband is lean
    //Serial.print("Boost Cut");//debug
    digitalWrite(13, HIGH);
   }
  else {
    myPID.Compute(); //Execute Pid and produce a pulse width if above 100kpa and not lean
    digitalWrite(13, LOW);
  }
  analogWrite(MacPin, PidPWM); //Energize mac valve
 
  /*
  //--------------------Debug--------------------
  Serial.print("--------------------PW=");
  Serial.println(PidPWM);
  Serial.print("--------KPA=");
  Serial.println(MapKPA);
  Serial.print("Target=");
  Serial.println(TargetBoost);
  Serial.print("AF=");
  Serial.println(AF);
  Serial.print("Cut=");
  Serial.println(Cut);
  Serial.print("Lean=");
  Serial.println(Lean);
  Serial.print("LeanTime=");
  Serial.println(LeanTime);
  Serial.println(" ");
  delay(250);*/
  }

Paulcet

As to your code, I am probably no better than you.  But I think you should examine your data types: double, float, etc.  Probably don't need 32-bit floating point precision for some of your variables.  smarter guys than me will probably be along to give better guidance there.

However, your wiring needs help.  Don't use a voltage divider to power the Arduino.  (Think that's what is show there?)  If you have a good 5v supply from the car, you can use that.  Also, you might want to consider using something other than the TIP120.  The writer of this article is a little agitated, but he has some good points.

cyclegadget

 As Paulcet said, you may be using bigger data types than you need in some places but, I am not sure either so I will leave that for someone else.

However, here is a part of your code that needs fixed.
Code: [Select]
if (((Lean >= 2)) && ((unsigned long)(millis() - LeanTime) > 250)) {//Check if lean for more than 250ms (.25 seconds) and lean more than 2 loops
    Cut = 1; //cut boost if too lean for too long


With this, because millis() is already assigned a data type.
Code: [Select]
if ((Lean >= 2) && (millis() - LeanTime) > 250) {//Check if lean for more than 250ms (.25 seconds) and lean more than 2 loops
    Cut = 1; //cut boost if too lean for too long
  }


You may want to consider using >= 250 as you will have to wait another loop if your millis() - Leantime equals 249 or less.


Also, in the terms of engines, wideband Air/Fuel sensors are fairly slow. Therefore, give yourself a wide margin when you are testing your ideas on your engine.
Good links: Eagle tutorial= http://www.youtube.com/playlist?list=PLDE1858BD83D19C70
General Arduion tutorials = http://tronixstuff.wordpress.com
http://www.gammon.com.au/forum/bbshowpost.php?bbtopic_id=123

Siris87

My schematic skills are worse than my c++ knowledge, but the voltage divider was to keep the arduino from seeing the 14+dcv common in an automotive charging system. If there is a better way let me know please. Also thank you for the link Paulcet, I very well may swap that component in the final draft.

Thank you for the tips on the code cyclegadget.

cyclegadget

Quote
the voltage divider was to keep the arduino from seeing the 14+dcv common in an automotive charging system. If there is a better way let me know please.
You can buy an USB adapter that plugs into your cigarette lighter of your car, the use the programming cable of the Arduino for power.  An USB adapter can supply enough power for the circuit diagram that you are providing. If you add more parts like LCD displays, you may have to calculate how much current you need to operate your devices.

 Resistor voltage dividers can cause problems when used for powering the Arduino. You are better served by using batteries or a power supply with the correct voltage.

You may use a divider on an (analog pin) when taking analog readings so that the voltage range is 0 to 5 volts.
Good links: Eagle tutorial= http://www.youtube.com/playlist?list=PLDE1858BD83D19C70
General Arduion tutorials = http://tronixstuff.wordpress.com
http://www.gammon.com.au/forum/bbshowpost.php?bbtopic_id=123

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy