Pop-up Headlight Management

Hi guys,

I have a 90s car with pop-up headlights but the former owner decided it was a good idea to remove the headlight module when it malfunctioned.

I was determined to build an Arduino based management module for headlights but after so many failed attempts I thought maybe you guys could give me a hand to resolve the issues.

Plan is simple:
1- I have a master switch - if master switch is ON, headlights should always be UP.
2- If master is OFF and light signal is ON - headlights should be UP and after light signal is OFF, headlights should close after 5 seconds.
3- If headlights are DOWN and flash signal is ON - headlights should be UP and wait for 5 seconds, if flash signal is not repeated in that time, headlights should close. If flash signal is ON again during that time, countdown should reset.

I am sure it is just an easy level practice for most of you but as a hobbyist, I am not much of a coder and I cannot make this one work. Please find the code I tried down below. Thank all of you for pitching in.

#define relayRight 8
#define relayLeft 9

#define lightSignal A2
#define flashSignal A3
#define masterSignal 13


int relayStatus = 0;
int Lights = 0;
int Flash = 0;
int Master = 0;
unsigned int lightThreshold = 10;
unsigned int flashThreshold = 2;
unsigned long lightOffTime = 0;
unsigned long flashOffTime = 0;
bool signalTimerActive = false;

void setup() {

  pinMode(relayLeft, OUTPUT);
  pinMode(relayRight, OUTPUT);
  pinMode(masterSignal, INPUT_PULLUP);

  digitalWrite(relayLeft, HIGH);
  digitalWrite(relayRight, HIGH);

  Serial.begin(9600);
}

void loop() {
  Lights = analogRead(lightSignal);
  Flash = analogRead(flashSignal);
  Master = digitalRead(masterSignal);

  unsigned long currentTime = millis();

  if (Master == LOW) {
    relayStatus = 1;
    HeadsUp();
  } else {
    // Light signal logic
    if (Lights >= lightThreshold) {
      relayStatus = 1;
      HeadsUp();
      signalTimerActive = false;
    } else if (Lights < lightThreshold && relayStatus == 1) {
      if (!signalTimerActive) {
        lightOffTime = currentTime;
        signalTimerActive = true;
      }
      if (currentTime - lightOffTime >= 4000) {
        relayStatus = 0;
        HeadsDown();
        signalTimerActive = false;
      }
    }

     if (relayStatus == 0 && Flash >= flashThreshold) {
      relayStatus = 1;
      HeadsUp();
      flashOffTime = currentTime;
      signalTimerActive = true;
    }

    if (signalTimerActive && (currentTime - flashOffTime >= 4000)) {
      relayStatus = 0;
      HeadsDown();
      signalTimerActive = false;
    }
  }

  Serial.print("Master: ");
  Serial.println(Master);
  Serial.print("Light: ");
  Serial.println(Lights);
  Serial.print("Flash: ");
  Serial.println(Flash);

  delay(200);
}

void HeadsUp() {
  digitalWrite(relayLeft, LOW);
  digitalWrite(relayRight, LOW);
  Serial.println("HeadsUp");
}

void HeadsDown() {
  digitalWrite(relayLeft, HIGH);
  digitalWrite(relayRight, HIGH);
  Serial.println("HeadsDown");
}

What happens when you try it?

I made minor changes while i try. Either headlights close right after opening without any kind of delay or they don't open at all.

Post a connection and wiring diagram to see how we can help.

I scribbled out some pseudocode and it is reasonably simple.

One thing you don't specify is want should happen if the pop up has been only because of the master switch, but you now open the master.

In the code I wrote, it stays up for five seconds. I just noticed that unplanned outcome and ask if it should instead go immediately to the pop down condition.

Do your print statements make sense, that is to ask are you three inputs being developed properly from your circuit and code?

Why did you use analog input for two? Just curious.

a7

1 Like

this is the simulation of the whole project

I used original lines of lights and flash from the car. I read them through an optocoupler module (4 lines) and those lines only produce analog values. I believe there are better ways to work with them but with my limited knowledge I could only make them work as this. I didn't see anything wrong with print statements.

Also I noticed if I don't include the flash operation, code works as intended :slight_smile:

Thanks for posting a link to your wokwi version.

If you do more work on it, use a copy so the link you posted is not a moving target and will always work (or not) the way you've said it doesn't.

One analog inputs will read random values, so you are already getting erroneous input.

Wire unused analog inputs to ground. In the wokwi, you can't burn anything out, be careful IRL to ensure that those inputs are never made into outputs.

I would make all those signal variables to be type bool.

Then get the input like

  Lights = analogRead(lightSignal) >= lightThreshold;

