Files
baby-mobile/DESIGN.md
T
2026-07-04 03:28:11 -07:00

279 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Baby Mobile Audio Board — 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.
**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
```
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)
```
**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
### 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
```
**J5 — Serial (1×3, for bootloader programming / debug):**
```
GND 1
TXD 2 (MCU TX out)
RXD 3 (MCU RX in)
```
## 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** | |
## Power Analysis
### Current draw by state:
| 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 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.
## PCB Layout Guidelines
**Board size target:** 40mm × 30mm (fits inside most mobile housings)
**Layer stackup:** 2-layer (plenty for this design)
**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 trace widths:**
- Power (VCC, GND, motor): 0.5mm+
- Signal (SPI, buttons): 0.25mm
- Audio: 0.25mm (keep short, away from digital noise)
## Programming
### Bootloader
Burn the Arduino bootloader for "ATmega328P 8MHz internal" using the ISP
header (J4) and an Arduino-as-ISP or USBasp programmer.
In Arduino IDE:
- Board: "ATmega328P"
- Clock: "Internal 8 MHz"
- BOD: "BOD 1.8V" (for low-voltage operation)
### 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).
### 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)