From 57df0a1293cee8637ca0ebce86924bc2e64c2f63 Mon Sep 17 00:00:00 2001 From: zyphlar Date: Sat, 4 Jul 2026 03:28:11 -0700 Subject: [PATCH] Initial commit --- DESIGN.md | 278 +++++++++++++++++ baby-mobile.kicad_pro | 61 ++++ baby-mobile.kicad_sch | 628 ++++++++++++++++++++++++++++++++++++++ baby_mobile_firmware.ino | 639 +++++++++++++++++++++++++++++++++++++++ flash_upload.py | 233 ++++++++++++++ generate_kicad.py | 614 +++++++++++++++++++++++++++++++++++++ 6 files changed, 2453 insertions(+) create mode 100644 DESIGN.md create mode 100644 baby-mobile.kicad_pro create mode 100644 baby-mobile.kicad_sch create mode 100644 baby_mobile_firmware.ino create mode 100644 flash_upload.py create mode 100644 generate_kicad.py diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..b81ebb6 --- /dev/null +++ b/DESIGN.md @@ -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 30–100mA 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 (~30–50KΩ). 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) diff --git a/baby-mobile.kicad_pro b/baby-mobile.kicad_pro new file mode 100644 index 0000000..2acd155 --- /dev/null +++ b/baby-mobile.kicad_pro @@ -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": [ + [ + "", + "" + ] + ] +} \ No newline at end of file diff --git a/baby-mobile.kicad_sch b/baby-mobile.kicad_sch new file mode 100644 index 0000000..dd7eb39 --- /dev/null +++ b/baby-mobile.kicad_sch @@ -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") + ) +) \ No newline at end of file diff --git a/baby_mobile_firmware.ino b/baby_mobile_firmware.ino new file mode 100644 index 0000000..1e746a0 --- /dev/null +++ b/baby_mobile_firmware.ino @@ -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 +#include +#include +#include + +// ---- 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(); + } +} diff --git a/flash_upload.py b/flash_upload.py new file mode 100644 index 0000000..1c670a1 --- /dev/null +++ b/flash_upload.py @@ -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 [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() diff --git a/generate_kicad.py b/generate_kicad.py new file mode 100644 index 0000000..02f3682 --- /dev/null +++ b/generate_kicad.py @@ -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+")