Skip to content

Repository files navigation

RPi Echo (EchoBerry)

A two-site audio installation linking Montreal and Newfoundland through live sound.

EchoBerry (2021) connects distant places with voice, forest ambience, and a ringing bell. When someone opens the enclosure at Montreal, audio flows outward — mixed with the sound of the forest — while Newfoundland listens in and sends its own stream back. The result is a quiet, long-distance conversation between two Raspberry Pi installations bridged over Icecast.

Note: This project documents a completed art installation. It is maintained for archival and redeployment reference, not active product development.

Design reference: Figma — Echo

Sites: YUL — Montreal · YDF — Newfoundland

Documentation

Doc Audience
README.md Deploy the installation
CONTRIBUTING.md Change code or docs
CODE_OF_CONDUCT.md Community standards
SECURITY.md Report vulnerabilities, handle secrets
docs/architecture.md How YUL/YDF, services, and streams fit together
docs/troubleshooting.md Common deploy and runtime issues
docs/documentation/README.md Hardware photo captions

Components

Project layout

echoberry/
├── config.example.yaml   # copy to config.yaml (gitignored)
├── requirements.txt
├── LICENSE
├── SECURITY.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── src/
│   ├── main.py           # YUL switch controller
│   ├── config.py
│   ├── audio_player.py
│   └── utils.py
├── scripts/
│   ├── install.sh        # unified installer (yul | ydf)
│   ├── install_ffmpeg.sh # legacy FFmpeg build
│   ├── render_configs.py
│   ├── validate_config.py
│   └── render_icecast.sh
├── conf/                 # templates + rendered configs (gitignored outputs)
├── systemd/
├── sounds/
│   ├── ring.mp3
│   └── forest_1h.mp3
├── docs/
│   ├── architecture.md
│   ├── troubleshooting.md
│   └── documentation/    # wiring/install photos
└── .github/
    └── workflows/ci.yml

Quick start

1. Configure

cp config.example.yaml config.yaml
# Edit config.yaml: set server host, passwords, and location (yul or ydf)

Never commit config.yaml — it contains secrets. The application refuses to start without a deployed config.yaml.

2. Set up the Icecast server

On your streaming server, clone this repo and create a local config (same secrets the Pis will use):

git clone https://github.com/adamsimms/echoberry
cd echoberry
cp config.example.yaml config.yaml
# edit config.yaml: server.host, passwords, etc.

Build and run Icecast-KH (or install Icecast2 from your distro):

sudo apt-get install build-essential libxslt-dev libogg-dev libvorbis-dev
git clone https://github.com/karlheyes/icecast-kh
cd icecast-kh && ./configure && make && sudo make install

Render and install the server config from the echoberry repo:

cd ~/echoberry
bash scripts/render_icecast.sh
sudo cp conf/icecast.xml /usr/local/etc/icecast.xml
icecast -c /usr/local/etc/icecast.xml

Admin UI: http://<server-host>:<port>/admin.html

3. Set up a Raspberry Pi

Use Raspberry Pi OS (Lite is fine).

cd ~
git clone https://github.com/adamsimms/echoberry
cd echoberry
git lfs install && git lfs pull
cp config.example.yaml config.yaml
# edit config.yaml

Montreal (YUL):

bash scripts/install.sh yul

Newfoundland (YDF):

bash scripts/install.sh ydf

install.sh syncs config.yaml location to match the install target and writes /etc/echoberry/*.env for systemd.

4. Manual streaming (debug)

Stream mic + looping forest ambience to YUL mount:

ffmpeg -ac 1 -re -f alsa -i hw:1,0 -stream_loop -1 -re -i sounds/forest_1h.mp3 \
  -filter_complex amerge=inputs=2 -f mp3 \
  icecast://source:<password>@<host>:<port>/echoberry-yul

Listen:

mpv http://<host>:<port>/echoberry-yul
# USB headset:
mpv --audio-device=alsa/hw=1.0 http://<host>:<port>/echoberry-yul

YDF uses darkice (configured by install.sh ydf) and auto-listens to the YUL stream via echoberry-listener.service.

Services

Service Location Purpose
darkice.service YUL, YDF Continuous mic stream to Icecast
echoberry.service YUL GPIO switch → ring + ffmpeg stream + remote listen
echoberry-listener.service YDF Plays remote YUL stream on boot
sudo systemctl status darkice echoberry          # YUL
sudo systemctl status darkice echoberry-listener # YDF

See docs/architecture.md for a full system diagram.

YUL dual streaming (intentional)

At YUL, both darkice and the switch-triggered ffmpeg stream target the same mount. This is deliberate for the installation: darkice provides a continuous baseline mic feed, while opening the switch layers ring + forest ambience via ffmpeg.

Darkice local recording (intentional)

darkice writes recording.m4a locally as an archive of the live stream. This is intentional for the art installation.

Audio assets

  • sounds/ring.mp3 — played locally when the switch opens
  • sounds/forest_1h.mp3 — mixed under the live mic stream, looped indefinitely (~83 MB, Git LFS)

Forest looping is controlled by audio.forest_stream_loop in config.yaml (default -1 = infinite).

If Git LFS is not installed: git lfs install && git lfs pull

Configuration

Key config.yaml fields:

Key Purpose
location Active site (yul or ydf); synced by install.sh
server.host Icecast server hostname or IP
server.port Icecast port (default 8000)
server.source_password Password for stream sources (darkice, ffmpeg)
server.admin_user / server.admin_password Icecast admin UI credentials
gpio.switch_pin BCM pin for magnetic switch (default 4)
audio.alsa_device ALSA capture device (e.g. hw:1,0)
audio.playback_device Local playback device (e.g. hw=1.0)
audio.ring_file Ring tone path (default sounds/ring.mp3)
audio.forest_file Forest ambience path (default sounds/forest_1h.mp3)
audio.forest_stream_loop ffmpeg loop count (-1 = infinite)
install.repo_root Install path (auto-set by install.sh)
install.amixer_analog_cmd Pi analogue audio routing command

Copy from config.example.yaml and see inline comments for location-specific mount names.

Security

See SECURITY.md. Summary:

  • Do not commit private keys, PEM files, or config.yaml.
  • Rotate credentials that were previously in git (especially the old Echo-Server.pem key).
  • Echo-Server.pem has been removed from the tree and purged from master git history.

Development

See CONTRIBUTING.md for the full workflow. Quick checks:

pip install PyYAML
python3 -m py_compile src/*.py scripts/*.py
python3 scripts/validate_config.py
shellcheck scripts/*.sh   # optional, matches CI

CI runs compile, config validation, and shellcheck on push/PR. GPIO and audio streaming require a Raspberry Pi to test fully.

Legacy install

scripts/install_ffmpeg.sh builds FFmpeg from source for very old ARM systems. Modern installs use apt-get install ffmpeg via scripts/install.sh.

License

MIT

About

Two-site audio art installation linking Montreal and Newfoundland through live forest ambience, voice echos.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages