A heavy-duty 12V motorized ball valve is the gold standard for automated main water shutoffs, irrigation manifolds, and fluid dosing systems. Unlike cheap solenoid valves that restrict flow and suffer from water hammer, a full-port ball valve offers zero pressure drop and handles high-torque mechanical loads. However, driving the internal DC gear motor requires serious current—often spiking to 4A or 5A during stall or startup. You cannot drive this directly from a microcontroller. When properly powered by Arduino and a high-current H-bridge, you get a robust, programmable fluid control system that won't fry your logic board.
This guide walks through building a reliable 12V motorized valve controller. We will use the Arduino Nano Every for its robust 5V logic and extra timers, paired with a BTS7960 43A motor driver to handle the inductive kickback and stall currents without breaking a sweat.
Hardware Spec Sheet & Parts List
To ensure the system survives real-world plumbing pressures and electrical noise, we are skipping hobby-grade components for the power stage. The assumptions here are a 3/4" NPT plumbing line, maximum 80 PSI water pressure, and an indoor/dry environment for the electronics enclosure.
| Component | Exact Model / Variant | Est. Price (2026) | Why This Variant? |
|---|---|---|---|
| Microcontroller | Arduino Nano Every (ATSAMD11 + ATmega4809) | $18.50 | True 5V logic, more SRAM than the classic Nano, better PWM timers. |
| Motor Driver | BTS7960 43A High-Power H-Bridge Module | $12.00 | Handles 43A peak. Essential for the 5A stall current of 12V valve motors. |
| Actuator | US Solid CR02-3/4" 12V DC Motorized Ball Valve | $45.00 | Stainless steel full-port design. 2-wire auto-return or 3-wire. We use the 2-wire polarity-reversal model. |
| Power Supply | Mean Well LRS-75-12 (12V 6A Enclosed) | $22.00 | Provides 72W of clean, regulated 12V DC with built-in overcurrent protection. |
| Wiring | 14 AWG THHN (Power) & 22 AWG Stranded (Logic) | $5.00 | 14 AWG prevents voltage sag during motor startup; 22 AWG is flexible for logic pins. |
Pin Mapping & Wiring Steps
The BTS7960 module separates logic from power, but they must share a common ground reference. Voltage spikes from the inductive motor load will destroy the Nano Every if the ground star-point is not established correctly.
| Arduino Nano Every Pin | BTS7960 Pin | Function |
|---|---|---|
| D4 | R_EN | Right Enable (Logic HIGH to enable right bridge) |
| D5 | L_EN | Left Enable (Logic HIGH to enable left bridge) |
| D6 (PWM) | R_PWM | Right PWM (Controls speed/direction 1) |
| D9 (PWM) | L_PWM | Left PWM (Controls speed/direction 2) |
| GND | B- (Logic GND) | Common Ground (CRITICAL) |
| 5V | VCC (Logic) | Powers the BTS7960 optocouplers/logic |
Numbered Wiring Procedure:
- Establish the Power Bus: Wire the Mean Well LRS-75-12 V+ and V- to a heavy-duty terminal block. Do not use a breadboard for the 12V motor loop; it will melt.
- Wire the Motor: Connect the two wires from the US Solid ball valve to the BTS7960
M+andM-terminals using 14 AWG wire. - Connect Driver Power: Run 14 AWG from the 12V terminal block to the BTS7960
B+andB-(Power GND) terminals. - Bridge the Grounds: Run a 22 AWG wire from the BTS7960
B-(Power GND) to the BTS7960GND(Logic GND) pin, and then to the Arduino Nano EveryGNDpin. This is your star ground. - Wire Logic Pins: Connect the Nano Every D4, D5, D6, and D9 pins to the corresponding R_EN, L_EN, R_PWM, and L_PWM pins on the driver.
- Verify: Before applying 12V power, use a multimeter in continuity mode to verify there are no shorts between the 12V V+ line and any logic pins or ground.
Complete Compilable Code (Arduino Nano Every)
This code targets the Arduino Nano Every. It implements a non-blocking state machine to open and close the valve. Because the US Solid CR02 valve does not have external limit switches, we use a time-based timeout to prevent the motor from burning out if the valve mechanically jams. If the valve doesn't reach its expected state within the timeout window, the system throws an error and cuts power to prevent a fire hazard.
// Target Board: Arduino Nano Every (ATmega4809)
// Project: 12V Motorized Ball Valve Controller
// Driver: BTS7960 High-Power H-Bridge
#define PIN_R_EN 4
#define PIN_L_EN 5
#define PIN_R_PWM 6
#define PIN_L_PWM 9
#define VALVE_OPEN_TIME_MS 12000 // Time it takes for valve to fully open/close
#define SAFETY_TIMEOUT_MS 15000 // Hard cutoff to prevent motor burnout
enum ValveState {
IDLE,
OPENING,
CLOSING,
ERROR_STALLED
};
ValveState currentState = IDLE;
unsigned long stateStartTime = 0;
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 2000); // Wait for serial monitor
pinMode(PIN_R_EN, OUTPUT);
pinMode(PIN_L_EN, OUTPUT);
pinMode(PIN_R_PWM, OUTPUT);
pinMode(PIN_L_PWM, OUTPUT);
stopMotor();
Serial.println("System Initialized. Valve state: UNKNOWN. Sending CLOSE command to home.");
// Home the valve on startup to ensure known state
closeValve();
}
void loop() {
// Example trigger: Open valve after 5 seconds, close after 10 seconds
if (currentState == IDLE && millis() > 5000 && millis() < 6000) {
openValve();
}
if (currentState == IDLE && millis() > 15000 && millis() < 16000) {
closeValve();
}
// State machine watchdog
handleMotorState();
}
void openValve() {
if (currentState == ERROR_STALLED) return;
Serial.println("CMD: OPEN_VALVE");
digitalWrite(PIN_R_EN, HIGH);
digitalWrite(PIN_L_EN, HIGH);
analogWrite(PIN_R_PWM, 255); // Full speed forward
analogWrite(PIN_L_PWM, 0);
currentState = OPENING;
stateStartTime = millis();
}
void closeValve() {
if (currentState == ERROR_STALLED) return;
Serial.println("CMD: CLOSE_VALVE");
digitalWrite(PIN_R_EN, HIGH);
digitalWrite(PIN_L_EN, HIGH);
analogWrite(PIN_R_PWM, 0);
analogWrite(PIN_L_PWM, 255); // Full speed reverse
currentState = CLOSING;
stateStartTime = millis();
}
void stopMotor() {
digitalWrite(PIN_R_EN, LOW);
digitalWrite(PIN_L_EN, LOW);
analogWrite(PIN_R_PWM, 0);
analogWrite(PIN_L_PWM, 0);
}
void handleMotorState() {
unsigned long elapsedTime = millis() - stateStartTime;
if (currentState == OPENING) {
if (elapsedTime >= VALVE_OPEN_TIME_MS) {
stopMotor();
currentState = IDLE;
Serial.println("STATE: VALVE_OPEN");
} else if (elapsedTime >= SAFETY_TIMEOUT_MS) {
triggerError();
}
}
else if (currentState == CLOSING) {
if (elapsedTime >= VALVE_OPEN_TIME_MS) {
stopMotor();
currentState = IDLE;
Serial.println("STATE: VALVE_CLOSED");
} else if (elapsedTime >= SAFETY_TIMEOUT_MS) {
triggerError();
}
}
}
void triggerError() {
stopMotor();
currentState = ERROR_STALLED;
Serial.println("ERR: ACTUATION_TIMEOUT_EXCEEDED");
Serial.println("ACTION REQUIRED: Check mechanical binding or power sag.");
// In a real system, trigger an interrupt or MQTT alert here
}
Debugging: Motor Jitters and Clicks but Won't Turn
When working with high-torque 12V gear motors, the most common failure mode on the bench is the driver clicking rapidly while the motor jitters in place. If your serial monitor prints ERR: ACTUATION_TIMEOUT_EXCEEDED, or the valve simply refuses to move, the issue is almost always related to voltage sag or ground loops.
The First Three Things to Check:
- Measure the 12V Rail Under Load: Connect your multimeter directly to the BTS7960
B+andB-terminals. Trigger the valve to open. If the voltage drops below 10.5V, your power supply is inadequate, or your wires are too thin. The Mean Well LRS-75-12 should hold steady at 11.8V minimum. - Verify the Common Ground: If the Arduino resets randomly when the motor starts, you have a ground loop or inductive spike. Ensure the Nano Every GND and the BTS7960 Logic GND are tied to the same physical point as the BTS7960 Power GND.
- Check for Mechanical Binding: Disconnect the 12V power. Try to turn the manual override lever on the US Solid valve. If it requires extreme force, the internal PTFE seats may be over-torqued from the factory, or debris is caught in the ball port.
According to motor drive principles outlined by All About Circuits, inductive loads generate massive back-EMF when switched off. The BTS7960 has built-in freewheeling diodes, but if you are using a cheaper clone board with undersized diodes, you may need to add an external RC snubber network across the motor terminals to prevent logic resets.
Extending and Simplifying the Build
Depending on your final application, you may need to scale this design up for industrial use or strip it down for a simple garden timer.
How to Extend (Smart Leak Detection):
Swap the Nano Every for an ESP32-WROOM-32 dev board. Add a YF-S201 Hall Effect water flow sensor to the output pipe. By monitoring flow rates via MQTT, the ESP32 can detect micro-leaks (e.g., a running toilet) and automatically command the ball valve to shut off the main line, sending a push notification to your phone.
How to Simplify (Low-Cost Irrigation):
If you don't need full-port flow and are only driving low-pressure drip irrigation, ditch the $45 motorized ball valve and the $12 BTS7960. Instead, use a 12V Normally Closed (NC) solenoid valve ($8) and a single IRLZ44N logic-level MOSFET ($1.50). Solenoids only require on/off switching (no H-bridge polarity reversal), drastically simplifying both the wiring and the code. Just be aware that solenoids suffer from water hammer when they snap shut, which can damage PVC fittings over time.
Frequently Asked Questions
Can a main water shutoff be safely powered by Arduino?
Yes, but it requires fail-safes. A microcontroller can freeze or crash due to a brownout or memory leak. If you are using a system powered by Arduino to protect a home from flooding, you must implement a hardware watchdog timer (WDT) that resets the board if the code hangs. Additionally, use a valve with a manual override lever so you can physically shut off the water if the electronics completely die.
What happens to a motorized valve powered by Arduino during a power outage?
A standard 2-wire DC motorized ball valve will stop exactly where it is when power is lost. If it was open, it stays open. If your application requires the valve to fail-closed (e.g., shutting off gas or hazardous chemicals), you must use a spring-return actuator or a 3-wire valve with an internal supercapacitor backup that automatically drives the valve to the closed position when VCC drops to zero.
Is it better to use a solenoid valve or a ball valve powered by Arduino for irrigation?
For main lines and high-pressure systems, a ball valve is vastly superior. Solenoid valves require a constant pressure differential to open, restrict flow, and cause water hammer. Ball valves draw high current only for the 10 seconds it takes to rotate 90 degrees, then draw zero holding current. Solenoids draw continuous current (often 1A to 2A) the entire time they are open, which generates heat and wastes energy in solar-powered irrigation setups.






