Hardware components | ||||||
![]() |
| × | 3 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
| ||||||
Hand tools and fabrication machines | ||||||
![]() |
| |||||
![]() |
|
Urban farming has become popular to provide fresh, locally grown produce to urban communities. However, traditional aquaponic systems often support only a limited variety of crops due to varying temperature, light, and nutrient requirements. Moreover, high costs and inefficient harvesting methods make the adoption challenging.
This project introduces a multi-crop aquaponic system, to overcome these limitations. With smart automation and monitoring, it improves efficiency, reducing costs, and simplifying farm management. By utilizing fish tank water and waste as natural fertilizers, while recycling water through a single loop, this system ensures sustainability and efficiency.
Driving question: How can we develop a cost-effective, automated aquaponic system that enhances the growth of diverse crops through precise environmental control?
Objectives:
- To develop an aquaponic system capable of growing multiple crops within a single stacked row.
- To implement automation for real-time monitoring and control of important environmental factors.
- To reduce water consumption and the need for chemical fertilizers, lowering the carbon footprint of traditional farming and food transport.
- To create a cost-effective, energy-efficient system that is scalable and accessible for urban farmers and home gardening.
Research:
- The closed-loop system recycles water and nutrients efficiently, reducing fertilizer use by 50%.
- Lettuce and basil were chosen for their fast growth and adaptability. They generally thrive in a pH range of 5.5-6.5, EC range of 0.8-1.6mS/cm and temperature between 25-30℃.
- Tilapia was selected for its resilience and nutrient-rich waste. It thrives in warm water and its waste supports plants like lettuce and basil.
- Humidity & Temperature: Affect plant transpiration and water requirements.
- Light Requirements: Plants compete for sunlight in confined spaces; artificial lighting can supplement natural light.
- Nutrient Distribution: Different plants require specific nutrient levels, requiring precise monitoring.
Project:
- Light Sensor: Adjusts LED intensity based on plant needs.
- Environment Sensor (ENV III): Indicate humidity levels & air temperature on the dashboard, adjusting the fans and humidifier.
- Air Conditioning (Fans): It regulates the airflow and humidity.
- Dashboard: Displays real-time data and enables remote monitoring and control.
- Master Controller & Angle Unit: Selects and sends control parameters to secondary controllers.
- Roof: Equipped with adjustable lighting to provide the necessary light levels for each plant type.
- Servo Motor: Rotates plant rows for better accessibility.
- prototype showcase: 5.23
import os, sys, io
import math
import M5
from M5 import *
from unit import AngleUnit
from m5espnow import M5ESPNow
import json
from hardware import Timer
import time
import network
from umqtt.simple import MQTTClient
home = None
Row1Label = None
Row2Label = None
option = None
label4 = None
paramLabel = None
valueLabel = None
plant1Label = None
plant2Label = None
plant3Label = None
plant4Label = None
SUFLabel = None
powerLabel = None
angle_0 = None
tableLabel = None
LEVEL = None
plant_profiles = None
angle = None
selectedPlant = None
selectedRow = None
selectedParam = None
flowRateLabel = None
co2LevelLabel = None
lightDurationLabel = None
lightIntensityLabel = None
humidityLabel = None
waterTempLabel = None
airTempLabel = None
clicked = False
timer0 = None
sendLabel = None
rotateRow=False
espnow_0 = None
mqtt_client = None
wlan = None
SSID = 'WIFI'
WLAN_PW = 'WIFI_PASSWORD'
# Row MAC addresses (for ESP-NOW communication)
ROW_MAC_LIST = ['2cbcbb81f7c0', '2cbcbb81F924']
MQTT = True #If MQTT is true, the master will send data to the mqtt server. if false, will only use espnow
def timer0_cb(t):
global espnow_0, clicked, selectedPlant, selectedRow
if clicked:
# label0.setText(str('a'))
clicked = False
profile_to_data(selectedRow, selectedPlant)
time.sleep_ms(10)
#Sends data from a plant profile dict, to send_data
#This is becuase old version had the ability to send/update single params, and keeping this setup allows for that to be reimplemented
def profile_to_data(row_number, plant_profile):
"""
Translates the plant profile data to the dataAsList format.
Args:
plant_profile (dict): The plant profile containing the plant parameters.
row_number (int): The row number to use for the MQTT topic.
"""
send_data(
row_number,
plant_profile['plantName'],
plant_profile['airTemp'],
plant_profile['waterTemp'],
plant_profile['humidity'],
plant_profile['lightIntensity'],
plant_profile['lightDuration'],
plant_profile['co2Level'],
plant_profile['flowRate']
)
# Function to send data to a specific row
def send_data(rowNo, plantName, airTemp, waterTemp, humidity, lightIntensity, lightDuration, co2Level, flowRate):
global espnow_0
# Convert data to dict
rowData = {
"plantName": plantName,
"airTemp": airTemp,
"waterTemp": waterTemp,
"humidity": humidity,
"lIntensity": lightIntensity,
"lDuration": lightDuration,
"co2Level": co2Level,
"flowRate": flowRate,
}
try:
#Convert to json
json_data = json.dumps(rowData)
payload = json_data.encode('utf-8')
# Send the data to the selectedRow
print(f"Sending to peer ID: {rowNo}, Data: {json_data}")
espnow_0.send_data(int(rowNo), payload)
print(f"Successfully sent to peer ID: {rowNo}")
except Exception as e:
print(f"Failed to send data: {e}")
# MQTT Publishing (optional)
if MQTT:
time.sleep_ms(100)
print("Mqtt send is on")
if not wlan.isconnected():
print("Wi-Fi not connected")
return
try:
json_data = json.dumps(rowData)
publish_with_retry(f'm5stack/row{rowNo}/dataList', json_data)
except Exception as e:
print(f"Publish error: {e}")
# MQTT Publishing with Retry
def publish_with_retry(topic, message, retries=3):
global mqtt_client
for attempt in range(retries):
try:
mqtt_client.publish(topic, message, qos=1)
print(f"Published: {message} to {topic}")
return
except Exception as e:
print(f"Publish failed ({attempt + 1}/{retries}): {e}")
if not reconnect_mqtt():
break
print("Failed to publish message after retries")
# MQTT Reconnect Function
def reconnect_mqtt():
global mqtt_client
try:
mqtt_client.connect()
print("MQTT reconnected")
return True
except Exception as e:
print(f"Failed to reconnect MQTT: {e}")
return False
#hdies all labels exceot the ones passed as a list
def hide_except(labelsToKeep):
all_labels = [sendLabel, Row1Label, Row2Label, plant1Label, plant2Label, plant3Label, plant4Label, paramLabel, valueLabel, option, SUFLabel, powerLabel, home, tableLabel, flowRateLabel, co2LevelLabel, lightDurationLabel, lightIntensityLabel, humidityLabel, waterTempLabel, airTempLabel]
# Hide all labels
for label in all_labels:
label.setVisible(False)
# Make labels in labelsToKeep visible
for label in labelsToKeep:
label.setVisible(True)
#Displays the selected plant profile using multiple labels.
def display_selected_plant(selected_plant_dict):
if not selected_plant_dict:
return
# Create or update labels
# plantNameLabel.setText(f"Plant: {selected_plant_dict['plantname']}")
airTempLabel.setText(f"Air Temp: {selected_plant_dict['airTemp']} C")
waterTempLabel.setText(f"Water Temp: {selected_plant_dict['waterTemp']} C")
humidityLabel.setText(f"Humidity: {selected_plant_dict['humidity']}%")
lightIntensityLabel.setText(f"Light Intensity: {selected_plant_dict['lightIntensity']}")
lightDurationLabel.setText(f"Light Duration: {selected_plant_dict['lightDuration']} hrs")
co2LevelLabel.setText(f"CO2 Level: {selected_plant_dict['co2Level']} ppm")
flowRateLabel.setText(f"Flow Rate: {selected_plant_dict['flowRate']} L/min")
# Position labels dynamically
labels = [
plantNameLabel, airTempLabel, waterTempLabel, humidityLabel,
lightIntensityLabel, lightDurationLabel, co2LevelLabel, flowRateLabel
]
for i, label in enumerate(labels):
# label.setPosition(base_x, base_y + (i * spacing))
label.setVisible(True) # Ensure it's visible
#Hide all labels except home labels
def MAIN():
global home, Row1Label, Row2Label, option, label4, paramLabel, valueLabel, plant1Label, plant2Label, plant3Label, plant4Label, SUFLabel, powerLabel, angle_0
# home.setImage("res/img/bg.png")
hide_except([SUFLabel, powerLabel, home])
powerLabel.setText('Powered by Aquaponics')
SUFLabel.setText('Smart Urban Farming')
def SEND():
hide_except([sendLabel,flowRateLabel, co2LevelLabel, lightDurationLabel, lightIntensityLabel, humidityLabel, waterTempLabel, airTempLabel])
display_selected_plant(selectedPlant)
# Describe this function...
def Options():
global rotateRow,LEVEL, plant_profiles, angle, selectedPlant, selectedRow, selectedParam, home, Row1Label, Row2Label, option, label4, paramLabel, valueLabel, plant1Label, plant2Label, plant3Label, plant4Label, SUFLabel, powerLabel, angle_0
# Helper function to set label colors
def set_plant_labels_color(selected_label, other_labels):
selected_label.setColor(0x330033, 0xffffff)
for label in other_labels:
label.setColor(0xffffff, 0x990000)
if LEVEL == 1:
# Handle row label colors based on angle
if angle < 45:
rotateRow = True
Row1Label.setColor(0xffffff, 0x5a2a2a)
Row2Label.setColor(0xffffff, 0x5a2a2a)
elif angle < 90:
rotateRow = False
selectedRow = 1
Row1Label.setColor(0x330033, 0xffffff)
Row2Label.setColor(0xffffff, 0x5a2a2a)
else:
rotateRow = False
selectedRow = 2
Row1Label.setColor(0xffffff, 0x5a2a2a)
Row2Label.setColor(0x330033, 0xffffff)
elif LEVEL == 2:
Row1Label.setVisible(False)
Row2Label.setVisible(False)
# Set plant labels based on angle and level
if angle < 45:
selectedPlant = plant_profiles['Lettuce']
set_plant_labels_color(plant1Label, [plant2Label, plant3Label, plant4Label])
elif angle < 90:
selectedPlant = plant_profiles['Basil']
set_plant_labels_color(plant2Label, [plant1Label, plant3Label, plant4Label])
elif angle < 135:
selectedPlant = plant_profiles['Strawberry']
set_plant_labels_color(plant3Label, [plant1Label, plant2Label, plant4Label])
else:
selectedPlant = plant_profiles['CUSTOM']
set_plant_labels_color(plant4Label, [plant1Label, plant2Label, plant3Label])
option.setText(str(selectedRow))
elif LEVEL == 3:
# Set parameters based on angle
params = [
('airTemp', 'Air Temp', selectedPlant['airTemp']),
('waterTemp', 'Water Temp', selectedPlant['waterTemp']),
('humidity', 'Humidity', selectedPlant['humidity']),
('lightIntensity', 'Light Intensity', selectedPlant['lightIntensity']),
('lightDuration', 'Light Duration', selectedPlant['lightDuration']),
('co2Level', 'CO2 Level', selectedPlant['co2Level']),
('flowRate', 'Flow Rate', selectedPlant['flowRate']),
]
for i, (param, label, value) in enumerate(params):
if angle < (i + 1) * 25:
selectedParam = param
paramLabel.setText(label)
valueLabel.setText(str(value))
break
option.setText(str(selectedPlant['plantName']))
elif LEVEL == 4:
# Update plant values based on selected parameter
param_updates = {
'airTemp': 10 + round(angle / 8),
'waterTemp': 5 + round(angle / 8),
'humidity': round(100 * (angle / 180)),
'lightIntensity': round(angle / 0.09),
'lightDuration': 10 + round(angle / 90),
'co2Level': 300 + round(900 * (angle / 180)),
'flowRate': round(angle / 18)
}
if selectedParam in param_updates:
selectedPlant[selectedParam] = param_updates[selectedParam]
valueLabel.setText(str(selectedPlant[selectedParam]))
# Describe this function...
def ROW():
# Use hide_except function to only show relevant labels
hide_except([Row1Label, Row2Label])
# Set row labels text
Row1Label.setText('Row1')
Row2Label.setText('Row2')
# Describe this function...
def PLANT():
# Use hide_except function to only show relevant labels
hide_except([option, plant1Label, plant2Label, plant3Label, plant4Label])
# Set plant labels text
plant1Label.setText('Lettuce')
plant2Label.setText('Basil')
plant3Label.setText('Strawberry')
plant4Label.setText('CUSTOM')
# Describe this function...
def PARAM():
hide_except([option, paramLabel, valueLabel])
def btnA_wasPressed_event(state):
global LEVEL, rotateRow, SUFLabel
# Increment LEVEL, reset to 0 if it exceeds 4
LEVEL += 1
# Call appropriate function based on LEVEL
if LEVEL == 1:
ROW()
elif LEVEL == 2:
if rotateRow:
LEVEL = 0
hide_except([SUFLabel])
for i in range(10):
SUFLabel.setText("Rotating Row...")
time.sleep_ms(500)
SUFLabel.setText("")
time.sleep_ms(500)
SUFLabel("Smart Urban Farming")
MAIN()
else:
PLANT()
elif LEVEL == 3:
PARAM()
elif LEVEL == 5:
SEND()
elif LEVEL > 5: # LEVEL > 4
LEVEL = 0
MAIN()
def btnB_wasPressed_event(state):
global clicked
print("beans")
if LEVEL == 5:
clicked = True
def btnC_wasPressed_event(state):
global LEVEL
# Decrement LEVEL, reset to 0 if it goes below 1
LEVEL -= 1
if LEVEL == 1:
ROW()
elif LEVEL == 2:
PLANT()
elif LEVEL == 3:
PARAM()
elif LEVEL == 4:
PARAM()
elif LEVEL ==5:
SEND()
else:
LEVEL = 0
MAIN()
def setup():
global sendLabel, plantNameLabel, airTempLabel, waterTempLabel, humidityLabel, espnow_0, timer0,wlan, mqtt_client
global lightIntensityLabel, lightDurationLabel, co2LevelLabel, flowRateLabel
global tableLabel, home, Row1Label, Row2Label, option, label4, paramLabel, valueLabel, plant1Label, plant2Label, plant3Label, plant4Label, SUFLabel, powerLabel, angle_0, LEVEL, plant_profiles, angle, selectedPlant, selectedRow, selectedParam
if MQTT:
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, WLAN_PW)
M5.begin()
Widgets.fillScreen(0x5a2a2a)
# Initialize widgets
home = Widgets.Image("res/img/default.png", 0, -30, scale_x=.4, scale_y=.4)
Row1Label = Widgets.Label("Row1", 131, 71, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
Row2Label = Widgets.Label("Row2", 131, 108, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
option = Widgets.Label("label0", 0, 0, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu24)
label4 = Widgets.Label("label4", 282, 215, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
paramLabel = Widgets.Label("Param:", 85, 63, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu24)
valueLabel = Widgets.Label("value", 134, 121, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
plant1Label = Widgets.Label("plant1", 120, 50, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
plant2Label = Widgets.Label("pant2", 135, 100, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
plant3Label = Widgets.Label("plant3", 105, 145, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
plant4Label = Widgets.Label("plant4", 127, 191, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
SUFLabel = Widgets.Label("Smart Urban Farming", 22, 94, 1.0, 0xffffff, 0x54ca2c, Widgets.FONTS.DejaVu24)
powerLabel = Widgets.Label("Powered by Aquaponics", 46, 121, 1.0, 0xffffff, 0x32637e, Widgets.FONTS.DejaVu18)
tableLabel = Widgets.Label("Table", 0,0,1.0,0xffffff, 0x32637e, Widgets.FONTS.DejaVu18)
plantNameLabel = Widgets.Label("", 20, 50, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
airTempLabel = Widgets.Label("", 20, 25, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
waterTempLabel = Widgets.Label("", 20, 50, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
humidityLabel = Widgets.Label("", 20, 75, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
lightIntensityLabel = Widgets.Label("", 20, 100, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
lightDurationLabel = Widgets.Label("", 20, 125, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
co2LevelLabel = Widgets.Label("", 20, 150, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
flowRateLabel = Widgets.Label("", 20, 175, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
sendLabel = Widgets.Label("send", 133, 214, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu18)
if MQTT:
while not wlan.isconnected():
print("Connecting to Wi-Fi...")
time.sleep(1)
print("Connected to Wi-Fi")
try:
mqtt_client = MQTTClient('TacoMaster', 'test.mosquitto.org', port=1883, user='', password='', keepalive=60)
mqtt_client.connect()
except Exception as e:
print(f"MQTT connection error: {str(e)}")
# Set button callbacks
BtnA.setCallback(type=BtnA.CB_TYPE.WAS_PRESSED, cb=btnA_wasPressed_event)
BtnB.setCallback(type=BtnB.CB_TYPE.WAS_PRESSED, cb=btnB_wasPressed_event)
BtnC.setCallback(type=BtnC.CB_TYPE.WAS_PRESSED, cb=btnC_wasPressed_event)
espnow_0 = M5ESPNow(0)
for index, mac in enumerate(ROW_MAC_LIST):
espnow_0.set_add_peer(mac, (index + 1), 0, False)
print(f"Peer added: {mac} with ID: {index + 1}")
print(f"Peer added: 2cbcbb81f7c0 with ID: 1")
# Initialize plant profiles
plant_profiles = {
'Lettuce': {'plantName': 'Lettuce', 'airTemp': 22, 'waterTemp': 18, 'humidity': 70, 'lightIntensity': 400, 'lightDuration': 16, 'co2Level': 400, 'flowRate': 1},
'Basil': {'plantName': 'Basil', 'airTemp': 25, 'waterTemp': 22, 'humidity': 65, 'lightIntensity': 500, 'lightDuration': 14, 'co2Level': 450, 'flowRate': 1.2},
'Strawberry': {'plantName': 'Strawberry', 'airTemp': 20, 'waterTemp': 19, 'humidity': 75, 'lightIntensity': 350, 'lightDuration': 12, 'co2Level': 350, 'flowRate': 1.5},
'CUSTOM': {'plantName': 'CUSTOM', 'airTemp': 0, 'waterTemp': 0, 'humidity': 0, 'lightIntensity': 0, 'lightDuration': 0, 'co2Level': 0, 'flowRate': 0}
}
timer0 = Timer(0)
timer0.init(mode=Timer.PERIODIC, period=500, callback=timer0_cb)
LEVEL = 0
angle_0 = AngleUnit((36, 26))
# Start the main process
MAIN()
def loop():
global home, Row1Label, Row2Label, option, label4, paramLabel, valueLabel, plant1Label, plant2Label, plant3Label, plant4Label, SUFLabel, powerLabel, angle_0, LEVEL, plant_profiles, angle, selectedPlant, selectedRow, selectedParam
M5.update()
# get to roughly between 0 and 180
angle = (angle_0.get_value()) / 364
label4.setText(str(LEVEL))
Options()
if __name__ == '__main__':
try:
setup()
while True:
loop()
except (Exception, KeyboardInterrupt) as e:
try:
from utility import print_error_msg
print_error_msg(e)
except ImportError:
print("please update to latest firmware")
Secondary M5 Code
Pythonimport os, sys, io
import M5
from M5 import *
from m5espnow import M5ESPNow
from unit import LightUnit
from hardware import I2C
from hardware import Pin
from unit import AngleUnit
from unit import ENVUnit
import time
import network
from umqtt.simple import MQTTClient
import json
from hardware import Timer
title = None
sensorLight = None
sensorTemp = None
sensorHumidity = None
LIGHT_label = None
AIR_label = None
HUMIDITY_label = None
dataLabel1 = None
plantname = None
dataLabel2 = None
airTemp = None
dataLabel3 = None
waterTemp = None
dataLabel4 = None
humidity = None
dataLabel5 = None
lightIntensity = None
dataLabel6 = None
lightDuration = None
dataLabel7 = None
co2Level = None
dataLabel8 = None
flowRate = None
espnow_0 = None
i2c1 = None
env3_0 = None
angle_0 = None
light_0 = None
light_label = None
LUX = None
LIGHT = None
AIR = None
HUMIDITY = None
stored_params = None
sensor_data = None
stored_params = {
"plantName": "Lettuce",
"airTemp": 22.0,
"waterTemp": 18.0,
"humidity": 70.0,
"lIntensity": 400,
"lDuration": 16,
"co2Level": 400.0,
"flowRate": 1.0,
}
sensor_data = {
"airTemp": 0.0,
"waterTemp": 0.0,
"humidity": 0.0,
"lIntensity": 0,
"lDuration": 0,
"co2Level": 0.0,
"flowRate": 0.0,
}
espnow_0 = None
espnow_mac = None
espnow_data = None
ROW_NO = 1
use_mqtt = True #Only subscribe to MQTT topic if true
SSID = "WIFI"
WLAN_PW = "WIFI_PASSWORD"
mqtt_client = None
MQTT = None
#publishes sensor data to mqtt reqularly
def timer0_cb(t):
try:
json_data = json.dumps(sensor_data)
print(f'sent {json_data}')
publish_with_retry(f'm5stack/data/row{ROW_NO}/dataList', json_data)
except Exception as e:
print(f"Publish error: {e}")
time.sleep_ms(5)
#publishes to mqtt with 3 retreis
def publish_with_retry(topic, message, retries=3):
global mqtt_client
for attempt in range(retries):
try:
mqtt_client.publish(topic, message, qos=1)
print(f"Published: {message} to {topic}")
return
except Exception as e:
print(f"Publish failed ({attempt + 1}/{retries}): {e}")
if not reconnect_mqtt():
break
print("Failed to publish message after retries")
# MQTT Reconnect Function
def reconnect_mqtt():
global mqtt_client
try:
mqtt_client.connect()
print("MQTT reconnected")
return True
except Exception as e:
print(f"Failed to reconnect MQTT: {e}")
return False
#Shows sensor data labels
def SensorData():
global light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data, title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0
dataLabel1.setVisible(False)
dataLabel2.setVisible(False)
dataLabel3.setVisible(False)
dataLabel4.setVisible(False)
dataLabel5.setVisible(False)
dataLabel6.setVisible(False)
dataLabel7.setVisible(False)
dataLabel8.setVisible(False)
plantname.setVisible(False)
airTemp.setVisible(False)
waterTemp.setVisible(False)
humidity.setVisible(False)
lightIntensity.setVisible(False)
lightDuration.setVisible(False)
co2Level.setVisible(False)
flowRate.setVisible(False)
sensorLight.setVisible(True)
sensorTemp.setVisible(True)
sensorHumidity.setVisible(True)
LIGHT_label.setVisible(True)
AIR_label.setVisible(True)
HUMIDITY_label.setVisible(True)
sensorLight.setText(f"LightIntensity: {sensor_data['lIntensity']} µmol/m²/s")
sensorTemp.setText(f"AirTemperature: {sensor_data['airTemp']} C")
sensorHumidity.setText(f"Humidity: {sensor_data['humidity']} %")
# displayes stored params
def DataDisplay():
global light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data, title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0
sensorLight.setVisible(False)
sensorTemp.setVisible(False)
sensorHumidity.setVisible(False)
LIGHT_label.setVisible(False)
AIR_label.setVisible(False)
HUMIDITY_label.setVisible(False)
dataLabel1.setVisible(True)
dataLabel2.setVisible(True)
dataLabel3.setVisible(True)
dataLabel4.setVisible(True)
dataLabel5.setVisible(True)
dataLabel6.setVisible(True)
dataLabel7.setVisible(True)
dataLabel8.setVisible(True)
plantname.setVisible(True)
airTemp.setVisible(True)
waterTemp.setVisible(True)
humidity.setVisible(True)
lightIntensity.setVisible(True)
lightDuration.setVisible(True)
co2Level.setVisible(True)
flowRate.setVisible(True)
dataLabel1.setText(str('Plantname:'))
dataLabel2.setText(str('AirTemperature:'))
dataLabel3.setText(str('WaterTemperature:'))
dataLabel4.setText(str('Humidity:'))
dataLabel5.setText(str('LightIntensity:'))
dataLabel6.setText(str('LightDuration:'))
dataLabel7.setText(str('CO2Level:'))
dataLabel8.setText(str('FlowRate:'))
plantname.setText(f"{stored_params['plantName']}")
airTemp.setText(f"{stored_params['airTemp']} C")
waterTemp.setText(f"{stored_params['waterTemp']} C")
humidity.setText(f"{stored_params['humidity']}%")
lightIntensity.setText(f"{stored_params['lIntensity']} µmol/m²/s")
lightDuration.setText(f"{stored_params['lDuration']} hrs")
co2Level.setText(f"{stored_params['co2Level']} ppm")
flowRate.setText(f"{stored_params['flowRate']} L/min")
# simple sensor normalization
def calc():
global light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data, title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0
LUX = 65535 - (light_0.get_analog_value())
LUX = LUX / 65535
LIGHT = int(LUX * 1000)
AIR = int(env3_0.read_temperature())
HUMIDITY = int(env3_0.read_humidity())
sensor_data['lIntensity'] = LIGHT
sensor_data['airTemp'] = AIR
sensor_data['humidity'] = HUMIDITY
SensorData()
Light()
AirTemp()
Humidity()
time.sleep(10)
DataDisplay()
time.sleep(3)
# Check necessary change in light
def Light():
global light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data, title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0
if LIGHT < (stored_params['lIntensity']):
LIGHT_label.setText(str('Increasing Light Intensity'))
elif LIGHT > (stored_params['lIntensity']):
LIGHT_label.setText(str('Decreasing Light Intensity'))
else:
LIGHT_label.setVisible(False)
# Check necessary change in airtemp
def AirTemp():
global light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data, title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0
if AIR < (stored_params['airTemp']):
AIR_label.setText(str('Increasing Air Temperature'))
elif AIR > (stored_params['airTemp']):
AIR_label.setText(str('Decreasing Air Temperature'))
else:
AIR_label.setVisible(False)
#Check necessary change in humidity
def Humidity():
global light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data, title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0
if HUMIDITY < (stored_params['humidity']):
HUMIDITY_label.setText(str('Increasing Humidity'))
elif HUMIDITY > (stored_params['humidity']):
HUMIDITY_label.setText(str('Decreasing Humidity'))
else:
HUMIDITY_label.setVisible(False)
#duh
def setupLabels():
global title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0, light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data
Widgets.fillScreen(0x5a2a2a)
title = Widgets.Title(f"ROW {ROW_NO}", 244, 0xffffff, 0x8d1a5c, Widgets.FONTS.DejaVu18)
sensorLight = Widgets.Label("", 78, 36, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
sensorTemp = Widgets.Label("", 65, 95, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
sensorHumidity = Widgets.Label("", 108, 161, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
LIGHT_label = Widgets.Label("", 40, 62, 1.0, 0xffffff, 0xce4a4a, Widgets.FONTS.DejaVu18)
AIR_label = Widgets.Label("", 29, 126, 1.0, 0xffffff, 0xce4a4a, Widgets.FONTS.DejaVu18)
HUMIDITY_label = Widgets.Label("", 62, 187, 1.0, 0xffffff, 0xce4a4a, Widgets.FONTS.DejaVu18)
dataLabel1 = Widgets.Label("Plantname:", 79, 43, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
plantname = Widgets.Label("", 179, 43, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
dataLabel2 = Widgets.Label("AirTemperature:", 47, 64, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
airTemp = Widgets.Label("", 179, 64, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
dataLabel3 = Widgets.Label("WaterTemperature:", 25, 85, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
waterTemp = Widgets.Label("", 179, 85, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
dataLabel4 = Widgets.Label("Humidity:", 92, 105, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
humidity = Widgets.Label("", 179, 105, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
dataLabel5 = Widgets.Label("LightIntensity:", 60, 125, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
lightIntensity = Widgets.Label("", 179, 125, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
dataLabel6 = Widgets.Label("LightDuration:", 59, 147, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
lightDuration = Widgets.Label("", 179, 147, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
dataLabel7 = Widgets.Label("CO2Level:", 88, 166, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
co2Level = Widgets.Label("", 179, 166, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
dataLabel8 = Widgets.Label("FlowRate:", 90, 186, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
flowRate = Widgets.Label("", 179, 186, 1.0, 0xffffff, 0x5a2a2a, Widgets.FONTS.DejaVu12)
espnow_0 = M5ESPNow(0)
i2c1 = I2C(1, scl=Pin(22), sda=Pin(21), freq=100000)
light_0 = LightUnit((36, 26))
angle_0 = AngleUnit((36, 26))
env3_0 = ENVUnit(i2c=i2c1, type=3)
def mqtt_callback(topic, msg):
# Handle incoming MQTT message
try:
topic = topic.decode('utf-8')
msg = msg.decode('utf-8')
print(f"Received MQTT message on topic {topic}: {msg}")
topic_parts = topic.split('/')
if len(topic_parts) >= 3 and topic_parts[1] == f"row{ROW_NO}":
param_name = topic_parts[2]
# Handle JSON payloads or single values
if msg.startswith('{') and msg.endswith('}'):
try:
json_data = json.loads(msg)
stored_params.update(json_data)
print(f"Updated parameters: {json_data}")
except json.JSONDecodeError as e:
print(f"JSON decoding error: {e}")
else:
print("single param")
# Handle single parameter updates
print(param_name)
if param_name in stored_params:
# Update parameter with type conversion if possible
stored_params[param_name] = (
float(msg) if msg.replace('.', '', 1).isdigit() else msg
)
print(f"Updated {param_name} to {stored_params[param_name]}")
else:
print(f"Unknown parameter: {param_name}")
DataDisplay()
else:
print(f"Irrelevant topic: {topic}")
except Exception as e:
print(f"MQTT callback error: {e}")
def espnow_recv_callback(espnow_obj):
# Run when espnow data recieved
global espnow_mac, espnow_data
print("got")
espnow_mac, espnow_data = espnow_obj.recv_data()
print(espnow_data, espnow_mac)
if espnow_data:
try:
espnow_data_dict = json.loads(espnow_data.decode('utf-8'))
stored_params.update(espnow_data_dict)
print("ESP-NOW data updated:", espnow_data_dict)
except Exception as e:
print(e)
print("Error decoding ESP-NOW data")
else:
print("espnow error bruh")
DataDisplay()
def setup_mqtt():
# Set up the MQTT
global mqtt_client
try:
mqtt_client = MQTTClient('Subscriber', 'test.mosquitto.org', port=1883)
mqtt_client.set_callback(mqtt_callback)
mqtt_client.connect()
mqtt_client.subscribe(f'm5stack/row{ROW_NO}/#')
print(f"Subscribed to topic: m5stack/row{ROW_NO}/#")
except Exception as e:
print(f"MQTT connection error: {str(e)}")
def setup():
global espnow_0
M5.begin()
setupLabels()
# Initialize ESP-NOW
espnow_0 = M5ESPNow(0)
espnow_0.set_irq_callback(espnow_recv_callback)
print("ESP-NOW initialized")
# Initialize MQTT if enabled
if use_mqtt:
connect_wifi()
setup_mqtt()
timer0 = Timer(0)
timer0.init(mode=Timer.PERIODIC, period=5000, callback=timer0_cb)
def connect_wifi():
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, WLAN_PW)
while not wlan.isconnected():
print("Connecting to Wi-Fi...")
time.sleep(1)
print("Connected to Wi-Fi")
def loop():
global title, sensorLight, sensorTemp, sensorHumidity, LIGHT_label, AIR_label, HUMIDITY_label, dataLabel1, plantname, dataLabel2, airTemp, dataLabel3, waterTemp, dataLabel4, humidity, dataLabel5, lightIntensity, dataLabel6, lightDuration, dataLabel7, co2Level, dataLabel8, flowRate, espnow_0, i2c1, rgb, env3_0, angle_0, light_0, light_label, LUX, LIGHT, AIR, HUMIDITY, stored_params, sensor_data
M5.update()
calc()
if __name__ == '__main__':
try:
setup()
while True:
loop()
except (Exception, KeyboardInterrupt) as e:
try:
from utility import print_error_msg
print_error_msg(e)
except ImportError:
print("please update to latest firmware")
Comments
Please log in or sign up to comment.