Skip to content

Eduponics MQTT Powered Mobile APP

The Eduponics App is a special app we've developed in order to turn your kit into a complete autonomous smart watering solution.

By using the app and pairing it with the Eduponics mini kit you'll be able to control your plants remotely by pumping water when needed, view sensors data in real time such as: temperature and humidity, light intensity, water quantity and off course monitoring soil moisture levels.

The app can work anywhere, whenever you are at home, at work or on vocation. it doesn't require a local server or port forwarding.

The app currently support the following languages:

English, Hebrew, Mandarin (Chinese), Spanish, German, Ukrainian, Russian, Hindi, Portuguese

The language is selected by your system language (your mobile phone language). If you'd like us to add extra translations and would like to help us translate the app for more languages, feel free to contact us!

For now, only Android app version is available

Although we developed both the Android and iOS version for the Eduponics app, we haven't released the iOS app yet for the apple appstore due to app regulations and application approval process, we plan to release it as soon as possible, stay tuned!

Preparing the Eduponics Mini

Before we'll jump directly into our app, we need to prepare our Eduponics Mini ESP32 board first with custom software that will enable our kit to connect to the MQTT broker and publish and subscribe to and from topics.

In order for this tutorial to work, make sure you follow the installation instructions below to install the micropython-eduponics library on your Eduponics Mini device.

Connecting Eduponics Mini to WiFi

Let's repeat the same process we've used in the basic MQTT client tutorial by creating boot.py file, this file will first load when our Eduponics Mini restart or the power plugged in, in this file we'll configure the WiFi credentials such as ESSID (WiFi name) and the WiFi password.

Once we've connected to the WiFi using the station.connect() command, we can print our ESP32 WiFi IP address into the console.

import network
import esp
import time
esp.osdebug(None)
import gc
gc.collect()

# set WiFi credentials
ssid = ''
password = ''

# check if there is username and password for wifi
if(ssid != '' and password != ''):

    station = network.WLAN(network.STA_IF)

    station.active(True)
    station.connect(ssid, password)

    timeout_interval = 10

    # try to connect with timeout interval
    for i in range(0,timeout_interval):
        if(station.isconnected() == False):
            time.sleep(1)
            pass
        else:
            break;

    if(station.isconnected()):
        print('Connected to WiFi successfully, IP: %s' % station.ifconfig()[0])
    else:
        print("Something went wrong, connection timeout, try again!")
else:
    print("Please add WiFi credentials properly")

Now every time we restart or power the Eduponics Mini board it will automatically connect to the WiFi at our home.

2.54GhZ WiFi support only

Just reminding once again, the ESP32 support only 2.54GhZ WiFi networks. Most of the 5Ghz WiFi routers / access points allow both 5Ghz and 2.54Ghz, make sure to choose the 2.54Ghz one. to avoid connectivity issues make sure your network is operated on 2.54Ghz.

Installing the micropython-eduponics library

The MicroPython-Eduponics library can be found on STEMinds Micropython-Eduponics repository the easist way to install the library is through uPip.

Make sure to change WiFi ESSID and Password. Once the ESP32 is connected to the Wifi, run the following commands:

import upip
upip.install("micropython-eduponics")

The installation should complete and once it's done you shall have a "lib" directory on your ESP32 device containing all the pre-requirements for this tutorial.

Another way would be to grab the firmwares directly from the repository and install them into your ESP32 device using the esptool mentioned in the first tutorials.

Generating UUID

As we've mentioned in the basic MQTT client tutorial, we could either head to uuidgenerator.net and copy paste the automatically generated UUID for us or generate it by ourselves.

The UUID is used to identify our device from other devices on the MQTT network.

it is crucial to generate a unique UUID and not re-use it.

If we want to use the second option, here is a MicroPython example code to generate unique UUID, there are few kinds: unique based on host ID and current timestamp which is what we recommend, a UUID based on MD5 hash of a namespace (if you use this method, make sure to change steminds.com to something else, something random) and the last option to generate a random UUID.

import uuid

# make a UUID based on the host ID and current time, best option.
uuid_x = uuid.uuid1()
print(uuid_x)

# make a UUID using an MD5 hash of a namespace UUID and a name
uuid_y = uuid.uuid3(uuid.NAMESPACE_DNS, 'steminds.com')
print(uuid_y)

# make a random UUID
uuid_z = uuid.uuid4()
print(uuid_z)

Adding main MQTT client

The main MQTT client we will use now is very different from the previous basic MQTT example we've done.

In this example, we will integrate all our sensors and use them when needed, we will start by defining in our ode all the sensors such as ADC for the soil moisture sensor, light sensor, BME280 sensor and water quantity sensor.

