# Author: Thomas Poteet # Date: 4/22/2024 # Program Name: lightserver.py # Description: A UDP server that receives motion detection events and sends blink parameters to a client. import socket import sys import struct import RPi.GPIO as GPIO import time from datetime import datetime # Setup GPIO for the LED LED_PIN = 17 GPIO.setmode(GPIO.BCM) GPIO.setup(LED_PIN, GPIO.OUT) GPIO.output(LED_PIN, GPIO.LOW) # Utility Functions def get_current_time(): """Returns the current timestamp in the format 'YYYY-MM-DD-HH-MM-SS'.""" return datetime.now().strftime("%Y-%m-%d-%H-%M-%S") def blink_led(blink_count, blink_duration): """ Blinks the LED a specified number of times, each lasting a specified duration. Args: blink_count (int): Number of times to blink the LED. blink_duration (float): Duration of each blink in seconds. """ for _ in range(blink_count): GPIO.output(LED_PIN, GPIO.HIGH) time.sleep(blink_duration / 2) GPIO.output(LED_PIN, GPIO.LOW) time.sleep(blink_duration / 2) # Packet Functions def create_packet(seq_num, ack_num, is_ack, is_syn, is_fin, payload): """ Creates a custom UDP packet with a specified header and payload. Args: seq_num (int): Sequence number. ack_num (int): Acknowledgment number. is_ack (int): ACK flag (1 or 0). is_syn (int): SYN flag (1 or 0). is_fin (int): FIN flag (1 or 0). payload (str): Data to include in the packet. Returns: bytes: Packed data ready for transmission. """ header = struct.pack('!I', seq_num) + struct.pack('!I', ack_num) flags = (is_ack << 2) | (is_syn << 1) | is_fin header += struct.pack('!I', flags) return header + payload.encode() def parse_packet(packet): """ Parses a received UDP packet and extracts its components. Args: packet (bytes): The received packet data. Returns: tuple: Parsed sequence number, acknowledgment number, flags, and payload. """ seq_num, ack_num, flags = struct.unpack('!III', packet[:12]) is_ack = (flags >> 2) & 1 is_syn = (flags >> 1) & 1 is_fin = flags & 1 payload = packet[12:].decode() return seq_num, ack_num, is_ack, is_syn, is_fin, payload # Main Server Logic def main(): try: # Validate command-line arguments if len(sys.argv) != 5: print("Usage: python3 lightserver.py -p -s ") sys.exit(1) # Parse arguments port = int(sys.argv[2]) log_file_path = sys.argv[4] # Create and bind the UDP socket print("[SERVER] Initializing server...") server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind(('', port)) seq_num = 1 # Handshake Process print("[SERVER] Awaiting connection request (SYN) from client...") data, client_addr = server_socket.recvfrom(1024) client_seq, _, is_ack, is_syn, is_fin, _ = parse_packet(data) with open(log_file_path, 'a') as log_file: log_file.write(f'{get_current_time()}: "RECEIVED" {client_seq} 0 ["{is_ack}"] ["{is_syn}"] ["{is_fin}"]\n') print("[SERVER] Sending connection acknowledgment (SYN-ACK)...") server_socket.sendto( create_packet(seq_num, client_seq + 1, is_ack=1, is_syn=1, is_fin=0, payload="Connection Established"), client_addr ) with open(log_file_path, 'a') as log_file: log_file.write(f'{get_current_time()}: "SENT" {seq_num} {client_seq + 1} ["1"] ["1"] ["0"]\n') seq_num += 1 # Await acknowledgment print("[SERVER] Waiting for final acknowledgment from client...") data, _ = server_socket.recvfrom(1024) client_seq, _, is_ack, _, _, _ = parse_packet(data) with open(log_file_path, 'a') as log_file: log_file.write(f'{get_current_time()}: "RECEIVED" {client_seq} 0 ["{is_ack}"] ["0"] ["0"]\n') # Process Blink Parameters print("[SERVER] Receiving blink parameters (count and duration)...") data, _ = server_socket.recvfrom(1024) client_seq, _, is_ack, _, _, payload = parse_packet(data) blink_count, blink_duration = map(float, payload.split()) with open(log_file_path, 'a') as log_file: log_file.write(f'{get_current_time()}: "RECEIVED" {client_seq} 0 ["{is_ack}"] ["0"] ["0"]\n') print("[SERVER] Acknowledging blink parameters...") server_socket.sendto( create_packet(seq_num, client_seq + 1, is_ack=1, is_syn=0, is_fin=0, payload="Parameters Accepted"), client_addr ) with open(log_file_path, 'a') as log_file: log_file.write(f'{get_current_time()}: "SENT" {seq_num} {client_seq + 1} ["1"] ["0"] ["0"]\n') seq_num += 1 # Monitor for motion detection or FIN while True: print("[SERVER] Waiting for motion detection or termination signal...") data, _ = server_socket.recvfrom(1024) client_seq, _, is_ack, _, is_fin, payload = parse_packet(data) with open(log_file_path, 'a') as log_file: log_file.write(f'{get_current_time()}: "RECEIVED" {client_seq} 0 ["{is_ack}"] ["0"] ["{is_fin}"]\n') if payload == ":MotionDetected": print("[SERVER] Motion detected. Executing LED blink sequence.") with open(log_file_path, 'a') as log_file: log_file.write(f"{get_current_time()}: Motion Detected\n") blink_led(int(blink_count), float(blink_duration)) elif is_fin: print("[SERVER] Client requested session termination.") with open(log_file_path, 'a') as log_file: log_file.write(f'{get_current_time()}: Interaction with {client_addr} completed\n') break except KeyboardInterrupt: print("[SERVER] Shutting down server...") finally: GPIO.cleanup() server_socket.close() if __name__ == "__main__": main()