Show HN: BillAI Bass, an AI-Powered Big Mouth Billy Bass Using Strands Agents
Hardware Setup
Turn a Big Mouth Billy Bass into a real-time voice assistant: you talk, the fish talks back - head swiveling, mouth lip-syncing to its own voice, tail flapping for emphasis.
The fish runs a Strands Agents bidirectional streaming agent (BidiAgent) on a Raspberry Pi 5, streaming live audio to and from Amazon Nova 2 Sonic on Amazon Bedrock. No robotics experience needed. No prior soldering experience needed. The person this guide is based on had never plugged in a Raspberry Pi before and got a talking fish in a weekend.
Inspired by the excellent billy-b-assistant project, rebuilt on Strands.
Files
| File | What it is |
|---|---|
| README.md | This guide - start here, work top to bottom |
| billy.py | The final working Python - the talking fish |
| motors.py | Standalone motor test rig - run this before billy.py to verify wiring |
| asoundrc.example | The ALSA config that pins the USB mic + speaker as defaults |
| requirements-frozen.txt | Exact dependency versions from a known-good Pi build - pip install -r for guaranteed-working setup |
| iot-identity/ | Optional production-grade credentials: CloudFormation template + walkthrough for X.509-cert auth instead of access keys |
Build This WITH an AI Assistant
Paste this README into Claude (or another AI assistant) and say: "I'm building this. Walk me through it one step at a time - wait for me to confirm each step before giving the next." That's the way it was built; that's the way it builds best.
See the Build this WITH an AI Assistant section below for what to send when stuck. This project was created with help from Claude Code and you can use any AI assistant to help you get through. Paste this entire guide into your AI and say: "I'm building this. Walk me through it one step at a time - wait for me to confirm each step before giving the next." Your AI will pace the build to you, debug your exact error messages, and adapt when your hardware differs slightly from this guide.
Things AI is great at during this build:
- Paste your exact error message - the scary 50-line Python traceback usually has one meaningful line, and AI will find it.
- Send photos - stuck identifying a motor? Can't tell which pin is which? Photograph it and ask. (In Claude Code on a Mac: drop the image file into the terminal, or say "look at ~/Downloads/IMG_1234.jpg".)
- Say "I know nothing about X" - AI will back up and explain. There are no dumb questions.
- Ask before cutting/soldering anything you're unsure about.
If you hit something this guide doesn't cover, that's not a failure of you. It's probably a gap in the guide. AI + your error message will get you through.
Parts List
If you got a kit: you have everything in this table already. Building from scratch: this is the original Amazon order (~$240 total, January 2026 prices).
| Item | What it's for | ~Price |
|---|---|---|
| Big Mouth Billy Bass (current "Gemmy" version) | The fish. The modern version has 2 motors, which this guide assumes | $30 |
| Raspberry Pi 5, 8GB | The brain. 4GB would also work | $80 |
| 27W USB-C PD power supply (official Pi or RasTech GaN) | Don't substitute a phone charger - the Pi limits power to USB ports without a proper supply | $15 |
| Official Raspberry Pi 5 Active Cooler | Heatsink + fan. The Pi runs hot without it | $10 |
| MicroSD card, 64GB (e.g. Transcend USD340, any name brand) | The Pi's hard drive | $12 |
| USB mini speaker (e.g. HONKYOB) | Billy's voice | $15 |
| USB gooseneck mini microphone (e.g. CGS-M1) | Billy's ears | $13 |
| MX1508 dual H-bridge motor driver, 5-pack (e.g. Aideepen, sold as "L298N replacement") | Lets the Pi safely drive the motors. You need 1; the spares forgive mistakes | $9 |
| DuPont jumper wire kit, 120pc male/female mix (e.g. ELEGOO) | Connects driver board to Pi. You'll use ~6 female-to-female | $7 |
| Soldering iron kit, 60W adjustable with tips + solder | 10 solder joints total. Cheapest kit is fine | $20 |
| Flush cutters (e.g. KATA 2-pack) | Snipping and stripping wires | $9 |
| JST XH connector kit | Optional - handy for tidy splices, but the build works without it | $9 |
Also needed, not in the kit:
- A personal computer (Mac/Windows/Linux) to flash the SD card - โ ๏ธ corporate laptops often block USB storage; use a home machine
- A way to plug a microSD into that computer (most cards include an SD adapter)
- An AWS account (personal, not your employer's) with a payment method - you'll create a locked-down, fish-only IAM user in the prerequisites below
- Tape (electrical ideally, Scotch works), a small Phillips screwdriver
- Home WiFi that both your computer and the Pi can join
AWS Account & Costs
๐ธ No AWS account yet? The free tier covers this project. New AWS accounts get $100 in credits (up to $200 with activities), and Amazon Bedrock is usable on the free plan with credits applied to Nova usage. Voice chat is cheap - roughly a penny per few minutes of conversation - so credits buy on the order of 100+ hours of fish banter.
Two free-plan caveats:
- Brand-new accounts sometimes start with Bedrock invocation quotas set to zero (no payment history yet). If model access is granted but every call fails with
ValidationException: Operation not allowed, that's the quota - file a (free) AWS support case asking for Nova Sonic on-demand quota, or upgrade to the paid plan (your remaining credits carry over). - Free-plan accounts auto-close after 6 months (or when credits run out). Fine for a demo fish; if Billy's becoming a permanent housemate, upgrade to paid before the deadline - unused credits survive the upgrade.
Your AWS keys will live in a plaintext file on a Pi inside a fish. Fish get demoed, lent out, and left on desks; SD cards pop out. So the keys must be able to do exactly one thing: talk to Nova Sonic. If they leak, the worst case is someone chats with a fish on your dime - not someone mining crypto in your account.
Prerequisites: AWS IAM Setup
Step 1 - Enable model access
- In the AWS console, switch region to us-east-1 (N. Virginia) - Nova Sonic lives there.
- Go to Amazon Bedrock โ Model access and request access to Nova 2 Sonic (
amazon.nova-2-sonic-v1:0). Usually instant. (Not v1 - original Nova Sonic hits end-of-life September 2026.)
Step 2 - Create a dedicated IAM user
- Console โ IAM โ Users โ Create user. Name it something honest like
billy-bass. - Do NOT check "Provide user access to the AWS Management Console" - this user is a robot fish; it never logs into anything.
- On the permissions screen, choose "Attach policies directly" but don't select any of the AWS-managed policies (no
AmazonBedrockFullAccess- that grants far more than the fish needs). Just click through and create the user with zero permissions.
Step 3 - Attach a policy that allows only Nova Sonic
- Open the new user โ Permissions โ Add permissions โ Create inline policy โ JSON tab.
- Paste:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BillyTalksToNovaSonicOnly",
"Effect": "Allow",
"Action": "bedrock:InvokeModelWithBidirectionalStream",
"Resource": "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-2-sonic-v1:0"
}
]
}
- Name it
billy-nova-sonic-only, save.
That's one action on one model in one region. No S3, no EC2, no other Bedrock models, no ability to see or change anything else in your account. (If you later give Billy tools that touch AWS - say, reading a DynamoDB table - add that specific permission to this policy then, not broad access now.)
Step 4 - Create the access key
- User โ Security credentials โ Create access key โ choose "Application running outside AWS".
- Copy the access key ID + secret somewhere safe - the secret is shown exactly once. These go on the Pi later (ยง1.6).
Step 5 - Optional but smart: set a billing alarm
- Console โ Billing โ Budgets โ create a $10/month budget with an email alert. Casual Nova Sonic chat costs cents; the alarm is there so a stuck always-listening fish (or leaked key) can't surprise you.
If the keys ever leak or a kit fish goes missing: IAM โ user billy-bass โ Security credentials โ deactivate the access key. One click, fish bricked, account safe.
Common Pitfalls: Pasting Code
When you paste code into a terminal/nano over SSH, two things love to go wrong:
- Extra indentation: every pasted line gains leading spaces. ALSA config shrugs; Python explodes (
IndentationError: unexpected indent on line 1is the signature). Fix:sed -i 's/^ //' yourfile.py, or re-paste into nano carefully. - Dropped/mangled characters: a missing comma, or quotes turned into "smart quotes." The habit that saves you: after creating any Python file, run
python -m py_compile yourfile.py && echo OKbefore running it for real. If it doesn't say OK, paste the error to Claude.
Build Strategy
Strategy note: we get the entire voice assistant working on the bare Pi first, and only then open the fish. Software problems and hardware problems are much easier to debug when they can't be each other.
1. Flash the Pi
On your personal computer:
- Download Raspberry Pi Imager from raspberrypi.com/software, insert the microSD.
- Choose: Device = Raspberry Pi 5 ยท OS = Raspberry Pi OS Lite (64-bit) ยท Storage = your card.
โ ๏ธ OS version matters: you need an image with Python 3.12+ (Nova Sonic requirement). The current "Trixie"-based Raspberry Pi OS ships 3.13 โ . Old "Bookworm" images ship 3.11 โ.
"Lite" = no desktop. Correct - you'll never plug in a monitor.
โ ๏ธ Pick "Lite" carefully. In the OS list, expand "Raspberry Pi OS (other)" and choose Raspberry Pi OS Lite (64-bit) - not the regular "Raspberry Pi OS (64-bit)" at the top of the list, which is the Desktop edition. The Desktop edition ships a Wayfire panel that aggressively rewrites ~/.asoundrc on every login, breaking your audio config repeatedly with no obvious cause. Lite has no GUI and no panel.
Already installed Desktop by mistake? It's fixable without re-flashing - sudo systemctl set-default multi-user.target && sudo systemctl disable lightdm.service && sudo reboot makes Desktop boot like Lite. But Lite from the start is cleaner.
When asked about OS customisation, say yes - this is the magic step:
- Hostname:
billy - Username + password: pick and remember them
- WiFi: your network name + password, exactly right (a typo here = mystery no-show later)
- Services tab: enable SSH (password authentication)
- Hostname:
Write, wait, eject. ("Raspberry Pi Connect" prompt: skip it - you can add remote access later with one command if you ever demo this somewhere.)
MicroSD into the slot on the Pi's underside. Plug in the USB-C power supply - there is no power button; plugging in = on.
Wait 2โ3 minutes (first boot resizes things and joins WiFi).
From your computer's terminal: Type
ssh yourusername@billy.localyes (lowercase) at the fingerprint prompt, then your password (the cursor won't move while typing a password - normal).
Gotchas:
Connection resetright after the fingerprint prompt โ the Pi is still finishing first-boot setup. Wait 1โ2 minutes, try again. (If it then complainsREMOTE HOST IDENTIFICATION HAS CHANGED: runssh-keygen -R billy.localand retry - the Pi regenerated its key mid-setup, this is expected.)billy.localnot found โ wait longer; then check your router's device list for "billy" and SSH to its IP. Persistent no-show usually = WiFi password typo โ re-flash.When done for the day: run
sudo shutdown now, wait ~20s, then unplug. Don't just yank power.
2. System Updates & Dependencies
sudo apt update && sudo apt full-upgrade -y
sudo apt install -y python3-dev portaudio19-dev swig git
(portaudio19-dev โ microphone/speaker access for Python. swig โ needed to build the GPIO library later; installing it now avoids a confusing error.)
3. Audio Setup
Plug the USB mic and USB speaker into the Pi, then:
arecord -l # lists MICROPHONES - note your card name, e.g. "Device"
aplay -l # lists SPEAKERS - note yours, e.g. "UACDemoV10" (ignore the two vc4hdmi entries)
nano ~/.asoundrc
Paste, substituting YOUR two card names:
pcm.!default {
type asym
playback.pcm "plug:hw:UACDemoV10"
capture.pcm "plug:hw:Device"
}
ctl.!default {
type hw
card UACDemoV10
}
Save (Ctrl+O, Enter) and exit (Ctrl+X). Then verify the file actually exists - cat ~/.asoundrc should echo it back. (In the original build this file silently failed to save, which surfaced as a baffling crash days later. Trust nothing; cat everything.)
The round-trip test - record 3 seconds of yourself, play it back:
arecord -d 3 -f S16_LE -r 16000 test.wav && aplay test.wav
Hear yourself? The hardest Pi-specific problem is now behind you. (16000 Hz is deliberate - that's Nova Sonic's rate.)
4. Python Environment
python3 -m venv ~/billy/.venv
source ~/billy/.venv/bin/activate
pip install "strands-agents[bidi,bidi-io]" gpiozero lgpio
Notes:
(.venv)appearing in your prompt = venv active. You must re-run thesourceline in every new SSH session - if Python suddenly can't findstrands, that's why.- If
lgpiofails to build mentioningswig: you skipped step 1.3's swig install.sudo apt install -y swig, retry. - Pin your version: this build uses Strands' experimental bidi API and subclasses a private class. Run
pip freeze | grep strandsand write the version down. If a future upgrade breaks things, you can return to it.
5. AWS Credentials on the Pi
Use the keys from the dedicated billy-bass IAM user you created in the prerequisites (the one that can only call Nova Sonic) - never your personal/root AWS keys.
Environment variables vanish every time you log out (this caused a crash mid-build), so do it permanently:
mkdir -p ~/.aws
nano ~/.aws/credentials
[default]
aws_access_key_id = YOUR_KEY_ID
aws_secret_access_key = YOUR_SECRET
nano ~/.aws/config
[default]
region = us-east-1
chmod 600 ~/.aws/credentials
6. The Python Script
nano ~/billy/billy.py
import asyncio
from strands.experimental.bidi import BidiAgent, BidiAudioIO
from strands.experimental.bidi.models import BidiNovaSonicModel
model = BidiNovaSonicModel(
model_id="amazon.nova-2-sonic-v1:0",
provider_config={
"audio": {
"input_rate": 16000,
"output_rate": 16000,
"voice": "matthew",
"channels": 1,
"format": "pcm",
}
},
)
agent = BidiAgent(
model=model,
system_prompt=(
"You are Billy, a wisecracking animatronic singing bass mounted on a "
"wooden plaque. Keep responses short, pu"
),
)
Comments
No comments yet. Start the discussion.