automatic-wiper-arduino-nano

Rain Sensing Wiper using Arduino Nano with OLED — Step-by-Step Guide Leave a comment

automatic-wiper-arduino-nano

2. Introduction

Have you ever wondered how a car could automatically detect rain and turn on its windshield wipers without the driver pressing a button? You can build a simple working model of this idea using an Arduino Uno, a rain sensor, an OLED display, and a small servo motor.

In this Arduino automatic car wiper project, the rain sensor detects water on its sensing surface. When rain is detected, the Arduino displays a cloud symbol on the OLED screen and moves an SG90 servo motor to a 90-degree position to represent the wiper turning on. When the sensor becomes dry, the servo returns to its original position and the display shows a sun symbol.

I built and tested this project myself, and it is a great hands-on experiment for beginners, electronics hobbyists, and STEM students who want to learn about sensors, displays, and motor control.

Watch the project in action

[YouTube Demo Video – Embed Your Video Here]

Suggested embed caption: Watch the Arduino automatically detect rain, display a cloud or sun on the OLED screen, and control the servo-based wiper.

Components used in this project

You can find the complete component list, wiring instructions, and Arduino sketch below. I’ve also included the full code so you can recreate the project on your own breadboard.

3. Components Required

The following components are used to build the prototype.

ComponentQuantityPurchase link
Arduino Uno R3 or compatible board1Amazon.com – affiliate link
Rain sensor module with sensing plate1Amazon.com – affiliate link
0.96-inch I2C OLED display, SSD1306, 128 × 641Amazon.com – affiliate link
SG90 9g micro servo motor1Amazon.com – affiliate link
Breadboard1Amazon.com – affiliate link
Jumper wires1 setAmazon.com – affiliate link
USB cable for Arduino1Amazon.com – affiliate link
Regulated 5V supply for the servo, if needed1Amazon.com – affiliate link

4. How the Main Components Work

4.1 Arduino Uno

The Arduino Uno is the controller of the entire project. It reads the rain sensor’s output, decides whether rain is present, updates the OLED display, and commands the servo motor.

The board runs a small program called a sketch. You upload the sketch from the Arduino IDE using a USB cable.

Typical Arduino Uno specifications:

  • Microcontroller: ATmega328P on the classic Uno R3
  • Operating logic: 5V
  • Digital I/O pins: 14, including PWM-capable pins
  • Analog inputs: 6
  • Clock speed: 16 MHz

4.2 Rain sensor module

The rain sensor usually consists of two parts: a conductive sensing plate and a small controller board.

When water droplets land on the plate, they change the electrical conductivity between its tracks. The module converts this change into an electrical signal that the Arduino can read.

For this project, we’ll use the analog output (AO) of the rain sensor.

  • Dry plate: usually a higher analog reading.
  • Wet plate: usually a lower analog reading.
  • Arduino input: A0.

The actual readings depend on the module, water coverage, and adjustment settings. We’ll use a threshold in the code that you can calibrate for your sensor.

4.3 OLED display

The OLED display shows the system’s current status.

  • Cloud symbol: Rain detected.
  • Sun symbol: No rain detected.

A typical 0.96-inch OLED uses the SSD1306 controller and a resolution of 128 × 64 pixels. The I2C version normally uses just four connections: VCC, GND, SDA, and SCL.

This makes it convenient for small Arduino projects.

4.4 SG90 9g servo motor

The SG90 is a small positional servo motor. Unlike a regular DC motor, it can move its output shaft to a commanded angle.

In this project:

  • At 0 degrees, the wiper is in its resting position.
  • At 90 degrees, the wiper moves to its active position.

Typical SG90 specifications:

  • Weight: approximately 9g
  • Control signal: servo PWM-style pulse control
  • Operating voltage: commonly 4.8–6V, depending on the model
  • Movement range: often around 180 degrees, but the actual range varies

The servo does not automatically rotate continuously like a normal motor. The sketch commands it to move between two positions. This is enough to demonstrate the basic automatic-wiper concept.

Power tip: A servo can draw much more current while moving or under load than its idle current. If the Arduino resets or the OLED flickers when the servo moves, power the servo from a suitable regulated 5V supply and connect the supply’s ground to the Arduino’s GND. Do not connect the servo’s power input to a 3.3V pin.

5. Circuit Diagram and Wiring

automatic-wiper-arduino-circuit-diagram

