|
|
@@ -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 |
|
|
|
|
|
|
|
|
|
|
|
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"]) |
|
|
|
|
|
|
|
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"]) |
|
|
|
|
|
|
|
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") |