A Raspberry Pi emergency device that turns one deliberate button press into a location-aware SMS alert for trusted contacts.
PEL is a capstone prototype for situations where a phone may be unavailable or too slow to operate. It combines a physical hold-to-arm control, LTE messaging, GNSS location, clear LED/buzzer feedback, and a cancel window in a small offline-first device. The design favors deliberate activation and local operation over cloud dependencies.
Caution
PEL is an educational prototype, not a certified life-safety device. Test the full alert path regularly and never use it as the only way to contact emergency services.
When the button is pressed and held, the device acquires a GPS fix via the SIMCOM A7670E LTE module's built-in GNSS, builds a Google Maps link, and sends personalized SMS messages to all configured emergency contacts.
- One-button operation — press and hold for 3 seconds to arm, with a cancel window
- Real-time GPS — GNSS coordinates via A7670E built-in receiver
- LTE SMS delivery — sends alerts over LTE Cat-1 (no 2G dependency)
- Multi-contact support — configurable list of emergency contacts
- Customizable SMS template — personalized messages with contact name, owner name, and map link
- Audio/visual feedback — buzzer patterns and LED indicators for every state
- Local event logging — all panic events logged to file with timestamps and coordinates
- 4-phase safety sequence — hold-to-arm → confirmation → cancel window → execute
| Component | Specification |
|---|---|
| Microcontroller | Raspberry Pi (any model with GPIO — Pi 3/4/5 recommended) |
| LTE + GPS Module | SIMCOM A7670E (LTE Cat-1 with built-in GNSS) |
| Relay Module | 5V relay module (Active LOW) with buzzer |
| Green LED | Standard LED + 220Ω–330Ω resistor |
| Red LED | Standard LED + 220Ω–330Ω resistor |
| Push Button | Momentary push button (normally open) |
| SIM Card | Nano/Micro SIM with SMS plan and data (for LTE registration) |
| Power Supply | 5V 2A dedicated supply for A7670E (not from Pi GPIO 5V pin) |
| BCM Pin | Component | Function |
|---|---|---|
| GPIO 14 (TXD) | A7670E RXD | Pi transmits AT commands to module |
| GPIO 15 (RXD) | A7670E TXD | Module sends responses to Pi |
| GPIO 17 | Green LED | Success / status indicator |
| GPIO 27 | Red LED | Error / arming indicator |
| GPIO 18 | Relay IN | Controls active buzzer (Active LOW) |
| GPIO 22 | Push Button | Panic trigger (internal pull-up, press = LOW) |
| A7670E Pin | Connect To | Notes |
|---|---|---|
| TXD | GPIO 15 (Pi RXD) | A7670E sends data to Pi |
| RXD | GPIO 14 (Pi TXD) | Pi sends AT commands to A7670E |
| GND | Pi GND | Common ground |
| VCC / 5V | Dedicated 5V 2A supply | Do NOT power from Pi 5V pin |
Important: The A7670E draws up to 2A during transmission bursts. Use a dedicated regulated 5V supply. Share a common GND with the Pi.
| Relay Pin | Connect To | Notes |
|---|---|---|
| VCC | 5V | Power |
| GND | Pi GND | Common ground |
| IN | GPIO 18 | Active LOW (configurable in config.py) |
| LED | Anode (+) | Cathode (−) | Notes |
|---|---|---|---|
| Green | GPIO 17 | Pi GND | Via 220Ω–330Ω resistor |
| Red | GPIO 27 | Pi GND | Via 220Ω–330Ω resistor |
| Pin | Connect To | Notes |
|---|---|---|
| One side | GPIO 22 | Internal pull-up enabled in software |
| Other side | Pi GND | Press = LOW signal |
The A7670E communicates over the Pi's hardware UART. On Pi 3/4/5, Bluetooth uses this UART by default and must be disabled.
Add to /boot/config.txt:
dtoverlay=disable-bt
enable_uart=1
Edit /boot/cmdline.txt and remove console=serial0,115200 if present.
Reboot after making these changes:
sudo rebootsudo systemctl disable hciuartAfter reboot, confirm /dev/serial0 exists:
ls -la /dev/serial0git clone https://github.com/CharlesNaig/PEL.git
cd PELpip install -r requirements.txtThis installs:
pyserial— UART communication with A7670ERPi.GPIO— GPIO control for LEDs, relay, and button
Contact details are intentionally excluded from Git. Create a local deployment file:
cp config/contacts.example.json config/contacts.jsonEdit config/contacts.json with trusted contacts in E.164 format, then set the owner
label without changing tracked source:
export PEL_OWNER_NAME="My PEL Device"For systemd or a secrets-mounted deployment, set PEL_CONTACTS_FILE to an absolute
JSON path. Never commit real contact details, SIM information, or alert logs.
If your wiring differs from the default, update the pin assignments in main/config.py:
PIN_GREEN_LED = 17
PIN_RED_LED = 27
PIN_RELAY = 18
PIN_BUTTON = 22sudo python3 main/main.py
sudois required for GPIO access on most Pi configurations.
The system uses a 4-phase safety sequence to prevent accidental triggers:
| Phase | Action | Feedback |
|---|---|---|
| 1 — Hold to Arm | Press and hold button for 3 seconds | Red LED blinks, buzzer ticks once per second |
| 2 — Armed Confirm | System confirms arm state | Both LEDs flash, double beep |
| 3 — Cancel Window | Release button; 3-second window to press again to cancel | Green LED blinks fast |
| 4 — Execute | GPS acquisition → SMS to all contacts → log event | Green = success, Red = failure |
To cancel: Press the button during Phase 3 (the cancel window). You'll hear a cancel sound and see both LEDs turn off.
During idle, the green LED is solid ON, indicating the system is ready. The main loop polls the button every 50ms.
All settings are in main/config.py:
| Setting | Default | Description |
|---|---|---|
PEL_OWNER_NAME |
"PEL Device" |
Environment variable used in SMS messages |
PEL_CONTACTS_FILE |
config/contacts.json |
Path to the untracked private contact list |
SMS_TEMPLATE |
Emergency alert | Message template with placeholders |
PIN_GREEN_LED |
17 |
BCM pin for green LED |
PIN_RED_LED |
27 |
BCM pin for red LED |
PIN_RELAY |
18 |
BCM pin for relay/buzzer |
PIN_BUTTON |
22 |
BCM pin for push button |
SERIAL_PORT |
"/dev/serial0" |
UART device path |
SERIAL_BAUD |
115200 |
Primary baud rate (fallback: 9600) |
GPS_TIMEOUT |
30 |
Seconds to wait for GNSS fix |
ARM_HOLD_TIME |
3.0 |
Seconds button must be held to arm |
CANCEL_WINDOW |
3.0 |
Seconds to cancel after arming |
SMS_RETRY_COUNT |
3 |
SMS send retries per contact |
LOG_FILE |
"logs.txt" |
Event log file path |
RELAY_ACTIVE_LOW |
True |
Set False if relay activates on HIGH |
PEL/
├── assets/ # Portfolio branding
├── config/
│ └── contacts.example.json # Safe deployment template
├── main/
│ ├── main.py # Application entry point (setup, loop, shutdown)
│ ├── config.py # Hardware settings and private contact loader
│ ├── a7670e.py # A7670E LTE driver (AT commands, GNSS, SMS)
│ ├── buzzer.py # Relay/buzzer control (patterns, Active LOW)
│ ├── led.py # LED control (solid, blink, threaded patterns)
│ ├── logger.py # File-based event logging
│ └── panic.py # 4-phase panic state machine
├── tests/
│ ├── test_a7670e.py # A7670E 9-test diagnostic suite + AT terminal
│ ├── test_buzzer_relay.py # Buzzer pattern verification (6 tests)
│ └── test_led.py # LED pattern verification (8 tests)
├── requirements.txt # Python dependencies
└── README.md
| Module | Lines | Purpose |
|---|---|---|
| main/a7670e.py | ~590 | Full A7670E driver — serial init with auto baud detection, AT command engine, SIM/signal/registration checks, GNSS enable/disable/acquire/parse, SMS send with retry logic |
| main/panic.py | ~215 | Core state machine — Phase 1 (hold-to-arm), Phase 2 (confirmation), Phase 3 (cancel window), Phase 4 (GPS → SMS → log → feedback) |
| main/led.py | ~165 | LED helpers with threaded non-blocking blink — blink_red(), blink_green(), blink_both(), solid_green(), etc. |
| main/main.py | ~115 | Entry point — GPIO setup, A7670E initialization, button polling loop, graceful shutdown with GPIO.cleanup() |
| main/buzzer.py | ~105 | Relay/buzzer patterns — tick(), double_beep(), cancel_sound(), success_sound(), fail_sound() |
| main/config.py | ~95 | All deployment settings — contacts, pins, timings, UART, SMS template |
| main/logger.py | ~75 | Event logger — writes structured log entries with GNSS or system timestamps |
Run these to verify individual hardware components before full deployment.
sudo python3 tests/test_a7670e.pyRuns 9 sequential tests:
- AT communication
- Module information
- SIM card status
- Signal quality (RSSI)
- Network registration
- Operator info
- GNSS power on
- GPS fix acquisition
- SMS send (interactive)
After tests complete, enters an interactive AT terminal for manual commands.
sudo python3 tests/test_buzzer_relay.pyTests 6 buzzer patterns: tick, double beep, cancel sound, success sound, fail sound, and continuous ON.
sudo python3 tests/test_led.pyTests 8 LED patterns: solid green, solid red, solid both, red blink fast, green blink slow, green blink fast, alternating, and rapid cycle.
- Verify
/dev/serial0exists (ls -la /dev/serial0) - Confirm
dtoverlay=disable-btandenable_uart=1are in/boot/config.txt - Check that
console=serial0,115200is removed from/boot/cmdline.txt - Ensure the A7670E has a dedicated 5V 2A power supply
- Run
sudo python3 tests/test_a7670e.pyfor diagnostics
- Ensure clear sky view (GNSS needs line-of-sight to satellites)
- First cold fix may take 30–60 seconds; subsequent fixes are faster
- Check that the A7670E's GNSS antenna is connected
- Increase
GPS_TIMEOUTin main/config.py if needed
- Verify SIM card is inserted and has SMS credit
- Check signal quality: RSSI should be > 10 (run test_a7670e.py, Test 4)
- Ensure network registration shows "home" or "roaming" (Test 5)
- Verify contact numbers include country code (e.g.,
+63for Philippines)
- Confirm GPIO 22 wiring: one side to GPIO 22, other side to GND
- The software enables an internal pull-up resistor; no external pull-up needed
- Check
PIN_BUTTONvalue in main/config.py
- Check
RELAY_ACTIVE_LOWin main/config.py matches your relay module - Verify relay module has separate 5V power
- Run
sudo python3 tests/test_buzzer_relay.pyto test patterns
- GPIO access requires root: use
sudo python3 main/main.py - Alternatively, add your user to the
gpiogroup:sudo usermod -aG gpio $USER
To start PEL automatically when the Pi boots, create a systemd service:
sudo nano /etc/systemd/system/pel.service[Unit]
Description=Panic Button Emergency Locator
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/pi/PEL/main/main.py
WorkingDirectory=/home/pi/PEL
Restart=on-failure
RestartSec=5
Environment=PEL_OWNER_NAME=My PEL Device
Environment=PEL_CONTACTS_FILE=/etc/pel/contacts.json
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable pel.service
sudo systemctl start pel.serviceCheck status:
sudo systemctl status pel.serviceReleased under the MIT License.
Created by Charles Naig as an embedded-systems capstone exploring accessible, offline-first emergency communication.