Suggested image alt text: Arduino Uno automatic car wiper circuit diagram with rain sensor, SSD1306 OLED display, and SG90 servo motor wiring

5.1 Rain sensor wiring

Rain sensor pinArduino Uno pin
VCC5V
GNDGND
AOA0
DONot connected

Some modules label their pins differently. Check the markings on your specific board before connecting the wires.

5.2 OLED display wiring

OLED pinArduino Uno pin
VCC5V, if supported by your module
GNDGND
SDAA4
SCLA5

These SDA and SCL pin assignments apply to the classic Arduino Uno R3. Check your board’s pinout if you’re using a different Uno model.

Many SSD1306 OLED modules use I2C address 0x3C, while some use 0x3D. The code below assumes 0x3C.

5.3 Servo motor wiring

SG90 wireConnection
Brown or blackGND
RedRegulated 5V supply
Orange, yellow, or whiteArduino digital pin 9

Servo wire colors can vary by manufacturer, so verify the pinout for your particular servo.

If you power the servo separately, connect the external supply’s negative terminal to Arduino GND. This shared ground is essential for a reliable control signal.

Do you need resistors?

No additional resistors are required for the basic circuit described here, assuming you use a standard rain sensor module and an I2C OLED module with suitable onboard circuitry.

However, don’t assume every bare component can be connected directly. Some OLEDs, sensor boards, and other modules require additional components or different supply voltages.

6. Arduino Library Installation

We’ll use three libraries to control the display and servo.

Step 1: Install the Arduino IDE

Download and install the Arduino IDE from the official website:

https://www.arduino.cc/en/software

Connect your Arduino Uno to your computer using a USB cable.

Step 2: Install the Adafruit SSD1306 library

  1. Open the Arduino IDE.
  2. Select Sketch → Include Library → Manage Libraries.
  3. Search for Adafruit SSD1306.
  4. Install the library by Adafruit.
  5. If prompted to install dependencies, install them as well.

Step 3: Install the Adafruit GFX library

In Library Manager, search for Adafruit GFX Library and install it.

The GFX library provides the drawing functions used to create the cloud and sun symbols.

Step 4: Check the Servo library

Search for Servo in Library Manager if it is not already available. The standard Servo library is commonly included with the Arduino IDE.

The code uses:

  • Adafruit_SSD1306
  • Adafruit_GFX
  • Servo

7. Complete Arduino Code

This sketch reads the rain sensor’s analog output, shows a cloud or sun on the OLED, and moves the SG90 servo to 90 degrees when rain is detected. When the sensor is dry, the servo returns to 0 degrees.

Copy the code into a new Arduino sketch.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Servo.h>

// -------------------- OLED CONFIG --------------------
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// -------------------- PINS --------------------
const int RAIN_PIN  = 12;   // Rain sensor DIGITAL output
const int SERVO_PIN = 3;    // MG90 signal

// Digital rain sensor is usually ACTIVE LOW (0 = rain, 1 = dry)
const int RAIN_ACTIVE_LEVEL = LOW;

// -------------------- RAIN STATE LOGIC --------------------
enum RainState : uint8_t { RAIN_DRY, RAIN_WET };
RainState rainState = RAIN_DRY;

// -------------------- TIMING --------------------
unsigned long lastSensorReadMs = 0;
const unsigned long SENSOR_INTERVAL_MS = 200;

unsigned long lastAnimMs = 0;
unsigned long animIntervalMs = 60;

// -------------------- RAIN ANIMATION --------------------
struct Drop { int x; int y; };
const int MAX_DROPS = 16;
Drop drops[MAX_DROPS];

void initDrops() {
  for (int i = 0; i < MAX_DROPS; i++) {
    drops[i].x = random(0, SCREEN_WIDTH);
    drops[i].y = random(-SCREEN_HEIGHT, 0);
  }
}

void updateDrops(int activeDrops) {
  for (int i = 0; i < activeDrops; i++) {
    int speed = 2;
    drops[i].y += speed;
    if (drops[i].y > SCREEN_HEIGHT) {
      drops[i].y = random(-20, 0);
      drops[i].x = random(0, SCREEN_WIDTH);
    }
  }
}

// -------------------- WIPER ANIMATION + SERVO --------------------
Servo wiperServo;
const int WIPER_MIN_ANGLE = 40;
const int WIPER_MAX_ANGLE = 140;
int wiperAngle = WIPER_MIN_ANGLE;
int wiperDir   = 1;

