Community project

Interactive Robotic Fan

Arduino
Photo of Interactive Robotic Fan
Generated with AI

jun kim

Published October 25, 2026

This interactive robotic fan combines motion sensing and obstacle detection to create a responsive device that reacts to its environment. The Arduino Uno controls a DC motor fan, two servo motors for head and tilt movement, and reads data from a 6-axis accelerometer and ultrasonic distance sensor to autonomously navigate and respond to nearby motion.

The guide provides a complete wiring diagram, detailed parts list, and step-by-step assembly instructions with power protection best practices. Included firmware enables the fan to detect motion via the MPU-6050 sensor, maintain safe distance from obstacles using the HC-SR04 sensor, and control motor speed and servo positioning through the DRV8833 motor driver.

Wiring diagram

Wiring diagram for Interactive Robotic Fan

Gather all the parts

QtyComponent
1

DFRobot SEN0142 Fermion MPU-6050 6 DOF Sensor Breakout

DFRobot SEN0142 MPU-6050 breakout with 3-5 V board input, I2C interface, onboard I2C pull-ups, and i2cdevlib Arduino example coverage.

1

HC-SR04

HC-SR04 ultrasonic distance sensor module with separate 40 kHz transmitter and receiver transducers, measuring 2 to 400 cm. A 10 us pulse on TRIG starts a measurement and ECHO stays high for the echo's round-trip time. This HC-SR04+ board runs from 3.3 V or 5 V.

1

MG92B Micro Servo (Metal Gear, High Torque)

TowerPro MG92B metal-gear micro servo delivering 3.1-3.5 kg-cm stall torque (5.0-6.6V) with ~180 deg of travel - noticeably stronger than standard 9g plastic-gear micro servos like the SG90/SG92R, while staying compact for robotics and RC builds that need extra torque in a small footprint.

1

MG92B Micro Servo (Metal Gear, High Torque)

TowerPro MG92B metal-gear micro servo delivering 3.1-3.5 kg-cm stall torque (5.0-6.6V) with ~180 deg of travel - noticeably stronger than standard 9g plastic-gear micro servos like the SG90/SG92R, while staying compact for robotics and RC builds that need extra torque in a small footprint.

1

TT Motor Bi-Metal Gearbox - 1:90 Gear Ratio

TT-format bi-metal DC gearbox motor with a 1:90 reduction ratio - roughly double the torque and half the speed of the more common 1:48 TT motor. Runs on 3-6VDC (60-120 RPM no-load depending on voltage), has steel output gears for durability, and is one of several TT-motor gearbox ratios (1:48, 1:90, 1:120) used across 2WD/4WD robot car and crawler chassis kits, this one favoring torque/climbing over speed. Ships with no wires attached and cannot be driven directly from a microcontroller pin; requires a separate high-current motor driver such as a DRV8833 or TB6612FNG.

1

Pololu DRV8833 Dual Motor Driver Carrier (one channel used)

A 5 V-compatible dual H-bridge carrier that reverses and varies the speed of the single brushed drive motor through its channel A terminals.

1

Regulated 5 V DC Power Supply (TBD current rating)

An external regulated 5 V supply that powers the motor driver and both servos without feeding power into the USB-powered Arduino.

1

470 µF electrolytic capacitor, 10 V minimum

A polarized capacitor placed across the 5 V motor-and-servo rail to reduce voltage dips when the motor or servos start moving.

1

100 nF ceramic capacitor, 10 V minimum

A non-polarized capacitor placed close to the motor-driver power pins to reduce electrical noise.

1

100 nF ceramic motor-noise capacitor, 10 V minimum

A non-polarized capacitor soldered directly across the brushed motor terminals to reduce electrical noise.

Assemble it in 7 steps

1. Keep the two power sources separate

Power the Arduino Nano Every from its USB socket only. Put the external regulated 5 V supply near the robot, but do not connect its positive V+ wire to the Nano 5V pin; this avoids feeding power back into the USB connection. Connect the supply V- wire to a Nano GND pin (common ground).

  • Use a regulated 5 V supply rated for at least 5 A provisionally; the final rating depends on the confirmed servo peak currents.
  • Connecting the external 5 V positive wire to the Nano 5V pin while USB is connected can damage the board or computer USB port.

2. Add the motor-driver power protection

At the point where the external supply wires reach the motor driver, connect bulk_cap with its + lead to external 5 V and its striped - lead to GND. Connect driver_ceramic_cap across the same two points. Keep the ceramic capacitor physically beside the motor_driver VM and GND pins.

  • The 470 µF capacitor has a stripe marking its negative lead; the 100 nF ceramic capacitor has no polarity.
  • Reversing the electrolytic capacitor can make it overheat or burst.

3. Wire the drive motor and its driver

