Building a biomimetic swimmer requires balancing torque, waterproofing, and power density. For a DIY robotic fish Arduino Nano project, the classic Nano V3.0 (ATmega328P, 16MHz) remains the optimal brain. It offers hardware PWM for smooth servo control, a compact 45x18mm footprint that fits inside micro-hulls, and a 5V logic level that interfaces directly with standard RC components without level shifters. Do not use the Nano Every (ATmega4809) for this specific build; its different timer architecture requires modifying the standard Servo library's interrupt vectors, which introduces unnecessary jitter in high-load aquatic environments.

This guide provides the exact architecture decisions, wiring schematic, and fail-safe firmware to get your robotic fish swimming reliably, along with the bench-level debugging steps to fix it when it inevitably leaks or jitters.

Project Verdict & Architecture Decisions

Before cutting wire, you must resolve three core engineering trade-offs. Here is the decision matrix that terminates in the exact parts used in this build.

Subsystem Option A Option B Verdict & Concrete Pick
Propulsion Coreless DC Motor + ESC + linkage Micro Servo direct-drive oscillation Pick Option B: TowerPro SG90. Eliminates complex mechanical linkages. The SG90 provides 1.8kg-cm stall torque, more than enough to sweep a 3D-printed TPU tail fin at 2-4Hz.
Power 4xAAA Alkaline (6V nominal) 2S LiPo (7.4V) + 5V Buck Converter Pick Option B: Turnigy nano-tech 460mAh 2S LiPo + Drok DC-DC step-down module. Alkalines suffer severe voltage sag under the 750mA servo stall current, causing Nano brownouts.
Waterproofing Epoxy potting the entire electronics bay O-ring sealed ABS enclosure Pick Option B: 100x60x40mm ABS project box with a 2mm silicone O-ring. Potting is permanent and traps heat; an O-ring enclosure allows battery swaps and firmware updates via USB.

Bill of Materials & Spec Sheet

Source these exact variants to ensure physical fitment and electrical compatibility. Total BOM cost is approximately $35-$45.

  • Microcontroller: Arduino Nano V3.0 (ATmega328P, 16MHz, CH340 or FTDI USB driver). $4 (clone) to $22 (genuine).
  • Servo: TowerPro SG90 9g Micro Servo (180-degree analog). $3.
  • Battery: Turnigy nano-tech 460mAh 2S 25C LiPo (7.4V, JST-XH balance lead, JST-PH discharge lead). $12.
  • Voltage Regulator: Drok DC-DC Buck Converter Step Down Module (LM2596 based, adjustable, set to 5.0V). $4.
  • Enclosure: 100x60x40mm ABS Plastic Project Box with neoprene/silicone O-ring groove. $6.
  • Wire: 22 AWG silicone stranded (for servo/signal), 18 AWG silicone stranded (for main power bus). $5.
  • Passives: 1x 470µF 10V electrolytic capacitor, 2x 10kΩ 1/4W resistors (for voltage divider). $1.

Pin Mapping & Wiring Guide

The most common cause of failure in aquatic robotics is sharing the 5V logic rail with high-current inductive loads. Never power the SG90 servo from the Nano's onboard 5V pin. The Nano's AMS1117-5.0 regulator maxes out around 800mA and will overheat or drop out when the servo stalls.

Arduino Nano Pin Component Wire Color Notes & Routing
D9 (PWM) SG90 Signal Wire Orange/Yellow Keep under 12 inches to prevent PWM signal degradation.
A0 (ADC) Voltage Divider Midpoint Green Connect between two 10kΩ resistors bridging LiPo V+ and GND.
D13 (SCK) Onboard LED N/A Used as low-battery warning indicator (blinks when V_batt < 6.4V).
GND System Common Ground Black Must be shared between Nano, Buck Converter OUT-, and Servo GND.
VIN Buck Converter OUT+ (5V) Red Feeds Nano via VIN pin (bypasses USB 5V diode protection).
Callout Tip: The Decoupling Capacitor
Solder the 470µF electrolytic capacitor directly across the SG90's VCC (Red) and GND (Brown) wires at the servo connector. This acts as a local energy reservoir, smoothing out the high-frequency current spikes generated by the servo's internal brushed motor and preventing electromagnetic interference (EMI) from resetting the Nano.

Numbered Assembly Steps

  1. Prep the Power Bus: Solder the 18 AWG main power leads from the LiPo to the buck converter IN+/IN-. Use a multimeter to verify the buck converter OUT+/OUT- is set exactly to 5.00V before connecting anything else.
  2. Build the Voltage Divider: Solder the two 10kΩ resistors in series. Connect one end to the LiPo V+ and the other to GND. Solder the green signal wire to the junction. This divides the 7.4V down to ~3.7V, safely within the Nano's 5V ADC limit.
  3. Wire the Servo: Connect the servo's Red wire to the Buck Converter OUT+, and Brown to System GND. Connect Orange to Nano D9. Solder the 470µF cap across Red and Brown.
  4. Seal the Enclosure: Drill a 6mm hole for the servo wire exit. Feed the wires through, then flood the hole with marine-grade hot glue to create a watertight gland. Apply silicone grease to the O-ring before closing the ABS lid.

Complete Arduino Nano Firmware

This firmware targets the Arduino Nano V3.0 (ATmega328P). It utilizes the standard Arduino Servo library to generate the 50Hz PWM signal required by the SG90. Crucially, it includes a hardware protection loop: it reads the LiPo voltage via A0 and safely detaches the servo if the battery drops below 6.4V (3.2V per cell), preventing catastrophic LiPo over-discharge which can cause cell swelling or fire.

#include <Servo.h>