// -------------------- SUN ANIMATION --------------------
int sunPhase = 0;

// -------------------- RAIN STATE --------------------
void updateRainStateDigital(int level) {
  RainState newState = (level == RAIN_ACTIVE_LEVEL) ? RAIN_WET : RAIN_DRY;

  if (newState != rainState) {
    rainState = newState;
    if (rainState == RAIN_DRY) {
      wiperAngle = WIPER_MIN_ANGLE;
      wiperServo.write(wiperAngle);
    }
  }
}

// -------------------- STEP ANIMATIONS --------------------
void stepAnimations() {
  if (rainState == RAIN_WET) {
    updateDrops(MAX_DROPS);

    wiperAngle += wiperDir * 3;
    if (wiperAngle >= WIPER_MAX_ANGLE) {
      wiperAngle = WIPER_MAX_ANGLE;
      wiperDir   = -1;
    } else if (wiperAngle <= WIPER_MIN_ANGLE) {
      wiperAngle = WIPER_MIN_ANGLE;
      wiperDir   = 1;
    }

    wiperServo.write(wiperAngle); // servo mirrors the drawn angle
  } else {
    sunPhase = (sunPhase + 1) % 16;
  }
}

// -------------------- DRAW HELPERS --------------------
void drawWiper() {
  int pivotX = SCREEN_WIDTH / 2;
  int pivotY = SCREEN_HEIGHT - 4;
  int length = 40;

  float rad = wiperAngle * PI / 180.0;
  int endX = pivotX + (int)(length * cos(rad));
  int endY = pivotY - (int)(length * sin(rad));

  display.drawLine(pivotX, pivotY, endX, endY, WHITE);
  display.drawLine(pivotX - 1, pivotY, endX - 1, endY, WHITE);
}

void drawSun() {
  int cx = SCREEN_WIDTH - 26;
  int cy = 22;
  int radius = 9;

  display.fillCircle(cx, cy, radius, WHITE);

  for (int i = 0; i < 8; i++) {
    float baseAngleDeg = i * 45;
    float angleDeg = baseAngleDeg + sunPhase * 4;
    float rad = angleDeg * PI / 180.0;

    int innerR = radius + 2;
    int outerR = radius + 7;

    int x1 = cx + (int)(innerR * cos(rad));
    int y1 = cy + (int)(innerR * sin(rad));
    int x2 = cx + (int)(outerR * cos(rad));
    int y2 = cy + (int)(outerR * sin(rad));

    display.drawLine(x1, y1, x2, y2, WHITE);
  }
}

void drawScene() {
  display.clearDisplay();

  int wx = 4, wy = 18;
  int ww = SCREEN_WIDTH - 8;
  int wh = SCREEN_HEIGHT - 22;
  display.drawRoundRect(wx, wy, ww, wh, 4, WHITE);

  if (rainState == RAIN_WET) {
    for (int i = 0; i < MAX_DROPS; i++) {
      int x = drops[i].x;
      int y = drops[i].y;
      if (x >= wx + 2 && x <= wx + ww - 4 &&
          y >= wy + 2 && y <= wy + wh - 4) {
        display.drawLine(x, y, x, y + 3, WHITE);
      }
    }
    drawWiper();
  } else {
    drawSun();
  }

  display.display();
}

void setup() {
  Serial.begin(9600);
  pinMode(RAIN_PIN, INPUT);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 init failed!"));
    while (true) {}
  }

  wiperServo.attach(SERVO_PIN);
  wiperServo.write(WIPER_MIN_ANGLE);

  randomSeed(analogRead(A1));
  initDrops();
  drawScene();
}

void loop() {
  unsigned long now = millis();

  if (now - lastSensorReadMs >= SENSOR_INTERVAL_MS) {
    lastSensorReadMs = now;
    int level = digitalRead(RAIN_PIN);
    updateRainStateDigital(level);
  }

  if (now - lastAnimMs >= animIntervalMs) {
    lastAnimMs = now;
    stepAnimations();
    drawScene();
  }
}

Important calibration note: The code assumes lower analog readings indicate rain. Test your own module to confirm this behavior. If your readings increase when wet, change the detection condition to sensorValue > RAIN_THRESHOLD and calibrate the threshold accordingly.

8. How the Code Works

Let’s break the program into a few simple steps.

Step 1: Detecting Rain Using the Digital Sensor Output

