This commit is contained in:
zyphlar
2026-07-04 03:28:11 -07:00
parent e681a74534
commit 5a18a689e0
9 changed files with 2561 additions and 2233 deletions
+193 -242
View File
@@ -1,278 +1,229 @@
# Baby Mobile Audio Board — Design Document
# Baby Mobile Audio Board v2 — Design Document
## Overview
Drop-in replacement board for a baby crib mobile. Plays custom audio from
SPI flash memory, drives a DC motor, handles 8 button inputs, and runs
for days on two AA batteries.
Drop-in replacement for baby crib mobile electronics. Custom audio from
SPI flash, DC motor control, 8 buttons, and long runtime on 2×AA batteries.
No programmer needed — upload audio via USB drag-and-drop or Bluetooth.
**Key design decisions for long battery life:**
- SPI flash instead of microSD (4mA vs 30100mA read current)
- Class-D amplifier at 90% efficiency (vs ~50% for class AB)
- ATmega328P internal 8MHz oscillator (no crystal, lower BOM)
- Logic-level MOSFET motor driver (no gate driver IC needed)
- Aggressive sleep with pin-change interrupt wake
## Schematic Netlist (Logical Connections)
### Power
```
2xAA Battery → SW9(PWR) → VCC rail
VCC → C1(100µF) → GND bulk decoupling
VCC → C2(100nF) → GND MCU VCC decoupling
VCC → C3(100nF) → GND MCU AVCC decoupling
VCC → C5(100nF) → GND Flash decoupling
VCC → C8(10µF) → GND Amp supply bypass
```
### MCU: U1 — ATmega328P-AU (TQFP-32)
| MCU Pin | Port | Function | Connected To |
|---------|----------|---------------|---------------------------|
| 30 | PD0/RXD | Serial RX | J5 pin 3 (SERIAL header) |
| 31 | PD1/TXD | Serial TX | J5 pin 2 (SERIAL header) |
| 32 | PD2/INT0 | Button 3 | SW3 → GND (active LOW) |
| 1 | PD3/INT1 | Button 4 | SW4 → GND (active LOW) |
| 2 | PD4 | Button 5 | SW5 → GND (active LOW) |
| 9 | PD5/OC0B | Motor PWM | R4(100Ω) → Q1 gate |
| 10 | PD6/OC0A | Button 6 | SW6 → GND (active LOW) |
| 11 | PD7 | Button 7 | SW7 → GND (active LOW) |
| 12 | PB0 | Button 8 | SW8 → GND (active LOW) |
| 13 | PB1/OC1A | Audio PWM | R2(4.7K) → C7(10nF) → U3 |
| 14 | PB2/SS | Flash ~CS | U2 pin 1 |
| 15 | PB3/MOSI | SPI MOSI | U2 pin 5, J4 MOSI |
| 16 | PB4/MISO | SPI MISO | U2 pin 2, J4 MISO |
| 17 | PB5/SCK | SPI SCK | U2 pin 6, J4 SCK |
| 23 | PC0/ADC0 | Button 1 | SW1 → GND (active LOW) |
| 24 | PC1/ADC1 | Button 2 | SW2 → GND (active LOW) |
| 25 | PC2/ADC2 | Amp ~SD | U3 pin 1 + R3(100K)→GND |
| 26 | PC3/ADC3 | Status LED | R6(1K) → D2(LED) → GND |
| 27 | PC4/SDA | (spare/I2C) | — |
| 28 | PC5/SCL | (spare/I2C) | — |
| 29 | PC6/~RST | Reset | R1(10K)→VCC, C4(100nF)→GND|
| 4 | VCC | Power | VCC rail |
| 6 | AVCC | Analog power | VCC rail (via ferrite opt) |
| 21 | AREF | Reference | C3(100nF) → GND |
| 3, 5 | GND | Ground | GND rail |
| 7 | XTAL1 | (unused) | leave floating |
| 8 | XTAL2 | (unused) | leave floating |
### SPI Flash: U2 — W25Q128JVSIQ (SOIC-8)
| Pin | Name | Connected To |
|-----|-----------|-----------------|
| 1 | ~CS | PB2 (FLASH_CS) |
| 2 | DO (MISO) | PB4 (SPI_MISO) |
| 3 | ~WP | VCC (tie high) |
| 4 | GND | GND |
| 5 | DI (MOSI) | PB3 (SPI_MOSI) |
| 6 | CLK | PB5 (SPI_SCK) |
| 7 | ~HOLD | VCC (tie high) |
| 8 | VCC | VCC |
### Audio Amplifier: U3 — PAM8302AASCR (SOIC-8)
| Pin | Name | Connected To |
|-----|------|-------------------------------------------|
| 1 | ~SD | PC2 (AMP_SD) + R3(100K) pulldown to GND |
| 2 | VDD | VCC + C8(10µF) bypass |
| 3 | GND | GND |
| 4 | A+ | Audio signal from RC filter |
| 5 | A- | GND (single-ended input) |
| 6 | PAD | GND (thermal pad) |
| 7 | VO- | Speaker - |
| 8 | VO+ | Speaker + |
**Audio signal path:**
```
PB1 (OC1A PWM) → R2 (4.7KΩ) → node → C6 (1µF coupling) → U3 A+ (pin 4)
|
C7 (10nF) → GND
RC low-pass filter: fc = 1/(2π × 4700 × 10e-9) ≈ 3.4 kHz
This removes the PWM carrier while passing audio baseband.
```
**Shutdown control:**
- R3 (100K) pulls ~SD LOW by default = amplifier OFF
- MCU drives PC2 HIGH to enable amplifier
- Saves ~2mA quiescent current when not playing
### Motor Driver
## Architecture
```
PD5 (OC0B) → R4 (100Ω gate resistor) → Q1 Gate
|
R5 (100K) → GND (pulldown, ensures OFF at boot)
Q1: AO3400A N-ch MOSFET (SOT-23)
Drain → Motor terminal 1
Source → GND
(Motor terminal 2 → VCC)
D1: SS14 Schottky flyback diode across motor (Cathode → VCC, Anode → Drain)
2×AA (1.8-3.2V) → SW9 → D1(BAT54S) → TPS61220 boost → 3.3V ──┐
├── MDBT50Q
USB-C VBUS (5V) → D3(BAT54S) → MCP1700 LDO → 3.3V ────────────┘ (nRF52840)
┌────────────── SPI ── IS25LP128F (16MB flash, 2.3-3.6V) │
│ ┌───────── PWM ── R+C LPF ── PAM8302A ── 8Ω speaker │
│ │ ┌───── GPIO ── AO3400A MOSFET ── DC motor │
│ │ │ ┌─── GPIO ── 8× buttons (internal pull-ups) │
│ │ │ │ ┌─ USB ── USB-C D+/D- (mass storage) │
│ │ │ │ │ BLE ── phone (wireless upload) │
└────┴───┴─┴─┴──────────────────────────────────────────────────┘
```
**Motor specs:**
- PWM on Timer0 (OC0B) for variable speed
- AO3400A: Vgs(th) = 0.65V typ, fully enhanced at 1.8V → works at depleted AA voltage
- R4 limits gate ringing; R5 ensures MOSFET OFF during MCU reset/programming
## Power Design — Diode OR-ing (Safe for ALL Battery Types)
### Button Matrix
All 8 buttons connect between their MCU pin and GND (active LOW).
MCU internal pull-ups enabled (~3050KΩ). No external pull-up resistors needed.
Buttons are on PCINT-capable pins for wake-from-sleep:
- SW1 (PC0) → PCINT8
- SW2 (PC1) → PCINT9
- SW3 (PD2) → INT0 (hardware interrupt)
- SW4 (PD3) → INT1 (hardware interrupt)
- SW5 (PD4) → PCINT20
- SW6 (PD6) → PCINT22
- SW7 (PD7) → PCINT23
- SW8 (PB0) → PCINT0
### Headers
**J4 — ISP (2×3, standard Atmel AVR ISP pinout):**
```
MISO 1 ● 2 VCC
SCK 3 4 MOSI
~RST 5 6 GND
BAT54S TPS61220
2×AA ──SW9──┤►├──────┐ ┌─ VIN VOUT ─── 3.3V rail
├── node ┤
USB VBUS ───┤►├── LDO ┘ └─ EN GND ─── GND
BAT54S MCP1700 L ─── L1(4.7µH)
```
**J5 — Serial (1×3, for bootloader programming / debug):**
**Why this is safe:** The Schottky diodes only pass current in one direction.
When USB provides 3.3V via the LDO, the boost converter stops switching and
the battery diode is reverse-biased. Zero current flows into the batteries
regardless of chemistry (alkaline, NiMH, lithium).
**Diode drop:** BAT54S drops ~0.25V. TPS61220 accepts inputs down to 0.7V.
Depleted 2×AA = 1.8V → boost sees 1.55V → still operational.
## Pin Mapping — MDBT50Q (nRF52840)
| Module Pin | nRF52840 Port | Function | Connected To |
|------------|---------------|--------------|-------------------------------|
| 4 | P0.02/AIN0 | Button 1 | SW1 → GND (active LOW) |
| 5 | P0.03/AIN1 | Button 2 | SW2 → GND |
| 6 | P0.04/AIN2 | Button 3 | SW3 → GND |
| 7 | P0.05/AIN3 | Button 4 | SW4 → GND |
| 8 | P0.06 | Button 5 | SW5 → GND |
| 9 | P0.07 | Button 6 | SW6 → GND |
| 10 | P0.08 | Button 7 | SW7 → GND |
| 11 | P0.09 | Button 8 | SW8 → GND |
| 12 | P0.10 | Motor PWM | R4(100Ω) → Q1 gate |
| 13 | P0.11 | Audio PWM | R2(4.7K) → C7(10nF) → C6 → U3|
| 14 | P0.12 | Flash ~CS | U2 pin 1 |
| 15 | P0.13 | SPI MOSI | U2 pin 5 |
| 16 | P0.14 | SPI MISO | U2 pin 2 |
| 17 | P0.15 | SPI SCK | U2 pin 6 |
| 18 | P0.16 | LED output | R6(1K) → D2 → GND |
| 19 | P0.17 | Amp ~SD | U3 pin 1, R3(100K) → GND |
| 20 | P0.18/~RESET | Reset | (internal pull-up) |
| 21 | P0.19 | TXD (serial) | J5 pin 2 |
| 22 | P0.20 | RXD (serial) | J5 pin 3 |
| 28 | D- | USB Data - | J6 D- |
| 29 | D+ | USB Data + | J6 D+ |
| 33 | SWDIO | Debug | J4 pin 2 (SWD header) |
| 34 | SWDCLK | Debug | J4 pin 3 (SWD header) |
| 32 | VCC | Power | 3V3 rail |
| 1 | GND | Ground | GND rail |
## SPI Flash: U2 — IS25LP128F (SOIC-8)
| Pin | Name | Connected To |
|-----|-----------|-------------------|
| 1 | ~CS | P0.12 (FLASH_CS) |
| 2 | SO (MISO) | P0.14 (SPI_MISO) |
| 3 | ~WP | 3V3 (tie high) |
| 4 | GND | GND |
| 5 | SI (MOSI) | P0.13 (SPI_MOSI) |
| 6 | SCLK | P0.15 (SPI_SCK) |
| 7 | ~HOLD | 3V3 (tie high) |
| 8 | VCC | 3V3 |
**Operating voltage: 2.3V3.6V** — works across entire boost output range.
## Audio Signal Path
```
GND 1
TXD 2 (MCU TX out)
RXD 3 (MCU RX in)
P0.11 (PWM) → R2(4.7K) → node → C6(1µF coupling) → PAM8302A A+
C7(10nF) → GND
RC LPF cutoff: 1/(2π × 4700 × 10e-9) ≈ 3.4 kHz
```
## USB-C Wiring
```
USB-C Pin → Board
────────────────────
VBUS (5V) → D3 anode → MCP1700 VIN
GND → GND
D+ → nRF52840 D+ (pin 29)
D- → nRF52840 D- (pin 28)
CC1 → R7(5.1K) → GND
CC2 → R8(5.1K) → GND
```
## Bill of Materials
| Ref | Value/Part | Package | Qty | ~Cost | Notes |
|------|------------------|-------------------|-----|--------|-------------------------------|
| U1 | ATmega328P-AU | TQFP-32 | 1 | $2.50 | Pre-burn Arduino bootloader |
| U2 | W25Q128JVSIQ | SOIC-8 | 1 | $1.20 | 16MB, 33min @ 8kHz/8bit |
| U3 | PAM8302AASCR | SOIC-8 | 1 | $0.80 | 2.5W class-D, Vmin=2.0V |
| Q1 | AO3400A | SOT-23 | 1 | $0.15 | Logic-level N-ch MOSFET |
| D1 | SS14 | SMA | 1 | $0.10 | Motor flyback protection |
| D2 | LED (green) | 0805 | 1 | $0.05 | Status indicator |
| R1 | 10KΩ | 0805 | 1 | $0.01 | Reset pull-up |
| R2 | 4.7KΩ | 0805 | 1 | $0.01 | Audio LPF resistor |
| R3 | 100KΩ | 0805 | 1 | $0.01 | Amp shutdown pull-down |
| R4 | 100Ω | 0805 | 1 | $0.01 | MOSFET gate resistor |
| R5 | 100KΩ | 0805 | 1 | $0.01 | MOSFET gate pull-down |
| R6 | 1KΩ | 0805 | 1 | $0.01 | LED current limit |
| C1 | 100µF/10V | Radial 5mm | 1 | $0.15 | Bulk power decoupling |
| C2 | 100nF | 0805 | 1 | $0.01 | MCU VCC bypass |
| C3 | 100nF | 0805 | 1 | $0.01 | MCU AVCC/AREF bypass |
| C4 | 100nF | 0805 | 1 | $0.01 | Reset noise filter |
| C5 | 100nF | 0805 | 1 | $0.01 | Flash bypass |
| C6 | 1µF | 0805 | 1 | $0.02 | Audio AC coupling |
| C7 | 10nF | 0805 | 1 | $0.01 | Audio LPF capacitor |
| C8 | 10µF | 0805 | 1 | $0.05 | Amp supply bypass |
| SW1-8| Tactile switch | 6mm SMD | 8 | $0.80 | Or reuse existing buttons |
| SW9 | SPDT slide | THT | 1 | $0.20 | Power switch |
| J1 | JST-PH 2-pin | THT | 1 | $0.15 | Battery connector |
| J2 | JST-PH 2-pin | THT | 1 | $0.15 | Speaker connector |
| J3 | JST-PH 2-pin | THT | 1 | $0.15 | Motor connector |
| J4 | 2×3 pin header | 2.54mm | 1 | $0.10 | ISP programming |
| J5 | 1×3 pin header | 2.54mm | 1 | $0.05 | Serial debug |
| | AA battery holder| 2×AA | 1 | $0.50 | Reuse existing |
| | | | | | |
| | **TOTAL** | | |**~$7** | |
| Ref | Part | Package | Qty | ~Cost |
|--------|----------------------|---------------|-----|--------|
| U1 | MDBT50Q-P1MV2 | Module | 1 | $6.00 |
| U2 | IS25LP128F-JBLE | SOIC-8 | 1 | $2.00 |
| U3 | PAM8302AASCR | SOIC-8 | 1 | $0.80 |
| U4 | TPS61220DCKR | SC-70-5 | 1 | $0.80 |
| U5 | MCP1700-3302E/TT | SOT-23 | 1 | $0.20 |
| Q1 | AO3400A | SOT-23 | 1 | $0.15 |
| D1 | BAT54S | SOT-23 | 1 | $0.05 |
| D3 | BAT54S | SOT-23 | 1 | $0.05 |
| D4 | SS14 | SMA | 1 | $0.10 |
| D2 | LED green | 0805 | 1 | $0.05 |
| L1 | 4.7µH inductor | 0805 | 1 | $0.15 |
| R2 | 4.7KΩ | 0805 | 1 | $0.01 |
| R3 | 100 | 0805 | 1 | $0.01 |
| R4 | 100Ω | 0805 | 1 | $0.01 |
| R5 | 100 | 0805 | 1 | $0.01 |
| R6 | 1 | 0805 | 1 | $0.01 |
| R7,R8 | 5.1KΩ | 0805 | 2 | $0.02 |
| C1 | 100µF/10V | Radial 5mm | 1 | $0.15 |
| C2 | 100nF | 0805 | 1 | $0.01 |
| C3 | 10µF | 0805 | 1 | $0.05 |
| C5 | 100nF | 0805 | 1 | $0.01 |
| C6 | 1µF | 0805 | 1 | $0.02 |
| C7 | 10nF | 0805 | 1 | $0.01 |
| C8 | 10µF | 0805 | 1 | $0.05 |
| C9,C10 | 10µF | 0805 | 2 | $0.10 |
| C11,C12| 1µF | 0805 | 2 | $0.04 |
| SW1-8 | Tactile switch | 6mm SMD | 8 | $0.80 |
| SW9 | SPDT slide | THT | 1 | $0.20 |
| J1 | JST-PH 2-pin | THT | 1 | $0.15 |
| J2 | JST-PH 2-pin | THT | 1 | $0.15 |
| J3 | JST-PH 2-pin | THT | 1 | $0.15 |
| J4 | 1×4 pin header | 2.54mm | 1 | $0.10 |
| J5 | 1×3 pin header | 2.54mm | 1 | $0.05 |
| J6 | USB-C receptacle | SMD | 1 | $0.30 |
| | **TOTAL** | | |**~$12**|
## Power Analysis
### Current draw by state:
| State | MCU | Flash | Amp | Motor | Boost η | Battery mA |
|--------------------|--------|--------|--------|--------|---------|------------|
| Audio + motor | 4 mA | 8 mA | 10 mA | 80 mA | 90% | ~113 mA |
| Audio only | 4 mA | 8 mA | 10 mA | 0 | 90% | ~24 mA |
| BLE idle | 0.5 mA | 0.001 | 0.001 | 0 | 85% | ~0.7 mA |
| Deep sleep | 0.002 | 0.001 | 0.001 | 0 | — | ~0.01 mA |
| State | MCU | Flash | Amp | Motor | Total |
|--------------------|--------|--------|--------|--------|---------|
| Playing + spinning | 4 mA | 4 mA | 3 mA | 80 mA | ~91 mA |
| Playing only | 4 mA | 4 mA | 3 mA | 0 | ~11 mA |
| Idle (awake) | 4 mA | 0.001 | 0.001 | 0 | ~4 mA |
| Deep sleep | 0.0001 | 0.001 | 0.001 | 0 | <0.01mA |
**Estimated runtimes on 2×AA alkaline (2500 mAh usable with boost):**
### Estimated runtime on 2× AA Alkaline (2500 mAh):
| Mode | Current | Runtime |
|--------------------|---------|-------------|
| Audio + Motor | 91 mA | ~27 hours |
| Audio only | 11 mA | ~227 hours |
| Sleep | <10 µA | ~28 years |
With typical use of 30 min/day (motor + audio), batteries last roughly **54 days**.
With 1 hour/day, roughly **27 days** — well within AA change interval for a toy.
| Mode | Runtime |
|------------------|--------------|
| Audio + Motor | ~22 hours |
| Audio only | ~104 hours |
| BLE standby | ~149 days |
| Deep sleep | ~28 years |
| 30 min/day use | **~44 days** |
## PCB Layout Guidelines
## Module Options (Soldering Difficulty)
**Board size target:** 40mm × 30mm (fits inside most mobile housings)
The schematic is designed around the MDBT50Q module, but the firmware
works on any nRF52840 board. Here are your options from easiest to hardest:
**Layer stackup:** 2-layer (plenty for this design)
| Module | ~Cost | USB-C | Bootloader | Soldering |
|-----------------------------|-------|-------|------------|------------------|
| **Seeed XIAO nRF52840** | $10 | Yes | UF2 preloaded | **Through-hole headers** — solder like any DIP. Has castellated pads too. |
| **Adafruit Feather nRF52840** | $25 | Yes | UF2 preloaded | Through-hole headers |
| **SparkFun Pro nRF52840** | $20 | Yes | UF2 preloaded | Through-hole headers |
| **Ebyte E73-2G4M08S1C** | $3 | No | None | Castellated edge pads — hand-solderable with regular iron |
| **MDBT50Q-P1MV2 (Raytac)** | $6 | No | None | SMD pads — needs reflow or hot air |
**Key layout rules:**
1. Place C2 and C3 as close to U1 pins 4/6 as physically possible
2. Place C5 directly adjacent to U2 pin 8
3. Keep SPI traces short and parallel-routed
4. Audio RC filter (R2, C7) close to U3 input
5. Wide traces for motor path (VCC → motor → Q1 drain): 0.5mm minimum
6. Ground pour on bottom layer
7. Thermal relief on U3 pad (pin 6) connected to ground pour
8. Keep battery/motor power traces away from audio section
**Recommended for beginners: Seeed XIAO nRF52840.** It plugs into standard
2.54mm headers like an Arduino Nano. USB-C and UF2 bootloader are built in,
so you never need a programmer. Just drag-and-drop firmware and audio files.
**Recommended trace widths:**
- Power (VCC, GND, motor): 0.5mm+
- Signal (SPI, buttons): 0.25mm
- Audio: 0.25mm (keep short, away from digital noise)
**If using a XIAO or Feather:** you can skip the TPS61220 boost converter,
MCP1700 LDO, USB-C connector, and CC resistors from the BOM. The dev board
handles all of that. Feed the boost converter's 3.3V output to the board's
3V3 pin. Total BOM drops to ~$15 including the XIAO.
**If using MDBT50Q or E73:** you need the full schematic as designed. The
E73 is the budget option — castellated pads are solderable with flux, a
fine tip, and patience. Pre-tin both the pads and the board footprint,
then tack one corner and reflow the rest.
## Programming
## User Experience
### Bootloader
Burn the Arduino bootloader for "ATmega328P 8MHz internal" using the ISP
header (J4) and an Arduino-as-ISP or USBasp programmer.
### Upload Audio (USB)
1. Plug USB-C cable into board
2. Board appears as USB mass storage drive
3. Drag .wav or .mp3 files onto drive
4. Eject and unplug — music ready to play
In Arduino IDE:
- Board: "ATmega328P"
- Clock: "Internal 8 MHz"
- BOD: "BOD 1.8V" (for low-voltage operation)
### Upload Audio (Bluetooth)
1. Open Web Bluetooth page on phone (no app install)
2. Connect to "BabyMobile" device
3. Select audio files → transfer
4. ~15 min for 30 minutes of audio at BLE speeds
### Loading audio onto W25Q128
Use the serial header (J5) with a USB-serial adapter. The firmware
includes a serial protocol to receive audio data and write it to flash.
Alternatively, program the flash chip directly with a CH341A SPI
programmer before soldering (~$3 on Amazon).
### Update Firmware
1. Double-tap reset button (or hold BTN1+BTN2 on power-on)
2. Board appears as UF2 drive
3. Drag .uf2 firmware file onto drive
4. Board reboots with new firmware
### Audio format
Raw unsigned 8-bit PCM, 8000 Hz sample rate, mono.
Convert with ffmpeg:
```
ffmpeg -i input.mp3 -ar 8000 -ac 1 -f u8 -acodec pcm_u8 output.raw
```
At 8KB/s, the 16MB flash holds about **34 minutes** of audio.
## Possible Enhancements
- **Volume control:** Use a digital pot on amp input, or just use
the PWM duty cycle adjustment in software
- **Battery voltage monitor:** Read VCC via bandgap reference trick
on ATmega328P (no extra pin needed) — blink LED when low
- **Touch sensing:** Replace buttons with copper pads; use ADC-based
capacitive sensing on PC0/PC1 (ATmega328P supports this with
careful firmware)
- **More audio:** Swap W25Q128 (16MB) for W25Q256 (32MB) → 68 min
- **Bluetooth:** Add HC-05 module on serial pins for wireless audio
upload from phone (adds ~30mA when active)
### No-Programmer Setup
The MDBT50Q ships with the nRF52840 bootloader ROM. Flash the Adafruit
nRF52 UF2 bootloader once via SWD (J4 header) using any $3 CMSIS-DAP
probe, J-Link, or even a Raspberry Pi. After that, all future updates
are drag-and-drop over USB. The SWD header is only needed once.