Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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()

Expand All @@ -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
}

Expand All @@ -75,4 +101,4 @@ def send_data():
if __name__ == "__main__":
while True:
send_data()
time.sleep(20)
time.sleep(20)
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
22 changes: 22 additions & 0 deletions src/Components/IoT/previous_implementation/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
146 changes: 146 additions & 0 deletions src/Components/IoT/previous_implementation/client_pi.py
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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 <registry-name>/ts-simulator:latest
restartPolicy: Always
10 changes: 10 additions & 0 deletions src/Components/IoT/previous_implementation/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
psutil
sounddevice
numpy
librosa
opencv-python
requests
pyserial
gps3
tflite-runtime
flask
54 changes: 54 additions & 0 deletions src/Prototypes/Iot/Component_Testing/audio.py
Original file line number Diff line number Diff line change
@@ -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)
35 changes: 35 additions & 0 deletions src/Prototypes/Iot/Component_Testing/audio_server.py
Original file line number Diff line number Diff line change
@@ -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()
Loading
Loading