Close
0%
0%

Toddler Phone That Calls Grandma

An ESP32-S3 as USB host to a retro handset, plus a tiny SIP client. Big lit buttons call family through your own Twilio account.

Similar projects worth following
0 followers
A phone for a small child: an old-style handset and up to four big lit arcade buttons, one per person. Pick up, press grandma's button, and her normal phone rings. She installs nothing.

Under the hood an ESP32-S3 is USB host to a USB-audio telephone handset and runs a minimal SIP client over WiFi to a Twilio SIP domain in the parent's own account. No server of mine is involved, the phone can only dial an allowlist, and nobody can call in.

About $30 to $50 in parts, one solder blob, a few dollars a month in calls. Firmware Apache 2.0, flashed from the browser.

Why

Our family is spread across countries and I wanted my kid to call them without a grown-up dialing. Kid phones you can buy come with a monthly plan and an app. This one is a dev board, a $8 handset and some arcade buttons.

How a call works

  1. Button press on a GPIO.
  2. The ESP32-S3 sends a SIP INVITE (digest auth) to a Twilio SIP Domain in the parent's own Twilio account.
  3. The SIP Domain hands the call to a small Twilio Function that holds the allowlist. It refuses any number that is not on it, then dials the real number from the parent's Twilio caller ID.
  4. Audio is G.711 mu-law at 8 kHz, RTP over UDP.

The handset is the fun part

It is sold as a "retro handset for mobile phones": a USB-C plug with a USB Audio Class chip inside (mine enumerates as "AB17X USB Audio", 001f:0b23, 48 kHz stereo). The ESP32-S3 runs as USB host and talks UAC to it directly, so there is no codec, amp or analog audio on the board at all. 

Things that took a while:

  • Power out of the USB port. The dual USB-C ESP32-S3 dev boards have a USB-OTG solder pad that lets the native USB port source 5 V. Bridge it or the handset gets no power. (The IN-OUT pad next to it does nothing for this.) After that, the COM port is for the computer or charger and the USB port is for the handset only; a computer on the USB port would get 5 V pushed into it.
  • USB host buffers. The earpiece endpoint has a 384-byte max packet size. The default ESP-IDF USB host config could not handle it; it needed CONTROL_TRANSFER_MAX_SIZE 1024 and a custom FIFO split (non-periodic TX 16, periodic TX 96, RX 88 of the 200 lines).
  • NAT and early media. With Twilio's answerOnBridge on, the call gets early media and then a 200 OK, and behind home NAT all audio vanished. Turning it off fixed it.
  • Twilio error 13225 ("number is blacklisted"). If your Twilio account was created outside the US/Canada and only has an individual profile, calls to +1 numbers get blocked if they aren't on the approved list.

Setup for non-technical parents

No toolchain:

  • Flash from Chrome or Edge with ESP Web Tools, then type the WiFi name and password into the installer (Improv WiFi over serial).
  • Open kidphone.local, paste the Twilio Account SID and Auth Token, add people.
  • The phone provisions the Twilio account by itself: a SIP credential with a per-phone login, the SIP domain, the Function holding the allowlist, and a restricted API key. Then it forgets the Auth Token.
  • The restricted key can only update the allowlist variables and redeploy the Function. It gets 401 on calls, the account and everything else. So changing grandma's number later is just Save, no token, and a leaked key cannot place calls.
  • Every step is rerun-safe, so a setup that dies halfway is fixed by running it again.

Lights and call hours

Each button's lamp means "you can call now". Each person can have call windows in their own time zone (the firmware carries its own POSIX TZ rule evaluator, so grandma's 6 pm stays 6 pm across her DST change). Outside the window the lamp is off and a press gets a spoken "They can't talk right now." Until NTP syncs, everyone counts as available; a clock problem never blocks a call. There is also a household bedtime window.

Talks instead of beeps

Errors are short spoken prompts generated offline with Piper TTS and stored in flash ("The phone isn't connected. Ask a grown-up for help."). Other kid-proofing: a 30-minute call cap, a 3 s cooldown so button mashing cannot fire off a burst of calls, a silent earpiece while idle (a handset left off the hook does not beep all day), and a factory reset on the board's BOOT button, inside the case.

Privacy

The phone talks only to the parent's Twilio account and an NTP server. It cannot ring (no inbound calls by design), it does not record anything, and it only dials numbers on the list. It is not an emergency phone.

Cost to run

About $2 to $3 a month: $1.15 for the US number plus per-minute charges.

Newer features (0.5 to 0.7)

Assignable pins....

Read more »

  • 1 × ESP32-S3 N16R8 dev board, dual USB-C Two USB-C ports (COM + native USB). Bridge the USB-OTG pad.
  • 1 × USB-C telephone handset with USB audio chip Retro handset for mobile phones; tested AB17X (001f:0b23)
  • 4 × 60 mm arcade button with 12 V lamp One per person, 1 to 4. Lamp glows at 3.3 V, no resistor
  • 16 × Female Dupont jumper wire Four per button: two for the switch, two for the lamp
  • 1 × USB-C phone charger Powers the board through the COM port

View all 8 components

  • The handset is a USB sound card

    Allan Binder • 7 hours ago • 0 comments

    The whole build hinges on one cheap part. "Retro handsets for mobile phones" with a USB-C plug have a USB Audio Class chip inside, so an ESP32-S3 in USB host mode can treat the handset as a sound card: microphone in, earpiece out, no analog audio anywhere on the board.

    Three things stood between "it enumerates" and "grandma can hear me":

    1. The dev board does not put 5 V on its native USB port until you bridge the USB-OTG pad.
    2. The earpiece endpoint wants 384-byte packets; ESP-IDF's USB host defaults could not handle that until I raised the control transfer size and re-split the FIFO.
    3. Twilio's answerOnBridge sends early media before the 200 OK, and behind NAT that lost all audio. Off it went.

    After that, a minimal SIP client, G.711 mu-law and RTP over UDP did the rest. The first real call went through on 2026-10-10. My daughter loves it.

View project log

  • 1
    Bridge the USB-OTG pad

    One blob of solder across the USB-OTG pad on the back of the board, near the USB-C ports. This is the only soldering. It lets the native USB port send 5 V out to power the handset. The IN-OUT pad next to it does nothing for the handset.

  • 2
    Learn the two ports

    USB port: the handset, and only the handset. COM port: the computer (for flashing) or the charger (every day).

    Never plug a computer into the USB port once the pad is bridged: that port now pushes 5 V out.

  • 3
    Wire the buttons and lamps

    Switch tabs: GPIO 4, 5, 6, 7 to GND, one per person. The switch is polarity-free.

    Lamp tabs: GPIO 15, 16, 17, 18 to GND, no resistor (the 12 V lamp has its own and still glows at 3.3 V). The lamps are polarized; if one stays dark, flip its two wires.

    A plain LED works too: pin, 330 ohm, LED, GND. Buttons you do not wire are ignored.

View all 8 instructions

Enjoy this project?

Share

Discussions

Does this project spark your interest?

Become a member to follow this project and never miss any updates