Connect motor_driver VM → external 5 V (power), motor_driver GND → common GND (ground), AOUT1 → drive_motor M1 (motor output), and AOUT2 → drive_motor M2 (motor output). Solder motor_noise_cap directly between the M1 and M2 motor tabs. Connect AIN1 → Nano D5 (speed signal), AIN2 → Nano D6 (direction signal), and nSLEEP → Nano D8 (enable signal).

  • If the robot moves opposite to the expected direction, swap the two motor wires at AOUT1 and AOUT2 after unplugging power.
  • The unused second DRV8833 channel remains physically disconnected.
  • Never connect either motor terminal directly to an Arduino pin; the motor current can damage the board.

4. Wire the head and tilt servos

For head_servo, connect red VCC → external 5 V (power), brown or black GND → common GND (ground), and orange SIGNAL → Nano D9 (head-rotation signal). For tilt_servo, connect red VCC → external 5 V (power), brown or black GND → common GND (ground), and orange SIGNAL → Nano D10 (tilt signal).

  • Use thicker, short power wires for the two servos so their movement does not make the supply voltage dip.
  • Do not power either servo from a Nano I/O pin or from the Nano 5V pin; a moving servo can draw enough current to reset or damage the board.

5. Wire the distance sensor

Connect ultrasonic_1 VCC → external 5 V (power), GND → common GND (ground), TRIG → Nano D2 (start-measurement signal), and ECHO → Nano D4 (distance-return signal).

  • Point the two round sensor openings toward the space where a hand or person will approach.
  • This selected HC-SR04+ sends a 5 V ECHO signal, which is safe for the Nano Every’s 5 V input; do not substitute a different sensor without checking its pin voltage.

6. Wire the motion sensor

Connect imu_1 VIN → external 5 V (power), GND → common GND (ground), SDA → Nano A4/SDA (data), and SCL → Nano A5/SCL (clock). Leave the optional INT pin disconnected for this starter design.

  • Keep the SDA and SCL wires short and place the IMU firmly on the robot body so it measures the robot’s movement rather than a loose cable.
  • VCC and GND must not be swapped — swapped power can damage the sensor.

7. Inspect before applying power

With USB and external power unplugged, check that every ground joins the same common ground and that the external 5 V positive rail reaches only the motor driver, servos, sensor modules, and capacitors. Confirm Nano D0 and D1 have no wires so the USB serial connection remains free for Processing.

  • Use the editable wiring diagram and pin list as your check-off sheet before connecting USB and then the external 5 V supply.
  • Do not run the motor or move the servos until the physical mechanism has free travel and the final supply-current requirement has been confirmed.

Review all connections

1. Connections between "supply_5v" and "Arduino"

Functionsupply_5vArduino
powerV+ → EXTERNAL 5 V motor-and-servo rail; do not connect this rail to Nano 5V while USB is connected.5V
groundV- → Common ground shared with the USB-powered Nano and all modules.GND

2. Connections between "imu_1" and "Arduino"

Functionimu_1Arduino
powerVIN → Power: DFRobot breakout accepts 3-5 V; supplied at 5 V.5V
groundGND → Ground.GND
i2cSDA → Signal: Nano Every A4 / SDA.GPIO 18
i2cSCL → Signal: Nano Every A5 / SCL.GPIO 19

3. Connections between "ultrasonic_1" and "Arduino"

Functionultrasonic_1Arduino
powerVCC → Power: 5 V HC-SR04+ supply.5V
groundGND → Ground.GND
digitalTRIG → Signal: Nano D2 output starts each distance measurement.GPIO 2
digitalECHO → Signal: Nano D4 input receives the 5 V echo pulse; no level shifter needed with 5 V Nano logic.GPIO 4

4. Connections between "head_servo" and "Arduino"

Functionhead_servoArduino
powerVCC → EXTERNAL power: red lead; never connect to a Nano I/O pin or Nano 5V pin.5V
groundGND → Ground: brown/black lead.GND
digitalSIGNAL → Signal: Nano D9 controls head rotation.GPIO 9

5. Connections between "tilt_servo" and "Arduino"

Functiontilt_servoArduino
powerVCC → EXTERNAL power: red lead; never connect to a Nano I/O pin or Nano 5V pin.5V
groundGND → Ground: brown/black lead.GND
digitalSIGNAL → Signal: Nano D10 controls head tilt.GPIO 10

6. Connections between "motor_driver" and "Arduino"

Functionmotor_driverArduino
powerVM → EXTERNAL motor supply: 5 V is inside the DRV8833 2.7-10.8 V VM range.5V
groundGND → Common ground.GND
pwmAIN1 → Signal: Nano D5 PWM speed control; high/PWM with AIN2 low drives forward.GPIO 5
pwmAIN2 → Signal: Nano D6 direction control; high/PWM with AIN1 low drives reverse.GPIO 6
digitalnSLEEP → Signal: Nano D8 drives HIGH to enable the motor driver.GPIO 8
dataAOUT1 → TT Motor Bi-Metal Gearbox - 1:90 Gear Ratio M1EXT
dataAOUT2 → TT Motor Bi-Metal Gearbox - 1:90 Gear Ratio M2EXT

7. Connections between "bulk_cap" and "Arduino"

Functionbulk_capArduino
power+ → Place close to the motor-driver and servo power distribution point.5V
ground- → Ground; striped capacitor lead.GND