The updateRainStateDigital() function checks the sensor’s digital signal and updates the current rain state.

void updateRainStateDigital(int level) {

  RainState newState =

      (level == RAIN_ACTIVE_LEVEL) ? RAIN_WET : RAIN_DRY;

  if (newState != rainState) {

    rainState = newState;

    if (rainState == RAIN_DRY) {

      wiperAngle = WIPER_MIN_ANGLE;

      wiperServo.write(wiperAngle);

    }

  }

}

Step 2: Controlling the Servo Motor

The servo simulates the movement of a windshield wiper.

const int WIPER_MIN_ANGLE = 40;

const int WIPER_MAX_ANGLE = 140;

int wiperAngle = WIPER_MIN_ANGLE;

int wiperDir = 1;

The wiper moves between 40° and 140°. These are example limits that should be adjusted to suit your servo and mechanical setup.

Step 3: Drawing the Wiper on the OLED

The drawWiper() function calculates the endpoint of the wiper using trigonometry.

float rad = wiperAngle * PI / 180.0;

int endX = pivotX + (int)(length * cos(rad));

int endY = pivotY - (int)(length * sin(rad));

The angle is converted from degrees to radians because the sin() and cos() functions use radians.

The program then draws two parallel lines from the wiper’s pivot point to the calculated endpoint. This makes the wiper appear thicker on the OLED.

.

Step 4: Drawing the Animated Sun

When the rain sensor indicates dry conditions, the drawSun() function draws a sun in the upper-right area of the display.

It uses fillCircle() to draw the sun’s center and drawLine() to create eight rays around it.

The sunPhase variable changes the angle of the rays over time, producing a simple rotating-ray animation.

sunPhase = (sunPhase + 1) % 16;

The modulo operation keeps the value within a repeating range, allowing the animation to continue indefinitely.

Step 5: Repeatedly Reading the Sensor and Updating Animations

The loop() function runs continuously and handles two timed operations.

Sensor reading:

if (now - lastSensorReadMs >= SENSOR_INTERVAL_MS) {

  lastSensorReadMs = now;

  int level = digitalRead(RAIN_PIN);

  updateRainStateDigital(level);

}

The sensor is checked approximately every 200 milliseconds.

Animation updates:

if (now - lastAnimMs >= animIntervalMs) {

  lastAnimMs = now;

  stepAnimations();

  drawScene();

}

The OLED animation updates approximately every 60 milliseconds, producing a nominal update rate of about 16–17 frames per second.

Using millis() allows the program to schedule these operations without relying on long delay() calls, so the sensor can continue to be checked while the animation runs.

9. How to Test the Project

Follow these steps after completing the wiring.

Step 1: Check the connections

Before powering the circuit, check the rain sensor, OLED, and servo connections against the wiring tables.

Make sure the servo has a suitable power supply and a common ground with the Arduino.

Step 2: Upload the sketch

Open the Arduino IDE and select:

  • Board: Arduino Uno
  • Port: The COM port assigned to your Arduino

Click Upload and wait for the IDE to finish.

Step 3: Test the dry condition

Keep the rain sensor plate dry.

Expected result:

  • The OLED displays a sun symbol.
  • The screen shows NO RAIN.
  • The servo stays at 0 degrees.

Step 4: Simulate rain

Carefully place a few water droplets on the sensor plate. Do not splash water onto the Arduino, OLED, or wiring.

Expected result:

  • The OLED displays a cloud symbol.
  • The screen shows RAIN DETECTED.
  • The servo moves to approximately 90 degrees.

Step 5: Let the plate dry

Remove the water and allow the sensor plate to dry.

Expected result:

  • The display returns to the sun symbol.
  • The servo returns to 0 degrees.

Step 6: Calibrate the threshold

Open the Serial Monitor and set the baud rate to 9600.

Record the sensor readings when the plate is dry and when it is wet. Adjust RAIN_THRESHOLD to a value that reliably separates those conditions.

For example, if your dry reading is around 850 and your wet reading is around 350, a threshold of 600 is a reasonable starting point.

10. Troubleshooting Common Problems

Problem 1: The OLED display is blank

Possible causes:

  • Incorrect I2C address.
  • SDA and SCL wires are reversed.
  • Missing or incorrectly installed libraries.

Fix: Check the wiring and confirm whether your OLED uses address 0x3C or 0x3D. If needed, run an I2C scanner sketch to identify the address.

Problem 2: The servo does not move