and later, the tests are just

    if (Lights) {

a different name might be better. I am bad at naming things and good at using any name any named thing has been given, so don't ask me.

  lightOnSignal = analogRead(lightSignal) >= lightThreshold;

//... 

    if (lightOnSignal) {

Later when (if) you want to figure out how to develop a digital input instead, the only code to change is the analogRead(). Plus it's just less reading in the rest of the code. Less ink alone can mean easier reading.

I can't play with your code at this time. If you make any progress and think it is worth doing, post your new version and say why you done or it still isn't just right.

a7

What if LIGHT and FLASH are ON? I assume FLASH wins?

sketch.ino for wokwi.com
#include <Servo.h>
Servo servo;
byte servopin = 7;
byte masterpin = 12;
byte lightpin = 11;
byte flashpin = 10;
byte ledpin = 2;
unsigned long timer, timeout = 500;

void setup() {
  Serial.begin(115200);
  servo.attach(servopin);
  servo.write(180);

  pinMode(ledpin, OUTPUT);

  pinMode(masterpin, INPUT_PULLUP);
  pinMode(lightpin, INPUT_PULLUP);
  pinMode(flashpin, INPUT_PULLUP);
}

void loop() {
  if (digitalRead(masterpin) == 0 || digitalRead(lightpin) == 0 || digitalRead(flashpin) == 0) {
    servo.write(90);
  } else {
    servo.write(180);
    digitalWrite(ledpin, LOW);
  }

  // lights
  if (digitalRead(lightpin) == 0 && digitalRead(flashpin) == 1) {
    digitalWrite(ledpin, HIGH);
  }

  // hazzard
  if (digitalRead(flashpin) == 0) {
    blink(); // blink LED
  }
  delay(100); // for serial printing
  displaystate();
}

void blink() {
  if (millis() - timer > timeout) { // timer overflow
    timer = millis(); // reset timer
    digitalWrite(ledpin, !digitalRead(ledpin)); // toggle LED
  }
}

void displaystate() {
  Serial.print(digitalRead(masterpin));
  Serial.print(digitalRead(lightpin));
  Serial.print(digitalRead(flashpin));
  if (!digitalRead(lightpin) && !digitalRead(flashpin))
    Serial.print(" <- FLASH OVERRIDE");
  Serial.println();
}
diagram.json for wokwi.com
{
  "version": 1,
  "author": "foreignpigdog x",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": 4.8, "left": 28.3, "attrs": {} },
    { "type": "wokwi-led", "id": "led1", "top": -128.4, "left": 119, "attrs": { "color": "red" } },
    {
      "type": "wokwi-servo",
      "id": "servo1",
      "top": -164.2,
      "left": 1.8,
      "rotate": 270,
      "attrs": {}
    },
    {
      "type": "wokwi-text",
      "id": "text1",
      "top": -211.2,
      "left": 48,
      "attrs": { "text": "MASTER ON" }
    },
    { "type": "wokwi-dip-switch-8", "id": "sw1", "top": -60.9, "left": -36.9, "attrs": {} },
    {
      "type": "wokwi-text",
      "id": "text2",
      "top": -115.2,
      "left": 19.2,
      "rotate": 90,
      "attrs": { "text": "FLASH" }
    },
    {
      "type": "wokwi-text",
      "id": "text3",
      "top": -115.2,
      "left": 9.6,
      "rotate": 90,
      "attrs": { "text": "LIGHT" }
    },
    {
      "type": "wokwi-text",
      "id": "text4",
      "top": -105.6,
      "left": -9.6,
      "rotate": 90,
      "attrs": { "text": "MASTER" }
    }
  ],
  "connections": [
    [ "nano:GND.3", "servo1:GND", "black", [ "v0", "h-19.2" ] ],
    [ "nano:5V.2", "servo1:V+", "red", [ "v0", "h-19.2" ] ],
    [ "nano:GND.3", "led1:C", "black", [ "v0", "h19.2" ] ],
    [ "nano:2", "led1:A", "green", [ "v0" ] ],
    [ "nano:7", "servo1:PWM", "green", [ "v0" ] ],
    [ "nano:12", "sw1:6a", "green", [ "v-9.6", "h-19.2" ] ],
    [ "nano:11", "sw1:7a", "green", [ "v-9.6", "h-38.4" ] ],
    [ "nano:10", "sw1:8a", "green", [ "v-9.6", "h-48" ] ],
    [ "nano:GND.3", "sw1:8b", "black", [ "v0", "h-105.6", "v-38.4", "h-19.2", "v-48" ] ],
    [ "sw1:8b", "sw1:7b", "green", [ "h0" ] ],
    [ "sw1:7b", "sw1:6b", "green", [ "v0" ] ]
  ],
  "dependencies": {}
}

Thank you very much for your suggestions. Constructing with bool variables is a bit complex to my level. I can read analog values IRL without any problem maybe i just set up the simulation wrong.

If lights are ON then there is no need to measure flash because headlights will be UP and flash only operates momentarily without changing the UP/DOWN state

But if LIGHTS are ON and overriding FLASH, you can not use FLASH. You should have FLASH (ON) override LIGHTS (ON).

What is your definition of FLASH?

there are two separate lines on the car for lights and flash. If lights are ON then headlights will be UP. flash operates on a different line and if flash is ON, lights will be ON too. in a regular car you switch between high beam and low beam without closing the other one ffirst. in my case the only difference is headlights popping up and down and that movement is either connected to low beam or high beam. regardless of the beam version, if any light is ON, headlights must be UP for light to be visible.

If FLASH is its own circuit, it is not needed in the logic. In that case, MASTER ON = LIGHTS ON. That's a DPDT switch.

I can not see the drawing you are imagining.

that is a good suggestion. I believe master/lights part and flash part are already separated in the code but still it doesn't function properly. I can UP the headlights with any of the three inputs but headlights don't stay open for 5 seconds. also flash part doesn't regard repeated signal. headlights open and close for each signal again and again.

I did not catch that.

This works on @xfpd's hardware and is mostly the original code.

I added a timer to keep the whatever whatevered for 3 seconds beyond the last activity because master, light or flash.

I just reset the timer whenever there is a signal and move closing teh servo to the test of time expired, viz:

  if (now - stayUpTimer > stayUpTime) {
    servo.write(180);
    digitalWrite(ledpin, LOW);
  }

In context.

#include <Servo.h>
Servo servo;
byte servopin = 7;
byte masterpin = 12;
byte lightpin = 11;
byte flashpin = 10;
byte ledpin = 2;
unsigned long timer, timeout = 500;

unsigned long now;
unsigned long stayUpTimer;
const unsigned long stayUpTime = 3000;



void setup() {
  Serial.begin(115200);
  servo.attach(servopin);
  servo.write(180);

  pinMode(ledpin, OUTPUT);

  pinMode(masterpin, INPUT_PULLUP);
  pinMode(lightpin, INPUT_PULLUP);
  pinMode(flashpin, INPUT_PULLUP);
}

void loop() {
  now = millis();

  if (digitalRead(masterpin) == 0 || digitalRead(lightpin) == 0 || digitalRead(flashpin) == 0) {
    servo.write(90);
    stayUpTimer = now;
  }
    
  if (digitalRead(masterpin) && digitalRead(lightpin) && digitalRead(flashpin))
    digitalWrite(ledpin, LOW);

/*
  else {
    servo.write(180);
    digitalWrite(ledpin, LOW);
  }
*/
  // lights
  if (digitalRead(lightpin) == 0 && digitalRead(flashpin) == 1) {
    digitalWrite(ledpin, HIGH);
  }

  // hazzard
  if (digitalRead(flashpin) == 0) {
    blink(); // blink LED
  }

  if (now - stayUpTimer > stayUpTime) {
    servo.write(180);
    digitalWrite(ledpin, LOW);
  }

  delay(100); // reasons
  displaystate();
}

void blink() {
  if (now - timer > timeout) { // timer expiry
    timer = now; // reset timer
    digitalWrite(ledpin, digitalRead(ledpin) == HIGH ? LOW : HIGH); // toggle LED
  }
}

void displaystate() {
  Serial.print(digitalRead(masterpin));
  Serial.print(digitalRead(lightpin));
  Serial.print(digitalRead(flashpin));

  if (!digitalRead(lightpin) && digitalRead(flashpin))
    Serial.print(" <- LIGHTS ONLY");
  if (!digitalRead(flashpin) && digitalRead(lightpin))
    Serial.print(" <- FLASH ONLY");
  if (!digitalRead(lightpin) && !digitalRead(flashpin))
    Serial.print(" <- FLASH OVERRIDE");

  Serial.println();
}

edit: added logic to turn light off if nothing means it should be on. Waiting for the thing to servo the thing. What is this thing?

It's even more fun to play with. I like the gang of 8 switches handled.

a7

Hello afmek38

I have read the thread.

As I am a big fan of the IPO model, I am confused about the mapping of the input and output signals to and from the sketch.

I have identified the following signals as input signals:

  • Master - digital input ?
  • Light on/off - analogue input ?
  • Flashing left - analogue input ?
  • Flashing right - analogue input ?

I have identified the following signals as output signals:

  • Headlight - servo ?
  • Light - relay ?
  • Left indicator - relay ?
  • Right indicator - relay ?

What do you think?

I propose to design a button manager that responds to the changes in the above input signals and control the output signals via simple subroutines, including a timer() function.

when headlights are DOWN and i send a flash signal (high beam signal) headlights pop UP and close after immediately. for each flash signal headlights just go UP and DOWN without any delay.

thank you so much for the code. i will try it today and let you know of the results.