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


Built the Remote Pico Controller schematic: Pico socket, ESP-01S WiFi UART on GP0/GP1, 9V screw input, 5V + 3.3V regulation, and four independent low-side MOSFET motor channels on GP18–GP21. ERC is clean; remaining DRC/manufacturing findings are expected until layout/routing and real MPN/packages are assigned for generic passives.
Open items:
  • Confirm motor running/stall current before layout.
  • Decide whether to add input fuse, reverse-polarity protection, and TVS clamp.
  • Assign final passive MPNs/footprints before manufacturing.
Suggested next steps:
  1. Add/provision input protection based on expected motor current.
  2. Move to PCB layout and placement.
  3. Set trace widths for V9_MOTOR and each motor channel.
  4. Route and run DRC.
i want the power supply to be via AA batteries . is that possible and if so, include a battery pack part and wire that up as the power source
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

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