// --- PIN DEFINITIONS ---
#define SERVO_PIN      9    // Hardware PWM pin for SG90
#define VBAT_PIN       A0   // Analog pin for voltage divider
#define STATUS_LED_PIN 13   // Nano onboard LED

// --- SYSTEM CONSTANTS ---
// Voltage divider ratio: R2 / (R1 + R2) = 10k / (10k + 10k) = 0.5
const float V_DIV_RATIO = 0.5;
const float ADC_VREF = 5.0;       // Nano 5V logic reference
const int ADC_RESOLUTION = 1023;
const float LOW_BAT_CUTOFF = 6.4; // 3.2V per cell * 2S
const int SWEEP_DELAY = 15;       // Controls tail oscillation speed (lower = faster)
const int TAIL_AMPLITUDE = 45;    // Max degrees from center (90)

Servo tailServo;
float currentVoltage = 0.0;
bool batterySafe = true;

void setup() {
  pinMode(STATUS_LED_PIN, OUTPUT);
  pinMode(VBAT_PIN, INPUT);
  
  tailServo.attach(SERVO_PIN, 500, 2400); // Custom pulse width to prevent servo over-travel
  tailServo.write(90); // Center the tail
  delay(500);
  
  // Boot sequence blink
  for(int i=0; i<3; i++) {
    digitalWrite(STATUS_LED_PIN, HIGH);
    delay(100);
    digitalWrite(STATUS_LED_PIN, LOW);
    delay(100);
  }
}

void loop() {
  // 1. Read Battery Voltage
  int rawADC = analogRead(VBAT_PIN);
  currentVoltage = (rawADC * ADC_VREF / ADC_RESOLUTION) / V_DIV_RATIO;
  
  // 2. Safety Check: Prevent LiPo Over-discharge
  if (currentVoltage < LOW_BAT_CUTOFF && currentVoltage > 1.0) { // >1.0V filters out disconnected ADC noise
    batterySafe = false;
  }
  
  if (!batterySafe) {
    tailServo.detach(); // Remove PWM signal to stop servo from drawing current
    // Blink LED to indicate dead battery
    digitalWrite(STATUS_LED_PIN, (millis() / 250) % 2);
    return; // Halt swim loop
  }
  
  // 3. Biomimetic Swim Pattern (Sinusoidal Sweep)
  // Using a simple triangle wave for the SG90 to avoid complex trig on 8-bit MCU
  for (int pos = 90 - TAIL_AMPLITUDE; pos <= 90 + TAIL_AMPLITUDE; pos += 2) {
    tailServo.write(pos);
    delay(SWEEP_DELAY);
  }
  for (int pos = 90 + TAIL_AMPLITUDE; pos >= 90 - TAIL_AMPLITUDE; pos -= 2) {
    tailServo.write(pos);
    delay(SWEEP_DELAY);
  }
}

Debugging: First 3 Things to Check When It Fails

When you put it in the water and it fails, do not guess. Follow this ranked diagnostic path.

1. Symptom: Servo jitters, twitches, or hums without sweeping

  • Most Likely Cause: PWM signal degradation or power rail ripple. The Arduino PWM frequency on D9 is roughly 490Hz, but the Servo library uses Timer1 interrupts to generate a precise 50Hz signal. Long wires act as antennas, picking up EMI from the servo's brushed motor.
  • The Fix: Verify the 470µF capacitor is installed. If the signal wire is longer than 12 inches, solder a 1kΩ pull-down resistor between D9 and GND at the Nano end to keep the line low during boot, and ensure the signal wire is not routed parallel to the LiPo power cables.

2. Symptom: Nano resets randomly mid-swim (LED flashes boot sequence)

  • Most Likely Cause: Brownout. The SG90 draws up to 750mA at stall. If the servo is powered from the Nano's 5V pin, or if the buck converter is undersized, the voltage drops below the ATmega328P's Brown-Out Detection (BOD) threshold of 2.7V, triggering a hardware reset.
  • The Fix: Confirm the servo is powered directly from the buck converter's OUT+ terminal, not the Nano's 5V pin. Verify the buck converter is rated for at least 2A continuous output (the LM2596 modules are typically rated 3A peak, 2A continuous).

3. Symptom: Water ingress after 10-15 minutes of swimming

  • Most Likely Cause: Capillary action or O-ring pinch. Water wicks down the stranded silicone wire into the hull, or the O-ring rolled out of its groove when the lid was screwed down.
  • The Fix: Open the enclosure and inspect. Apply a thin layer of marine-grade dielectric grease to the O-ring. For the wire exit, strip the outer silicone jacket back 1 inch inside the box, and flood the exit hole with hot glue so it bonds directly to the inner copper strands and the ABS plastic, breaking the capillary path.

Extending vs. Simplifying the Build

Depending on your end goal, you can scale this architecture up or down.

How to Simplify (The Analog Route)

If you want to eliminate the microcontroller entirely to save cost and remove firmware complexity, drop the Nano. Build an astable multivibrator circuit using an NE555 timer IC. Configure it to output a 50Hz square wave. Replace the fixed timing resistor with a 100kΩ potentiometer. By turning the pot, you manually adjust the PWM duty cycle, which changes the physical sweep angle of the SG90 servo, allowing you to tune the tail amplitude on the fly without rewriting code. Total cost drops to under $15.

How to Extend (The Smart Swarm Route)

To add remote control or telemetry, integrate an HM-10 Bluetooth Low Energy (BLE) module. Wire the HM-10 TX/RX to Nano D10 and D11 using the SoftwareSerial library. This allows you to send single-byte commands from a smartphone app to change the SWEEP_DELAY variable in real-time, effectively giving you throttle control over the fish's swim speed. Ensure you add a 3.3V LDO regulator (like the HT7333) to power the HM-10, as its VCC pin is not 5V tolerant.