Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- """
- Combined SOS Emergency Alert System for Raspberry Pi Zero 2W
- Features:
- - GPS location tracking (GPIO 14, 15)
- - SIM900A GSM module (GPIO 13, 19)
- - Email alerts
- - Telegram messages with images
- - SMS to multiple contacts
- - Voice calls (miss calls) to emergency contacts
- - Emergency button and buzzer
- """
- import RPi.GPIO as GPIO
- import time
- import smtplib
- import subprocess
- import requests
- import serial
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart
- from threading import Thread, Lock
- import socket
- from datetime import datetime
- import queue
- import threading
- import logging
- # Configure logging
- logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s - %(levelname)s - %(message)s'
- )
- logger = logging.getLogger(__name__)
- # --- GPIO SETUP ---
- button_pin = 17 # GPIO17 (Pin 11) - SOS Button
- buzzer_pin = 18 # GPIO18 (Pin 12) - Buzzer
- # GPS uses GPIO 14, 15 (hardware UART)
- # SIM900A uses GPIO 13, 19 (software serial)
- GPIO.setmode(GPIO.BCM)
- GPIO.setup(button_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
- GPIO.setup(buzzer_pin, GPIO.OUT)
- # --- EMAIL SETUP ---
- sender_email = "[email protected]"
- receiver_email = "[email protected]"
- app_password = "ytaf rttf gcwk fatf"
- subject = "šØ SOS ALERT"
- body_template = """EMERGENCY ALERT!
- šØ I need immediate assistance!
- š Location: {location_info}
- š Time: {timestamp}
- š¶ IP Address: {ip_address}
- Google Maps: {google_maps_link}
- This is an automated alert from my Raspberry Pi SOS device."""
- # --- TELEGRAM SETUP ---
- BOT_TOKEN = "7606896913:AAF-qQKNwlIgf4GEHyu2uZgE1osVSEFz87c"
- CHAT_ID = "5994490090"
- # --- EMERGENCY CONTACTS FOR SMS AND CALLS ---
- EMERGENCY_CONTACTS = [
- "+919876543210", # Replace with actual emergency contact numbers
- "+919876543211",
- "+919876543212",
- "+919876543213",
- "+919876543214"
- ]
- # --- GPS LOCK ---
- gps_lock = Lock()
- # --- SOFTWARE SERIAL FOR SIM900A ---
- class SoftwareSerial:
- """Software serial implementation for GPIO communication"""
- def __init__(self, tx_pin: int, rx_pin: int, baudrate: int = 9600):
- self.tx_pin = tx_pin
- self.rx_pin = rx_pin
- self.baudrate = baudrate
- self.bit_duration = 1.0 / baudrate
- self.rx_buffer = queue.Queue()
- self.rx_thread = None
- self.rx_running = False
- # Setup GPIO for SIM900A
- GPIO.setup(self.tx_pin, GPIO.OUT, initial=GPIO.HIGH)
- GPIO.setup(self.rx_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
- def start_rx(self):
- """Start RX thread for receiving data"""
- self.rx_running = True
- self.rx_thread = threading.Thread(target=self._rx_worker, daemon=True)
- self.rx_thread.start()
- def stop_rx(self):
- """Stop RX thread"""
- self.rx_running = False
- if self.rx_thread:
- self.rx_thread.join(timeout=1)
- def _rx_worker(self):
- """RX thread worker - continuously monitor for incoming data"""
- while self.rx_running:
- if GPIO.input(self.rx_pin) == GPIO.LOW: # Start bit detected
- # Wait for middle of start bit
- time.sleep(self.bit_duration * 1.5)
- byte_val = 0
- # Read 8 data bits
- for bit in range(8):
- if GPIO.input(self.rx_pin) == GPIO.HIGH:
- byte_val |= (1 << bit)
- time.sleep(self.bit_duration)
- # Stop bit (no need to check)
- time.sleep(self.bit_duration)
- # Add to buffer
- self.rx_buffer.put(byte_val)
- else:
- time.sleep(self.bit_duration * 0.1)
- def write(self, data: bytes):
- """Send data via TX pin"""
- for byte_val in data:
- # Start bit
- GPIO.output(self.tx_pin, GPIO.LOW)
- time.sleep(self.bit_duration)
- # Data bits (LSB first)
- for bit in range(8):
- if byte_val & (1 << bit):
- GPIO.output(self.tx_pin, GPIO.HIGH)
- else:
- GPIO.output(self.tx_pin, GPIO.LOW)
- time.sleep(self.bit_duration)
- # Stop bit
- GPIO.output(self.tx_pin, GPIO.HIGH)
- time.sleep(self.bit_duration)
- def read(self, size: int = 1) -> bytes:
- """Read data from RX buffer"""
- data = bytearray()
- for _ in range(size):
- try:
- byte_val = self.rx_buffer.get(timeout=0.1)
- data.append(byte_val)
- except queue.Empty:
- break
- return bytes(data)
- def in_waiting(self) -> int:
- """Return number of bytes waiting in RX buffer"""
- return self.rx_buffer.qsize()
- def reset_input_buffer(self):
- """Clear RX buffer"""
- while not self.rx_buffer.empty():
- try:
- self.rx_buffer.get_nowait()
- except queue.Empty:
- break
- # --- SIM900A GSM MODULE CLASS ---
- class SIM900A:
- def __init__(self, tx_pin: int = 13, rx_pin: int = 19, baudrate: int = 9600, timeout: int = 10):
- self.tx_pin = tx_pin
- self.rx_pin = rx_pin
- self.baudrate = baudrate
- self.timeout = timeout
- self.serial = None
- self.initialized = False
- def connect(self) -> bool:
- """Establish software serial connection with SIM900A module"""
- try:
- self.serial = SoftwareSerial(self.tx_pin, self.rx_pin, self.baudrate)
- self.serial.start_rx()
- logger.info(f"SIM900A connected via GPIO - TX:{self.tx_pin}, RX:{self.rx_pin}")
- time.sleep(2)
- return self.test_connection()
- except Exception as e:
- logger.error(f"SIM900A connection failed: {e}")
- return False
- def disconnect(self):
- """Close serial connection"""
- if self.serial:
- self.serial.stop_rx()
- logger.info("SIM900A disconnected")
- def send_at_command(self, command: str, expected_response: str = "OK", timeout: int = None) -> tuple:
- """Send AT command and wait for response"""
- if not self.serial:
- return False, "Serial connection not available"
- timeout = timeout or self.timeout
- try:
- self.serial.reset_input_buffer()
- cmd = f"{command}\r\n"
- self.serial.write(cmd.encode())
- logger.debug(f"SIM900A sent: {command}")
- start_time = time.time()
- response = ""
- while (time.time() - start_time) < timeout:
- if self.serial.in_waiting() > 0:
- new_data = self.serial.read(self.serial.in_waiting())
- response += new_data.decode('utf-8', errors='ignore')
- if expected_response in response:
- logger.debug(f"SIM900A received: {response.strip()}")
- return True, response.strip()
- time.sleep(0.1)
- logger.warning(f"SIM900A timeout waiting for '{expected_response}'. Got: {response.strip()}")
- return False, response.strip()
- except Exception as e:
- logger.error(f"SIM900A AT command error: {e}")
- return False, str(e)
- def test_connection(self) -> bool:
- """Test if module is responding"""
- logger.info("Testing SIM900A connection...")
- success, _ = self.send_at_command("AT")
- if success:
- logger.info("SIM900A is responding")
- return True
- else:
- logger.error("SIM900A not responding")
- return False
- def initialize_module(self) -> bool:
- """Initialize and configure the SIM900A module"""
- logger.info("Initializing SIM900A module...")
- if not self.test_connection():
- return False
- # Disable echo
- self.send_at_command("ATE0")
- # Check SIM card
- success, _ = self.send_at_command("AT+CPIN?", timeout=10)
- if not success:
- logger.error("SIM card not detected or PIN required")
- return False
- # Wait for network registration
- max_attempts = 12
- for attempt in range(max_attempts):
- success, response = self.send_at_command("AT+CREG?", timeout=15)
- if success and ("+CREG: 0,1" in response or "+CREG: 0,5" in response):
- logger.info("Network registered successfully")
- self.initialized = True
- return True
- logger.info(f"Waiting for network registration... ({attempt + 1}/{max_attempts})")
- time.sleep(5)
- logger.error("Failed to register to network")
- return False
- def send_sms(self, phone_number: str, message: str) -> bool:
- """Send SMS to specified number"""
- if not self.initialized:
- logger.error("SIM900A not initialized")
- return False
- logger.info(f"Sending SMS to {phone_number}")
- # Set SMS text mode
- success, _ = self.send_at_command("AT+CMGF=1")
- if not success:
- return False
- # Set recipient
- success, response = self.send_at_command(f'AT+CMGS="{phone_number}"', expected_response=">", timeout=10)
- if not success:
- return False
- # Send message content
- try:
- self.serial.write(f"{message}\x1A".encode())
- start_time = time.time()
- response = ""
- while (time.time() - start_time) < 30:
- if self.serial.in_waiting() > 0:
- new_data = self.serial.read(self.serial.in_waiting())
- response += new_data.decode('utf-8', errors='ignore')
- if "OK" in response or "+CMGS:" in response:
- logger.info(f"SMS sent successfully to {phone_number}")
- return True
- elif "ERROR" in response:
- logger.error(f"SMS sending failed: {response}")
- return False
- time.sleep(0.5)
- logger.error("SMS sending timeout")
- return False
- except Exception as e:
- logger.error(f"SMS sending error: {e}")
- return False
- def make_call(self, phone_number: str, duration: int = 3) -> bool:
- """Make a miss call to specified number"""
- if not self.initialized:
- logger.error("SIM900A not initialized")
- return False
- logger.info(f"Making call to {phone_number}")
- # Initiate call
- success, response = self.send_at_command(f"ATD{phone_number};", expected_response="OK", timeout=15)
- if not success:
- logger.error(f"Failed to initiate call to {phone_number}")
- return False
- logger.info(f"Call initiated, waiting {duration} seconds...")
- time.sleep(duration)
- # Hang up call
- success, _ = self.send_at_command("ATH", timeout=10)
- if success:
- logger.info(f"Miss call completed to {phone_number}")
- return True
- else:
- logger.error("Failed to hang up call")
- return False
- # --- GPS FUNCTIONS ---
- def convert_to_decimal(degree_min, direction):
- """Convert NMEA coordinate format to decimal degrees"""
- if not degree_min:
- return 0.0
- degrees = int(float(degree_min) / 100)
- minutes = float(degree_min) - degrees * 100
- decimal = degrees + minutes / 60
- return -decimal if direction in ['S', 'W'] else decimal
- def parse_gps_data(line):
- """Parse NMEA sentences and extract relevant data"""
- if line.startswith('$GNRMC') or line.startswith('$GPRMC'):
- parts = line.split(',')
- if len(parts) >= 10 and parts[2] == 'A':
- return {
- 'time': parts[1][:2] + ":" + parts[1][2:4] + ":" + parts[1][4:6],
- 'lat': convert_to_decimal(parts[3], parts[4]),
- 'lon': convert_to_decimal(parts[5], parts[6]),
- 'speed': float(parts[7]) if parts[7] else 0.0,
- 'course': float(parts[8]) if parts[8] else 0.0,
- 'date': parts[9][:2] + "/" + parts[9][2:4] + "/20" + parts[9][4:6],
- 'valid': True
- }
- return None
- def get_gps_location(max_attempts=10, attempt_delay=1):
- """Get GPS location with multiple attempts"""
- location_info = "Location not available"
- google_maps_link = "Location not available"
- # Try GPS on hardware UART (GPIO 14, 15)
- possible_ports = ['/dev/serial0', '/dev/ttyS0', '/dev/ttyAMA0']
- for port in possible_ports:
- try:
- with gps_lock:
- print(f"š Trying GPS on {port}...")
- with serial.Serial(port, 115200, timeout=1) as ser:
- print(f"š” GPS connected on {port}, waiting for fix...")
- attempts = 0
- while attempts < max_attempts:
- try:
- line = ser.readline().decode('ascii', errors='ignore').strip()
- if not line:
- continue
- data = parse_gps_data(line)
- if data and 'valid' in data:
- location_info = (
- f"Coordinates: {data['lat']:.6f}°N, {data['lon']:.6f}°E\n"
- f"Time: {data['time']} UTC | Date: {data['date']}\n"
- f"Speed: {data['speed']:.1f} knots | Course: {data['course']:.1f}°"
- )
- google_maps_link = f"https://www.google.com/maps?q={data['lat']},{data['lon']}"
- print("š GPS location acquired!")
- return location_info, google_maps_link
- except Exception as e:
- print(f"ā GPS read error: {str(e)}")
- attempts += 1
- print(f"š GPS attempt {attempts}/{max_attempts}")
- time.sleep(attempt_delay)
- except serial.SerialException as e:
- print(f"ā Could not open {port}: {str(e)}")
- continue
- # Fallback to IP location
- try:
- ip = get_ip_address()
- if ip != "IP not available":
- location_info = f"IP Address: {ip}\nApproximate location based on network"
- google_maps_link = f"https://www.google.com/maps/search/?api=1&query={ip}"
- print("š Using IP-based location fallback")
- except Exception as e:
- print(f"ā IP location error: {str(e)}")
- return location_info, google_maps_link
- def get_ip_address():
- """Get the device's IP address"""
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- s.connect(("8.8.8.8", 80))
- ip = s.getsockname()[0]
- s.close()
- return ip
- except:
- return "IP not available"
- # --- EMERGENCY ALERT FUNCTIONS ---
- def send_emergency_email(location_info, google_maps_link):
- """Send emergency email"""
- try:
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- ip_address = get_ip_address()
- body = body_template.format(
- location_info=location_info,
- timestamp=timestamp,
- ip_address=ip_address,
- google_maps_link=google_maps_link
- )
- msg = MIMEMultipart()
- msg['From'] = sender_email
- msg['To'] = receiver_email
- msg['Subject'] = subject
- msg.attach(MIMEText(body, 'plain'))
- server = smtplib.SMTP('smtp.gmail.com', 587)
- server.starttls()
- server.login(sender_email, app_password)
- server.send_message(msg)
- server.quit()
- print("š§ SOS email sent successfully!")
- return True
- except Exception as e:
- print(f"š§ Email error: {str(e)}")
- return False
- def send_emergency_sms(gsm_module, location_info, google_maps_link):
- """Send emergency SMS to all contacts"""
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- sms_message = f"šØ EMERGENCY ALERT! šØ\n\nI need immediate assistance!\n\nš Location:\n{location_info}\n\nš Time: {timestamp}\nš Maps: {google_maps_link}\n\nPlease send help!"
- print("š± Sending emergency SMS to all contacts...")
- success_count = 0
- for i, contact in enumerate(EMERGENCY_CONTACTS, 1):
- print(f"Sending SMS {i}/{len(EMERGENCY_CONTACTS)} to {contact}")
- if gsm_module.send_sms(contact, sms_message):
- print(f"ā SMS sent to {contact}")
- success_count += 1
- else:
- print(f"ā Failed to send SMS to {contact}")
- # Small delay between SMS
- if i < len(EMERGENCY_CONTACTS):
- time.sleep(2)
- print(f"š± SMS sending completed: {success_count}/{len(EMERGENCY_CONTACTS)} successful")
- return success_count > 0
- def make_emergency_calls(gsm_module):
- """Make miss calls to all emergency contacts"""
- print("š Making emergency miss calls...")
- success_count = 0
- call_duration = 3 # seconds
- call_delay = 8 # seconds between calls
- for i, contact in enumerate(EMERGENCY_CONTACTS, 1):
- print(f"Making call {i}/{len(EMERGENCY_CONTACTS)} to {contact}")
- if gsm_module.make_call(contact, call_duration):
- print(f"ā Miss call completed to {contact}")
- success_count += 1
- else:
- print(f"ā Failed to call {contact}")
- # Delay between calls (except after last call)
- if i < len(EMERGENCY_CONTACTS):
- print(f"Waiting {call_delay} seconds before next call...")
- time.sleep(call_delay)
- print(f"š Emergency calls completed: {success_count}/{len(EMERGENCY_CONTACTS)} successful")
- return success_count > 0
- def send_images_with_location(location_info, google_maps_link):
- """Continuously send images with location to Telegram"""
- while True:
- try:
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- image_file = f"/home/pi/image_{timestamp.replace(':', '-')}.jpg"
- # Capture image
- subprocess.run([
- "libcamera-still",
- "-o", image_file,
- "--width", "640",
- "--height", "480",
- "-n",
- "--timeout", "1000"
- ], check=True)
- # Prepare message
- caption = f"""šØ EMERGENCY ALERT! šØ
- š Location Information:
- {location_info}
- š Time: {timestamp}
- š Google Maps: {google_maps_link}
- ā Please send help immediately!"""
- # Send to Telegram
- url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendPhoto"
- with open(image_file, 'rb') as img:
- files = {'photo': img}
- data = {'chat_id': CHAT_ID, 'caption': caption}
- response = requests.post(url, files=files, data=data)
- print(f"š¤ Telegram image sent (Status: {response.status_code})")
- except subprocess.CalledProcessError as e:
- print(f"š· Camera error: {str(e)}")
- except Exception as e:
- print(f"š± Telegram error: {str(e)}")
- time.sleep(5)
- # --- MAIN FUNCTION ---
- def main():
- print("šØ SOS Emergency Alert System Starting...")
- print("š Initializing GSM module...")
- # Initialize SIM900A module
- gsm = SIM900A(tx_pin=13, rx_pin=19)
- gsm_available = False
- if gsm.connect():
- if gsm.initialize_module():
- gsm_available = True
- print("ā SIM900A module ready!")
- else:
- print("ā SIM900A failed to initialize - SMS/Calls disabled")
- else:
- print("ā SIM900A not connected - SMS/Calls disabled")
- print("š System armed. Waiting for SOS button press...")
- try:
- while True:
- if GPIO.input(button_pin) == GPIO.LOW:
- print("\nšØ SOS TRIGGERED! EMERGENCY PROTOCOL ACTIVATED!")
- # Activate buzzer immediately
- GPIO.output(buzzer_pin, GPIO.HIGH)
- # Get location information
- print("š° Getting GPS location...")
- location_info, google_maps_link = get_gps_location(max_attempts=10, attempt_delay=1)
- # Send emergency email
- print("š§ Sending emergency email...")
- Thread(target=send_emergency_email, args=(location_info, google_maps_link), daemon=True).start()
- # Send initial Telegram message
- print("š± Sending initial Telegram alert...")
- try:
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- initial_message = f"""šØ EMERGENCY ALERT! šØ
- š Location Information:
- {location_info}
- š Time: {timestamp}
- š Google Maps: {google_maps_link}
- ā Emergency button pressed! All alert systems activated!"""
- url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
- data = {'chat_id': CHAT_ID, 'text': initial_message}
- response = requests.post(url, data=data)
- print(f"š¤ Initial Telegram message sent (Status: {response.status_code})")
- except Exception as e:
- print(f"š± Initial Telegram message failed: {str(e)}")
- # Start image streaming to Telegram
- print("šø Starting continuous image updates...")
- Thread(target=send_images_with_location, args=(location_info, google_maps_link), daemon=True).start()
- # Send SMS alerts if GSM available
- if gsm_available:
- print("š± Sending emergency SMS alerts...")
- Thread(target=send_emergency_sms, args=(gsm, location_info, google_maps_link), daemon=True).start()
- # Wait a bit before making calls to avoid interference
- time.sleep(5)
- print("š Making emergency calls...")
- Thread(target=make_emergency_calls, args=(gsm,), daemon=True).start()
- else:
- print("ā GSM module not available - Skipping SMS and calls")
- print("šØ ALL EMERGENCY SYSTEMS ACTIVATED!")
- print("š” System will continue sending updates until manually stopped...")
- # Keep system active
- while True:
- time.sleep(1)
- time.sleep(0.1)
- except KeyboardInterrupt:
- print("\nš Emergency system interrupted by user.")
- finally:
- print("š Shutting down emergency system...")
- GPIO.output(buzzer_pin, GPIO.LOW)
- if gsm_available:
- gsm.disconnect()
- GPIO.cleanup()
- print("š“ Emergency system shut down safely.")
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment