ArDRLidle - schield for DRL and engine idle control

I just finished a arduino Uno schield for my car to control Daytime Running Lights (DRL), and, more importantly, idle speed.

DRL part checks if engine is running, and if it is, it switches DRL on (long beams at ~ 35%). If handbrake or lights switch is on, DRL shuts off.

Idle part is somewhat complicated. I tried to do closed loop controll, but I added open loop just in case.

I have potentiometer at my lights switch that controlls how bright will lights in the dashboard be. I used that to control idle. If analog input senses voltage it will set that value as reference point. Turning the knob one or the other way (raising or lowering voltage) will move step motor that controls idle one or the other way accordingly.

But if there is no voltage at the analog input it will switch to closed loop mode, and depending on the engine temperature will try to keep idle speed at +/- 25 rpm of set value.

English is not my native language, so perhaps this is a bit confusing, so feel free to ask if something is not clear.

Also, I am open for suggestions.

So far schield is only tested on my bench (and not yet with the stepper motor), but I hope it will be OK.

I am trying to attach image and sketch, but I get error that "The message exceeds the maximum allowed length (9500 characters)." and that the folder is full, try with a smaller attachment - but the image is only 52,748 bytes.

I am tired now, I will try tomorrow again.

Daytime Running Lights (DRL

Sorry, but if it is daytime,why do you need lights?

Because DRL are required by law.

Here is the schematics:

Please note that that there is an error at this schematics. Vcc should be connected to the +5V pin not to Vin, and, in the lower right corner CLT should be where TEMP is, and vice versa.

And here is the sketch part one:

#define DEBUG

#ifdef DEBUG
#define DEBUG_PRINT(x)         Serial.print (x)
#define DEBUG_PRINTzeroDEC(x)  Serial.print (x, 0) // 0 decimal places
#define DEBUG_PRINTDEC(x)      Serial.print (x, DEC)
#define DEBUG_PRINTLN(x)       Serial.println (x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTzeroDEC(x)
#define DEBUG_PRINTDEC(x)
#define DEBUG_PRINTLN(x)
#endif

// Input pins:
const int temperaturePin = A0;      // pin for the temp sensor
const int tpsPin = A1;              // TPS input pin
const int saugPin = A2;             // manual saug control input pin

const int acPin = 2;                // pin for the a/c ON sensing
const int brakeswPin = 6;           // "handbrake on" switch input
const int lightswPin = 7;           // "lights on" switch input

// Output pins:
const int DRLoutPin = 5;            // DRL output
const int enableB = 8;              // enable step motor
const int stepB = 9;                // drive step motor
const int enableA = 10;             // enable step motor
const int stepA = 11;               // drive step motor
const int lowTempPin = 12;          // pin for the low temp relay
const int hiTempPin = 13;           // pin for the hi temp relay

// These constants won't change:
const int lowthreshold = 91;        // threshold level for low temp relay
const int hithreshold = 99;         // threshold level for hi temp relay
const int idleIdeal = 850;          // idle rpm for warmed up engine
const int stepsPerRevolution = 200; // steps per revolution for the motor
const int tpsLimit = 100;           // top TPS limit for idle adjustment

int previousSaug = 0;               // for manual idle controll via potentiometer
int stepUp = 0;
int stepDown = 0;
#ifdef DEBUG
int stepCount = 0;
#endif
float pullupR = 2385;               // measured resistance of CLT pullup resistor
long resistance;
float temp;
float rpm;

#include <Stepper.h>
// initialize the stepper library:
Stepper myStepper(stepsPerRevolution, stepA, stepB);


// RPM Calculation variables
unsigned long lasttachint;          //time in millis of most recent tach interrupt
float insttachrpm=0;                //tach instantaneous rpm
//float rpm=0;                      //tach instantaneous rpm
volatile int ilog=0;
int maxlog=31;
volatile boolean logoflo;           //index to log table, max entry #, log overflow flag
volatile byte lflag[32];
volatile unsigned long lmillis[32]; //log of interrupts

// Init function
void setup()
{
#ifdef DEBUG
  // Initialize serial comms
  Serial.begin(9600);
#endif
  // Clear the interrupt table
  clearlog();

  // Attach interrupt from tachometer
  attachInterrupt(1, tachmonitor, RISING);
}

... part two:

// Main loop
void loop()
{
  float RawADC = analogRead(temperaturePin);
  resistance = ((pullupR * RawADC) / (1024 - RawADC));
  temp = log(resistance);
  // use the Steinhart-Hart Thermistor Equation:
  // Temperature in Kelvin = 1 / {A + B[ln(R)] + C[ln(R)]^3}
  temp = 1 / (0.001761660642 + (0.0002364853666 * temp) + \
               (0.0000002766518199 * temp * temp * temp ));
  temp = temp - 273.15;  // Convert Kelvin to Celsius

  int idle = idleIdeal;  // set the initial idle speed
  int idleAC = 0;
  int idleWarmup = 0;
  if (digitalRead(acPin) == LOW) {
    idleAC = 250; // raise rpm if a/c is on
    idle = idleIdeal + idleWarmup + idleAC; // calculate idle speed
  }

  // calculate warmup rpm
  if (temp < 67) {
    idleWarmup = -6 * temp + 400;
    idle = idleIdeal + idleWarmup + idleAC; // calculate idle speed
  }

 

  if (analogRead(tpsPin) < tpsLimit) {

    // get and map the value for the maual idle adjustment
    int saug = (map(analogRead(2), 0, 1023, 0, 200));
    // and if there is NO signal for maual idle adjustment
    // proceed with automatic idle control:
    if (saug < 1) {

      // if rpm is 25rmp less than desired idle rpm
      if (rpm < idle - 25) {
        stepUp=1;
      }

      // and when idle is corrected
      else if (rpm >= idle) {
        digitalWrite(enableA, LOW);  // disable
        digitalWrite(enableB, LOW);  // stepper motor
        stepUp=0;
      }

      // if rpm is 25rmp more than desired idle rpm
      if (rpm > idle + 25) {
        stepDown=1 ;
      }
      // and when idle is corrected
      else if (rpm <= idle) {
        digitalWrite(enableA, LOW);  // disable
        digitalWrite(enableB, LOW);  // stepper motor
        stepDown=0;
      }


      if (stepUp != 0) {
#ifdef DEBUG
        stepCount++;
#endif
        digitalWrite(enableA, HIGH);  // enable
        digitalWrite(enableB, HIGH);  // stepper motor
        myStepper.step(1);            // turn the motor 1 step forward
      }
      if (stepDown != 0) {
#ifdef DEBUG
        stepCount--;
#endif
        digitalWrite(enableA, HIGH);  // enable
        digitalWrite(enableB, HIGH);  // stepper motor
        myStepper.step(-1);           // turn the motor 1 step backward
      }
    }

    else {
      // but if there IS signal for maual idle adjustment
      // move a number of steps equal
      // to the change in the reading for maual idle adjustment
      myStepper.step(saug - previousSaug);
      DEBUG_PRINT("  SaugUpDown: ");
      DEBUG_PRINT(saug - previousSaug);
      // remember the previous value of the Saug
      previousSaug = saug;
    }

    DEBUG_PRINT("  Saug: ");
    DEBUG_PRINT(saug);
    // DEBUG_PRINT(",  Time: ");
    // DEBUG_PRINTzeroDEC((micros() - timeold)/eventcount);
    // DEBUG_PRINT("us,  Freq: ");
    // DEBUG_PRINT(rpm/30);
    DEBUG_PRINT("  rpm: ");
    DEBUG_PRINTzeroDEC(rpm);
    DEBUG_PRINT(",  StepUp: ");
    DEBUG_PRINT(stepUp);
    DEBUG_PRINT(",  StepDown: ");
    DEBUG_PRINT(stepDown);
    DEBUG_PRINT(",  StepCount: ");
    DEBUG_PRINT(stepCount);
    DEBUG_PRINT(",  Target idle: ");
    DEBUG_PRINT(idle);
  }

  // if handbrake AND light switchches are HIGH,
  // and the engine is running (rpm>555), DRL is ON:
  if ((digitalRead(brakeswPin) == HIGH) && \ 
  (digitalRead(lightswPin) == HIGH) && \
        (rpm > 555)) {
    analogWrite(DRLoutPin, 90);   // set PWM to ~ 35%
  } 
  // in any other case - DRL is OFF
  else {
    digitalWrite(DRLoutPin, LOW);
  }

  // if coolant temp >= 91C, turn on the low temp relay:
  if (temp >= lowthreshold) {
    digitalWrite(lowTempPin, HIGH);
  } 
  // when coolant temp is low enough switch off the temp low relay:
  else if (temp < lowthreshold-4){
    digitalWrite(lowTempPin,LOW); 
  }

  // if the coolant temp >= 99C, turn on the hi temp relay:
  if (temp >= hithreshold) {
    digitalWrite(hiTempPin, HIGH);
  } 
  // when coolant temp is low enaugh switch off the hi temp relay:
  else if (temp < hithreshold-4){
    digitalWrite(hiTempPin,LOW); 
  }

  // print the temperature:
  DEBUG_PRINT(",  Celsius: "); 
  DEBUG_PRINT(temp);
  DEBUG_PRINT("deg,   Resistance: ");
  DEBUG_PRINTLN(resistance);

  checklog();    // make sure log is ok before processing
  processlog();  // process and dump the log file to the PC
  chkstale();    // check to see if tach readings are > 3 seconds old (stopped!)
}

... and the last part:

void chkstale()
{
  if ((millis()> (lasttachint+3000)) && (insttachrpm>0)){
    insttachrpm=0; 
  }
}

// Make sure log hasn't overflowed
void checklog()
{
  if (logoflo==true)
  {
    noInterrupts();
    clearlog();
    interrupts();
    DEBUG_PRINT("log overflowed at ");
    DEBUG_PRINTLN(millis());
  }
}

void processlog()
{
  int dlog,i;                 // how many entries to copy and dump
  unsigned long dmillis[32];
  byte dflag[32];             // space for the log entries
  noInterrupts();             // disable interrupts while we copy the log
  dlog=ilog;                  // copy the count
  if (dlog!=0)                // if there's anything to copy
  {
    for(int i=0; i<dlog;i++)
    {
      dmillis[i]=lmillis[i];
      dflag[i]=lflag[i];
    }
    ilog=0;                   // reset the count
  }
  interrupts();               // reenable interrupts
  if (dlog!=0)                // if there's anything to process
  {
    for (i=0;i<dlog;i++)
    {
      if (dflag[i]=='T')
      {
        processtach(dmillis[i]);
      }
    }
  }
}

// RPM Calculation logic
void processtach(unsigned long lmillis) // input is interrupt time in millis
{
  unsigned long tachtime;          // elapsed time for 10 tach events
  if (lasttachint!=0)              // skip calculation on first time in
  {
    tachtime=lmillis-lasttachint;  // time for last 10 events
    if (tachtime>0)                // just avoid divide by 0
    {
      insttachrpm=(30000/(float)tachtime)*10;  //rpm based on time in ms for 10 tach pulses
      rpm=insttachrpm;    // TO CONNECT WITH THE PART FROM PREVIOUS VERSION OF THIS SKETCH
    }
  }
  lasttachint=lmillis;
}

// Tach interrupt function
void tachmonitor()
{
  static int tachcount;
  if (tachcount <9)               // bulking up tach interrupts by a factor of 10
  {
    tachcount++;
  }
  else                             // every 10th tach pulse we spawn a log entry
  {
    if (ilog<=maxlog)
    {
      lflag[ilog]='T';             // pedal pass flag
      lmillis[ilog]=millis();
      ilog++;
    }
    else
    {
      logoflo=true;                // we've overrun the log table
    }
    tachcount=0;
  }
}

void clearlog()                    // clear the interrupt log
{
  for(int i=0; i<=maxlog; i++)
  {
    lflag[i]=' ';
    lmillis[i]=12345678;
  }
  ilog=0;
  logoflo=false;
}

BTW, I coped rpm part from here:

Any coments, suggestion for improvement?

Hi, I was searching the net for a PWM idle control circuit, and found this helpful post. I just wanted to know what car you were using this on? Also, it's not clear to me where the idle valve wires are connected on this diagram. Is this for a 3-wire idle valve? Any help would be appreciated. Thanks in advance. -Tim XD

It was pointed out elsewhere on the forum yesterday or the day before that the very very last paragraph (in tiny print) in the Atmel datasheet says that Atmel processors are NOT to be used in automotive applications.

...R