100%
millennium phonev6 view
Description

Imported from GitHub: kmatzen/millennium · commit 4867b4f · license MIT

Description

I took an old pay phone from the 2000's and replaced the brains with a raspberry pi and some arduinos.

README

Millennium Project

The Millennium Project breathes new life into the Nortel Millennium public telephone by replacing much of its original electronics with modern hardware and software. The project integrates a Raspberry Pi Zero 2 W, two Arduinos, and a VoIP software stack while retaining all the original hardware, including the display, keypad, coin acceptor, magstripe reader, handset, and ringer speaker.

Table of Contents

  1. Overview
  2. Architecture
  3. Features
  4. Directory Structure
  5. Setup
  6. Configuration
  7. Development
  8. Resources
  9. License

Overview

This project reimagines the functionality of the Nortel Millennium telephone by combining modern hardware and software while preserving its iconic hardware features:

  • Raspberry Pi Zero 2 W: Manages the system, runs the VoIP stack, and interfaces with the original hardware.
  • Arduinos: Control the keypad, display, and other peripherals.
  • Custom PCB: Consolidates the connections between components.
  • Host Software: Includes a daemon, plugin system, web dashboard, and ALSA audio configuration.
  • Preserved Hardware: The original display, keypad, coin acceptor, magstripe reader, handset, and ringer speaker are retained and functional.

Architecture

┌────────────────────────── Raspberry Pi Zero 2 W ───────────────────────────┐
│                                                                            │
│  ┌──────────────┐     ┌──────────────┐      ┌──────────────────────────┐   │
│  │ Daemon       │────▶│ PJSIP (VoIP) │      │ Web Dashboard :80        │   │
│  │              │     └──────────────┘      │                          │   │
│  │ Plugins:     │                           │ - phone state            │   │
│  │ - Phone      │     ┌──────────────┐      │ - plugin switching       │   │
│  │ - Fortune    │     │ ALSA Audio   │      │ - OTA updates            │   │
│  │ - Jukebox    │     └──────────────┘      │ - health / metrics       │   │
│  │ - +4 games   │                           └──────────────────────────┘   │
│  └──────┬───────┘                                                          │
│         │ USB serial (9600 baud)                                           │
└─────────┬──────────────────────────────────────────────────────────────────┘
          │
┌─────────┴───────────────── Custom PCB (phonev6) ───────────────────────────┐
│                                                                            │
│  ┌──────────────────────┐ I2C      ┌────────────────────────┐              │
│  │ Display Arduino      │◀─────────│ Keypad Arduino         │              │
│  │ (millennium_beta)    │          │ (millennium_alpha)     │              │
│  │                      │          │                        │              │
│  │ - VFD display        │          │ - 4x7 keypad           │              │
│  │   (parallel 8-bit)   │          │ - magstripe reader     │              │
│  │ - coin validator     │          │ - hook switch          │              │
│  │   (SoftSerial)       │          └────────────────────────┘              │
│  └──────────────────────┘                                                  │
└────────────────────────────────────────────────────────────────────────────┘

Data flow: Keypad/hook/card events originate on the Keypad Arduino, are sent via I2C to the Display Arduino, which forwards them to the Raspberry Pi over USB serial. The daemon processes events through an event processor, dispatches them to the active plugin, and sends display updates back down the same path. VoIP calls are handled by PJSIP (PJSUA C API) with audio routed through ALSA (dmix + route plugins for mono channel splitting).


Features

  • Pay phone operation: Insert coins, dial numbers, make VoIP calls — authentic payphone experience
  • Plugin system: Swap modes live — Classic Phone, Fortune Teller, Jukebox, plus games (Number Guess, Simon, Dial-A-Joke, Trivia). Write your own against the plugin SDK; new plugins appear in the dashboard automatically
  • Emergency calls: Dial 911, 311, or 0 without coins (configurable free numbers)
  • Magstripe card support: Swipe a registered card for free calling or admin access
  • Web dashboard: Real-time phone state, plugin switching, health monitoring via WebSocket
  • OTA updates: Check for and apply updates from the web dashboard (git pull, build, restart)
  • Audio tones: DTMF, dial tone, busy tone, coin tone, ringback via ALSA
  • Idle timeout: Automatically resets phone state after configurable inactivity period
  • State persistence: Saves and restores coin balance and plugin state across restarts
  • Health monitoring: Tracks serial connection, SIP registration, and daemon activity
  • Metrics collection: Counters for calls, coins, keypresses, card swipes
  • Version tracking: Build version and git hash displayed on dashboard
  • Scenario testing: Simulator-based integration tests with simulated time

