Smart Cities - Phone for Seniors with Vision Impairment
Building a simple telphone for seniors with vision impairment
Building a simple telephone for seniors with vision impairments using a Raspberry Pi can be an enriching and functional project. Below are the steps to create a device that allows seniors to call loved ones, leveraging either the internet (e.g., VoIP) or interfacing with a traditional mobile phone. The design emphasizes ease of use, accessibility, and practicality.
Project Overview
- Features: Large, Accessible Buttons:
- Physical buttons for calling predefined contacts.
- Tactile feedback for easy navigation.
- Voice Assistance: Text-to-Speech (TTS) for confirmation of actions.
- Simple voice commands to perform tasks.
- Internet Calling: Using VoIP services like SIP (Session Initiation Protocol).
- Mobile Phone Interface (Optional): Interfacing with a traditional mobile phone via Bluetooth.
- Speaker and Microphone: High-quality audio input/output for calls.
Materials Required
- Raspberry Pi (Model 3, 4, or Zero 2 W recommended for performance).
- MicroSD card with Raspberry Pi OS installed.
- USB speaker or 3.5mm-compatible speaker.
- USB microphone or headset with a mic.
- Large tactile buttons or a numeric keypad.
- Breadboard and jumper wires (for prototyping buttons).
- A touchscreen or an e-ink display (optional for feedback).
- USB or Bluetooth module (for mobile interfacing, optional).
- Power supply for the Raspberry Pi.
Step 1: Setting Up the Raspberry Pi
Install the OS: Install Raspberry Pi OS on the MicroSD card using the Raspberry Pi Imager. Enable SSH, Wi-Fi, and audio support. Update and Upgrade: Run the following commands:
sudo apt update
sudo apt upgrade
- Install Required Libraries: Install libraries for GPIO, audio processing, and VoIP:
sudo apt install python3-pip pyaudio alsa-utils libportaudio2
pip3 install gpiozero pyttsx3
To use Linphone on a Raspberry Pi for making calls to Australian mobile phones, you will need to install Linphone on your Raspberry Pi, configure it with a VoIP service provider (e.g., SIP provider), and integrate it with Python for initiating calls. Linphone is a powerful, open-source, SIP-based VoIP client that supports both voice and video calls.
Steps to Set Up Linphone for Calling Australian Mobile Phones:
1. Install Linphone on Raspberry Pi
First, you need to install Linphone on your Raspberry Pi. There are a few ways to install it, but the easiest method is using the Raspberry Pi OS repository.
Step 1.1: Install Linphone from the official repository:
sudo apt update
sudo apt install linphone
Alternatively, you can download the Linphone package for Raspberry Pi from the official Linphone download page.
2. Choose a VoIP Service Provider (SIP Provider)
You need a SIP (Session Initiation Protocol) provider that allows you to make calls to Australian mobile numbers. Some reliable SIP providers include:
VoIP.ms (Offers pay-as-you-go plans, and supports calls to landlines and mobiles).
For this tutorial, we will use VoIP.ms as an example. They offer good pricing for calls to Australian mobile numbers.
Step 2.1: Create an Account with VoIP.ms
Visit the VoIP.ms website and create an account. After logging in, you'll be able to obtain a SIP username and password to use in Linphone. Top up your balance to ensure you have credit to make calls to Australian mobile numbers.
Step 2.2: Configure VoIP.ms SIP Credentials
Once you’ve signed up, you'll need the following credentials:
SIP username
SIP password
SIP server (e.g., sip.voip.ms)
3. Configure Linphone for VoIP.ms SIP Account
Step 3.1: Set up Linphone with VoIP.ms
Once Linphone is installed, launch it and configure it with your SIP provider (e.g., VoIP.ms):
- Open Linphone from the Raspberry Pi’s application menu.
- Click on the Settings (gear icon) in Linphone.
- Go to the Accounts tab.
- Click on Add to add a new SIP account.
Username: Enter your SIP username from VoIP.ms.
Password: Enter your SIP password from VoIP.ms.
Domain: Enter sip.voip.ms (or another VoIP provider’s SIP server).
- Enable Account enabled.
- Save the configuration.
Step 3.2: Test Linphone
To ensure your configuration works, you can make a test call to a VoIP number or another SIP user. If the call connects successfully, you are ready to proceed with making calls to Australian mobile phones.
4. Using Python for Linphone Integration
To integrate Linphone with Python, you will need to use the Linphone Python bindings. This allows Python scripts to interact with Linphone, making it possible to initiate calls programmatically.
Using Linphone on a Raspberry Pi with Python requires some preparation because the Python bindings for Linphone are not always straightforward to install via pip. Here's a simpler approach to make Python communicate with Linphone.
1. Use Linphone's CLI and Control It via Python
Linphone provides a command-line interface (CLI) that you can use to control it. Python can communicate with the CLI to send commands and receive outputs.
Step 1: Install Linphone CLI
Install the Linphone CLI on your Raspberry Pi:
sudo apt update
sudo apt install linphone-cli
Verify the installation:
linphonec --version
You should see the version of Linphone CLI installed.
Step 2: Run Linphone CLI
Start the Linphone CLI in a terminal:
linphonec
You'll see a prompt like:
linphonec>
You can now issue commands to make calls, answer calls, and control Linphone.
Example commands:
Register your SIP account:
Username, domain, password
linphonec> register sip:123456@voip.ms sydney1.voip.ms <password>
Make a call:
linphonec> call sip:+614XXXXXXXX@sydney1.voip.ms
Hang up:
linphonec> terminate
Quit:
linphonec> quit
Help: Also useful for finding the correct command syntax. This assisted with the initial registration process.
linphonec> help register
linphonec> help advanced
Step 3: Automate with Python
You can use Python to automate Linphone CLI commands using the subprocess module.
Example Python Script
This script shows how to register, make a call, and hang up using Linphone CLI.
import subprocess
import selectors
import time
def send_command(process, command, delay=1):
"""Sends a command to linphonec and captures the output."""
# Write command to the process
process.stdin.write(command + "\n")
process.stdin.flush()
time.sleep(delay) # Allow some time for output
# Use selectors for non-blocking reading
sel = selectors.DefaultSelector()
sel.register(process.stdout, selectors.EVENT_READ)
output = []
timeout = time.time() + 5 # Set a timeout for reading (5 seconds)
while time.time() < timeout:
events = sel.select(timeout=1)
for key, _ in events:
line = key.fileobj.readline()
if line:
output.append(line.strip())
print(line.strip()) # Optional: Print real-time output for debugging
if "Ready" in line or "success" in line: # Adjust the stopping condition
sel.close()
return "\n".join(output)
sel.close()
return "\n".join(output)
# Start linphonec
process = subprocess.Popen(
["linphonec"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1 # Line-buffered I/O
)
try:
# Register command
register_command = "register sip:123456@voip.ms sydney1.voip.ms <myPassword>"
response = send_command(process, register_command)
print("Register Response:", response)
# Quit linphonec
quit_response = send_command(process, "quit")
print("Quit Response:", quit_response)
finally:
# Cleanup
process.stdin.close()
process.stdout.close()
process.stderr.close()
process.terminate()
ey Changes
- Non-blocking I/O with Selectors: The selectors module monitors stdout for readable events without blocking the script indefinitely.
- Timeout Handling: Added a 5-second timeout to prevent readline() from hanging forever if linphonec doesn't produce output.
- Dynamic Stopping Condition: Stops reading when specific keywords like "Ready" or "success" are detected in the output.
- Real-Time Debugging Output: Prints each line of output as it's read, making it easier to debug what linphonec is returning.
- Adjust the Stopping Condition: Replace "Ready" or "success" in the send_command function with specific keywords or patterns you expect from linphonec.
- Testing Interactivity: If the script still hangs, verify the behavior of linphonec manually by typing the same commands in a terminal.
Python code with GPIO buttons
Here's the modified Python code that integrates GPIO buttons for calling and terminating a call while keeping the existing functionality.
Key Modifications:
- Added GPIO Initialization: Configures GPIO pins for the call and terminate buttons.
- Loop for Button Interaction: Monitors the buttons and sends the corresponding SIP commands.
- Uses send_command Function: Handles the command execution and ensures proper output handling.
import subprocess
import selectors
import time
import RPi.GPIO as GPIO
# GPIO Pin Configuration
CALL_BUTTON_PIN = 18 # GPIO pin for the call button
END_BUTTON_PIN = 23 # GPIO pin for the terminate button
# Configure GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(CALL_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull-up resistor for call button
GPIO.setup(END_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull-up resistor for end button
def send_command(process, command, delay=1):
"""Sends a command to linphonec and captures the output."""
process.stdin.write(command + "\n")
process.stdin.flush()
time.sleep(delay)
# Use selectors for non-blocking reading
sel = selectors.DefaultSelector()
sel.register(process.stdout, selectors.EVENT_READ)
output = []
timeout = time.time() + 5 # Set a timeout for reading (5 seconds)
while time.time() < timeout:
events = sel.select(timeout=1)
for key, _ in events:
line = key.fileobj.readline()
if line:
output.append(line.strip())
print(line.strip()) # Optional: Print real-time output for debugging
if "Ready" in line or "success" in line: # Adjust the stopping condition
sel.close()
return "\n".join(output)
sel.close()
return "\n".join(output)
# Start linphonec process
process = subprocess.Popen(
["linphonec"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1 # Line-buffered I/O
)
try:
# Send the register command
register_command = "register sip:123456@voip.ms sydney1.voip.ms <myPassword>"
response = send_command(process, register_command)
print("Register Response:", response)
print("Ready for button interactions...")
while True:
# Check if the call button is pressed
if GPIO.input(CALL_BUTTON_PIN) == GPIO.LOW: # Button pressed (LOW)
print("Call button pressed. Initiating call...")
call_command = "call sip:+61412978633@sydney1.voip.ms"
response = send_command(process, call_command)
print("Call Response:", response)
time.sleep(0.5) # Debounce delay
# Check if the end button is pressed
if GPIO.input(END_BUTTON_PIN) == GPIO.LOW: # Button pressed (LOW)
print("End button pressed. Terminating call...")
terminate_command = "terminate"
response = send_command(process, terminate_command)
print("Terminate Response:", response)
time.sleep(0.5) # Debounce delay
time.sleep(0.1) # Short delay to prevent high CPU usage
finally:
print("Cleaning up resources...")
GPIO.cleanup() # Clean up GPIO
quit_response = send_command(process, "quit")
print("Quit Response:", quit_response)
process.stdin.close()
process.stdout.close()
process.stderr.close()
process.terminate()
Python with GPIO pins as buttons
Here's the modified Python code that integrates GPIO buttons for calling and terminating a call while keeping the existing functionality.
Key Modifications:
- Added GPIO Initialization: Configures GPIO pins for the call and terminate buttons.
- Loop for Button Interaction: Monitors the buttons and sends the corresponding SIP commands.
- Uses send_command Function: Handles the command execution and ensures proper output handling.
import subprocess
import selectors
import time
import RPi.GPIO as GPIO
# GPIO Pin Configuration
CALL_BUTTON_PIN = 18 # GPIO pin for the call button
END_BUTTON_PIN = 23 # GPIO pin for the terminate button
# Configure GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(CALL_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull-up resistor for call button
GPIO.setup(END_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull-up resistor for end button
def send_command(process, command, delay=1):
"""Sends a command to linphonec and captures the output."""
process.stdin.write(command + "\n")
process.stdin.flush()
time.sleep(delay)
# Use selectors for non-blocking reading
sel = selectors.DefaultSelector()
sel.register(process.stdout, selectors.EVENT_READ)
output = []
timeout = time.time() + 5 # Set a timeout for reading (5 seconds)
while time.time() < timeout:
events = sel.select(timeout=1)
for key, _ in events:
line = key.fileobj.readline()
if line:
output.append(line.strip())
print(line.strip()) # Optional: Print real-time output for debugging
if "Ready" in line or "success" in line: # Adjust the stopping condition
sel.close()
return "\n".join(output)
sel.close()
return "\n".join(output)
# Start linphonec process
process = subprocess.Popen(
["linphonec"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1 # Line-buffered I/O
)
try:
# Send the register command
register_command = "register sip:435211@example.com mypassword sip:proxy.example.com"
response = send_command(process, register_command)
print("Register Response:", response)
print("Ready for button interactions...")
while True:
# Check if the call button is pressed
if GPIO.input(CALL_BUTTON_PIN) == GPIO.LOW: # Button pressed (LOW)
print("Call button pressed. Initiating call...")
call_command = "call sip:+61412978633@sydney1.voip.ms"
response = send_command(process, call_command)
print("Call Response:", response)
time.sleep(0.5) # Debounce delay
# Check if the end button is pressed
if GPIO.input(END_BUTTON_PIN) == GPIO.LOW: # Button pressed (LOW)
print("End button pressed. Terminating call...")
terminate_command = "terminate"
response = send_command(process, terminate_command)
print("Terminate Response:", response)
time.sleep(0.5) # Debounce delay
time.sleep(0.1) # Short delay to prevent high CPU usage
finally:
print("Cleaning up resources...")
GPIO.cleanup() # Clean up GPIO
quit_response = send_command(process, "quit")
print("Quit Response:", quit_response)
process.stdin.close()
process.stdout.close()
process.stderr.close()
process.terminate()
How It Works:
- Registration: The script sends the registration command to linphonec upon startup.
Button Press Detection:
- When the call button is pressed, the call command is sent to initiate the call.
- When the end button is pressed, the terminate command is sent to end the call.
- Debouncing: A short delay prevents multiple signals from being processed due to button bounce.
- Cleanup: Ensures GPIO pins are cleaned up and the Linphone process is terminated gracefully when the script exits.
Wiring the Buttons:
- Connect one side of each button to the respective GPIO pin (e.g., GPIO 18 for the call button, GPIO 23 for the end button).
- Connect the other side of each button to ground (GND).
- Use internal pull-up resistors (GPIO.PUD_UP) to ensure stable signals.
Adding Speech Synthesis
To add simple speech synthesis to your code so that you can say "Call Edmond now..." when the call button is pressed, you can use the pyttsx3 library for text-to-speech synthesis. This library works offline and is compatible with Raspberry Pi.
Here's how you can modify your code to include speech synthesis:
import subprocess
import selectors
import time
import RPi.GPIO as GPIO
import pyttsx3 # Import text-to-speech library
# GPIO Pin Configuration
CALL_BUTTON_PIN = 18 # GPIO pin for the call button
END_BUTTON_PIN = 23 # GPIO pin for the terminate button
# Configure GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(CALL_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull-up resistor for call button
GPIO.setup(END_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull-up resistor for end button
# Initialize text-to-speech engine
speech_engine = pyttsx3.init()
def say_message(message):
"""Speaks the given message using text-to-speech."""
speech_engine.say(message)
speech_engine.runAndWait()
def send_command(process, command, delay=1):
"""Sends a command to linphonec and captures the output."""
process.stdin.write(command + "\n")
process.stdin.flush()
time.sleep(delay)
# Use selectors for non-blocking reading
sel = selectors.DefaultSelector()
sel.register(process.stdout, selectors.EVENT_READ)
output = []
timeout = time.time() + 5 # Set a timeout for reading (5 seconds)
while time.time() < timeout:
events = sel.select(timeout=1)
for key, _ in events:
line = key.fileobj.readline()
if line:
output.append(line.strip())
print(line.strip()) # Optional: Print real-time output for debugging
if "Ready" in line or "success" in line: # Adjust the stopping condition
sel.close()
return "\n".join(output)
sel.close()
return "\n".join(output)
# Start linphonec process
process = subprocess.Popen(
["linphonec"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1 # Line-buffered I/O
)
try:
# Send the register command
register_command = "register sip:12345@example.com mypassword sip:proxy.example.com"
response = send_command(process, register_command)
print("Register Response:", response)
print("Ready for button interactions...")
while True:
# Check if the call button is pressed
if GPIO.input(CALL_BUTTON_PIN) == GPIO.LOW: # Button pressed (LOW)
print("Call button pressed. Initiating call...")
say_message("Call Edmond now.") # Speak message
call_command = "call sip:+614XXXXXXXX@sydney1.voip.ms"
response = send_command(process, call_command)
print("Call Response:", response)
time.sleep(0.5) # Debounce delay
# Check if the end button is pressed
if GPIO.input(END_BUTTON_PIN) == GPIO.LOW: # Button pressed (LOW)
print("End button pressed. Terminating call...")
terminate_command = "terminate"
response = send_command(process, terminate_command)
print("Terminate Response:", response)
time.sleep(0.5) # Debounce delay
time.sleep(0.1) # Short delay to prevent high CPU usage
finally:
print("Cleaning up resources...")
GPIO.cleanup() # Clean up GPIO
quit_response = send_command(process, "quit")
print("Quit Response:", quit_response)
process.stdin.close()
process.stdout.close()
process.stderr.close()
process.terminate()
Install Dependencies
Run the following command on your Raspberry Pi to install the required library:
pip install pyttsx3
Dependencies for pyttsx3: Ensure that required libraries (e.g., espeak, espeak-ng) are installed. On Raspberry Pi:
sudo apt-get install espeak-ng
Notes
- Voice Customization: You can customize the voice, rate, and volume of the speech by adjusting the pyttsx3 engine settings. For example:
- engine.setProperty('rate', 150) # Speed of speech
- engine.setProperty('volume', 0.9) # Volume (0.0 to 1.0)
- Performance: The speech synthesis might introduce slight delays, but they are generally negligible for this use case.
Testing: Test the speech synthesis and ensure the GPIO buttons work as intended.