8. Connections between "driver_ceramic_cap" and "Arduino"

Functiondriver_ceramic_capArduino
power1 → Place physically beside DRV8833 VM.5V
ground2 → Place physically beside DRV8833 GND.GND

9. Connections between "motor_noise_cap" and "Arduino"

Functionmotor_noise_capArduino
power1 → TT Motor Bi-Metal Gearbox - 1:90 Gear Ratio M1EXT
power2 → TT Motor Bi-Metal Gearbox - 1:90 Gear Ratio M2EXT

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <Servo.h>

const uint8_t TRIG_PIN = 2;
const uint8_t ECHO_PIN = 4;
const uint8_t MOTOR_PWM_PIN = 5;
const uint8_t MOTOR_DIR_PIN = 6;
const uint8_t MOTOR_SLEEP_PIN = 8;
const uint8_t HEAD_SERVO_PIN = 9;
const uint8_t TILT_SERVO_PIN = 10;
const uint8_t MPU6050_ADDRESS = 0x68;
const uint16_t STOP_DISTANCE_CM = 25;
const uint8_t DRIVE_SPEED = 170;
const unsigned long SENSOR_INTERVAL_MS = 80;
const unsigned long MOTOR_COMMAND_TIMEOUT_MS = 1000;

Servo headServo;
Servo tiltServo;
unsigned long lastSensorReadMs = 0;
unsigned long lastMotorCommandMs = 0;
uint16_t distanceCm = 0;
int8_t requestedDirection = 0;

void writeMpuRegister(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(MPU6050_ADDRESS);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

bool readMpuMotion(int16_t &ax, int16_t &ay, int16_t &az) {
  Wire.beginTransmission(MPU6050_ADDRESS);
  Wire.write(0x3B);
  if (Wire.endTransmission(false) != 0) return false;
  if (Wire.requestFrom(MPU6050_ADDRESS, (uint8_t)6) != 6) return false;
  ax = (int16_t)((Wire.read() << 8) | Wire.read());
  ay = (int16_t)((Wire.read() << 8) | Wire.read());
  az = (int16_t)((Wire.read() << 8) | Wire.read());
  return true;
}

uint16_t readDistanceCm() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(12);
  digitalWrite(TRIG_PIN, LOW);
  unsigned long echoUs = pulseIn(ECHO_PIN, HIGH, 30000UL);
  if (echoUs == 0) return 0;
  return (uint16_t)(echoUs / 58UL);
}

void stopMotor() {
  analogWrite(MOTOR_PWM_PIN, 0);
  digitalWrite(MOTOR_DIR_PIN, LOW);
}

void driveMotor(int8_t direction) {
  if (direction == 0) {
    stopMotor();
    return;
  }
  digitalWrite(MOTOR_DIR_PIN, direction < 0 ? HIGH : LOW);
  analogWrite(MOTOR_PWM_PIN, DRIVE_SPEED);
}

void processSerialCommand() {
  while (Serial.available() > 0) {
    char command = (char)Serial.read();
    if (command == 'F' || command == 'f') {
      requestedDirection = 1;
      lastMotorCommandMs = millis();
    } else if (command == 'B' || command == 'b') {
      requestedDirection = -1;
      lastMotorCommandMs = millis();
    } else if (command == 'S' || command == 's') {
      requestedDirection = 0;
      stopMotor();
    }
  }
}

void updateHeadFromOrientation() {
  int16_t ax, ay, az;
  if (!readMpuMotion(ax, ay, az)) return;
  int headAngle = constrain(map(ax, -16384, 16384, 45, 135), 45, 135);
  int tiltAngle = constrain(map(ay, -16384, 16384, 60, 120), 60, 120);
  headServo.write(headAngle);
  tiltServo.write(tiltAngle);
}

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(MOTOR_PWM_PIN, OUTPUT);
  pinMode(MOTOR_DIR_PIN, OUTPUT);
  pinMode(MOTOR_SLEEP_PIN, OUTPUT);
  digitalWrite(MOTOR_SLEEP_PIN, HIGH);
  stopMotor();

  headServo.attach(HEAD_SERVO_PIN);
  tiltServo.attach(TILT_SERVO_PIN);
  headServo.write(90);
  tiltServo.write(90);

  Wire.begin();
  writeMpuRegister(0x6B, 0x00);
  Serial.println(F("Robot fan ready: F=forward, B=backward, S=stop"));
}

void loop() {
  processSerialCommand();
  unsigned long now = millis();
  if (now - lastSensorReadMs >= SENSOR_INTERVAL_MS) {
    lastSensorReadMs = now;
    distanceCm = readDistanceCm();
    updateHeadFromOrientation();
    if (distanceCm > 0 && distanceCm <= STOP_DISTANCE_CM) {
      stopMotor();
      Serial.println(F("Obstacle/hand close: motor stopped"));
    } else if (requestedDirection != 0 && now - lastMotorCommandMs <= MOTOR_COMMAND_TIMEOUT_MS) {
      driveMotor(requestedDirection);
    } else {
      stopMotor();
    }
  }
  delay(1);
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik