I want a simplest possible schematic that allows m...

I’ll check suitable AA battery-holder options, then update the schematic so the battery pack is the 9V input source and verify the power ERC afterward.
Yes — for the existing 9V motor rail, the right AA approach is six AA cells in series. I didn’t find a single 6×AA holder in the library, so I’m using a 4×AA holder plus a 2×AA holder wired in series for 9V nominal alkaline output.
Implemented the AA battery power update: added BT1 4×AA + BT2 2×AA in series as a 6×AA pack, wired to V9_MOTOR/GND through restored battery input terminal J2, and kept the holders as external/off-board parts. Updated the project files and verified ERC plus key placement/DRC checks are clean.
Open items:
  • 6× alkaline AA = ~9V nominal; 6× NiMH AA = ~7.2V nominal, so 9V motors may run slower on rechargeables.
  • Confirm motor stall current and AA cell discharge capability before relying on four motors at once.
Suggested next steps:
  1. Add an input fuse/polyfuse for battery short-circuit protection.
  2. Add reverse-polarity protection for the battery input.
  3. Re-check layout/routing with the restored J2 battery connector.
Add protections and verify
Add input fuse
Add reverse protection
Check battery current
Re-check layout

Refine this doc
Ask about this doc
Firmware Starter — Raspberry Pi Pico / ESP-01S Motor Controller
Platform & Toolchain
  • Controller: Raspberry Pi Pico / RP2040 seated on J1.
  • Framework: Arduino-Pico core via PlatformIO.
  • WiFi path: ESP-01S AT firmware on Serial1.
  • Motor outputs: PWM-capable GPIO outputs GP18GP21.
Pin Mapping

Table


FunctionPico GPIOConnected ToDirectionNotes
ESP UART TX from PicoGP0U1:RXDOutputPico sends AT commands to ESP-01S.
ESP UART RX to PicoGP1U1:TXDInputPico receives AT responses.
Motor 1GP18R5 -> Q1:GOutput/PWMControls J3 motor output.
Motor 2GP19R6 -> Q2:GOutput/PWMControls J4 motor output.
Motor 3GP20R7 -> Q3:GOutput/PWMControls J5 motor output.
Motor 4GP21R8 -> Q4:GOutput/PWMControls J6 motor output.
platformio.ini

Ini


[env:pico]
platform = raspberrypi
board = pico
framework = arduino
monitor_speed = 115200
Complete Firmware Source (src/main.cpp)

Cpp


#include <Arduino.h>

// Schematic pin mapping
static constexpr uint8_t ESP_TX_FROM_PICO = 0;   // GP0 -> ESP RXD
static constexpr uint8_t ESP_RX_TO_PICO   = 1;   // GP1 <- ESP TXD
static constexpr uint8_t MOTOR1_PIN = 18;        // GP18 -> Q1 gate via R5
static constexpr uint8_t MOTOR2_PIN = 19;        // GP19 -> Q2 gate via R6
static constexpr uint8_t MOTOR3_PIN = 20;        // GP20 -> Q3 gate via R7
static constexpr uint8_t MOTOR4_PIN = 21;        // GP21 -> Q4 gate via R8

static constexpr uint32_t DEBUG_BAUD = 115200;
static constexpr uint32_t ESP_BAUD   = 115200;
static constexpr uint8_t PWM_RESOLUTION_BITS = 8;   // analogWrite range 0..255

// Replace with your network credentials if you use station mode.
const char WIFI_SSID[] = "YOUR_SSID";
const char WIFI_PASS[] = "YOUR_PASSWORD";

bool waitForESPResponse(const char *expected, uint32_t timeoutMs) {
  String response;
  uint32_t start = millis();
  while (millis() - start < timeoutMs) {
    while (Serial1.available() > 0) {
      char c = static_cast<char>(Serial1.read());
      response += c;
      Serial.write(c);
      if (response.indexOf(expected) >= 0) {
        return true;
      }
      if (response.indexOf("ERROR") >= 0 || response.indexOf("FAIL") >= 0) {
        return false;
      }
    }
  }
  Serial.print("\nTimed out waiting for: ");
  Serial.println(expected);
  return false;
}

bool sendAT(const char *command, const char *expected = "OK", uint32_t timeoutMs = 3000) {
  Serial.print("AT> ");
  Serial.println(command);
  Serial1.print(command);
  Serial1.print("\r\n");
  return waitForESPResponse(expected, timeoutMs);
}

void initMotors() {
  analogWriteResolution(PWM_RESOLUTION_BITS);

  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  pinMode(MOTOR3_PIN, OUTPUT);
  pinMode(MOTOR4_PIN, OUTPUT);

  analogWrite(MOTOR1_PIN, 0);
  analogWrite(MOTOR2_PIN, 0);
  analogWrite(MOTOR3_PIN, 0);
  analogWrite(MOTOR4_PIN, 0);
}

