diff --git a/src/Components/IoT/2025_T3_prototype/Device_Demonstration.mp4 b/src/Components/IoT/2025_T3_prototype/Device_Demonstration.mp4 new file mode 100644 index 000000000..de7e1202b Binary files /dev/null and b/src/Components/IoT/2025_T3_prototype/Device_Demonstration.mp4 differ diff --git a/src/Components/IoT/Testing/revamp/client.py b/src/Components/IoT/2025_T3_prototype/client.py similarity index 71% rename from src/Components/IoT/Testing/revamp/client.py rename to src/Components/IoT/2025_T3_prototype/client.py index 26895a408..0bbb57daa 100644 --- a/src/Components/IoT/Testing/revamp/client.py +++ b/src/Components/IoT/2025_T3_prototype/client.py @@ -2,6 +2,7 @@ import sounddevice as sd import numpy as np import paho.mqtt.client as mqtt +from gps3 import gps3 import time import wave import psutil @@ -15,7 +16,7 @@ os.makedirs(AUDIO_DIR, exist_ok=True) -sd.default.device = (2, None) +sd.default.device = (1, None) print("Connecting to MQTT Broker...") client = mqtt.Client() @@ -43,14 +44,36 @@ def record_audio(duration=5, samplerate=44100): print("Recording complete", filename) return filepath + def get_health_report(): return { "cpu": psutil.cpu_percent(), "ram": psutil.virtual_memory().percent, "disk": psutil.disk_usage("/").percent, - "uptime": psutil.boot_time() + "uptime": time.time() - psutil.boot_time() } +def get_gps_reading(): + gps_socket = gps3.GPSDSocket() + data_stream = gps3.DataStream() + + gps_socket.connect() + gps_socket.watch() + + for new_data in gps_socket: + if new_data: + data_stream.unpack(new_data) + + lat = data_stream.TPV.get('lat') + lon = data_stream.TPV.get('lon') + mode = data_stream.TPV.get('mode') + + if mode == 3 and lat != 'n/a' and lon != 'n/a': + print("GPS FIX:", lat, lon) + return {"lat": lat, "lon": lon} + + return None + def send_data(): audio_file = record_audio() @@ -59,10 +82,13 @@ def send_data(): health_data = get_health_report() + gps_data = get_gps_reading() + audio_b64 = base64.b64encode(audio_bytes).decode('ascii') payload = { "health_data": health_data, + "gps_data": gps_data, "audio_file": audio_b64 } @@ -75,4 +101,4 @@ def send_data(): if __name__ == "__main__": while True: send_data() - time.sleep(20) \ No newline at end of file + time.sleep(20) diff --git a/src/Components/IoT/Testing/revamp/server.py b/src/Components/IoT/2025_T3_prototype/server.py similarity index 97% rename from src/Components/IoT/Testing/revamp/server.py rename to src/Components/IoT/2025_T3_prototype/server.py index 4cd55723a..1953a70db 100644 --- a/src/Components/IoT/Testing/revamp/server.py +++ b/src/Components/IoT/2025_T3_prototype/server.py @@ -33,6 +33,7 @@ def on_message(client, userdata, msg): json_filename = f"data_{timestamp}.json" with open(os.path.join(JSON_DIR, json_filename), "w") as f: json.dump(payload["health_data"], f) + json.dump(payload["gps_data"], f) print(f"Saved JSON: {json_filename}") print("Writing audio data") diff --git a/src/Components/IoT/previous_implementation/Dockerfile b/src/Components/IoT/previous_implementation/Dockerfile new file mode 100644 index 000000000..d1b84a3e6 --- /dev/null +++ b/src/Components/IoT/previous_implementation/Dockerfile @@ -0,0 +1,22 @@ + +FROM python:3.9-slim +WORKDIR /app +# Install system libraries for sounddevice, OpenCV, numpy, and scientific packages +RUN apt update && apt install -y \ + portaudio19-dev \ + libgl1 \ + libglib2.0-0 \ + libsm6 \ + libxext6 \ + libxrender1 \ + libsndfile1 \ + ffmpeg \ + libatlas3-base \ + liblapack-dev \ + gfortran \ + build-essential \ + gcc \ + --no-install-recommends +COPY . /app +RUN pip install --no-cache-dir -r requirements.txt +CMD ["python", "client_pi.py"] diff --git a/src/Components/IoT/management_application/README.me b/src/Components/IoT/previous_implementation/README.me similarity index 100% rename from src/Components/IoT/management_application/README.me rename to src/Components/IoT/previous_implementation/README.me diff --git a/src/Components/IoT/previous_implementation/client_pi.py b/src/Components/IoT/previous_implementation/client_pi.py new file mode 100644 index 000000000..d90b60e04 --- /dev/null +++ b/src/Components/IoT/previous_implementation/client_pi.py @@ -0,0 +1,146 @@ +import os +import psutil +import sounddevice as sd +import numpy as np +import librosa +import cv2 +import json +import wave +import time +import requests +import serial +from gps3 import gps3 +from tflite_runtime.interpreter import Interpreter + +SERVER_URL = "http://192.168.1.122:5000/upload" +SAMPLE_RATE = 16000 +DURATION = 5 +N_MFCC = 40 +MODEL_PATH = "models/checkpoint_MobileNetV3-Large.tflite" +CLASS_PATH = "models/class_names_MobileNetV3-Large.json" +SAVE_DIR = "audioLocal" +os.makedirs(SAVE_DIR, exist_ok=True) + +sd.default.device = (2, None) + +print("Loading Model......") +interpreter = Interpreter(model_path=MODEL_PATH) +interpreter.allocate_tensors() +input_details = interpreter.get_input_details() +output_details = interpreter.get_output_details() +input_index = input_details[0]['index'] +output_index = output_details[0]['index'] + +with open(CLASS_PATH, "r") as f: + ID_TO_SPECIES = json.load(f) + +def get_health_report(): + return { + "cpu": psutil.cpu_percent(), + "ram": psutil.virtual_memory().percent, + "disk": psutil.disk_usage("/").percent, + "uptime": psutil.boot_time() + } + +# sub-optimal, convert to using gps3 and gpsd +def get_gps_location(port="/dev/ttyACM0",baudrate=9600,timeout=1): + try: + with serial.Serial(port, baudrate, timeout=timeout) as ser: + while True: + print("1") + line = ser.readline().decode("ascii", errors="replace").strip() + if line.startswith("$GPGGA") or line.startswith("$GPRMC"): + print("2") + parts = line.split(",") + if len(parts) > 5 and parts[2] and parts[4]: + lat = convert_to_decimal(parts[2], parts[3]) + lon = convert_to_decimal(parts[4], parts[5]) + print("lat: ", lat) + return {"latitude": lat, "longitude": lon} + + except Exception as e: + print("GPS error: ", e) + + return {"latitude": None, "longitude": None} + + +#Check audio recording duration and quality, method for sending .wav file to server +def record_audio(duration=5, samplerate=44100): + timestamp = time.strftime("%Y-%m-%d_%H-%M-%S") + filename = f"audio_{timestamp}.wav" + filepath = os.path.join(SAVE_DIR, filename) + + print("Recording...") + audio_data = sd.rec(int(duration * samplerate), samplerate=samplerate, channels=1, dtype='float32') + sd.wait() + with wave.open(filepath, 'wb') as wf: + wf.setnchannels(1) + wf.setsampwidth(2) + wf.setframerate(samplerate) + wf.writeframes((audio_data * 32767).astype(np.int16).tobytes()) + print("Recording complete", filename) + return filepath, np.squeeze(audio_data) + +# Is processing neccessary? How does the model expect input? +def preprocess_audio(audio, sr=SAMPLE_RATE): + mel_spec = librosa.feature.melspectrogram(y=audio, sr=sr, n_mels=224, fmax=sr//2) + mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max) + mel_norm = 255 * (mel_spec_db - mel_spec_db.min()) / (mel_spec_db.max() - mel_spec_db.min()) + mel_norm = mel_norm.astype(np.uint8) + mel_resized = cv2.resize(mel_norm, (224, 224)) + mel_rgb = np.stack([mel_resized]*3, axis=-1) + mel_rgb = np.expand_dims(mel_rgb, axis=0) + + return mel_rgb.astype(np.float32) + +def softmax(x): + e_x = np.exp(x - np.max(x)) + return e_x / e_x.sum() + + +# Does this prediction help the actual model in any way? +def predict_species(audio): + input_data = preprocess_audio(audio) + + interpreter.set_tensor(input_details[0]['index'], input_data) + interpreter.invoke() + output_data = interpreter.get_tensor(output_index)[0] + + probs = softmax(output_data) + + class_id = int(np.argmax(probs)) + confidence = float(probs[class_id]) + + species = ID_TO_SPECIES[class_id] + return species, confidence + +def cleanup_files(max_files=100): + files = sorted([os.path.join(SAVE_DIR, f) for f in os.listdir(SAVE_DIR)], key=os.path.getmtime) + if len(files) > max_files: + for f in files[:-max_files]: + os.remove(f) + print(f"Deleted old file: {f}") + +def send_data(): + filepath, audio = record_audio() + + species, confidence = predict_species(audio) + print(f"Predicted: {species} ({confidence:.2f})") + + cleanup_files(max_files=100) + + health_data = get_health_report() + # gps_data = get_gps_location() + + payload = { + **health_data, + # **gps_data, + "species": species, + "confidence": confidence + } + + response = requests.post(SERVER_URL, json=payload) + +if __name__ == "__main__": + while True: + send_data() diff --git a/src/Components/IoT/management_application/config/devices.json b/src/Components/IoT/previous_implementation/config/devices.json similarity index 100% rename from src/Components/IoT/management_application/config/devices.json rename to src/Components/IoT/previous_implementation/config/devices.json diff --git a/src/Components/IoT/previous_implementation/iot-simulator-deployment.yaml b/src/Components/IoT/previous_implementation/iot-simulator-deployment.yaml new file mode 100644 index 000000000..556c1a843 --- /dev/null +++ b/src/Components/IoT/previous_implementation/iot-simulator-deployment.yaml @@ -0,0 +1,19 @@ + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: iot-simulator +spec: + replicas: 1 + selector: + matchLabels: + app: iot-simulator + template: + metadata: + labels: + app: iot-simulator + spec: + containers: + - name: iot-simulator + image: ts-simulator:latest #for development, use /ts-simulator:latest + restartPolicy: Always diff --git a/src/Components/IoT/previous_implementation/requirements.txt b/src/Components/IoT/previous_implementation/requirements.txt new file mode 100644 index 000000000..84f1667fa --- /dev/null +++ b/src/Components/IoT/previous_implementation/requirements.txt @@ -0,0 +1,10 @@ +psutil +sounddevice +numpy +librosa +opencv-python +requests +pyserial +gps3 +tflite-runtime +flask diff --git a/src/Components/IoT/management_application/server_main.py b/src/Components/IoT/previous_implementation/server_main.py similarity index 100% rename from src/Components/IoT/management_application/server_main.py rename to src/Components/IoT/previous_implementation/server_main.py diff --git a/src/Components/IoT/management_application/setup/onboarding_client.py b/src/Components/IoT/previous_implementation/setup/onboarding_client.py similarity index 100% rename from src/Components/IoT/management_application/setup/onboarding_client.py rename to src/Components/IoT/previous_implementation/setup/onboarding_client.py diff --git a/src/Components/IoT/management_application/setup/onboarding_server.py b/src/Components/IoT/previous_implementation/setup/onboarding_server.py similarity index 100% rename from src/Components/IoT/management_application/setup/onboarding_server.py rename to src/Components/IoT/previous_implementation/setup/onboarding_server.py diff --git a/src/Components/IoT/management_application/setup/setup_py.sh b/src/Components/IoT/previous_implementation/setup/setup_py.sh similarity index 100% rename from src/Components/IoT/management_application/setup/setup_py.sh rename to src/Components/IoT/previous_implementation/setup/setup_py.sh diff --git a/src/Prototypes/Iot/Component_Testing/audio.py b/src/Prototypes/Iot/Component_Testing/audio.py new file mode 100644 index 000000000..fe442ba06 --- /dev/null +++ b/src/Prototypes/Iot/Component_Testing/audio.py @@ -0,0 +1,54 @@ +import os +import sounddevice as sd +import numpy as np +import paho.mqtt.client as mqtt +import time +import wave + +SAVE_DIR = "audioLocal" +os.makedirs(SAVE_DIR, exist_ok=True) + +sd.default.device = (1, None) + +print("Connecting to MQTT Broker...") +client = mqtt.Client() +client.connect("broker.hivemq.com", 1883, 60) +client.loop_start() +topic = "iot/data/test" +print("Connected.") + +def record_audio(duration=5, samplerate=44100): + timestamp = time.strftime("%Y-%m-%d_%H-%M-%S") + filename = f"audio_{timestamp}.wav" + filepath = os.path.join(SAVE_DIR, filename) + + print("Recording...") + audio_data = sd.rec(int(duration * samplerate), samplerate=samplerate, channels=1, dtype='float32') + sd.wait() + with wave.open(filepath, 'wb') as wf: + wf.setnchannels(1) + wf.setsampwidth(2) + wf.setframerate(samplerate) + wf.writeframes((audio_data * 32767).astype(np.int16).tobytes()) + print("Recording complete", filename) + return filepath + +def on_publish(client, userdata, mid): + print(f"Message {mid} published.") + +def send_data(): + audio_file = record_audio() + + with open(audio_file, "rb") as f: + imagestring = f.read() + audio_data = bytearray(imagestring) + + + client.on_publish = on_publish + client.publish(topic, audio_data, qos=1) + print(f"Published audio to {topic}: {audio_file}") + +if __name__ == "__main__": + while True: + send_data() + time.sleep(20) \ No newline at end of file diff --git a/src/Prototypes/Iot/Component_Testing/audio_server.py b/src/Prototypes/Iot/Component_Testing/audio_server.py new file mode 100644 index 000000000..bf08c649f --- /dev/null +++ b/src/Prototypes/Iot/Component_Testing/audio_server.py @@ -0,0 +1,35 @@ +import paho.mqtt.client as mqtt +import os +import time + +SAVE_DIR = "audioLocal" +os.makedirs(SAVE_DIR, exist_ok=True) + +def on_connect(client, userdata, flags, rc): + if rc == 0: + print("Subscriber connected successfully") + client.subscribe(topic, qos=1) + else: + print(f"Subscriber failed to connect, code {rc}") + +def on_subscribe(client, userdata, mid, granted_qos): + print(f"Subscribed with QoS {granted_qos}") + +def on_message(client, userdata, msg): + print(f"Received message on {msg.topic}") + print("Writing") + timestamp = time.strftime("%Y-%m-%d_%H-%M-%S") + filename = f"audio_{timestamp}.wav" + filepath = os.path.join(SAVE_DIR, filename) + with open(filepath, "wb") as f: + f.write(msg.payload) + +client = mqtt.Client() +client.on_connect = on_connect +client.on_subscribe = on_subscribe +client.on_message = on_message +client.connect("broker.hivemq.com", 1883, 60) +topic = "iot/data/test" +client.subscribe(topic, qos=1) +print(f"Subscribed to {topic}") +client.loop_forever() \ No newline at end of file diff --git a/src/Prototypes/Iot/Component_Testing/gps.py b/src/Prototypes/Iot/Component_Testing/gps.py new file mode 100644 index 000000000..fff2f2055 --- /dev/null +++ b/src/Prototypes/Iot/Component_Testing/gps.py @@ -0,0 +1,40 @@ +import os +import paho.mqtt.client as mqtt +import json +from gps3 import gps3 +import time + +print("Connecting to MQTT Broker...") +client = mqtt.Client() +client.connect("broker.hivemq.com", 1883, 60) +topic = "iot/data/test" +print("Connected.") + +def get_gps_reading(): + gps_socket = gps3.GPSDSocket() + data_stream = gps3.DataStream() + + gps_socket.connect() + gps_socket.watch() + + for new_data in gps_socket: + if new_data: + data_stream.unpack(new_data) + + lat = data_stream.TPV['lat'] + lon = data_stream.TPV['lon'] + + return {"lat": lat, "lon": lon} + +def send_data(): + gps_data = get_gps_reading() + + json_payload = json.dumps(gps_data) + + client.publish(topic, json_payload) + print(f"Published to {topic}: {json_payload}") + +if __name__ == "__main__": + while True: + send_data() + time.sleep(20) \ No newline at end of file diff --git a/src/Prototypes/Iot/Component_Testing/health.py b/src/Prototypes/Iot/Component_Testing/health.py new file mode 100644 index 000000000..57da74e64 --- /dev/null +++ b/src/Prototypes/Iot/Component_Testing/health.py @@ -0,0 +1,33 @@ +import psutil +import paho.mqtt.client as mqtt +import json +import time + +print("Connecting to MQTT Broker...") +client = mqtt.Client() +client.connect("broker.hivemq.com", 1883, 60) +topic = "iot/data/test" +print("Connected.") + +def get_health_report(): + return { + "cpu": psutil.cpu_percent(), + "ram": psutil.virtual_memory().percent, + "disk": psutil.disk_usage("/").percent, + "uptime": psutil.boot_time() + } + +def send_data(): + health_data = get_health_report() + + json_payload = json.dumps(health_data) + + client.publish(topic, json_payload) + print(f"Published to {topic}: {json_payload}") + +if __name__ == "__main__": + while True: + send_data() + time.sleep(20) + + diff --git a/src/Prototypes/Iot/Component_Testing/json_server.py b/src/Prototypes/Iot/Component_Testing/json_server.py new file mode 100644 index 000000000..fa6ebd938 --- /dev/null +++ b/src/Prototypes/Iot/Component_Testing/json_server.py @@ -0,0 +1,18 @@ +import paho.mqtt.client as mqtt +import json + +def on_message(client, userdata, msg): + print(f"Received message on {msg.topic}: {msg.payload.decode()}") + try: + data = json.loads(msg.payload.decode()) + print(f"Parsed JSON data: {data}") + except json.JSONDecodeError: + print("Failed to decode JSON") + +client = mqtt.Client() +client.on_message = on_message +client.connect("broker.hivemq.com", 1883, 60) +topic = "iot/data/test" +client.subscribe(topic) +print(f"Subscribed to {topic}") +client.loop_forever() \ No newline at end of file