Then we'll have multiple topics, the topics we will use are:

  • plants/soil
  • plants/environment
  • plants/water

The plants/soil topic is used to publish soil moisture sensor data, take a look at the JSON file below:

  plant = {
      "id":0,
      "name":"Plant A",
      "enabled":1,
      "moisture":normal_reading
  }

We can name the plant as we wish and it will show on our app, enabled can be changed between 1 or 0 which will describe if the plant can be used or not (watering functionality mainly) and inside moisture we add the soil moisture sensor values.

It's import to keep the ID 0 as we use only one soil moisture, if you use the IO extension pins to add more plants, you can change the ID from 0 to 1,2,3 .. as follows:

  plants = [{
      "id":0,
      "name":"Plant A",
      "enabled":1,
      "moisture":normal_reading
    },{
      "id":1,
      "name":"Plant B",
      "enabled":1,
      "moisture":normal_reading_two
    }]

In the plants/environment topic we will publish temperature, humidity, sunlight and water quantity using the rest of the sensors that are integrated in the Eduponics Mini kit.

And finally plants/water will be used to subscribe instead of publishing, in our app we can press the watering icon which will water our plants, our ESP32 will receive the message from the plants/water topic and water the plants accordingly.

Note in water_plant() function the following lines:

  # check if soil moisture is bigger than 60
  if(int(json.loads(get_soil_moisture())["moisture"].replace("%","")) > 60):
      # enough water, stop giving water
      break;

We've set default of allowing to give water only if the plant has less than 60% soil moisture or if the water quantity sensor says there is no more water left. this configuration can be played with as well as to add automatic watering and not manual operation through the app.

Here is the complete code, make sure to save it as main.py as this will be our main program:

"""
MicroPython MQTT Eduponics APP Client
https://github.com/STEMinds/micropython-eduponics
MIT License
Copyright (c) 2020 STEMinds

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from Eduponics import umqttsimple,bh1750,bme280
import machine
import time
import json

# set adc (analog to digital) on pin 35
adc = machine.ADC(machine.Pin(35))
# set 11dB input attenuation (voltage range roughly 0.0v - 3.6v)
adc.atten(machine.ADC.ATTN_11DB)

# Configure light sensor
light_sensor = bh1750.BH1750()

# Configure BME280
# setup I2C connection
i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(4))
# Initialize BME280 object with default address 0x76
bme_sensor = bme280.BME280(i2c=i2c)
# initialize dht object, DHT11 coonected to IO19
#d = dht.DHT11(machine.Pin(19))

# define water level sensor as INPUT on IO pin number 21
water_level = machine.Pin(21, machine.Pin.IN)

# define pump on pin IO23 as OUTPUT, define pump state
pump = machine.Pin(23, machine.Pin.OUT)
pump_state = False

# MQTT Unique ID
UUID = "YOUR_UUID_GENERATED_ID"
# MQTT Topics
topics = ["plants/soil","plants/environment","plants/water"]

def handle_water_level(pin):
    global pump_state
    # water level triggered, turn off the pump
    # wait for 0.3 seconds to make sure it's just a little below the water sensor
    # else the pump might become unstable
    time.sleep(0.3)
    pump.value(0)
    pump_state = False

def get_soil_moisture():

    # sensor min and max values
    # can be changed and callibrated
    minVal = 710
    maxVal = 4095

    # read soil moisture sensor data
    val = adc.read()

    # scale the value based on maxVal and minVal
    scale = 100 / (minVal - maxVal)
    # get calculated scale
    normal_reading = ("%s%s" % (int((val - maxVal) * scale),"%"))
    # we can also get inverted value if needed
    inverted_reading = ("%s%s" % (int((minVal - val) * scale),"%"))
    # for this example we'll return only the normal reading
    # put everything in a JSON format suitable for the eduponics app
    plant = {
        "id":0,
        "name":"Plant A",
        "enabled":1,
        "moisture":normal_reading
    }
    # return the data
    return str(plant).replace("'",'"')

def get_environmental_data():

    # get light from the light sensor
    lux = int(light_sensor.readLight())
    # get bme280 sensor data
    bme280_values = bme_sensor.values
    temperature = bme280_values[0].replace("C","")
    pressure = bme280_values[1]
    humidity = bme280_values[2].replace("%","")
    # get DHT11 sensor data
    # measure sensor data
    #d.measure()
    # get temperature and humidity
    #temperature = d.temperature()
    #humidity = d.humidity()
    # get water quantity
    water_quantity = water_level.value()

    # put all this data into a JSON object
    data = {
        "temp":temperature,
        "humidity":humidity,
        "sunlight":lux,
        "water_quantity":water_quantity
    }

    return str(data).replace("'",'"')

def water_plant():
    global pump_state
    if(pump_state or water_level.value() == 1):
        # turn off the pump
        pump.value(0)
        pump_state = False
    else:
        # turn on the pump
        pump.value(1)
        pump_state = True
    return True

def on_message_callback(topic, msg):
    '''
    this is a callback, will be called when the app asks for certain information
    such as to water the plants when the watering button pressed
    '''
    # convert topic and message byte to string
    topic = str(topic, 'utf-8')
    msg = json.loads(str(msg, 'utf-8'))

    if(topic == "%s/plants/soil" % UUID or topic == "%s/plants/environment" % UUID):
        # Do nothing, we only publish to those topics
        pass
    elif(topic == "%s/plants/water" % UUID):
        # when the app request for plant watering it goes here
        if("key" in msg and "status" in msg):
            # valid request, let's process it
            if(msg["status"] == "pending"):
                # it's waiting for us to water it, let's water it
                water_plant()
                # after watering, publish success message of watering
                response = {"key":msg["key"],"status":"ok"}
                client.publish("%s/plants/water" % UUID, str(response).replace("'",'"'))
    else:
        print((topic, msg))

def connect_and_subscribe():

    print("[-] Connecting to MQTT client ...")
    # set the MQTT broker object
    client = umqttsimple.MQTTClient()
    # set a callback for incoming messages (subscribed topics)
    client.set_callback(on_message_callback)
    # connect to the broker
    client.connect()

    # subscribe to the topics
    for topic in topics:
        client.subscribe("%s/%s" % (UUID,topic))
        print("[-] Subscribed to %s successfully" % topic)
        print("[-] Connected to %s MQTT broker successfully" % client.server)

    return client

def restart_and_reconnect():
    # something  went wrong, reconnect in 5 seconds ...
    print('[-] Failed to connect to MQTT broker. Reconnecting...')
    time.sleep(5)
    machine.reset()

try:
    client = connect_and_subscribe()
except OSError as e:
    restart_and_reconnect()

# configure few variables
last_message = 0
message_interval = 5
# set callback on the water level sensor, if no water stop the pump
water_level.irq(trigger=machine.Pin.IRQ_RISING, handler=handle_water_level)

while True:
    try:
        # check if there are new messages pending to be processed
        # if there are, redirect them to callback on_message_callback()
        client.check_msg()

        # check if the last published data wasn't less than message_interval
        if (time.time() - last_message) > message_interval:
            # get soil moisture
            soil_moisture = get_soil_moisture()
            # publish soil moisture data
            client.publish("%s/plants/soil" % UUID, soil_moisture)
            #print("[-] published soil moisture")

            # update environmetal data
            env = get_environmental_data()
            client.publish("%s/plants/environment" % UUID, env)
            #print("[-] published evironmental data")

            # update last message timestamp
            last_message = time.time()

    except OSError as e:
        # if something goes wrong, reconnct to MQTT server
        restart_and_reconnect()

Changing the MQTT broker

If you'd like to change the existing MQTT broker (mqtt.eclipseprojects.io) you can modify the umqttsimple.py located in the lib directory of the Eduponics library an add your custom broker of your choice.

The Eduponics Mobile app support any type of broker with or without SSL support.

Preparing the Eduponics APP

Now once we have everything ready on our ESP32 Eduponics Mini hardware, it's time to move to the app installation and preparation.

Downloading & Installing Eduponics APP

Currently the app is only available on the Android store and should be able to work on most if not all Android phones. To download, search "Eduponics" in playstore app and you should find it right away.

Alternatively you can install it directly to your phone using the web browser playstore application: Eduponics Playstore APP

Connecting Eduponics APP to Eduponics Mini

After downloading and launching the app successfully a popup window will show that the hardware is not initialized, this is normal and will happen on first launch of the app or if we've pressed the "wipe data" button in settings to wipe the internal memory of the app.

In this window press "Let's go!" and it will take you automatically to the settings page to bind the Eduponics Mini hardware with the APP.

Once we've been redirected successfully, it's time to bind the app with our hardware. In order to do so - we'll need the UUID we've generated earlier and initialized in our eduponics mini hardware, we can either type it manually or take a picture of a QR code with the UUID inside of it.

Make sure to press the 'Enter' key on the virtual keyboard

After you've typed the UUID completely, press the enter key on the virtual keyboard to confirm the UUID, this should complete the process.

Once the virtual key entered successfully, we are now successfully connected to the same channel (the topics) as our Eduponics Mini hardware! it's time to go back to the "Control" tab and see if we can get real time data from our device.

Now you can monitor your plant from everywhere, water it remotely and track the sensors data to make smarter decisions. As the app doesn't use any external server except the MQTT broker, you can control it even outside of your home network.