Possible causes:

  • Incorrect signal wire connection.
  • Insufficient power.
  • Loose ground connection.

Fix: Confirm that the signal wire is connected to pin 9. Verify the servo’s power and make sure an external supply shares ground with the Arduino. Test the servo with a simple servo sweep sketch if necessary.

Problem 3: The servo moves when there is no rain

Possible causes:

  • The threshold does not match your sensor.
  • The sensor plate is dirty or damp.
  • Your module behaves differently from the assumed analog response.

Fix: Check the Serial Monitor readings in dry and wet conditions. Adjust the threshold and reverse the comparison if your sensor produces higher readings when wet.

Problem 4: The Arduino keeps restarting

Possible cause: The servo draws too much current from the Arduino’s power source, causing the supply voltage to drop.

Fix: Use a suitable regulated 5V servo supply, connect grounds together, and keep wiring short and secure. Do not attempt to power a loaded servo from the Arduino’s 3.3V output.

Problem 5: The cloud and sun keep switching rapidly

Possible cause: The sensor reading is fluctuating near the threshold.

Fix: Calibrate the threshold using actual dry and wet readings. You can also add hysteresis, which uses separate thresholds for switching on and off, to prevent rapid changes around the boundary.

11. Ideas to Expand the Project

Once the basic project works, try adding these features.

1. Automatic wiper sweeping

Instead of moving directly to 90 degrees and staying there, make the servo move repeatedly between the resting and active positions while rain is detected.

2. Adjustable wiper speed

Add a potentiometer or buttons to select slow, medium, and fast sweeping modes.

3. Rain intensity detection

Use the analog sensor reading to estimate whether the sensing plate is lightly wet or heavily covered with water. Display different rain levels on the OLED.

Remember that a simple resistive rain plate measures water contact, not rainfall in calibrated inches per hour.

4. Add a manual control button

Add a push button that lets you activate the wiper manually, even when the sensor is dry.

5. Add a buzzer

Use a buzzer to produce an alert when the rain sensor detects water.

6. Create a miniature car model

Mount the servo on a cardboard, acrylic, or 3D-printed windshield model and attach a lightweight wiper arm. This makes the project more realistic for a school science exhibition.

7. Add an IoT dashboard

Use a Wi-Fi-enabled board such as an ESP32 to send sensor readings and wiper status to a web dashboard.

Safety note: This project is an educational prototype. A real vehicle wiper system needs automotive-rated components, reliable power management, environmental protection, and safety testing before use in a vehicle.

12. Frequently Asked Questions

1. How do you make an automatic car wiper using Arduino?

Connect a rain sensor’s analog output to an Arduino Uno analog input, an OLED display over I2C, and an SG90 servo to a digital control pin. Program the Arduino to detect water, display the rain status, and move the servo to the desired position.

2. Which rain sensor is used in an Arduino automatic wiper project?

A common resistive rain sensor module with a sensing plate and controller board works well for a basic prototype. Its analog output lets the Arduino distinguish between dry and wet conditions using a calibrated threshold.

3. How does Arduino detect rain?

The Arduino reads an analog voltage from the rain sensor module. Water changes the electrical conductivity across the sensing plate, changing the output voltage. The program compares the reading against a threshold to determine whether rain is present.

4. Can an SG90 servo motor be used as a car wiper?

An SG90 servo can demonstrate the movement of a miniature wiper on a model car. It is not designed to drive a full-size automotive windshield wiper, which needs a motor and mechanical system designed for the required load and environmental conditions.

5. Why does my Arduino rain sensor give different readings?

Readings can vary with water coverage, sensor plate condition, supply voltage, sensor design, and electrical noise. Measure the values when the plate is dry and wet, then calibrate the threshold for your particular module.

13. Conclusion

This Arduino automatic car wiper project is a practical way to learn how sensors, servo motors, and OLED displays work together. With just a few components, you can build a prototype that detects rain, shows a cloud symbol when water is present, displays a sun when the sensor is dry, and moves a servo between two positions.

The best part is that you can expand the project with automatic sweeping, adjustable speed, rain-level indication, and wireless monitoring.

If you build this project, watch the demo video, like the video, subscribe to the channel, and leave a comment describing your results or any improvements you’ve made. I’d love to see what you create!

You can find the full Arduino sketch and component links in this article so you can follow along and build your own version.


Leave a Reply

Your email address will not be published. Required fields are marked *