Directory Structure

  • Arduino/: Arduino sketches for the keypad and display microcontrollers, plus a Makefile for building and flashing.
  • case/: 3D model files (.blend and .stl) for a custom enclosure. See case/README.md for dimensions and print settings.
  • pcb/: KiCad schematic, PCB layout, BOM, and Gerber files for the custom PCB (phonev6).
  • HARDWARE.md: Physical assembly reference — USB topology, cable routing, power budget, and component list.
  • host/: Raspberry Pi software:
    • daemon.c — Main daemon loop and event routing
    • plugins/ — Plugin implementations (classic_phone, fortune_teller, jukebox, number_guess, simon, dial_a_joke, trivia)
    • plugin_sdk.c — Plugin authoring facade (see PLUGIN_AUTHORING.md)
    • web_server.c — HTTP server with dashboard and REST API
    • audio_tones.c — Tone generation (DTMF, dial tone, etc.)
    • updater.c — OTA update checker and applier
    • simulator.c — Test runner with scenario file support and simulated time
    • tests/ — Unit tests and scenario test files
    • systemd/ — Service file for the daemon
    • daemon.conf.example — Example configuration file
    • asoundrc.example — ALSA audio configuration for mono channel splitting

Setup

1. Arduino Firmware

Navigate to the Arduino/ directory and follow the instructions in its README to build and flash firmware to the microcontrollers.

2. PCB

The PCB files are in the pcb/ directory. Upload the Gerber files to a PCB fabricator like JLCPCB for manufacturing. See the pcb README for details.

3. Host Software

Follow the setup guide for full step-by-step instructions, or use the quick install:

cd host
make daemon        # Build the daemon
sudo make install  # Install to /usr/local/bin and set up systemd

See the host README for dependency installation (PJSIP, ALSA) and audio configuration.

4. Configuration

Copy and edit the configuration file:

sudo cp host/daemon.conf.example /etc/millennium/daemon.conf
sudo nano /etc/millennium/daemon.conf

See Configuration below for all available options.


Configuration

The daemon reads configuration from /etc/millennium/daemon.conf. See host/daemon.conf.example for a complete annotated example. Key settings:

KeyDefaultDescription
hardware.display_device/dev/serial/by-id/usb-Arduino_LLC_Millennium_Beta-if00Serial device for display Arduino
hardware.baud_rate9600Serial baud rate
call.cost_cents50Cost per call in cents
call.timeout_seconds300Maximum call duration
call.free_numbers911,311,0Comma-separated numbers that bypass coin requirement
call.idle_timeout_seconds60Seconds of inactivity before phone resets
card.enabledtrueEnable magstripe card support
card.free_cards(empty)Comma-separated card numbers for free calling
card.admin_cards(empty)Comma-separated card numbers for admin access
web_server.enabledtrueEnable the web dashboard
web_server.port80Web dashboard port
system.source_dir/home/matzen/millenniumSource directory for OTA updates

Development

Building and testing

cd host
make test          # Build simulator + unit tests, run both
make daemon        # Build the full daemon (requires PJSIP/libpjproject on the Pi)
make clean         # Remove all build artifacts

Running tests locally

Tests run on any platform (macOS, Linux) without PJSIP or ALSA — the simulator stubs out hardware dependencies:

cd host
make test

This runs:

  • Unit tests: Config, daemon state, plugins, emergency numbers, card config, updater
  • Scenario tests: Full integration tests (basic call, timeout, emergency, card swipe, hook lifecycle, state persistence, display scrolling, idle timeout)

Adding a plugin

See host/PLUGIN_AUTHORING.md for the full guide. In short:

  1. Create host/plugins/your_plugin.c (write it against plugin_sdk.h)
  2. Implement handler functions for the events you care about (coin, keypad, hook, call state, card, tick)
  3. Register with plugins_register() in a register_your_plugin() function
  4. Call the registration function from plugins_init() in plugins.c
  5. Add the .o file to the Makefile build targets (daemon, SIM_OBJS, UNIT_TEST_OBJS)
  6. Run make test — your plugin then appears in the dashboard automatically

Coding style

  • C89 across the whole codebase, compiled with -std=gnu89 (the C89 language plus the GNU extensions the ALSA/PJSIP headers require, e.g. inline; strict -std=c89 can't parse those headers)
  • -Wall -Wextra -Wdeclaration-after-statement -Werror on every target — warnings are errors, and -Wdeclaration-after-statement keeps the source free of C99-style mixed declarations
  • No unnecessary comments — code should be self-documenting

Resources

This project draws on several resources for understanding and interfacing with the Nortel Millennium telephone's hardware:

  1. General Millennium Payphone Documentation The Millennium Payphone Wiki provides extensive documentation on the phone's hardware and protocols.

    • Coin Validator Protocol: Used the documented higher-level protocol to interface with the coin validator.
    • EEPROM Data: Referenced for configuring the coin validator to accept Canadian currency.
  2. Display Documentation A detailed discussion on the Noritake CU20026SCPB-T23A VFD display can be found in this HardForum thread. This was essential for understanding and controlling the display.

  3. Device Pinouts The New Fire Millennium Payphone Page includes pinout diagrams for various components, which were critical when developing the Arduino sketches.

  4. Millennium Phone Source The Millennium phone used in this project was sourced from Ballard Reuse in Seattle, a store specializing in salvaged and reusable materials.


License

This project is licensed under the MIT License. See LICENSE for details.

Comments
Sign in to comment

No comments yet. Be the first to ask about this board.