Initial commit

This commit is contained in:
zyphlar
2026-07-04 03:28:11 -07:00
commit 57df0a1293
6 changed files with 2453 additions and 0 deletions
+278
View File
@@ -0,0 +1,278 @@
# 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)
+61
View File
@@ -0,0 +1,61 @@
{
"meta": {
"filename": "baby-mobile.kicad_pro",
"version": 1
},
"board": {
"design_settings": {
"defaults": {
"board_outline_line_width": 0.1,
"copper_line_width": 0.2,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"other_line_width": 0.15
},
"rules": {
"min_copper_edge_clearance": 0.3,
"min_hole_clearance": 0.25,
"min_track_width": 0.2,
"min_via_annular_width": 0.13,
"min_via_diameter": 0.6,
"solder_mask_to_copper_clearance": 0
}
},
"layer_presets": []
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.3,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
]
},
"schematic": {
"legacy_lib_dir": "",
"legacy_lib_list": []
},
"sheets": [
[
"",
""
]
]
}
+628
View File
@@ -0,0 +1,628 @@
(kicad_sch (version 20230121) (generator "custom_generator")
(uuid "391da8c7-7217-4ed9-a918-95efab6bbe33")
(paper "A3")
(title_block
(title "Baby Mobile Audio Board")
(date "2026-03-08")
(rev "1.0")
(comment 1 "ATmega328P + W25Q128 + PAM8302A")
(comment 2 "2xAA Battery Powered - Long Runtime Design")
(comment 3 "8-ohm 0.25W Speaker, DC Motor, 8 Buttons")
(comment 4 "Arduino Compatible (8MHz Internal Osc)")
)
(lib_symbols
(symbol "baby_mobile:ATmega328P-AU" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 0 26 0) (effects (font (size 1.27 1.27))))
(property "Value" "ATmega328P-AU" (at 0 24 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_QFP:TQFP-32_7x7mm_P0.8mm" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:ATmega328P-AU_0_1"
(rectangle (start -17 23) (end 17 -23) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:ATmega328P-AU_1_1"
(pin power_in line (at -7 -26 90) (length 3) (name "GND" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin power_in line (at -5 -26 90) (length 3) (name "GND2" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin power_in line (at -3 26 270) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin power_in line (at -1 26 270) (length 3) (name "AVCC" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
(pin passive line (at 1 26 270) (length 3) (name "AREF" (effects (font (size 1 1)))) (number "21" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 15 0) (length 3) (name "PD0/RXD" (effects (font (size 1 1)))) (number "30" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 12.5 0) (length 3) (name "PD1/TXD" (effects (font (size 1 1)))) (number "31" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 10 0) (length 3) (name "PD2/INT0" (effects (font (size 1 1)))) (number "32" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 7.5 0) (length 3) (name "PD3/INT1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 5 0) (length 3) (name "PD4" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 2.5 0) (length 3) (name "PD5/OC0B" (effects (font (size 1 1)))) (number "9" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 0 0) (length 3) (name "PD6/OC0A" (effects (font (size 1 1)))) (number "10" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 -2.5 0) (length 3) (name "PD7" (effects (font (size 1 1)))) (number "11" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 15 180) (length 3) (name "PB0" (effects (font (size 1 1)))) (number "12" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 12.5 180) (length 3) (name "PB1/OC1A" (effects (font (size 1 1)))) (number "13" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 10 180) (length 3) (name "PB2/SS" (effects (font (size 1 1)))) (number "14" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 7.5 180) (length 3) (name "PB3/MOSI" (effects (font (size 1 1)))) (number "15" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 5 180) (length 3) (name "PB4/MISO" (effects (font (size 1 1)))) (number "16" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 2.5 180) (length 3) (name "PB5/SCK" (effects (font (size 1 1)))) (number "17" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -2.5 180) (length 3) (name "PC0/ADC0" (effects (font (size 1 1)))) (number "23" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -5 180) (length 3) (name "PC1/ADC1" (effects (font (size 1 1)))) (number "24" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -7.5 180) (length 3) (name "PC2/ADC2" (effects (font (size 1 1)))) (number "25" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -10 180) (length 3) (name "PC3/ADC3" (effects (font (size 1 1)))) (number "26" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -12.5 180) (length 3) (name "PC4/SDA" (effects (font (size 1 1)))) (number "27" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -15 180) (length 3) (name "PC5/SCL" (effects (font (size 1 1)))) (number "28" (effects (font (size 1 1)))))
(pin input line (at -20 -10 0) (length 3) (name "~{RESET}" (effects (font (size 1 1)))) (number "29" (effects (font (size 1 1)))))
(pin input line (at -20 -15 0) (length 3) (name "XTAL1" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
(pin output line (at -20 -17.5 0) (length 3) (name "XTAL2" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:W25Q128" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 0 9 0) (effects (font (size 1.27 1.27))))
(property "Value" "W25Q128JVSIQ" (at 0 7 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:W25Q128_0_1"
(rectangle (start -7 6) (end 7 -6) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:W25Q128_1_1"
(pin input line (at -10 4 0) (length 3) (name "~{CS}" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin output line (at -10 1.5 0) (length 3) (name "DO/IO1" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin input line (at -10 -1.5 0) (length 3) (name "~{WP}/IO2" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin power_in line (at -10 -4 0) (length 3) (name "GND" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin input line (at 10 -4 180) (length 3) (name "DI/IO0" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin input line (at 10 -1.5 180) (length 3) (name "CLK" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
(pin input line (at 10 1.5 180) (length 3) (name "~{HOLD}/IO3" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
(pin power_in line (at 10 4 180) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:PAM8302A" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 0 11 0) (effects (font (size 1.27 1.27))))
(property "Value" "PAM8302A" (at 0 9 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:PAM8302A_0_1"
(rectangle (start -8 8) (end 8 -8) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:PAM8302A_1_1"
(pin input line (at -11 5 0) (length 3) (name "~{SD}" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin power_in line (at -11 2 0) (length 3) (name "VDD" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin power_in line (at -11 -1 0) (length 3) (name "GND" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin input line (at -11 -4 0) (length 3) (name "A+" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin input line (at -11 -7 0) (length 3) (name "A-" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin output line (at 11 2 180) (length 3) (name "VO+" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
(pin output line (at 11 -1 180) (length 3) (name "VO-" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
(pin power_in line (at 11 -4 180) (length 3) (name "PAD" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:NMOS" (in_bom yes) (on_board yes)
(property "Reference" "Q" (at 3 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "AO3400A" (at 5 -2.5 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_TO_SOT_SMD:SOT-23" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:NMOS_0_1"
(polyline (pts (xy -0.5 0.5) (xy -0.5 -0.5)) (stroke (width 0.33) (type default)) (fill (type none)))
(polyline (pts (xy -0.5 -2) (xy -0.5 2)) (stroke (width 0.254) (type default)) (fill (type none)))
(polyline (pts (xy 0 -2.5) (xy 0 -1.5)) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy 0 1.5) (xy 0 2.5)) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy 0 0) (xy -1.5 0)) (stroke (width 0) (type default)) (fill (type none)))
)
(symbol "baby_mobile:NMOS_1_1"
(pin passive line (at 0 5 270) (length 2.5) (name "D" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin input line (at -4 0 0) (length 2.5) (name "G" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -5 90) (length 2.5) (name "S" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Capacitor" (in_bom yes) (on_board yes)
(property "Reference" "C" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "C" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Capacitor_0_1"
(polyline (pts (xy -1.5 1) (xy 1.5 1)) (stroke (width 0.4) (type default)) (fill (type none)))
(polyline (pts (xy -1.5 -1) (xy 1.5 -1)) (stroke (width 0.4) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Capacitor_1_1"
(pin passive line (at 0 3.5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -3.5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Resistor" (in_bom yes) (on_board yes)
(property "Reference" "R" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "R" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Resistor_0_1"
(rectangle (start -1 2.5) (end 1 -2.5) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Resistor_1_1"
(pin passive line (at 0 5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Diode_Schottky" (in_bom yes) (on_board yes)
(property "Reference" "D" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "SS14" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Diode_SMD:D_SMA" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Diode_Schottky_0_1"
(polyline (pts (xy -1.5 1) (xy 0 -1) (xy 1.5 1) (xy -1.5 1)) (stroke (width 0.2) (type default)) (fill (type none)))
(polyline (pts (xy -1.5 -1) (xy 1.5 -1)) (stroke (width 0.3) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Diode_Schottky_1_1"
(pin passive line (at 0 3.5 270) (length 2.5) (name "A" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -3.5 90) (length 2.5) (name "K" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Inductor" (in_bom yes) (on_board yes)
(property "Reference" "L" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "L" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Inductor_0_1"
(arc (start 0 2.5) (mid 0.75 1.875) (end 0 1.25) (stroke (width 0.254) (type default)) (fill (type none)))
(arc (start 0 1.25) (mid 0.75 0.625) (end 0 0) (stroke (width 0.254) (type default)) (fill (type none)))
(arc (start 0 0) (mid 0.75 -0.625) (end 0 -1.25) (stroke (width 0.254) (type default)) (fill (type none)))
(arc (start 0 -1.25) (mid 0.75 -1.875) (end 0 -2.5) (stroke (width 0.254) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Inductor_1_1"
(pin passive line (at 0 5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Switch" (in_bom yes) (on_board yes)
(property "Reference" "SW" (at 0 3 0) (effects (font (size 1.27 1.27))))
(property "Value" "SW" (at 0 -3 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Switch_0_1"
(circle (center -2 0) (radius 0.4) (stroke (width 0) (type default)) (fill (type none)))
(circle (center 2 0) (radius 0.4) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy -1.6 0) (xy 1.6 1)) (stroke (width 0.254) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Switch_1_1"
(pin passive line (at -5 0 0) (length 3) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 5 0 180) (length 3) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Conn_2Pin" (in_bom yes) (on_board yes)
(property "Reference" "J" (at 0 4 0) (effects (font (size 1.27 1.27))))
(property "Value" "Conn_2" (at 0 -4 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Conn_2Pin_0_1"
(rectangle (start -2 3) (end 2 -3) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Conn_2Pin_1_1"
(pin passive line (at -5 1.5 0) (length 3) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at -5 -1.5 0) (length 3) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Conn_3Pin" (in_bom yes) (on_board yes)
(property "Reference" "J" (at 0 5.5 0) (effects (font (size 1.27 1.27))))
(property "Value" "Conn_3" (at 0 -5.5 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Conn_3Pin_0_1"
(rectangle (start -2 4) (end 2 -4) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Conn_3Pin_1_1"
(pin passive line (at -5 2.5 0) (length 3) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at -5 0 0) (length 3) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin passive line (at -5 -2.5 0) (length 3) (name "3" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:Conn_6Pin" (in_bom yes) (on_board yes)
(property "Reference" "J" (at 0 9 0) (effects (font (size 1.27 1.27))))
(property "Value" "ISP_2x3" (at 0 -9 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Conn_6Pin_0_1"
(rectangle (start -3 8) (end 3 -8) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Conn_6Pin_1_1"
(pin passive line (at -6 5 0) (length 3) (name "MISO" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 6 5 180) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin passive line (at -6 0 0) (length 3) (name "SCK" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin passive line (at 6 0 180) (length 3) (name "MOSI" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin passive line (at -6 -5 0) (length 3) (name "~{RST}" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin passive line (at 6 -5 180) (length 3) (name "GND" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:LED" (in_bom yes) (on_board yes)
(property "Reference" "D" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "LED" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:LED_0_1"
(polyline (pts (xy -1.5 1) (xy 0 -1) (xy 1.5 1) (xy -1.5 1)) (stroke (width 0.2) (type default)) (fill (type none)))
(polyline (pts (xy -1.5 -1) (xy 1.5 -1)) (stroke (width 0.3) (type default)) (fill (type none)))
)
(symbol "baby_mobile:LED_1_1"
(pin passive line (at 0 3.5 270) (length 2.5) (name "A" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -3.5 90) (length 2.5) (name "K" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)
(symbol "baby_mobile:PWR_FLAG" (power) (in_bom no) (on_board no)
(property "Reference" "#FLG" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Value" "PWR_FLAG" (at 0 3 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:PWR_FLAG_0_1"
(polyline (pts (xy 0 0) (xy 0 1.5)) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy -1 1.5) (xy 0 3) (xy 1 1.5) (xy -1 1.5)) (stroke (width 0) (type default)) (fill (type none)))
)
(symbol "baby_mobile:PWR_FLAG_1_1"
(pin power_out line (at 0 0 90) (length 0) (name "pwr" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
)
)
)
(symbol (lib_id "baby_mobile:Conn_2Pin") (at 25 35 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "507bdf9f-e516-4e7b-8916-4c751f1b84e2")
(property "Reference" "J1" (at 25 32 0) (effects (font (size 1.27 1.27))))
(property "Value" "BATTERY" (at 25 38 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal" (at 25 35 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 25 35 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 55 35 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "c40c113d-1405-44ce-a1f0-58bef96d538e")
(property "Reference" "C1" (at 55 32 0) (effects (font (size 1.27 1.27))))
(property "Value" "100uF" (at 55 38 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_THT:CP_Radial_D5.0mm_P2.00mm" (at 55 35 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 55 35 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 100 30 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "399c833b-8964-48dd-b796-3cb9e41ff4d0")
(property "Reference" "C2" (at 100 27 0) (effects (font (size 1.27 1.27))))
(property "Value" "100nF" (at 100 33 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 100 30 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 100 30 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 115 30 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "da1e40d4-4aae-4393-8ac3-273b705cd8d2")
(property "Reference" "C3" (at 115 27 0) (effects (font (size 1.27 1.27))))
(property "Value" "100nF" (at 115 33 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 115 30 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 115 30 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 40 25 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "d5f6e2c8-1f5a-4b82-abbb-cb3f97490465")
(property "Reference" "SW9" (at 40 22 0) (effects (font (size 1.27 1.27))))
(property "Value" "PWR" (at 40 28 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_THT:SW_SPDT_CK-JS102011SAQN" (at 40 25 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 40 25 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:ATmega328P-AU") (at 130 90 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "45044e9c-b35e-4aac-8523-60d85a6a6df8")
(property "Reference" "U1" (at 130 87 0) (effects (font (size 1.27 1.27))))
(property "Value" "ATmega328P-AU" (at 130 93 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_QFP:TQFP-32_7x7mm_P0.8mm" (at 130 90 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 130 90 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Resistor") (at 95 55 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "94af8e3c-b3da-4777-8e91-865edfc76cdf")
(property "Reference" "R1" (at 95 52 0) (effects (font (size 1.27 1.27))))
(property "Value" "10K" (at 95 58 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Resistor_SMD:R_0805_2012Metric" (at 95 55 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 95 55 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 95 65 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "65aa9490-ed2d-43cc-bec1-e1f276413ef2")
(property "Reference" "C4" (at 95 62 0) (effects (font (size 1.27 1.27))))
(property "Value" "100nF" (at 95 68 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 95 65 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 95 65 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:W25Q128") (at 230 60 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "b5dc96e3-fbe8-4c5b-b1a7-82f3926f520f")
(property "Reference" "U2" (at 230 57 0) (effects (font (size 1.27 1.27))))
(property "Value" "W25Q128JVSIQ" (at 230 63 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 230 60 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 230 60 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 255 50 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "63c2829e-7fd6-485b-82f8-94ec57fe959b")
(property "Reference" "C5" (at 255 47 0) (effects (font (size 1.27 1.27))))
(property "Value" "100nF" (at 255 53 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 255 50 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 255 50 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:PAM8302A") (at 230 140 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "dc5d606b-6f45-42d0-82bb-b0db3a981adf")
(property "Reference" "U3" (at 230 137 0) (effects (font (size 1.27 1.27))))
(property "Value" "PAM8302A" (at 230 143 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 230 140 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 230 140 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 200 135 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "85967935-a5f6-4065-9ae7-b181b28d0674")
(property "Reference" "C6" (at 200 132 0) (effects (font (size 1.27 1.27))))
(property "Value" "1uF" (at 200 138 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 200 135 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 200 135 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Resistor") (at 185 135 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "2d985162-8660-47cc-9982-688a70e5af66")
(property "Reference" "R2" (at 185 132 0) (effects (font (size 1.27 1.27))))
(property "Value" "4.7K" (at 185 138 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Resistor_SMD:R_0805_2012Metric" (at 185 135 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 185 135 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 192 145 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "136fb80a-2afa-44da-887f-5760ca0c20ee")
(property "Reference" "C7" (at 192 142 0) (effects (font (size 1.27 1.27))))
(property "Value" "10nF" (at 192 148 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 192 145 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 192 145 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Capacitor") (at 255 130 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "47509d19-833e-498c-8131-5ad8bc4f9b89")
(property "Reference" "C8" (at 255 127 0) (effects (font (size 1.27 1.27))))
(property "Value" "10uF" (at 255 133 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 255 130 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 255 130 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Conn_2Pin") (at 265 145 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "d84e0dcb-8d75-4de2-b76d-db6013404837")
(property "Reference" "J2" (at 265 142 0) (effects (font (size 1.27 1.27))))
(property "Value" "SPEAKER" (at 265 148 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal" (at 265 145 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 265 145 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Resistor") (at 210 150 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "c73fafc0-ebff-46a7-b8a6-93719e4ba183")
(property "Reference" "R3" (at 210 147 0) (effects (font (size 1.27 1.27))))
(property "Value" "100K" (at 210 153 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Resistor_SMD:R_0805_2012Metric" (at 210 150 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 210 150 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:NMOS") (at 140 190 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "f44467d7-d2d3-4056-b74e-d8e247ada0c0")
(property "Reference" "Q1" (at 140 187 0) (effects (font (size 1.27 1.27))))
(property "Value" "AO3400A" (at 140 193 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_TO_SOT_SMD:SOT-23" (at 140 190 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 140 190 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Resistor") (at 120 190 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "d2efdc5b-60e1-40ac-b81e-b671ae9e2524")
(property "Reference" "R4" (at 120 187 0) (effects (font (size 1.27 1.27))))
(property "Value" "100R" (at 120 193 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Resistor_SMD:R_0805_2012Metric" (at 120 190 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 120 190 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Resistor") (at 130 200 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "fa36bf14-db5f-4c2d-aa38-322990e1a079")
(property "Reference" "R5" (at 130 197 0) (effects (font (size 1.27 1.27))))
(property "Value" "100K" (at 130 203 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Resistor_SMD:R_0805_2012Metric" (at 130 200 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 130 200 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Diode_Schottky") (at 155 180 90) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "a86dfdae-f4a0-4d77-9501-410a463b4929")
(property "Reference" "D1" (at 155 177 0) (effects (font (size 1.27 1.27))))
(property "Value" "SS14" (at 155 183 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Diode_SMD:D_SMA" (at 155 180 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 155 180 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Conn_2Pin") (at 160 170 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "e215997e-3139-4cb2-903e-f84cabd0b1af")
(property "Reference" "J3" (at 160 167 0) (effects (font (size 1.27 1.27))))
(property "Value" "MOTOR" (at 160 173 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal" (at 160 170 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 160 170 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 75 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "7f01bcc9-33e2-4fec-9df9-ee78a82cb270")
(property "Reference" "SW1" (at 30 72 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN1" (at 30 78 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 75 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 75 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 90 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "bfa6ab0a-df05-4515-8d1f-72749aba4a20")
(property "Reference" "SW2" (at 30 87 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN2" (at 30 93 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 90 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 90 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 105 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "1b1fc388-ea85-4a5c-bee5-7e769d4b59eb")
(property "Reference" "SW3" (at 30 102 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN3" (at 30 108 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 105 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 105 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 120 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "bc5829d7-ff9c-4c0a-834e-e85b6795fb22")
(property "Reference" "SW4" (at 30 117 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN4" (at 30 123 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 120 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 120 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 135 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "182c3254-622e-4fb9-a960-ce401433f071")
(property "Reference" "SW5" (at 30 132 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN5" (at 30 138 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 135 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 135 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 150 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "86d0f6ec-c847-470d-9bbd-f4bed67fc489")
(property "Reference" "SW6" (at 30 147 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN6" (at 30 153 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 150 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 150 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 165 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "cf404039-6a3b-4246-816c-1be4597bebb0")
(property "Reference" "SW7" (at 30 162 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN7" (at 30 168 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 165 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 165 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Switch") (at 30 180 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "80093bcc-1f31-4639-8591-9f1cfa8e1721")
(property "Reference" "SW8" (at 30 177 0) (effects (font (size 1.27 1.27))))
(property "Value" "BTN8" (at 30 183 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Button_Switch_SMD:SW_SPST_TL3342" (at 30 180 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 180 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:LED") (at 85 190 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "e0fae357-d301-48e8-a964-a410bbb95bc6")
(property "Reference" "D2" (at 85 187 0) (effects (font (size 1.27 1.27))))
(property "Value" "LED" (at 85 193 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "LED_SMD:LED_0805_2012Metric" (at 85 190 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 85 190 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Resistor") (at 75 190 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "898d416a-1773-4205-a6ee-11a04534c363")
(property "Reference" "R6" (at 75 187 0) (effects (font (size 1.27 1.27))))
(property "Value" "1K" (at 75 193 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Resistor_SMD:R_0805_2012Metric" (at 75 190 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 75 190 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Conn_6Pin") (at 250 200 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "049eaee8-ace8-4be4-8664-1f02e3371a2e")
(property "Reference" "J4" (at 250 197 0) (effects (font (size 1.27 1.27))))
(property "Value" "ISP" (at 250 203 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Vertical" (at 250 200 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 250 200 0) (effects (font (size 1.27 1.27)) hide))
)
(symbol (lib_id "baby_mobile:Conn_3Pin") (at 30 210 0) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "6e205180-7724-405f-af1d-daa148abacc5")
(property "Reference" "J5" (at 30 207 0) (effects (font (size 1.27 1.27))))
(property "Value" "SERIAL" (at 30 213 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical" (at 30 210 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 30 210 0) (effects (font (size 1.27 1.27)) hide))
)
(label "VCC" (at 55 25 0) (effects (font (size 1.27 1.27))) (uuid "38d66dc5-0dfe-483d-b51e-2d579b758f33"))
(label "VCC" (at 100 25 0) (effects (font (size 1.27 1.27))) (uuid "56a7d1c9-361a-42f8-9d77-7cc94df15408"))
(label "VCC" (at 115 25 0) (effects (font (size 1.27 1.27))) (uuid "78fe02e6-db3b-4885-8a22-d0d962a13bd1"))
(label "VCC" (at 130 50 0) (effects (font (size 1.27 1.27))) (uuid "8dcc821a-8787-4ba3-aa6e-a13c6942949f"))
(label "VCC" (at 255 45 0) (effects (font (size 1.27 1.27))) (uuid "dc2b0694-b4c5-4e2f-a970-c86f81c186ed"))
(label "VCC" (at 255 125 0) (effects (font (size 1.27 1.27))) (uuid "1e7be094-6bef-468b-b0d8-960ef3ee34da"))
(label "VCC" (at 95 50 0) (effects (font (size 1.27 1.27))) (uuid "813d1f5e-21ee-46ad-90c9-d2746d49c93a"))
(label "VCC" (at 160 165 0) (effects (font (size 1.27 1.27))) (uuid "3cc3028b-6ef6-4394-b379-d6fddafb61c7"))
(label "GND" (at 55 45 0) (effects (font (size 1.27 1.27))) (uuid "e58d5e07-b8ad-408f-a3e2-23cba82e951c"))
(label "GND" (at 100 40 0) (effects (font (size 1.27 1.27))) (uuid "427f2a2e-c2c9-4e85-8dc5-6ac456496582"))
(label "GND" (at 115 40 0) (effects (font (size 1.27 1.27))) (uuid "c40ed645-0a77-432f-98e1-3022a591a6bf"))
(label "GND" (at 130 130 0) (effects (font (size 1.27 1.27))) (uuid "13423fca-5ee9-42db-97d7-8d291085433a"))
(label "GND" (at 255 70 0) (effects (font (size 1.27 1.27))) (uuid "f1680748-3b5e-4b5c-9231-f5b123334267"))
(label "GND" (at 255 155 0) (effects (font (size 1.27 1.27))) (uuid "3f2ad3e8-cab3-4661-8c6d-27a5c37248bd"))
(label "GND" (at 95 70 0) (effects (font (size 1.27 1.27))) (uuid "fdb07bc1-2a3f-4bfa-8e0d-44095ddb4eed"))
(label "GND" (at 140 205 0) (effects (font (size 1.27 1.27))) (uuid "a2e87db6-701b-4ee4-9680-ce3e641a1ce3"))
(label "GND" (at 192 155 0) (effects (font (size 1.27 1.27))) (uuid "2f5d05c3-2b85-442d-82e1-f9b8f73b99ae"))
(label "SPI_MOSI" (at 165 85 0) (effects (font (size 1.27 1.27))) (uuid "3c4184cd-6302-4f79-ad70-499253e6926a"))
(label "SPI_MOSI" (at 215 55 0) (effects (font (size 1.27 1.27))) (uuid "f6281dca-e340-4987-a951-2f82a1714181"))
(label "SPI_MISO" (at 165 90 0) (effects (font (size 1.27 1.27))) (uuid "15344a55-c345-4d4f-bc69-d1123a2c9edf"))
(label "SPI_MISO" (at 215 60 0) (effects (font (size 1.27 1.27))) (uuid "979ceaf3-85e4-449e-957c-41ef8f11c043"))
(label "SPI_SCK" (at 165 95 0) (effects (font (size 1.27 1.27))) (uuid "4382cc5e-6bd2-4b3c-a286-89e68a9bfe44"))
(label "SPI_SCK" (at 215 65 0) (effects (font (size 1.27 1.27))) (uuid "5a6dfb3a-06a4-49ca-aad3-f7193a293ce3"))
(label "FLASH_CS" (at 165 80 0) (effects (font (size 1.27 1.27))) (uuid "c791cf26-bc22-410d-aaea-c2eda0b338cd"))
(label "FLASH_CS" (at 215 50 0) (effects (font (size 1.27 1.27))) (uuid "ef59be0a-fc41-456f-947c-1692d138b101"))
(label "AUDIO_PWM" (at 165 100 0) (effects (font (size 1.27 1.27))) (uuid "29036b7e-9203-47e8-9d64-1f906a93ef48"))
(label "AUDIO_PWM" (at 175 135 0) (effects (font (size 1.27 1.27))) (uuid "44583660-84d0-446b-a09f-9b7c62d110a6"))
(label "AMP_SD" (at 165 105 0) (effects (font (size 1.27 1.27))) (uuid "52045a7c-f447-4d0c-a9eb-850e34f11964"))
(label "AMP_SD" (at 210 145 0) (effects (font (size 1.27 1.27))) (uuid "1bcd7182-a352-4545-afe0-2f863d13594c"))
(label "MOTOR_PWM" (at 165 110 0) (effects (font (size 1.27 1.27))) (uuid "6c11ecd3-674e-4579-b26f-fa7cc592b2ef"))
(label "MOTOR_PWM" (at 110 190 0) (effects (font (size 1.27 1.27))) (uuid "18a66086-7b01-4c86-904c-f721fc2edce7"))
(label "BTN1" (at 45 75 0) (effects (font (size 1.27 1.27))) (uuid "92101bd4-f77a-410a-ab6a-0e3d3d66bc02"))
(label "BTN1" (at 95 85 0) (effects (font (size 1.27 1.27))) (uuid "28383130-7933-45e2-bfed-0dd34ff8e876"))
(label "BTN2" (at 45 90 0) (effects (font (size 1.27 1.27))) (uuid "a2f3390b-61b3-4812-ada2-1f4246e4f0cb"))
(label "BTN2" (at 95 90 0) (effects (font (size 1.27 1.27))) (uuid "42b61c7b-b336-4758-b778-9b4ea4905074"))
(label "BTN3" (at 45 105 0) (effects (font (size 1.27 1.27))) (uuid "fe284da5-c9e9-48e5-a293-7bf5f6aa33c9"))
(label "BTN3" (at 95 95 0) (effects (font (size 1.27 1.27))) (uuid "46e2de5c-c19d-4699-8367-e91303e840d5"))
(label "BTN4" (at 45 120 0) (effects (font (size 1.27 1.27))) (uuid "1d9dff94-06c9-49c6-b8e0-66657dd4245a"))
(label "BTN4" (at 95 100 0) (effects (font (size 1.27 1.27))) (uuid "e53835b8-f6b2-40f7-9245-d0d67614ec45"))
(label "BTN5" (at 45 135 0) (effects (font (size 1.27 1.27))) (uuid "4322ba20-38b0-41cf-a3c4-8bf56908ce34"))
(label "BTN5" (at 95 105 0) (effects (font (size 1.27 1.27))) (uuid "a04ee831-82c5-48b5-85c1-9d7fb0a24e33"))
(label "BTN6" (at 45 150 0) (effects (font (size 1.27 1.27))) (uuid "f64f61ae-b22d-41a4-a8ea-770610aebd0a"))
(label "BTN6" (at 95 110 0) (effects (font (size 1.27 1.27))) (uuid "53e714b1-e914-4811-8290-a921f65cac0e"))
(label "BTN7" (at 45 165 0) (effects (font (size 1.27 1.27))) (uuid "ce9834a4-38a7-4ea9-931a-2f9919142d64"))
(label "BTN7" (at 95 115 0) (effects (font (size 1.27 1.27))) (uuid "ddf7162b-f974-4375-885a-f25f1b1d9f57"))
(label "BTN8" (at 45 180 0) (effects (font (size 1.27 1.27))) (uuid "783f1d6f-fe69-492a-aab0-a98b6ec4338b"))
(label "BTN8" (at 95 120 0) (effects (font (size 1.27 1.27))) (uuid "12159112-3e7e-478d-96a5-db94ad9ab8a7"))
(label "SPI_MOSI" (at 240 195 0) (effects (font (size 1.27 1.27))) (uuid "28dda728-344b-400c-81bf-085acc569b01"))
(label "SPI_MISO" (at 240 200 0) (effects (font (size 1.27 1.27))) (uuid "3c258f39-bf6c-4f9d-977a-ca79c60e541a"))
(label "SPI_SCK" (at 240 205 0) (effects (font (size 1.27 1.27))) (uuid "a7f8b6cd-ff0a-4f2e-95e2-c09c7380629b"))
(label "RESET" (at 240 210 0) (effects (font (size 1.27 1.27))) (uuid "8ca5b857-1e0a-4810-9169-7a53d9010e7a"))
(label "RESET" (at 95 60 0) (effects (font (size 1.27 1.27))) (uuid "892fcef9-51e4-47c6-95bc-23995df359e9"))
(label "TXD" (at 40 210 0) (effects (font (size 1.27 1.27))) (uuid "b7910a83-f672-4c68-89d5-a06db98757ec"))
(label "RXD" (at 40 215 0) (effects (font (size 1.27 1.27))) (uuid "db832982-7322-4447-8344-cc95d72ebb5b"))
(label "TXD" (at 95 75 0) (effects (font (size 1.27 1.27))) (uuid "16f4752d-6c95-4710-ba23-85842ce78a5d"))
(label "RXD" (at 95 80 0) (effects (font (size 1.27 1.27))) (uuid "e269931d-0da1-4020-9952-ef4385a672f7"))
(label "LED_OUT" (at 65 190 0) (effects (font (size 1.27 1.27))) (uuid "3cf2c0dd-1277-46aa-bbfa-0319ce140d17"))
(label "LED_OUT" (at 95 125 0) (effects (font (size 1.27 1.27))) (uuid "231cf2b7-4593-4067-9607-34041c287667"))
(text "POWER: 2xAA (1.8V-3.3V) → Long runtime design" (at 15 15 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "950687a6-9e66-444c-bf5d-ac1f097f704f")
)
(text "MCU: ATmega328P-AU @ 8MHz internal osc, Arduino compatible" (at 15 18 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "f0cdcdd5-ae9a-4fac-b551-a92f7212bfdd")
)
(text "FLASH: W25Q128 16MB = ~33 min @ 8kHz 8-bit PCM" (at 200 15 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "dd881348-3856-462b-bd58-72d20fb89ca3")
)
(text "AMP: PAM8302A Class-D, 90% efficient, Vmin=2.0V" (at 200 18 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "7c7a46f6-98e4-4bf2-ae72-3ea92549e1d7")
)
(text "BUTTONS: Internal pull-ups, active LOW, PCINT wake-from-sleep" (at 15 220 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "eca71b6b-2202-4712-8241-1daf4bd4d0b3")
)
(text "MOTOR: AO3400A logic-level N-MOSFET, PWM speed control" (at 120 220 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "d36eb55c-80a1-4119-a4d5-92f77873caa6")
)
(text "ESTIMATED RUNTIME: Audio+Motor ~23h, Audio only ~200h, Sleep ~years" (at 15 225 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "79709c38-5874-4567-856d-15479cbe33cd")
)
)
+639
View File
@@ -0,0 +1,639 @@
/*
* Baby Mobile Audio Board — Firmware
*
* ATmega328P @ 8MHz internal oscillator
* Plays 8-bit unsigned PCM audio from W25Q128 SPI flash
* Controls DC motor via PWM
* 8 button inputs with sleep/wake support
*
* Board: ATmega328P, 8MHz internal, BOD 1.8V
*
* Pin mapping:
* PB1 (OC1A, pin 9) - Audio PWM output
* PB2 (pin 10) - Flash ~CS
* PB3 (pin 11) - SPI MOSI
* PB4 (pin 12) - SPI MISO
* PB5 (pin 13) - SPI SCK
* PD5 (OC0B, pin 5) - Motor PWM
* PC2 (A2) - Amp ~SD (HIGH=on, LOW=off)
* PC3 (A3) - Status LED
* PC0 (A0) - Button 1
* PC1 (A1) - Button 2
* PD2 (pin 2) - Button 3 (INT0)
* PD3 (pin 3) - Button 4 (INT1)
* PD4 (pin 4) - Button 5
* PD6 (pin 6) - Button 6
* PD7 (pin 7) - Button 7
* PB0 (pin 8) - Button 8
*/
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <SPI.h>
// ---- Pin definitions ----
#define PIN_AUDIO_PWM 9 // PB1 / OC1A
#define PIN_FLASH_CS 10 // PB2
#define PIN_MOTOR_PWM 5 // PD5 / OC0B
#define PIN_AMP_SD A2 // PC2 - amp shutdown (active HIGH = enabled)
#define PIN_LED A3 // PC3
#define PIN_BTN1 A0 // PC0
#define PIN_BTN2 A1 // PC1
#define PIN_BTN3 2 // PD2 / INT0
#define PIN_BTN4 3 // PD3 / INT1
#define PIN_BTN5 4 // PD4
#define PIN_BTN6 6 // PD6
#define PIN_BTN7 7 // PD7
#define PIN_BTN8 8 // PB0
// ---- Audio config ----
#define SAMPLE_RATE 8000 // Hz
#define FLASH_PAGE_SIZE 256
#define FLASH_SECTOR 4096
#define AUDIO_BUF_SIZE 256 // double buffer
// ---- Flash commands (W25Q128) ----
#define FLASH_CMD_READ 0x03
#define FLASH_CMD_FAST_READ 0x0B
#define FLASH_CMD_WRITE_EN 0x06
#define FLASH_CMD_PAGE_PROG 0x02
#define FLASH_CMD_SECT_ERASE 0x20
#define FLASH_CMD_CHIP_ERASE 0xC7
#define FLASH_CMD_READ_SR1 0x05
#define FLASH_CMD_JEDEC_ID 0x9F
#define FLASH_CMD_POWER_DOWN 0xB9
#define FLASH_CMD_WAKE 0xAB
// ---- Track table ----
// First 4KB (sector 0) of flash stores a track index:
// Offset 0x000: uint8_t num_tracks
// Offset 0x004: uint32_t track_start[32] (byte address in flash)
// Offset 0x084: uint32_t track_length[32] (length in bytes = samples)
// Audio data begins at sector 1 (address 0x1000).
#define TRACK_TABLE_ADDR 0x000000
#define AUDIO_START_ADDR 0x001000
#define MAX_TRACKS 32
// ---- State ----
volatile bool g_playing = false;
volatile bool g_motorOn = false;
volatile bool g_wakeFlag = false;
volatile uint8_t g_buttonPressed = 0;
uint8_t g_numTracks = 0;
uint32_t g_trackStart[MAX_TRACKS];
uint32_t g_trackLen[MAX_TRACKS];
uint8_t g_currentTrack = 0;
uint32_t g_playPos = 0; // current position in track (byte offset)
uint32_t g_playEnd = 0; // end position
uint8_t g_motorSpeed = 180; // PWM duty cycle (0-255)
// Double buffer for audio DMA-style playback
uint8_t g_audioBuf[2][AUDIO_BUF_SIZE];
volatile uint8_t g_activeBuf = 0; // buffer currently being played
volatile uint16_t g_bufPos = 0; // position in active buffer
volatile bool g_bufReady[2] = {false, false};
uint32_t g_nextReadAddr = 0;
// Auto-shutoff timer (milliseconds)
#define AUTO_OFF_MS (30UL * 60UL * 1000UL) // 30 minutes
unsigned long g_lastActivity = 0;
// ============================================================
// FLASH FUNCTIONS
// ============================================================
void flashSelect() { digitalWrite(PIN_FLASH_CS, LOW); }
void flashDeselect() { digitalWrite(PIN_FLASH_CS, HIGH); }
void flashWake() {
flashSelect();
SPI.transfer(FLASH_CMD_WAKE);
flashDeselect();
delayMicroseconds(5);
}
void flashSleep() {
flashSelect();
SPI.transfer(FLASH_CMD_POWER_DOWN);
flashDeselect();
}
uint32_t flashReadJEDEC() {
flashSelect();
SPI.transfer(FLASH_CMD_JEDEC_ID);
uint32_t id = (uint32_t)SPI.transfer(0) << 16;
id |= (uint32_t)SPI.transfer(0) << 8;
id |= SPI.transfer(0);
flashDeselect();
return id;
}
void flashReadBytes(uint32_t addr, uint8_t *buf, uint16_t len) {
flashSelect();
SPI.transfer(FLASH_CMD_READ);
SPI.transfer((addr >> 16) & 0xFF);
SPI.transfer((addr >> 8) & 0xFF);
SPI.transfer(addr & 0xFF);
for (uint16_t i = 0; i < len; i++) {
buf[i] = SPI.transfer(0);
}
flashDeselect();
}
void flashWaitBusy() {
flashSelect();
SPI.transfer(FLASH_CMD_READ_SR1);
while (SPI.transfer(0) & 0x01) { /* spin */ }
flashDeselect();
}
void flashWriteEnable() {
flashSelect();
SPI.transfer(FLASH_CMD_WRITE_EN);
flashDeselect();
}
void flashEraseSector(uint32_t addr) {
flashWriteEnable();
flashSelect();
SPI.transfer(FLASH_CMD_SECT_ERASE);
SPI.transfer((addr >> 16) & 0xFF);
SPI.transfer((addr >> 8) & 0xFF);
SPI.transfer(addr & 0xFF);
flashDeselect();
flashWaitBusy();
}
void flashPageProgram(uint32_t addr, const uint8_t *data, uint16_t len) {
flashWriteEnable();
flashSelect();
SPI.transfer(FLASH_CMD_PAGE_PROG);
SPI.transfer((addr >> 16) & 0xFF);
SPI.transfer((addr >> 8) & 0xFF);
SPI.transfer(addr & 0xFF);
for (uint16_t i = 0; i < len; i++) {
SPI.transfer(data[i]);
}
flashDeselect();
flashWaitBusy();
}
// ============================================================
// TRACK TABLE
// ============================================================
void loadTrackTable() {
uint8_t header[4];
flashReadBytes(TRACK_TABLE_ADDR, header, 4);
g_numTracks = header[0];
if (g_numTracks == 0 || g_numTracks == 0xFF || g_numTracks > MAX_TRACKS) {
g_numTracks = 0;
return;
}
// Read start addresses
uint8_t buf[4];
for (uint8_t i = 0; i < g_numTracks; i++) {
flashReadBytes(TRACK_TABLE_ADDR + 4 + i * 4, buf, 4);
g_trackStart[i] = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
((uint32_t)buf[2] << 8) | buf[3];
}
// Read lengths
for (uint8_t i = 0; i < g_numTracks; i++) {
flashReadBytes(TRACK_TABLE_ADDR + 4 + MAX_TRACKS * 4 + i * 4, buf, 4);
g_trackLen[i] = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
((uint32_t)buf[2] << 8) | buf[3];
}
}
// ============================================================
// AUDIO PLAYBACK (Timer1 ISR)
// ============================================================
void audioInit() {
// Timer1: Fast PWM, 8-bit, TOP=255
// At 8MHz clock: 8000000 / 256 = 31250 Hz PWM frequency
// Good enough — well above audible range
// We use a separate Timer1 compare match for sample rate:
// Timer1 in Fast PWM mode for output,
// Timer2 for sample rate interrupt
// Timer1: Fast PWM 8-bit on OC1A (PB1)
TCCR1A = _BV(COM1A1) | _BV(WGM10); // Clear on match, 8-bit fast PWM
TCCR1B = _BV(WGM12) | _BV(CS10); // No prescaler
OCR1A = 128; // 50% = silence for unsigned 8-bit
// Timer2: CTC mode for sample rate interrupt
// 8MHz / 8 / 125 = 8000 Hz
TCCR2A = _BV(WGM21); // CTC mode
TCCR2B = _BV(CS21); // prescaler /8
OCR2A = 124; // 8000000/8/(124+1) = 8000 Hz
TIMSK2 = 0; // interrupt disabled until playing
}
void audioStart(uint8_t trackNum) {
if (trackNum >= g_numTracks) return;
g_currentTrack = trackNum;
g_playPos = g_trackStart[trackNum];
g_playEnd = g_playPos + g_trackLen[trackNum];
// Pre-fill both buffers
flashReadBytes(g_playPos, g_audioBuf[0], AUDIO_BUF_SIZE);
g_playPos += AUDIO_BUF_SIZE;
if (g_playPos < g_playEnd) {
flashReadBytes(g_playPos, g_audioBuf[1], AUDIO_BUF_SIZE);
g_playPos += AUDIO_BUF_SIZE;
}
g_activeBuf = 0;
g_bufPos = 0;
g_bufReady[0] = true;
g_bufReady[1] = true;
g_nextReadAddr = g_playPos;
// Enable amp
digitalWrite(PIN_AMP_SD, HIGH);
delay(10); // amp startup time
g_playing = true;
TIMSK2 = _BV(OCIE2A); // enable sample rate interrupt
}
void audioStop() {
TIMSK2 = 0; // disable interrupt
OCR1A = 128; // silence
g_playing = false;
// Disable amp to save power
digitalWrite(PIN_AMP_SD, LOW);
}
// Timer2 Compare Match A — fires at 8000 Hz
ISR(TIMER2_COMPA_vect) {
if (!g_playing) return;
// Output sample to PWM
OCR1A = g_audioBuf[g_activeBuf][g_bufPos];
g_bufPos++;
if (g_bufPos >= AUDIO_BUF_SIZE) {
// Switch to other buffer
g_bufReady[g_activeBuf] = false; // mark for refill
g_activeBuf ^= 1;
g_bufPos = 0;
if (!g_bufReady[g_activeBuf]) {
// Buffer underrun — stop playback
g_playing = false;
OCR1A = 128;
}
}
}
// ============================================================
// MOTOR CONTROL
// ============================================================
void motorInit() {
pinMode(PIN_MOTOR_PWM, OUTPUT);
analogWrite(PIN_MOTOR_PWM, 0); // off
}
void motorStart(uint8_t speed) {
g_motorSpeed = speed;
g_motorOn = true;
analogWrite(PIN_MOTOR_PWM, speed);
}
void motorStop() {
g_motorOn = false;
analogWrite(PIN_MOTOR_PWM, 0);
}
// ============================================================
// BUTTONS
// ============================================================
const uint8_t BTN_PINS[] = {
PIN_BTN1, PIN_BTN2, PIN_BTN3, PIN_BTN4,
PIN_BTN5, PIN_BTN6, PIN_BTN7, PIN_BTN8
};
#define NUM_BUTTONS 8
void buttonsInit() {
for (uint8_t i = 0; i < NUM_BUTTONS; i++) {
pinMode(BTN_PINS[i], INPUT_PULLUP);
}
}
// Returns button number 1-8, or 0 if none pressed
uint8_t buttonRead() {
for (uint8_t i = 0; i < NUM_BUTTONS; i++) {
if (digitalRead(BTN_PINS[i]) == LOW) {
delay(20); // debounce
if (digitalRead(BTN_PINS[i]) == LOW) {
return i + 1;
}
}
}
return 0;
}
// ============================================================
// SLEEP / WAKE
// ============================================================
// Pin change interrupts for wake-from-sleep
ISR(PCINT0_vect) { g_wakeFlag = true; } // PB0 (BTN8)
ISR(PCINT1_vect) { g_wakeFlag = true; } // PC0, PC1 (BTN1, BTN2)
ISR(PCINT2_vect) { g_wakeFlag = true; } // PD2-7 (BTN3-BTN7)
ISR(INT0_vect) { g_wakeFlag = true; }
ISR(INT1_vect) { g_wakeFlag = true; }
void enterSleep() {
// Stop everything
audioStop();
motorStop();
flashSleep();
digitalWrite(PIN_LED, LOW);
// Enable pin change interrupts for wake
PCICR = _BV(PCIE0) | _BV(PCIE1) | _BV(PCIE2);
PCMSK0 = _BV(PCINT0); // PB0
PCMSK1 = _BV(PCINT8) | _BV(PCINT9); // PC0, PC1
PCMSK2 = _BV(PCINT18) | _BV(PCINT19) | _BV(PCINT20) |
_BV(PCINT22) | _BV(PCINT23); // PD2,3,4,6,7
// Also enable INT0/INT1 for PD2/PD3
EICRA = 0; // LOW level trigger
EIMSK = _BV(INT0) | _BV(INT1);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sei();
sleep_cpu();
// --- ZZZ --- wakes up here ---
sleep_disable();
// Disable wake interrupts
PCICR = 0;
EIMSK = 0;
// Wake flash
flashWake();
g_lastActivity = millis();
g_wakeFlag = false;
// Blink LED to indicate wake
digitalWrite(PIN_LED, HIGH);
delay(100);
digitalWrite(PIN_LED, LOW);
}
// ============================================================
// SERIAL FLASH PROGRAMMER
// ============================================================
// Simple protocol for uploading audio over serial:
// 'I' → respond with flash JEDEC ID
// 'E' → erase entire flash chip
// 'W' addr(3 bytes) len(2 bytes) data... → write page
// 'R' addr(3 bytes) len(2 bytes) → read bytes back
// 'T' → read track table
void handleSerial() {
if (!Serial.available()) return;
uint8_t cmd = Serial.read();
switch (cmd) {
case 'I': {
uint32_t id = flashReadJEDEC();
Serial.print("JEDEC: 0x");
Serial.println(id, HEX);
break;
}
case 'E': {
Serial.println("Erasing flash...");
flashWriteEnable();
flashSelect();
SPI.transfer(FLASH_CMD_CHIP_ERASE);
flashDeselect();
flashWaitBusy(); // takes ~40 seconds for 16MB
Serial.println("Done");
break;
}
case 'W': {
// Wait for address (3 bytes) and length (2 bytes)
while (Serial.available() < 5) {}
uint32_t addr = (uint32_t)Serial.read() << 16;
addr |= (uint32_t)Serial.read() << 8;
addr |= Serial.read();
uint16_t len = (uint16_t)Serial.read() << 8;
len |= Serial.read();
if (len > 256) len = 256;
uint8_t buf[256];
uint16_t received = 0;
while (received < len) {
if (Serial.available()) {
buf[received++] = Serial.read();
}
}
flashPageProgram(addr, buf, len);
Serial.print("OK ");
Serial.println(addr, HEX);
break;
}
case 'R': {
while (Serial.available() < 5) {}
uint32_t addr = (uint32_t)Serial.read() << 16;
addr |= (uint32_t)Serial.read() << 8;
addr |= Serial.read();
uint16_t len = (uint16_t)Serial.read() << 8;
len |= Serial.read();
uint8_t buf[256];
while (len > 0) {
uint16_t chunk = (len > 256) ? 256 : len;
flashReadBytes(addr, buf, chunk);
Serial.write(buf, chunk);
addr += chunk;
len -= chunk;
}
break;
}
}
}
// ============================================================
// MAIN
// ============================================================
void setup() {
// Disable unused peripherals for power saving
power_adc_disable(); // we don't use ADC
power_twi_disable(); // we don't use I2C
// Pin setup
pinMode(PIN_FLASH_CS, OUTPUT);
pinMode(PIN_AMP_SD, OUTPUT);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_FLASH_CS, HIGH); // deselect flash
digitalWrite(PIN_AMP_SD, LOW); // amp off
digitalWrite(PIN_LED, LOW);
// Init peripherals
Serial.begin(9600);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV2); // 4MHz SPI (8MHz/2)
buttonsInit();
motorInit();
audioInit();
// Init flash
flashWake();
delay(1);
uint32_t jedec = flashReadJEDEC();
if (jedec == 0xEF4018) {
// W25Q128 detected
digitalWrite(PIN_LED, HIGH);
delay(200);
digitalWrite(PIN_LED, LOW);
}
// Load track table
loadTrackTable();
g_lastActivity = millis();
Serial.print("Baby Mobile v1.0 — ");
Serial.print(g_numTracks);
Serial.println(" tracks loaded");
}
void loop() {
// ---- Handle serial commands (for programming) ----
handleSerial();
// ---- Refill audio buffer in main loop ----
if (g_playing) {
for (uint8_t b = 0; b < 2; b++) {
if (!g_bufReady[b] && g_nextReadAddr < g_playEnd) {
uint32_t remaining = g_playEnd - g_nextReadAddr;
uint16_t toRead = (remaining > AUDIO_BUF_SIZE) ? AUDIO_BUF_SIZE : remaining;
flashReadBytes(g_nextReadAddr, g_audioBuf[b], toRead);
// Zero-pad if short
for (uint16_t i = toRead; i < AUDIO_BUF_SIZE; i++) {
g_audioBuf[b][i] = 128; // silence
}
g_nextReadAddr += toRead;
g_bufReady[b] = true;
}
}
// Check if playback finished
if (!g_bufReady[0] && !g_bufReady[1]) {
audioStop();
// Auto-advance to next track or loop
g_currentTrack = (g_currentTrack + 1) % g_numTracks;
audioStart(g_currentTrack);
}
}
// ---- Read buttons ----
uint8_t btn = buttonRead();
if (btn > 0) {
g_lastActivity = millis();
switch (btn) {
case 1: // Play / Pause
if (g_playing) {
audioStop();
} else {
audioStart(g_currentTrack);
}
break;
case 2: // Next track
if (g_numTracks > 0) {
g_currentTrack = (g_currentTrack + 1) % g_numTracks;
if (g_playing) {
audioStop();
audioStart(g_currentTrack);
}
}
break;
case 3: // Previous track
if (g_numTracks > 0) {
g_currentTrack = (g_currentTrack == 0) ? g_numTracks - 1 : g_currentTrack - 1;
if (g_playing) {
audioStop();
audioStart(g_currentTrack);
}
}
break;
case 4: // Motor toggle
if (g_motorOn) {
motorStop();
} else {
motorStart(g_motorSpeed);
}
break;
case 5: // Motor speed up
if (g_motorSpeed < 235) g_motorSpeed += 20;
if (g_motorOn) analogWrite(PIN_MOTOR_PWM, g_motorSpeed);
break;
case 6: // Motor speed down
if (g_motorSpeed > 80) g_motorSpeed -= 20;
if (g_motorOn) analogWrite(PIN_MOTOR_PWM, g_motorSpeed);
break;
case 7: // Play all (music + motor)
motorStart(g_motorSpeed);
if (!g_playing) audioStart(0);
break;
case 8: // Stop all
audioStop();
motorStop();
break;
}
// Wait for button release
while (buttonRead() == btn) { delay(10); }
}
// ---- Auto shutoff ----
if (millis() - g_lastActivity > AUTO_OFF_MS) {
enterSleep();
}
// ---- Sleep if idle (no audio, no motor) ----
if (!g_playing && !g_motorOn && (millis() - g_lastActivity > 60000)) {
enterSleep();
}
}
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env python3
"""
flash_upload.py — Upload audio files to the Baby Mobile's W25Q128 flash.
Usage:
python3 flash_upload.py /dev/ttyUSB0 song1.raw song2.raw song3.raw ...
Audio files must be raw unsigned 8-bit PCM at 8000 Hz mono.
Convert with ffmpeg:
ffmpeg -i song.mp3 -ar 8000 -ac 1 -f u8 -acodec pcm_u8 song.raw
The script writes a track table to sector 0, then audio data starting
at sector 1 (address 0x1000).
"""
import serial
import struct
import sys
import time
import os
import subprocess
import shutil
BAUD = 9600
PAGE_SIZE = 256
SECTOR_SIZE = 4096
AUDIO_START = 0x1000
MAX_TRACKS = 32
FLASH_SIZE = 16 * 1024 * 1024 # 16MB
def open_serial(port):
ser = serial.Serial(port, BAUD, timeout=2)
time.sleep(2) # wait for Arduino reset
# Flush
ser.read(ser.in_waiting)
return ser
def flash_identify(ser):
ser.write(b'I')
line = ser.readline().decode().strip()
print(f" {line}")
return line
def flash_erase(ser):
print(" Erasing entire flash (takes ~40 seconds)...")
ser.write(b'E')
line = ser.readline().decode().strip()
while "Done" not in line:
line = ser.readline().decode().strip()
if line:
print(f" {line}")
print(" Erase complete.")
def flash_write_page(ser, addr, data):
"""Write up to 256 bytes to flash."""
assert len(data) <= 256
cmd = b'W'
cmd += struct.pack('>I', addr)[1:] # 3 bytes address
cmd += struct.pack('>H', len(data))
ser.write(cmd)
ser.write(data)
resp = ser.readline().decode().strip()
return resp
def flash_write_data(ser, start_addr, data, label=""):
"""Write arbitrary length data, page by page."""
total = len(data)
written = 0
addr = start_addr
while written < total:
chunk = data[written:written + PAGE_SIZE]
# Pad last page
if len(chunk) < PAGE_SIZE:
chunk = chunk + b'\x80' * (PAGE_SIZE - len(chunk)) # 0x80 = silence
flash_write_page(ser, addr, chunk)
written += PAGE_SIZE
addr += PAGE_SIZE
pct = min(100, written * 100 // total)
print(f"\r Writing {label}: {pct}% ({written}/{total} bytes)", end="", flush=True)
print()
return addr
def convert_to_raw(input_path):
"""Convert any audio file to 8kHz 8-bit unsigned PCM using ffmpeg."""
raw_path = input_path + ".raw"
if input_path.endswith('.raw'):
return input_path
if not shutil.which('ffmpeg'):
print(f"ERROR: ffmpeg not found. Please convert manually:")
print(f" ffmpeg -i {input_path} -ar 8000 -ac 1 -f u8 -acodec pcm_u8 {raw_path}")
sys.exit(1)
print(f" Converting {os.path.basename(input_path)} to 8kHz/8bit PCM...")
subprocess.run([
'ffmpeg', '-y', '-i', input_path,
'-ar', '8000', '-ac', '1', '-f', 'u8', '-acodec', 'pcm_u8',
raw_path
], capture_output=True)
return raw_path
def build_track_table(tracks):
"""Build the track index (stored in sector 0 of flash).
Format:
byte 0: num_tracks (uint8)
bytes 1-3: padding
bytes 4+: track_start[MAX_TRACKS] as uint32 big-endian
then: track_length[MAX_TRACKS] as uint32 big-endian
"""
num = len(tracks)
table = struct.pack('B', num) + b'\x00' * 3
# Start addresses
for start, length in tracks:
table += struct.pack('>I', start)
for _ in range(MAX_TRACKS - num):
table += struct.pack('>I', 0)
# Lengths
for start, length in tracks:
table += struct.pack('>I', length)
for _ in range(MAX_TRACKS - num):
table += struct.pack('>I', 0)
# Pad to sector size
table += b'\xFF' * (SECTOR_SIZE - len(table))
return table
def main():
if len(sys.argv) < 3:
print("Usage: python3 flash_upload.py <serial_port> <audio_file> [audio_file ...]")
print()
print("Supported input formats: .raw (8kHz/8bit/unsigned), .mp3, .wav, .ogg, .flac")
print("Non-.raw files will be converted automatically using ffmpeg.")
print()
print("Example:")
print(" python3 flash_upload.py /dev/ttyUSB0 lullaby.mp3 twinkle.wav brahms.mp3")
sys.exit(1)
port = sys.argv[1]
audio_files = sys.argv[2:]
if len(audio_files) > MAX_TRACKS:
print(f"ERROR: Maximum {MAX_TRACKS} tracks supported.")
sys.exit(1)
# Convert files to raw PCM
print("=== Preparing audio files ===")
raw_files = []
for f in audio_files:
raw = convert_to_raw(f)
size = os.path.getsize(raw)
duration = size / 8000
print(f" {os.path.basename(f)}: {size} bytes ({duration:.1f} seconds)")
raw_files.append(raw)
# Check total size
total_audio = sum(os.path.getsize(f) for f in raw_files)
available = FLASH_SIZE - AUDIO_START
if total_audio > available:
print(f"ERROR: Audio ({total_audio} bytes) exceeds flash capacity ({available} bytes)")
sys.exit(1)
total_duration = total_audio / 8000
print(f"\n Total: {total_audio} bytes ({total_duration:.1f} seconds / {total_duration/60:.1f} minutes)")
print(f" Flash usage: {(total_audio + AUDIO_START) * 100 // FLASH_SIZE}%")
# Connect
print(f"\n=== Connecting to {port} ===")
ser = open_serial(port)
flash_identify(ser)
# Erase
print("\n=== Erasing flash ===")
flash_erase(ser)
# Write audio data and build track list
print("\n=== Writing audio ===")
tracks = []
addr = AUDIO_START
for i, raw_path in enumerate(raw_files):
with open(raw_path, 'rb') as f:
data = f.read()
start = addr
length = len(data)
tracks.append((start, length))
name = os.path.basename(audio_files[i])
addr = flash_write_data(ser, start, data, label=f"Track {i+1} ({name})")
# Align to sector boundary for clean layout
if addr % SECTOR_SIZE != 0:
addr = ((addr // SECTOR_SIZE) + 1) * SECTOR_SIZE
# Write track table to sector 0
print("\n=== Writing track table ===")
table = build_track_table(tracks)
flash_write_data(ser, 0, table, label="Track table")
# Summary
print("\n=== Upload complete! ===")
print(f" {len(tracks)} tracks written")
for i, (start, length) in enumerate(tracks):
print(f" Track {i+1}: addr=0x{start:06X}, {length} bytes ({length/8000:.1f}s)")
print(f"\n Reset the board to start playback.")
# Cleanup temp files
for raw, orig in zip(raw_files, audio_files):
if raw != orig and os.path.exists(raw):
os.remove(raw)
ser.close()
if __name__ == "__main__":
main()
+614
View File
@@ -0,0 +1,614 @@
#!/usr/bin/env python3
"""
Generate KiCad 7 schematic and project files for Baby Mobile Audio Board.
Design: ATmega328P + W25Q128 SPI Flash + PAM8302A Class-D Amp + MOSFET motor driver
Power: 2x AA batteries (1.8V-3.3V), optimized for long runtime
Audio: 30+ min at 8kHz/8-bit mono via 16MB SPI flash
"""
import uuid
import json
import os
def uid():
return str(uuid.uuid4())
# ============================================================
# SCHEMATIC GENERATION
# ============================================================
def generate_schematic():
"""Generate the complete .kicad_sch file."""
# We use net labels for connections instead of routing every wire.
# This keeps the schematic clean and KiCad resolves connectivity by label name.
sch = []
sch.append(f"""(kicad_sch (version 20230121) (generator "custom_generator")
(uuid "{uid()}")
(paper "A3")
(title_block
(title "Baby Mobile Audio Board")
(date "2026-03-08")
(rev "1.0")
(comment 1 "ATmega328P + W25Q128 + PAM8302A")
(comment 2 "2xAA Battery Powered - Long Runtime Design")
(comment 3 "8-ohm 0.25W Speaker, DC Motor, 8 Buttons")
(comment 4 "Arduino Compatible (8MHz Internal Osc)")
)
""")
# ---- LIB_SYMBOLS ----
sch.append(" (lib_symbols")
sch.append(_sym_atmega328p())
sch.append(_sym_w25q128())
sch.append(_sym_pam8302a())
sch.append(_sym_nmos())
sch.append(_sym_capacitor())
sch.append(_sym_resistor())
sch.append(_sym_diode_schottky())
sch.append(_sym_inductor())
sch.append(_sym_switch())
sch.append(_sym_conn2())
sch.append(_sym_conn3())
sch.append(_sym_conn6())
sch.append(_sym_led())
sch.append(_sym_pwr_flag())
sch.append(" )") # end lib_symbols
# ---- COMPONENT INSTANCES ----
# Layout regions (KiCad uses mm, origin top-left):
# Power supply section: x=30, y=30
# MCU (ATmega328P): x=130, y=90
# SPI Flash (W25Q128): x=230, y=60
# Audio section (PAM8302A): x=230, y=140
# Motor driver: x=130, y=190
# Buttons (8x): x=30, y=80-180
# ISP Header: x=230, y=200
# Serial Header: x=30, y=200
# ---- POWER SUPPLY SECTION ----
# Battery connector J1
sch.append(_instance("baby_mobile:Conn_2Pin", "J1", "BATTERY", "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal",
25, 35, 0))
# Main decoupling C1: 100uF electrolytic
sch.append(_instance("baby_mobile:Capacitor", "C1", "100uF", "Capacitor_THT:CP_Radial_D5.0mm_P2.00mm",
55, 35, 0))
# Decoupling C2: 100nF ceramic near MCU VCC
sch.append(_instance("baby_mobile:Capacitor", "C2", "100nF", "Capacitor_SMD:C_0805_2012Metric",
100, 30, 0))
# Decoupling C3: 100nF ceramic near MCU AVCC
sch.append(_instance("baby_mobile:Capacitor", "C3", "100nF", "Capacitor_SMD:C_0805_2012Metric",
115, 30, 0))
# Power switch SW_PWR
sch.append(_instance("baby_mobile:Switch", "SW9", "PWR", "Button_Switch_THT:SW_SPDT_CK-JS102011SAQN",
40, 25, 0))
# ---- MCU: ATmega328P-AU ----
sch.append(_instance("baby_mobile:ATmega328P-AU", "U1", "ATmega328P-AU", "Package_QFP:TQFP-32_7x7mm_P0.8mm",
130, 90, 0))
# Reset pullup R_RST: 10K
sch.append(_instance("baby_mobile:Resistor", "R1", "10K", "Resistor_SMD:R_0805_2012Metric",
95, 55, 0))
# Reset capacitor C_RST: 100nF
sch.append(_instance("baby_mobile:Capacitor", "C4", "100nF", "Capacitor_SMD:C_0805_2012Metric",
95, 65, 0))
# ---- SPI FLASH: W25Q128JVSIQ ----
sch.append(_instance("baby_mobile:W25Q128", "U2", "W25Q128JVSIQ", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm",
230, 60, 0))
# Flash decoupling C5: 100nF
sch.append(_instance("baby_mobile:Capacitor", "C5", "100nF", "Capacitor_SMD:C_0805_2012Metric",
255, 50, 0))
# ---- AUDIO AMP: PAM8302A ----
sch.append(_instance("baby_mobile:PAM8302A", "U3", "PAM8302A", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm",
230, 140, 0))
# Input coupling cap C6: 1uF
sch.append(_instance("baby_mobile:Capacitor", "C6", "1uF", "Capacitor_SMD:C_0805_2012Metric",
200, 135, 0))
# RC low-pass filter on PWM: R_LPF + C_LPF
# f_c = 1/(2*pi*R*C) ~ 3.4kHz with 4.7K + 10nF
sch.append(_instance("baby_mobile:Resistor", "R2", "4.7K", "Resistor_SMD:R_0805_2012Metric",
185, 135, 0))
sch.append(_instance("baby_mobile:Capacitor", "C7", "10nF", "Capacitor_SMD:C_0805_2012Metric",
192, 145, 0))
# Amp supply bypass C8: 10uF
sch.append(_instance("baby_mobile:Capacitor", "C8", "10uF", "Capacitor_SMD:C_0805_2012Metric",
255, 130, 0))
# Speaker connector J2
sch.append(_instance("baby_mobile:Conn_2Pin", "J2", "SPEAKER", "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal",
265, 145, 0))
# Shutdown pulldown R_SD: 100K (default ON, MCU can shut down)
sch.append(_instance("baby_mobile:Resistor", "R3", "100K", "Resistor_SMD:R_0805_2012Metric",
210, 150, 0))
# ---- MOTOR DRIVER ----
# N-ch MOSFET Q1
sch.append(_instance("baby_mobile:NMOS", "Q1", "AO3400A", "Package_TO_SOT_SMD:SOT-23",
140, 190, 0))
# Gate resistor R_GATE: 100 ohm
sch.append(_instance("baby_mobile:Resistor", "R4", "100R", "Resistor_SMD:R_0805_2012Metric",
120, 190, 0))
# Gate pulldown R_GPD: 100K
sch.append(_instance("baby_mobile:Resistor", "R5", "100K", "Resistor_SMD:R_0805_2012Metric",
130, 200, 0))
# Flyback diode D1
sch.append(_instance("baby_mobile:Diode_Schottky", "D1", "SS14", "Diode_SMD:D_SMA",
155, 180, 90))
# Motor connector J3
sch.append(_instance("baby_mobile:Conn_2Pin", "J3", "MOTOR", "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal",
160, 170, 0))
# ---- 8 BUTTONS ----
button_pins = ["BTN1", "BTN2", "BTN3", "BTN4", "BTN5", "BTN6", "BTN7", "BTN8"]
for i, name in enumerate(button_pins):
y = 75 + i * 15
sch.append(_instance("baby_mobile:Switch", f"SW{i+1}", name, "Button_Switch_SMD:SW_SPST_TL3342",
30, y, 0))
# ---- STATUS LED ----
sch.append(_instance("baby_mobile:LED", "D2", "LED", "LED_SMD:LED_0805_2012Metric",
85, 190, 0))
sch.append(_instance("baby_mobile:Resistor", "R6", "1K", "Resistor_SMD:R_0805_2012Metric",
75, 190, 0))
# ---- ISP HEADER (2x3) ----
sch.append(_instance("baby_mobile:Conn_6Pin", "J4", "ISP", "Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Vertical",
250, 200, 0))
# ---- SERIAL HEADER (3-pin: GND, TX, RX) ----
sch.append(_instance("baby_mobile:Conn_3Pin", "J5", "SERIAL", "Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical",
30, 210, 0))
# ---- NET LABELS ----
# Power nets
net_labels = [
# Power distribution
("VCC", 55, 25), ("VCC", 100, 25), ("VCC", 115, 25),
("VCC", 130, 50), ("VCC", 255, 45), ("VCC", 255, 125),
("VCC", 95, 50), ("VCC", 160, 165),
("GND", 55, 45), ("GND", 100, 40), ("GND", 115, 40),
("GND", 130, 130), ("GND", 255, 70), ("GND", 255, 155),
("GND", 95, 70), ("GND", 140, 205), ("GND", 192, 155),
# SPI bus
("SPI_MOSI", 165, 85), ("SPI_MOSI", 215, 55),
("SPI_MISO", 165, 90), ("SPI_MISO", 215, 60),
("SPI_SCK", 165, 95), ("SPI_SCK", 215, 65),
("FLASH_CS", 165, 80), ("FLASH_CS", 215, 50),
# Audio
("AUDIO_PWM", 165, 100), ("AUDIO_PWM", 175, 135),
("AMP_SD", 165, 105), ("AMP_SD", 210, 145),
# Motor
("MOTOR_PWM", 165, 110), ("MOTOR_PWM", 110, 190),
# Buttons (directly named for MCU pins mapping)
("BTN1", 45, 75), ("BTN1", 95, 85),
("BTN2", 45, 90), ("BTN2", 95, 90),
("BTN3", 45, 105), ("BTN3", 95, 95),
("BTN4", 45, 120), ("BTN4", 95, 100),
("BTN5", 45, 135), ("BTN5", 95, 105),
("BTN6", 45, 150), ("BTN6", 95, 110),
("BTN7", 45, 165), ("BTN7", 95, 115),
("BTN8", 45, 180), ("BTN8", 95, 120),
# ISP
("SPI_MOSI", 240, 195), ("SPI_MISO", 240, 200),
("SPI_SCK", 240, 205), ("RESET", 240, 210),
("RESET", 95, 60),
# Serial
("TXD", 40, 210), ("RXD", 40, 215),
("TXD", 95, 75), ("RXD", 95, 80),
# LED
("LED_OUT", 65, 190), ("LED_OUT", 95, 125),
]
for name, x, y in net_labels:
sch.append(f""" (label "{name}" (at {x} {y} 0) (effects (font (size 1.27 1.27))) (uuid "{uid()}"))""")
# ---- TEXT NOTES ----
notes = [
(15, 15, "POWER: 2xAA (1.8V-3.3V) → Long runtime design"),
(15, 18, "MCU: ATmega328P-AU @ 8MHz internal osc, Arduino compatible"),
(200, 15, "FLASH: W25Q128 16MB = ~33 min @ 8kHz 8-bit PCM"),
(200, 18, "AMP: PAM8302A Class-D, 90% efficient, Vmin=2.0V"),
(15, 220, "BUTTONS: Internal pull-ups, active LOW, PCINT wake-from-sleep"),
(120, 220, "MOTOR: AO3400A logic-level N-MOSFET, PWM speed control"),
(15, 225, "ESTIMATED RUNTIME: Audio+Motor ~23h, Audio only ~200h, Sleep ~years"),
]
for x, y, text in notes:
sch.append(f""" (text "{text}" (at {x} {y} 0)
(effects (font (size 1.5 1.5)) (justify left))
(uuid "{uid()}")
)""")
sch.append(")") # end kicad_sch
return "\n".join(sch)
# ============================================================
# SYMBOL DEFINITIONS
# ============================================================
def _sym_atmega328p():
return f"""
(symbol "baby_mobile:ATmega328P-AU" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 0 26 0) (effects (font (size 1.27 1.27))))
(property "Value" "ATmega328P-AU" (at 0 24 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_QFP:TQFP-32_7x7mm_P0.8mm" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:ATmega328P-AU_0_1"
(rectangle (start -17 23) (end 17 -23) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:ATmega328P-AU_1_1"
(pin power_in line (at -7 -26 90) (length 3) (name "GND" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin power_in line (at -5 -26 90) (length 3) (name "GND2" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin power_in line (at -3 26 270) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin power_in line (at -1 26 270) (length 3) (name "AVCC" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
(pin passive line (at 1 26 270) (length 3) (name "AREF" (effects (font (size 1 1)))) (number "21" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 15 0) (length 3) (name "PD0/RXD" (effects (font (size 1 1)))) (number "30" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 12.5 0) (length 3) (name "PD1/TXD" (effects (font (size 1 1)))) (number "31" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 10 0) (length 3) (name "PD2/INT0" (effects (font (size 1 1)))) (number "32" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 7.5 0) (length 3) (name "PD3/INT1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 5 0) (length 3) (name "PD4" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 2.5 0) (length 3) (name "PD5/OC0B" (effects (font (size 1 1)))) (number "9" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 0 0) (length 3) (name "PD6/OC0A" (effects (font (size 1 1)))) (number "10" (effects (font (size 1 1)))))
(pin bidirectional line (at -20 -2.5 0) (length 3) (name "PD7" (effects (font (size 1 1)))) (number "11" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 15 180) (length 3) (name "PB0" (effects (font (size 1 1)))) (number "12" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 12.5 180) (length 3) (name "PB1/OC1A" (effects (font (size 1 1)))) (number "13" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 10 180) (length 3) (name "PB2/SS" (effects (font (size 1 1)))) (number "14" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 7.5 180) (length 3) (name "PB3/MOSI" (effects (font (size 1 1)))) (number "15" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 5 180) (length 3) (name "PB4/MISO" (effects (font (size 1 1)))) (number "16" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 2.5 180) (length 3) (name "PB5/SCK" (effects (font (size 1 1)))) (number "17" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -2.5 180) (length 3) (name "PC0/ADC0" (effects (font (size 1 1)))) (number "23" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -5 180) (length 3) (name "PC1/ADC1" (effects (font (size 1 1)))) (number "24" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -7.5 180) (length 3) (name "PC2/ADC2" (effects (font (size 1 1)))) (number "25" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -10 180) (length 3) (name "PC3/ADC3" (effects (font (size 1 1)))) (number "26" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -12.5 180) (length 3) (name "PC4/SDA" (effects (font (size 1 1)))) (number "27" (effects (font (size 1 1)))))
(pin bidirectional line (at 20 -15 180) (length 3) (name "PC5/SCL" (effects (font (size 1 1)))) (number "28" (effects (font (size 1 1)))))
(pin input line (at -20 -10 0) (length 3) (name "~{{RESET}}" (effects (font (size 1 1)))) (number "29" (effects (font (size 1 1)))))
(pin input line (at -20 -15 0) (length 3) (name "XTAL1" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
(pin output line (at -20 -17.5 0) (length 3) (name "XTAL2" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
)
)"""
def _sym_w25q128():
return f"""
(symbol "baby_mobile:W25Q128" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 0 9 0) (effects (font (size 1.27 1.27))))
(property "Value" "W25Q128JVSIQ" (at 0 7 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:W25Q128_0_1"
(rectangle (start -7 6) (end 7 -6) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:W25Q128_1_1"
(pin input line (at -10 4 0) (length 3) (name "~{{CS}}" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin output line (at -10 1.5 0) (length 3) (name "DO/IO1" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin input line (at -10 -1.5 0) (length 3) (name "~{{WP}}/IO2" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin power_in line (at -10 -4 0) (length 3) (name "GND" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin input line (at 10 -4 180) (length 3) (name "DI/IO0" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin input line (at 10 -1.5 180) (length 3) (name "CLK" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
(pin input line (at 10 1.5 180) (length 3) (name "~{{HOLD}}/IO3" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
(pin power_in line (at 10 4 180) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
)
)"""
def _sym_pam8302a():
return f"""
(symbol "baby_mobile:PAM8302A" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 0 11 0) (effects (font (size 1.27 1.27))))
(property "Value" "PAM8302A" (at 0 9 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:PAM8302A_0_1"
(rectangle (start -8 8) (end 8 -8) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:PAM8302A_1_1"
(pin input line (at -11 5 0) (length 3) (name "~{{SD}}" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin power_in line (at -11 2 0) (length 3) (name "VDD" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin power_in line (at -11 -1 0) (length 3) (name "GND" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin input line (at -11 -4 0) (length 3) (name "A+" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin input line (at -11 -7 0) (length 3) (name "A-" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin output line (at 11 2 180) (length 3) (name "VO+" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
(pin output line (at 11 -1 180) (length 3) (name "VO-" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
(pin power_in line (at 11 -4 180) (length 3) (name "PAD" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
)
)"""
def _sym_nmos():
return f"""
(symbol "baby_mobile:NMOS" (in_bom yes) (on_board yes)
(property "Reference" "Q" (at 3 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "AO3400A" (at 5 -2.5 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Package_TO_SOT_SMD:SOT-23" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:NMOS_0_1"
(polyline (pts (xy -0.5 0.5) (xy -0.5 -0.5)) (stroke (width 0.33) (type default)) (fill (type none)))
(polyline (pts (xy -0.5 -2) (xy -0.5 2)) (stroke (width 0.254) (type default)) (fill (type none)))
(polyline (pts (xy 0 -2.5) (xy 0 -1.5)) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy 0 1.5) (xy 0 2.5)) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy 0 0) (xy -1.5 0)) (stroke (width 0) (type default)) (fill (type none)))
)
(symbol "baby_mobile:NMOS_1_1"
(pin passive line (at 0 5 270) (length 2.5) (name "D" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin input line (at -4 0 0) (length 2.5) (name "G" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -5 90) (length 2.5) (name "S" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_capacitor():
return f"""
(symbol "baby_mobile:Capacitor" (in_bom yes) (on_board yes)
(property "Reference" "C" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "C" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Capacitor_0_1"
(polyline (pts (xy -1.5 1) (xy 1.5 1)) (stroke (width 0.4) (type default)) (fill (type none)))
(polyline (pts (xy -1.5 -1) (xy 1.5 -1)) (stroke (width 0.4) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Capacitor_1_1"
(pin passive line (at 0 3.5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -3.5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_resistor():
return f"""
(symbol "baby_mobile:Resistor" (in_bom yes) (on_board yes)
(property "Reference" "R" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "R" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Resistor_0_1"
(rectangle (start -1 2.5) (end 1 -2.5) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Resistor_1_1"
(pin passive line (at 0 5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_diode_schottky():
return f"""
(symbol "baby_mobile:Diode_Schottky" (in_bom yes) (on_board yes)
(property "Reference" "D" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "SS14" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "Diode_SMD:D_SMA" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Diode_Schottky_0_1"
(polyline (pts (xy -1.5 1) (xy 0 -1) (xy 1.5 1) (xy -1.5 1)) (stroke (width 0.2) (type default)) (fill (type none)))
(polyline (pts (xy -1.5 -1) (xy 1.5 -1)) (stroke (width 0.3) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Diode_Schottky_1_1"
(pin passive line (at 0 3.5 270) (length 2.5) (name "A" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -3.5 90) (length 2.5) (name "K" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_inductor():
return f"""
(symbol "baby_mobile:Inductor" (in_bom yes) (on_board yes)
(property "Reference" "L" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "L" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Inductor_0_1"
(arc (start 0 2.5) (mid 0.75 1.875) (end 0 1.25) (stroke (width 0.254) (type default)) (fill (type none)))
(arc (start 0 1.25) (mid 0.75 0.625) (end 0 0) (stroke (width 0.254) (type default)) (fill (type none)))
(arc (start 0 0) (mid 0.75 -0.625) (end 0 -1.25) (stroke (width 0.254) (type default)) (fill (type none)))
(arc (start 0 -1.25) (mid 0.75 -1.875) (end 0 -2.5) (stroke (width 0.254) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Inductor_1_1"
(pin passive line (at 0 5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_switch():
return f"""
(symbol "baby_mobile:Switch" (in_bom yes) (on_board yes)
(property "Reference" "SW" (at 0 3 0) (effects (font (size 1.27 1.27))))
(property "Value" "SW" (at 0 -3 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Switch_0_1"
(circle (center -2 0) (radius 0.4) (stroke (width 0) (type default)) (fill (type none)))
(circle (center 2 0) (radius 0.4) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy -1.6 0) (xy 1.6 1)) (stroke (width 0.254) (type default)) (fill (type none)))
)
(symbol "baby_mobile:Switch_1_1"
(pin passive line (at -5 0 0) (length 3) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 5 0 180) (length 3) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_conn2():
return f"""
(symbol "baby_mobile:Conn_2Pin" (in_bom yes) (on_board yes)
(property "Reference" "J" (at 0 4 0) (effects (font (size 1.27 1.27))))
(property "Value" "Conn_2" (at 0 -4 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Conn_2Pin_0_1"
(rectangle (start -2 3) (end 2 -3) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Conn_2Pin_1_1"
(pin passive line (at -5 1.5 0) (length 3) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at -5 -1.5 0) (length 3) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_conn3():
return f"""
(symbol "baby_mobile:Conn_3Pin" (in_bom yes) (on_board yes)
(property "Reference" "J" (at 0 5.5 0) (effects (font (size 1.27 1.27))))
(property "Value" "Conn_3" (at 0 -5.5 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Conn_3Pin_0_1"
(rectangle (start -2 4) (end 2 -4) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Conn_3Pin_1_1"
(pin passive line (at -5 2.5 0) (length 3) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at -5 0 0) (length 3) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin passive line (at -5 -2.5 0) (length 3) (name "3" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
)
)"""
def _sym_conn6():
return f"""
(symbol "baby_mobile:Conn_6Pin" (in_bom yes) (on_board yes)
(property "Reference" "J" (at 0 9 0) (effects (font (size 1.27 1.27))))
(property "Value" "ISP_2x3" (at 0 -9 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:Conn_6Pin_0_1"
(rectangle (start -3 8) (end 3 -8) (stroke (width 0.254) (type default)) (fill (type background)))
)
(symbol "baby_mobile:Conn_6Pin_1_1"
(pin passive line (at -6 5 0) (length 3) (name "MISO" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 6 5 180) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
(pin passive line (at -6 0 0) (length 3) (name "SCK" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
(pin passive line (at 6 0 180) (length 3) (name "MOSI" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
(pin passive line (at -6 -5 0) (length 3) (name "~{{RST}}" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
(pin passive line (at 6 -5 180) (length 3) (name "GND" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
)
)"""
def _sym_led():
return f"""
(symbol "baby_mobile:LED" (in_bom yes) (on_board yes)
(property "Reference" "D" (at 2 0 0) (effects (font (size 1.27 1.27))))
(property "Value" "LED" (at 2 -2 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:LED_0_1"
(polyline (pts (xy -1.5 1) (xy 0 -1) (xy 1.5 1) (xy -1.5 1)) (stroke (width 0.2) (type default)) (fill (type none)))
(polyline (pts (xy -1.5 -1) (xy 1.5 -1)) (stroke (width 0.3) (type default)) (fill (type none)))
)
(symbol "baby_mobile:LED_1_1"
(pin passive line (at 0 3.5 270) (length 2.5) (name "A" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
(pin passive line (at 0 -3.5 90) (length 2.5) (name "K" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
)
)"""
def _sym_pwr_flag():
return f"""
(symbol "baby_mobile:PWR_FLAG" (power) (in_bom no) (on_board no)
(property "Reference" "#FLG" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Value" "PWR_FLAG" (at 0 3 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
(symbol "baby_mobile:PWR_FLAG_0_1"
(polyline (pts (xy 0 0) (xy 0 1.5)) (stroke (width 0) (type default)) (fill (type none)))
(polyline (pts (xy -1 1.5) (xy 0 3) (xy 1 1.5) (xy -1 1.5)) (stroke (width 0) (type default)) (fill (type none)))
)
(symbol "baby_mobile:PWR_FLAG_1_1"
(pin power_out line (at 0 0 90) (length 0) (name "pwr" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
)
)"""
def _instance(lib_id, ref, value, footprint, x, y, angle):
"""Generate a component instance."""
return f""" (symbol (lib_id "{lib_id}") (at {x} {y} {angle}) (unit 1)
(in_bom yes) (on_board yes) (dnp no)
(uuid "{uid()}")
(property "Reference" "{ref}" (at {x} {y-3} 0) (effects (font (size 1.27 1.27))))
(property "Value" "{value}" (at {x} {y+3} 0) (effects (font (size 1.27 1.27))))
(property "Footprint" "{footprint}" (at {x} {y} 0) (effects (font (size 1.27 1.27)) hide))
(property "Datasheet" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) hide))
)"""
# ============================================================
# PROJECT FILE
# ============================================================
def generate_project():
return json.dumps({
"meta": {
"filename": "baby-mobile.kicad_pro",
"version": 1
},
"board": {
"design_settings": {
"defaults": {
"board_outline_line_width": 0.1,
"copper_line_width": 0.2,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"other_line_width": 0.15
},
"rules": {
"min_copper_edge_clearance": 0.3,
"min_hole_clearance": 0.25,
"min_track_width": 0.2,
"min_via_annular_width": 0.13,
"min_via_diameter": 0.6,
"solder_mask_to_copper_clearance": 0
}
},
"layer_presets": []
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"net_settings": {
"classes": [{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.3,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}]
},
"schematic": {
"legacy_lib_dir": "",
"legacy_lib_list": []
},
"sheets": [["", ""]]
}, indent=2)
# ============================================================
# MAIN
# ============================================================
if __name__ == "__main__":
outdir = "/home/claude/baby-mobile-pcb"
# Generate schematic
sch = generate_schematic()
with open(os.path.join(outdir, "baby-mobile.kicad_sch"), "w") as f:
f.write(sch)
print(f"Written: baby-mobile.kicad_sch ({len(sch)} bytes)")
# Generate project file
proj = generate_project()
with open(os.path.join(outdir, "baby-mobile.kicad_pro"), "w") as f:
f.write(proj)
print(f"Written: baby-mobile.kicad_pro ({len(proj)} bytes)")
print("Done! Open baby-mobile.kicad_pro in KiCad 7+")