From b798e138fab74ce4557b3115da8573c161da4317 Mon Sep 17 00:00:00 2001 From: waldluis Date: Mon, 24 Apr 2023 15:48:53 +0200 Subject: [PATCH] Added PythonDoc and simplified drive thread --- software/roboter/raspy/mainProg.py | 199 +++++++++++++----------- software/roboter/raspy/raspy_sensors.py | 62 +++++--- 2 files changed, 156 insertions(+), 105 deletions(-) diff --git a/software/roboter/raspy/mainProg.py b/software/roboter/raspy/mainProg.py index d8ed488..29d662e 100644 --- a/software/roboter/raspy/mainProg.py +++ b/software/roboter/raspy/mainProg.py @@ -1,98 +1,104 @@ +""" +created by waldluis + +This file contains the main script for the RaspberryPi of smart garden project +It has the task to control the EV3 robot and take measurements with the mounted sensor +The sensor data is published to MQTT topics to be available for the backend server +Used protocol for interaction: mqtt (paho-mqtt module) +Interaction with the EV3 via SSH + """ + import paho.mqtt.client as mqtt import json import threading import os -import signal import time from raspy_sensors import RaspySensors -from software.defines import Topics +from software.defines import Topics, RASPI_CLIENT_ID, MQTT_BROKER_GLOBAL #region global Varaibles -#sensors:RaspySensors() -leftRight = 50 -plantID = 0 -sensorData = { - "Air Temperature [°C]" : 0, - "Air Humidity [%]" : 0, - "Earth Humidity [%]" : 0, - "Brightness [Lux]" : 0, - "Plant ID": 0, - "Action ID": 0 -} - -gpsPosition = { - "Position": 0, - "Action ID": 0 -} - -batteryStatus = { - "Battery": 0, - "Action ID": 0 -} +sensors: RaspySensors #endregion #region global functions -def measure_send_data(plantID, actionID): - '''Measure data for one plant via sensor class and send via MQTT''' +# TODO auslagern +def measure_send_data(plantID, actionID, client: mqtt.Client): + """ + Reads all sensors and publishes data via MQTT + + Args: + plantID (_type_): plant to measure + actionID (_type_): current ID of driving action + client (mqtt.Client): current mqtt client for publishing + """ sensorData = sensors.readSensors() - sensorData["Plant_ID"] = plantID - sensorData["Action_ID"] = actionID + sensorData["PlantID"] = plantID + sensorData["ActionID"] = actionID client.publish(Topics["ROBOT_DATA_SENSORDATA"], json.dumps(sensorData, indent=4)) +# TODO auslagern +def drive_plant_thread(plantID, actionID, client: mqtt.Client): + """ + Function to drive to plant according to number from MQTT message in thread + Meassure and publish data via MQTT + Drive home to starting point + + Args: + plantID (_type_): plant to measure + actionID (_type_): current ID of driving action + client (mqtt.Client): current mqtt client for publishing + """ + os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/plant_{plantID}.py') + #CHECK if working properly + + print("Measuring Sensors") + measure_send_data(plantID, actionID, client) -def drive_home(): - '''Function to drive robot back to starting position in thread''' print("Robot driving home") + if plantID % 2 == 0: + leftRight = -50 #rotating left + else: + leftRight = 50 #rotating right os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back.py {leftRight}') print("Robot home") + #TODO decide about robot occupied message #client.publish("ROBOT/DATA/OCCUPIED", "false") +def on_connect(client: mqtt.Client, userdata, flags, rc): + """ + This method gets called, when it connects to a mqtt broker. + It is used to subscribe to the specific topics -def drive_plant_thread(): - '''Function to drive to plant according to number from MQTT message in thread''' - os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/plant_{plantID}.py') - print("Raising Signal to meassure") - signal.raise_signal(signal.SIGUSR1) - #TODO alles hier ohne weitere Threads + Args: + client (mqtt.Client): current mqtt client + userdata (_type_): _description_ + flags (_type_): _description_ + rc (_type_): _description_ + """ + if rc == 0: + #Add callbacks + client.message_callback_add("ROBOT/DATA", send_data_json) #Testing + client.message_callback_add(Topics["ROBOT_ACTION_DRIVE"], drive_plant) + client.message_callback_add(Topics["ROBOT_ACTION_GETPOSITION"], get_position) + client.message_callback_add(Topics["ROBOT_ACTION_GETBATTERY"], get_BatteryStatus) + #Subscribe to topics + client.subscribe("ROBOT/DATA") #Testing + client.subscribe(Topics["ROBOT_ACTION_DRIVE"]) + client.subscribe(Topics["ROBOT_ACTION_GETPOSITION"]) + client.subscribe(Topics["ROBOT_ACTION_GETBATTERY"]) -def init_mqtt(): - '''Initialise MQTT client''' - mqttBroker = "mqtt.eclipseprojects.io" - global client - client = mqtt.Client("Robot") - - #Add callbacks - client.message_callback_add("ROBOT/DATA", send_data_json) #Testing - client.message_callback_add(Topics["ROBOT_ACTION_DRIVE"], drive_plant) - client.message_callback_add(Topics["ROBOT_ACTION_GETPOSITION"], get_position) - client.message_callback_add(Topics["ROBOT_ACTION_GETBATTERY"], get_BatteryStatus) - - client.connect(mqttBroker) #Has to be before subscribing - - #Subscribe to topics - client.subscribe("ROBOT/DATA") #Testing - client.subscribe(Topics["ROBOT_ACTION_DRIVE"]) - client.subscribe(Topics["ROBOT_ACTION_GETPOSITION"]) - client.subscribe(Topics["ROBOT_ACTION_GETBATTERY"]) - + print("MQTT initialized") - -def signal_measure(signum, frame): - '''Signal function to measure one plant and start driving home''' - print("Measuring Sensors") - #measure_send_data() - print("Driving Home") - drive_home() - #endregion +# TODO auslagern #region MQTT callbacks #Testing @@ -102,30 +108,43 @@ def send_data_json(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): print("Received data: ", json.dumps(dataDict)) +# CHECK if working properly def drive_plant(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): - '''Function to drive to plant according to request - Starting Drive in Thread''' - global plantID, leftRight - #TODO define MQTT message - plantID = int(message.payload.decode("UTF-8")) + """ + Function to drive to plant according to request + Starting Drive in Thread + + Args: + clients (mqtt.Client): current mqtt client + userdata (_type_): _description_ + message (mqtt.MQTTMessage): received message + """ + dictMessage = json.loads(str(message.payload.decode("UTF-8"))) + plantID = dictMessage["PlantID"] + actionID = dictMessage["ActionID"] print(f"received drive to plant {plantID}") - if plantID % 2 == 0: - leftRight = -50 #rotating left - else: - leftRight = 50 #rotating right - - thread = threading.Thread(target= drive_plant_thread, daemon=True) + thread = threading.Thread(target= drive_plant_thread, args=(plantID, actionID, clients), daemon=True) thread.start() def get_position(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): - '''Callback function for GPS position request - Function to send actual GPS position via MQTT''' + """ + Callback function for GPS position request + Function to send actual GPS position via MQTT - #[ ]TODO Write Sensor Function - client.publish("ROBOT/DATA/POSITION", json.dumps(gpsPosition, indent=4)) + Args: + clients (mqtt.Client): current mqtt client + userdata (_type_): _description_ + message (mqtt.MQTTMessage): received message + """ + # TODO Write Sensor Function + position = { + "Position": "" + } + + clients.publish(Topics["ROBOT_DATA_POSITION"], json.dumps(position, indent=4)) def get_BatteryStatus(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): @@ -134,13 +153,17 @@ def get_BatteryStatus(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage) Function to read battery status from ev3 and send via MQTT Args: - clients (mqtt.Client): _description_ + clients (mqtt.Client): current mqtt client userdata (_type_): _description_ - message (mqtt.MQTTMessage): _description_ + message (mqtt.MQTTMessage): received message """ - #[ ]TODO read Battery - client.publish("ROBOT/DATA/BATTERY", json.dumps(batteryStatus, indent=4)) + battery = { + "Battery": 0.0 + } + + #TODO read Battery + clients.publish(Topics["ROBOT_DATA_BATTERY"], json.dumps(battery, indent=4)) #endregion @@ -152,13 +175,15 @@ def main(): Initialises MQTT and Sensors Runs forever and controlls all robot functions """ - - #TODO on_connect - init_mqtt() - print("MQTT initialized") - signal.signal(signal.SIGUSR1, signal_measure) + # CHECK if global + global sensors + sensors = RaspySensors() + client = mqtt.Client(RASPI_CLIENT_ID) + client.on_connect = on_connect + client.connect(MQTT_BROKER_GLOBAL) # dataDict = {} #Testing + # CHECK forever or start client.loop_start() print("Starting Loop") diff --git a/software/roboter/raspy/raspy_sensors.py b/software/roboter/raspy/raspy_sensors.py index 5da4710..04ef62b 100644 --- a/software/roboter/raspy/raspy_sensors.py +++ b/software/roboter/raspy/raspy_sensors.py @@ -5,10 +5,17 @@ import board import json class RaspySensors: - '''Class to handle all sensors''' + """ + Class to handle all sensors + + Returns: + _type_: _description_ + """ def __init__(self) -> None: - '''Init all Sensors''' + """ + Init all Sensors + """ #[ ]TODO Message if Error @@ -20,46 +27,65 @@ class RaspySensors: #global Variables self.sensorData ={ - "Air Temperature [°C]" : 0, - "Air Humidity [%]" : 0, - "Earth Humidity [%]" : 0, - "Brightness [Lux]" : 0, - "Plant ID": 0, - "Action ID": 0 + "AirTemperature": 0.0, + "AirHumidity" : 0.0, + "SoilMoisture" : 0.0, + "Brightness" : 0, + "PlantID": 0, + "ActionID": 0 } def readSensors(self): - '''Read all Sensors and return Dictionary with data''' + """ + Read all Sensors and return Dictionary with data + + Returns: + sensordata (dict): all data of sensors + """ #read DHT22 #if Error reading Data try again while True: try: - self.sensorData["Air Temperature [°C]"] = self.dht22.temperature - self.sensorData["Air Humidity [%]"] = self.dht22.humidity + self.sensorData["AirTemperature"] = self.dht22.temperature + self.sensorData["AirHumidity"] = self.dht22.humidity except: continue break #read TSL2561 - self.sensorData["Brightness [Lux]"] = round(self.tsl2561.lux, 2) + self.sensorData["Brightness"] = round(self.tsl2561.lux, 2) return self.sensorData - #[ ]TODO - take picture function + #TODO - take picture function def takePicture(self): - '''Take picture and return image''' + """ + Take picture and return image + Returns: + _type_: _description_ + """ return self.image - #[ ]TODO + #TODO - read position with sensor def readPosition(self): - '''Read and return Position''' + """ + Read and return Position + + Returns: + _type_: _description_ + """ return self.position - #[ ]TODO + #TODO - read battery from EV3 def readBattery(self): - '''Read and return battery of ev3''' + """ + Read and return battery of ev3 + + Returns: + _type_: _description_ + """ return self.battery