void setMotor(uint8_t motorIndex, uint8_t duty) {
  switch (motorIndex) {
    case 1: analogWrite(MOTOR1_PIN, duty); break;
    case 2: analogWrite(MOTOR2_PIN, duty); break;
    case 3: analogWrite(MOTOR3_PIN, duty); break;
    case 4: analogWrite(MOTOR4_PIN, duty); break;
    default: break;
  }
}

void allMotorsOff() {
  setMotor(1, 0);
  setMotor(2, 0);
  setMotor(3, 0);
  setMotor(4, 0);
}

bool initESP01S() {
  Serial1.setTX(ESP_TX_FROM_PICO);
  Serial1.setRX(ESP_RX_TO_PICO);
  Serial1.begin(ESP_BAUD);
  delay(500);

  bool ok = true;
  ok &= sendAT("AT", "OK", 2000);
  ok &= sendAT("ATE0", "OK", 2000);          // echo off
  ok &= sendAT("AT+CWMODE=1", "OK", 3000);   // station mode

  if (strlen(WIFI_SSID) > 0 && strcmp(WIFI_SSID, "YOUR_SSID") != 0) {
    char joinCommand[160];
    snprintf(joinCommand, sizeof(joinCommand), "AT+CWJAP=\"%s\",\"%s\"", WIFI_SSID, WIFI_PASS);
    ok &= sendAT(joinCommand, "WIFI GOT IP", 20000);
  } else {
    Serial.println("WiFi credentials are placeholders; skipping AP join.");
  }

  return ok;
}

void demoMotorSequence() {
  // Gentle smoke-test sequence. Remove or guard this in production firmware.
  for (uint8_t duty = 0; duty <= 128; duty += 16) {
    setMotor(1, duty);
    delay(40);
  }
  delay(300);
  allMotorsOff();
}

void setup() {
  Serial.begin(DEBUG_BAUD);
  delay(1000);
  Serial.println("Remote Pico Controller firmware starting");

  initMotors();

  if (initESP01S()) {
    Serial.println("ESP-01S AT interface ready");
  } else {
    Serial.println("ESP-01S init failed or timed out; motors remain controllable locally");
  }

  demoMotorSequence();
}

void loop() {
  // USB serial commands for bench testing:
  //   m1 0      -> motor 1 off
  //   m2 128    -> motor 2 ~50% duty
  //   alloff    -> all motors off
  if (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    cmd.trim();

    if (cmd.equalsIgnoreCase("alloff")) {
      allMotorsOff();
      Serial.println("All motors off");
    } else if (cmd.length() >= 4 && cmd.charAt(0) == 'm') {
      uint8_t motor = cmd.substring(1, 2).toInt();
      int duty = cmd.substring(3).toInt();
      duty = constrain(duty, 0, 255);
      setMotor(motor, static_cast<uint8_t>(duty));
      Serial.printf("Motor %u duty %d\n", motor, duty);
    } else if (cmd.startsWith("AT")) {
      sendAT(cmd.c_str(), "OK", 5000);
    } else {
      Serial.println("Commands: m1 0..255, m2 0..255, m3 0..255, m4 0..255, alloff, or raw AT...");
    }
  }

  // Pass ESP unsolicited messages to USB serial.
  while (Serial1.available()) {
    Serial.write(Serial1.read());
  }
}
Build and Flash

Bash


pio run
pio run -t upload
pio device monitor -b 115200
Bring-Up Notes
  • Start with no motors connected.
  • Verify V5 and V3V3_WIFI rails before flashing firmware.
  • Confirm AT command response from ESP-01S before trying WiFi join.
  • Test motors one channel at a time with low duty cycle.
  • Platform & Toolchain

  • Pin Mapping

  • `platformio.ini`

  • Complete Firmware Source (`src/main.cpp`)

  • Build and Flash

  • Bring-Up Notes

Documents

  • Project Specification — Remote Pico Controller

  • Design Notes and Power Budget — Remote Pico Controller

  • Firmware Starter — Raspberry Pi Pico / ESP-01S Motor Controller

  • Board Bring-Up Plan — Remote Pico Controller

  • Regulatory and Safety Notes — Remote Pico Controller

  • Preliminary FMEA — Remote Pico Controller

Assets

Assets are files uploaded to this project which can be used in various ways.

Remote Pico Controller thumbnail
Raspberry Pi Pico HAT for WiFi communication and independent control of up to four 9V motors from GPIO GP18-GP21.

Properties

Properties describe core aspects of the project.

Pricing & Availability

Distributor

Qty 1

Arrow

$8.88–$16.52

Digi-Key

$15.48

HQonline

$4.63

LCSC

$23.80–$23.85

Mouser

$18.27

TME

$13.82

Verical

$3.68–$11.37

Controls