From 43fbbfcbbbea31039ec436873f7dc2da7688a7a7 Mon Sep 17 00:00:00 2001 From: waldhauserlu78556 Date: Fri, 12 May 2023 16:51:31 +0200 Subject: [PATCH 1/3] RobotReady and SendError added --- software/roboter/raspy/defines.py | 8 ++++-- software/roboter/raspy/functions.py | 40 +++++++++++++++++++---------- software/roboter/raspy/mainProg.py | 2 +- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/software/roboter/raspy/defines.py b/software/roboter/raspy/defines.py index 974eca1..30880a4 100644 --- a/software/roboter/raspy/defines.py +++ b/software/roboter/raspy/defines.py @@ -19,6 +19,8 @@ Topics = { "ROBOT_DATA_BATTERY": "ROBOT/DATA/BATTERY", "ROBOT_DATA_POSITION": "ROBOT/DATA/POSITION", "ROBOT_DATA_PICTURE": "ROBOT/DATA/PICTURE", + "ROBOT_DATA_ERROR": "ROBOT/DATA/ERROR", + "ROBOT_DATA_ROBOTREADY": "ROBOT/DATA/ROBOTREADY", "BACKEND_ACTION_DRIVE": "BACKEND/ACTION/DRIVE", "BACKEND_ACTION_DRIVEPALL": "BACKEND/ACTION/DRIVEALL", @@ -36,6 +38,8 @@ Topics = { "BACKEND_DATA_BATTERY": "BACKEND/DATA/BATTERY", "BACKEND_DATA_PICTURE": "BACKEND/DATA/PICTURE", "BACKEND_DATA_PLANTCOUNT": "BACKEND/DATA/PLANTCOUNT", + "BACKEND_DATA_ERROR": "BACKEND/DATA/ERROR", + "BACKEND_DATA_ROBOTREADY": "BACKEND/DATA/ROBOTREADY" } @@ -49,7 +53,7 @@ SENSORDATA = { "SoilMoisture": 0.0, "Brightness": 0, "PlantID": 0, - "ActionID": 0 + "ActionID": "" } # TODO When sensor is available @@ -67,7 +71,7 @@ BATTERY = { DRIVE = { "PlantID": 0, - "ActionID": 0 + "ActionID": "" } # GETPOSITION -> no message needed diff --git a/software/roboter/raspy/functions.py b/software/roboter/raspy/functions.py index 338340e..84fcaa4 100644 --- a/software/roboter/raspy/functions.py +++ b/software/roboter/raspy/functions.py @@ -20,12 +20,13 @@ def measure_send_data(plantID, actionID, client: mqtt.Client): try: sensorData = Sensors.readSensors() except Exception as e: - print(str(e)) + logging.error(str(e)) # TODO Error message MQTT + client.publish(Topics["ROBOT_DATA_ERROR"], str(e), qos=1) sensorData["PlantID"] = plantID sensorData["ActionID"] = actionID - client.publish(Topics["ROBOT_DATA_SENSORDATA"], json.dumps(sensorData, indent=4)) + client.publish(Topics["ROBOT_DATA_SENSORDATA"], json.dumps(sensorData, indent=4), qos=1) def drive_plant_thread(plantID, actionID, client: mqtt.Client): @@ -43,10 +44,13 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client): errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/plant_{plantID}.py') # errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_plant.py {plantID}') - # TODO Error Message if errorCode != 0: logging.error(f"Robot Error {errorCode} occurred") - logging.error("Drive Plant aborted, Robot at starting position") + logging.info("Drive Plant aborted, Robot at starting position") + client.publish(Topics["ROBOT_DATA_ERROR"], "Drive Plant aborted, Robot at starting position", qos=1) + #TODO decide about robot occupied message + client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1) + return logging.info("Measuring Sensors") @@ -55,21 +59,23 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client): logging.info("Robot driving home") errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back.py {plantID}') - # TODO Error Message if errorCode != 0: logging.error(f"Robot Error {errorCode} occurred") - logging.error(f"Drive Home aborted, Robot at plant {plantID}") + logging.info(f"Drive Home aborted, Robot at plant {plantID}") + client.publish(Topics["ROBOT_DATA_ERROR"], f"Drive Home aborted, Robot at plant {plantID}", qos=1) + #TODO decide about robot occupied message + client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1) return logging.info("Robot home") #TODO decide about robot occupied message - #client.publish("ROBOT/DATA/OCCUPIED", "false") + client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1) #region MQTT callbacks -def drive_plant(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): +def drive_plant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): """ Function to drive to plant according to request Starting Drive in Thread to not block main programm @@ -80,12 +86,18 @@ def drive_plant(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): message (mqtt.MQTTMessage): received DRIVE message with PlantID and ActionID """ dictMessage = json.loads(str(message.payload.decode("UTF-8"))) + + print(dictMessage) + plantID = dictMessage["PlantID"] actionID = dictMessage["ActionID"] logging.info(f"Received Drive-request to plant {plantID}, ActionID: {actionID}") + + #TODO decide about robot occupied message + client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "False", qos=1) - thread = threading.Thread(target= drive_plant_thread, args=(plantID, actionID, clients), daemon=True) + thread = threading.Thread(target= drive_plant_thread, args=(plantID, actionID, client), daemon=True) thread.start() @@ -99,15 +111,17 @@ def get_position(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): userdata (_type_): _description_ message (mqtt.MQTTMessage): received message """ + logging.info("Received position request") + # TODO Write GPS Sensor Function position = { "Position": "" } - clients.publish(Topics["ROBOT_DATA_POSITION"], json.dumps(position, indent=4)) + clients.publish(Topics["ROBOT_DATA_POSITION"], json.dumps(position, indent=4), qos=1) -def get_BatteryStatus(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): +def get_BatteryStatus(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): """ Callback function for battery status request Function to read battery status from ev3 and send via MQTT in form of BATTERY @@ -125,7 +139,7 @@ def get_BatteryStatus(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage) batteryLevel = int(os.popen('sshpass -p maker ssh robot@ev3dev.local cat /sys/devices/platform/battery/power_supply/lego-ev3-battery/voltage_now').read()) except: logging.error("Robot not connected") - #TODO Error Message + client.publish(Topics["ROBOT_DATA_ERROR"], "Robot not connected", qos=1) return batteryLevel = round(batteryLevel / 1000000, 2) # Voltage @@ -135,6 +149,6 @@ def get_BatteryStatus(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage) battery = { "Battery": batteryLevel } - clients.publish(Topics["ROBOT_DATA_BATTERY"], json.dumps(battery, indent=4)) + client.publish(Topics["ROBOT_DATA_BATTERY"], json.dumps(battery, indent=4), qos=1) #endregion \ No newline at end of file diff --git a/software/roboter/raspy/mainProg.py b/software/roboter/raspy/mainProg.py index fdc1a2b..4edc26a 100644 --- a/software/roboter/raspy/mainProg.py +++ b/software/roboter/raspy/mainProg.py @@ -57,7 +57,7 @@ def main(): # CHECK forever or start client.loop_start() - logging.info("Starting Loop") + logging.info("Robot initialised") while True: pass From 7452d6aae8c16986eb4b320deee868a6cc724263 Mon Sep 17 00:00:00 2001 From: waldhauserlu78556 Date: Sat, 13 May 2023 15:19:27 +0200 Subject: [PATCH 2/3] SoilMoisture, Camera (and GPS) added --- requirements.txt | 6 +- software/roboter/raspy/defines.py | 1 + software/roboter/raspy/functions.py | 37 ++++++++++--- software/roboter/raspy/raspy_sensors.py | 74 +++++++++++++++++++++++-- 4 files changed, 103 insertions(+), 15 deletions(-) diff --git a/requirements.txt b/requirements.txt index c983847..f2128e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,4 +29,8 @@ urllib3==1.26.14 Werkzeug==2.2.3 zipp==3.15.0 python-ev3dev2==2.1.0.post1 -pytest \ No newline at end of file +pytest +pip==23.1.2 +# spidev==3.6 +# picamera==1.13 +numpy==1.19.5 \ No newline at end of file diff --git a/software/roboter/raspy/defines.py b/software/roboter/raspy/defines.py index 30880a4..929cf64 100644 --- a/software/roboter/raspy/defines.py +++ b/software/roboter/raspy/defines.py @@ -21,6 +21,7 @@ Topics = { "ROBOT_DATA_PICTURE": "ROBOT/DATA/PICTURE", "ROBOT_DATA_ERROR": "ROBOT/DATA/ERROR", "ROBOT_DATA_ROBOTREADY": "ROBOT/DATA/ROBOTREADY", + "ROBOT_DATA_PICTURE": "ROBOT/DATA/PICTURE", "BACKEND_ACTION_DRIVE": "BACKEND/ACTION/DRIVE", "BACKEND_ACTION_DRIVEPALL": "BACKEND/ACTION/DRIVEALL", diff --git a/software/roboter/raspy/functions.py b/software/roboter/raspy/functions.py index 84fcaa4..cf89136 100644 --- a/software/roboter/raspy/functions.py +++ b/software/roboter/raspy/functions.py @@ -21,7 +21,6 @@ def measure_send_data(plantID, actionID, client: mqtt.Client): sensorData = Sensors.readSensors() except Exception as e: logging.error(str(e)) - # TODO Error message MQTT client.publish(Topics["ROBOT_DATA_ERROR"], str(e), qos=1) sensorData["PlantID"] = plantID @@ -48,7 +47,6 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client): logging.error(f"Robot Error {errorCode} occurred") logging.info("Drive Plant aborted, Robot at starting position") client.publish(Topics["ROBOT_DATA_ERROR"], "Drive Plant aborted, Robot at starting position", qos=1) - #TODO decide about robot occupied message client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1) return @@ -56,6 +54,9 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client): logging.info("Measuring Sensors") measure_send_data(plantID, actionID, client) + # FIXME Send Picture + # sendPicture() + logging.info("Robot driving home") errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back.py {plantID}') @@ -63,25 +64,24 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client): logging.error(f"Robot Error {errorCode} occurred") logging.info(f"Drive Home aborted, Robot at plant {plantID}") client.publish(Topics["ROBOT_DATA_ERROR"], f"Drive Home aborted, Robot at plant {plantID}", qos=1) - #TODO decide about robot occupied message client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1) return logging.info("Robot home") - #TODO decide about robot occupied message client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1) #region MQTT callbacks +# TODO Test with all sensors def drive_plant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): """ Function to drive to plant according to request Starting Drive in Thread to not block main programm Args: - clients (mqtt.Client): current mqtt client + client (mqtt.Client): current mqtt client userdata (_type_): _description_ message (mqtt.MQTTMessage): received DRIVE message with PlantID and ActionID """ @@ -93,8 +93,6 @@ def drive_plant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): actionID = dictMessage["ActionID"] logging.info(f"Received Drive-request to plant {plantID}, ActionID: {actionID}") - - #TODO decide about robot occupied message client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "False", qos=1) thread = threading.Thread(target= drive_plant_thread, args=(plantID, actionID, client), daemon=True) @@ -107,7 +105,7 @@ def get_position(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage): Function to send actual GPS position via MQTT in form of POSITION Args: - clients (mqtt.Client): current mqtt client + client (mqtt.Client): current mqtt client userdata (_type_): _description_ message (mqtt.MQTTMessage): received message """ @@ -129,7 +127,7 @@ def get_BatteryStatus(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): 5V -> 0% Args: - clients (mqtt.Client): current mqtt client + client (mqtt.Client): current mqtt client userdata (_type_): _description_ message (mqtt.MQTTMessage): received message """ @@ -151,4 +149,25 @@ def get_BatteryStatus(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): } client.publish(Topics["ROBOT_DATA_BATTERY"], json.dumps(battery, indent=4), qos=1) +# TODO Test needed +def sendPicture(client: mqtt.Client): + """ + Takes picture and publish via MQTT + + Args: + client (mqtt.Client): current mqtt client + """ + try: + Sensors.takePicture() + except Exception as e: + logging.error(str(e)) + client.publish(Topics["ROBOT_DATA_ERROR"], str(e), qos=1) + + with open("picture.txt", "rb") as f: + file = f.read() + byteArr = bytearray(file) + print(byteArr) + client.publish(Topics["ROBOT_DATA_IMAGE"], byteArr) + print("Published") + #endregion \ No newline at end of file diff --git a/software/roboter/raspy/raspy_sensors.py b/software/roboter/raspy/raspy_sensors.py index 0500902..455ff5e 100644 --- a/software/roboter/raspy/raspy_sensors.py +++ b/software/roboter/raspy/raspy_sensors.py @@ -3,6 +3,8 @@ import adafruit_dht import adafruit_tsl2561 import board import json +import spidev +from picamera import PiCamera from defines import SENSORDATA @@ -34,7 +36,7 @@ def readDHT22(): def readTSL2561(): """ - Reads TSL2561 brightness in Lux + Reads TSL2561 brightness in Lux and returns integer value Raises: Exception: If TSL2561 not connected properly @@ -57,8 +59,36 @@ def readTSL2561(): return brightness +def readMCP3008(): + """ + Reads YL-69 via MCP3008 ADC soil moisture in percent and returns float value + + Raises: + Exception: If YL-69 not connected properly + + Returns: + float: soil moisture [%] + """ + + channel = 0 # Input channel into MCP3008 ADC + try: + spi = spidev.SpiDev() + spi.open(0,0) + spi.max_speed_hz = 1000000 + except: + raise Exception("YL69 not connected") + + val = spi.xfer2([1,(8+channel) << 4, 0]) + data = ((val[1] & 3) << 9) +val[2] + percentage = data - 680 # Return values between ~1780 and ~680 + percentage = round((1100 - percentage) / 1100, 2) # 680 -> 100% moisture, 1780 -> 0% moisture + + if percentage > 1 or percentage < 0: # If not connected values above 100% appear + percentage = 0 + raise Exception("YL69 not connected") + + return percentage -# TODO SoilMoisture Sensor Function # TODO Function for all sensors @@ -69,6 +99,7 @@ def readSensors(): Raises: Exception: DHT22 not connected Exception: TSL2561 not connected + Exception: YL69 not connected Returns: dict: Sensordata @@ -76,6 +107,7 @@ def readSensors(): sensorData = SENSORDATA errorMessage = "" + # read DHT22 try: sensorData["AirTemperature"], sensorData["AirHumidity"] = readDHT22() except Exception as e: @@ -83,12 +115,20 @@ def readSensors(): sensorData["AirTemperature"] = 0 errorMessage = str(e) + "\n" + # read TSL2561 try: sensorData["Brightness"] = readTSL2561() except Exception as e: sensorData["Brightness"] = 0 # otherwise old value errorMessage = errorMessage + str(e) + "\n" + # read YL-69 + try: + sensorData["SoilMoisture"] = readMCP3008() + except Exception as e: + sensorData["SoilMoisture"] = 0 + errorMessage = errorMessage + str(e) + "\n" + # combined error message if errorMessage != "": raise Exception(errorMessage) @@ -96,16 +136,25 @@ def readSensors(): return sensorData -# TODO - take picture function +# TODO - Test needed def takePicture(): """ Take picture and return picture Returns: _type_: _description_ """ - picture = "" + try: + camera = PiCamera() + except: + raise Exception("Camera not connected") + return + + camera.start_preview() + camera.capture("picture.png") + print("Camera on") + camera.stop_preview() - return picture + return # TODO - read position with sensor @@ -118,3 +167,18 @@ def readPosition(): """ position = "" return position + + +# Testing + +def main(): + value = SENSORDATA + try: + takePicture() + except Exception as e: + print(str(e)) + + print(value) + +if __name__ == "__main__": + main() \ No newline at end of file From 062d08fe1ac3bfea1696b8c71478d84874ccc904 Mon Sep 17 00:00:00 2001 From: waldhauserlu78556 Date: Sun, 14 May 2023 10:44:33 +0200 Subject: [PATCH 3/3] Bugfixes after drive test --- software/roboter/raspy/functions.py | 18 ++++++++---------- software/roboter/raspy/raspy_sensors.py | 5 +---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/software/roboter/raspy/functions.py b/software/roboter/raspy/functions.py index cf89136..b43382a 100644 --- a/software/roboter/raspy/functions.py +++ b/software/roboter/raspy/functions.py @@ -54,8 +54,8 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client): logging.info("Measuring Sensors") measure_send_data(plantID, actionID, client) - # FIXME Send Picture - # sendPicture() + logging.info("Taking Picture") + sendPicture(client) logging.info("Robot driving home") errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back.py {plantID}') @@ -74,7 +74,6 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client): #region MQTT callbacks -# TODO Test with all sensors def drive_plant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): """ Function to drive to plant according to request @@ -87,8 +86,6 @@ def drive_plant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): """ dictMessage = json.loads(str(message.payload.decode("UTF-8"))) - print(dictMessage) - plantID = dictMessage["PlantID"] actionID = dictMessage["ActionID"] @@ -148,8 +145,9 @@ def get_BatteryStatus(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): "Battery": batteryLevel } client.publish(Topics["ROBOT_DATA_BATTERY"], json.dumps(battery, indent=4), qos=1) + logging.info(f"Battery done {batteryLevel}") + -# TODO Test needed def sendPicture(client: mqtt.Client): """ Takes picture and publish via MQTT @@ -162,12 +160,12 @@ def sendPicture(client: mqtt.Client): except Exception as e: logging.error(str(e)) client.publish(Topics["ROBOT_DATA_ERROR"], str(e), qos=1) + return - with open("picture.txt", "rb") as f: + with open("picture.png", "rb") as f: file = f.read() byteArr = bytearray(file) - print(byteArr) - client.publish(Topics["ROBOT_DATA_IMAGE"], byteArr) - print("Published") + client.publish(Topics["ROBOT_DATA_PICTURE"], byteArr) + logging.info("Picture Published") #endregion \ No newline at end of file diff --git a/software/roboter/raspy/raspy_sensors.py b/software/roboter/raspy/raspy_sensors.py index 455ff5e..42ad204 100644 --- a/software/roboter/raspy/raspy_sensors.py +++ b/software/roboter/raspy/raspy_sensors.py @@ -136,7 +136,6 @@ def readSensors(): return sensorData -# TODO - Test needed def takePicture(): """ Take picture and return picture @@ -147,11 +146,9 @@ def takePicture(): camera = PiCamera() except: raise Exception("Camera not connected") - return camera.start_preview() camera.capture("picture.png") - print("Camera on") camera.stop_preview() return @@ -174,7 +171,7 @@ def readPosition(): def main(): value = SENSORDATA try: - takePicture() + readSensors() except Exception as e: print(str(e))