I’m trying to build an intermittent wiper circuit using an Arduino. Originally, I built this based on an Electronics Australia project from the 1980’s that used discrete components.
The circuit operation was simple. It comprised two push-button switches: A and B. Switch A caused the wipers to activate once. Switch B resets the timer. The operation was as follows: Press A and the wipers activated once. Press A, and the wipers were activated for a second time. The circuit then remembered the time interval between the two A presses and triggered the wipers at that interval. If the rain intensity increased, pressing A again would trigger a new, shorter interval. If the rain intensity decreased, one would press B to reset the timer circuit. One would press A again, repeating the whole process over again. The entire process worked surprisingly well (the original project won an award) and didn’t require the constant adjustments that still seem necessary with intermittent wipers, even today.
I can tap into the wiring on the wiper lever on the steering column to trigger a single wipe, without having to go back to the wipers, per se, in the engine compartment. So a single digital pulse is all I need as an output.
I’m hoping for some help with the actual code to achieve this on an Arduino. Any help would be very much appreciated.
One thing to be aware of is that vehicle electrics can be a difficult environment for devices like microcontrollers. You will probably need to give your board some extra protection.
An Uno could be used for developing the code for a prototype, but would be wasted on the final version. Once tested & debugged, you could use the Uno to upload the code to an ATTINY45 or similar.
The forum will be happy to help you with the code, but not write it for you. So have a go and if you get stuck, post the code and schematic here for assistance.
Suggest you start on the bench using the Uno's onboard LED to represent the pulse that would go to the wiper motor control board. Connect up a couple of pushbuttons and use some of the basic sketches from the examples menu in the IDE as starting points, such as the "blink without delay" and "state change" sketches.
An idea: your "B" button could probably be the Arduino's reset button....
Seconding @tigger suggestion: check out the "related topics" at the bottom of the page, looks like they might actually be relevant/useful.
It may not meet all requirements, but it demonstrates the basic idea and is intended as a learning example rather than a finished solution.
/*
================================================================
Intermittent Wiper Logic - Learning Example
================================================================
This sketch is only a simple proof-of-concept.
Intended behavior:
- Press button A once:
Perform one wipe and remember the current time.
- Press button A a second time:
Perform another wipe and calculate the time interval
between the first and second button press.
- The sketch then automatically repeats a wipe using the
learned interval.
- Press button B:
Forget the learned interval and stop the automatic mode.
Assumptions:
- Buttons use INPUT_PULLUP and connect to GND when pressed.
- The output pin drives a relay/transistor stage.
- The output does NOT drive a wiper motor directly.
- This is a learning example, not automotive-grade hardware.
Main Arduino concepts demonstrated:
- millis() timing
- state variables
- non-blocking timing
- simple button handling
- code organisation using functions
================================================================
*/
const byte buttonA = 2;
const byte buttonB = 3;
const byte outputPin = 8;
const unsigned long pulseLength = 500; // output pulse length in ms
const unsigned long debounceTime = 50; // simple debounce
unsigned long firstPressTime = 0;
unsigned long intervalTime = 0;
unsigned long lastWipeTime = 0;
unsigned long pulseStartTime = 0;
bool intervalKnown = false;
bool waitingForSecondPress = false;
bool outputActive = false;
bool lastButtonAState = HIGH;
bool lastButtonBState = HIGH;
unsigned long lastDebounceA = 0;
unsigned long lastDebounceB = 0;
void setup()
{
pinMode(buttonA, INPUT_PULLUP);
pinMode(buttonB, INPUT_PULLUP);
pinMode(outputPin, OUTPUT);
digitalWrite(outputPin, LOW);
Serial.begin(115200);
Serial.println("Intermittent wiper test");
}
void loop()
{
handleButtonA();
handleButtonB();
handleOutputPulse();
handleAutomaticWipe();
}
// ================================================================
// Handle Button A
//
// Detects a press of Button A.
//
// First press:
// - perform one wipe
// - store current time
//
// Second press:
// - perform another wipe
// - calculate the interval
// - enable automatic wiping
// ================================================================
void handleButtonA()
{
bool reading = digitalRead(buttonA);
if (reading != lastButtonAState)
{
lastDebounceA = millis();
lastButtonAState = reading;
}
if ((millis() - lastDebounceA) > debounceTime)
{
if (reading == LOW)
{
while (digitalRead(buttonA) == LOW)
{
handleOutputPulse();
}
buttonAPressed();
}
}
}
// ================================================================
// Process a valid Button A press
//
// Learns the desired wipe interval from the time difference
// between two button presses.
// ================================================================
void buttonAPressed()
{
triggerWipe();
if (!waitingForSecondPress)
{
firstPressTime = millis();
waitingForSecondPress = true;
intervalKnown = false;
Serial.println("First press stored");
}
else
{
intervalTime = millis() - firstPressTime;
lastWipeTime = millis();
intervalKnown = true;
waitingForSecondPress = false;
Serial.print("Interval learned: ");
Serial.print(intervalTime);
Serial.println(" ms");
}
}
// ================================================================
// Handle Button B
//
// Button B clears all learned timing information and disables
// automatic wiping.
// ================================================================
void handleButtonB()
{
bool reading = digitalRead(buttonB);
if (reading != lastButtonBState)
{
lastDebounceB = millis();
lastButtonBState = reading;
}
if ((millis() - lastDebounceB) > debounceTime)
{
if (reading == LOW)
{
while (digitalRead(buttonB) == LOW)
{
handleOutputPulse();
}
resetTimer();
}
}
}
// ================================================================
// Reset the learned interval
//
// Returns the sketch to its initial state.
// ================================================================
void resetTimer()
{
intervalKnown = false;
waitingForSecondPress = false;
intervalTime = 0;
Serial.println("Timer reset");
}
// ================================================================
// Generate one wipe pulse
//
// Activates the output for a short time.
//
// In a real system this would typically drive a relay or
// transistor stage.
// ================================================================
void triggerWipe()
{
digitalWrite(outputPin, HIGH);
outputActive = true;
pulseStartTime = millis();
Serial.println("Wipe pulse");
}
// ================================================================
// End the output pulse after the configured pulse duration
//
// Uses millis() so the sketch remains responsive.
// ================================================================
void handleOutputPulse()
{
if (outputActive && millis() - pulseStartTime >= pulseLength)
{
digitalWrite(outputPin, LOW);
outputActive = false;
}
}
// ================================================================
// Automatic wipe scheduler
//
// Once an interval has been learned, this function checks
// whether it is time for the next wipe.
// ================================================================
void handleAutomaticWipe()
{
if (intervalKnown && millis() - lastWipeTime >= intervalTime)
{
triggerWipe();
lastWipeTime = millis();
}
}
The code is intentionally kept simple and heavily commented. Even if it does not exactly match the original Electronics Australia design, it may provide a useful starting point and demonstrates several important Arduino concepts such as state machines and non-blocking timing with millis().
Please note:
This sketch is intentionally simplified and focuses on illustrating the basic logic rather than providing a complete solution.
In particular, the button handling is deliberately simple. A button press is processed after the button is released, and the sketch does not implement a fully non-blocking button handler.
For a learning example this keeps the code easier to understand, but there is certainly room for refinement.
What is the 'politically correct language to use to ask a fellow helper to NOT 'give the man a fish', but 'teach him to fish' as suggested by post 10 where he says
What I want to say would get me in trouble with the mods.
Your point is well taken. I’m being lazy here. I have a reasonable handle on the electronics involved and have programmed in C, albeit only for a short while, some thirty-plus years ago.
I’ve looked at the ATTINY45, and I agree it might be the better chip to use than the Arduino, particularly if this is the sole function being performed, moreover, if size is a concern.
In this case, I want a number of functions performed in addition to the wiper function. I am planning to use the Arduino with a couple of relay shields stacked on top of it to decouple it from the 12-volt vehicle wiring.
In this situation, using the Arduino's “reset” function may not work.
I'll dig deeper into the weeds regarding your suggestion of checking out the "related topics" sections of the forum.
The original EA circuit did use a couple of 555 timers. But it needed additional ICs to ensure the logic worked correctly.
I have used a couple of 555 timers in monostable mode when I retrofitted a foot pedal to switch between the dim and high beams. The problem with the foot pedal (as opposed to just using the light switch on the steering column) is that it rested permanently in one of two states. It really isn’t possible to get a foot pedal that will rest on an “off” state in the same way as an on-off-on switch does. This is something I was also going to add to this Arduino project.
Yes, that's really what I needed. Hand a man a fish and you feed him for a day. Teach a man to fish and you feed him for life. (And hopefully, the man will pass the skill on to another man along the way.)
One thing to consider is the wipers need to be parked when you are finished running them. This process is not standardized and varies from vehicle to vehicle. When doing intermittent wipers you do not want to park each cycle but when finished.