Xiaomi Plant Sensor – Review and how to hack

I was looking to acquire some cool and cheap sensors for monitoring my indoor plants’ health. I’d never clicked so fast to purchase a $20 Xiaomi plant sensor on ebay claiming to measure light intensity, temperature, soil moisture and plant fertility.

MiFlora

How does this piece of plastic measure ‘fertility’ I wondered. After peaking under the hood of this sensor’s operations using the MiFlora python module I saw there was an attribute called ‘conductivity’ instead of fertility. Turns out soil electrical conductivity (EC) is a metric correlated with soil moisture, drainage ability, organic matter level, crop productivity, salinity and is used worldwide as a yardstick for soil health.

Depending on the amount of moisture in the soil and soil salinity (water alone does not conduct electricity so the salts dissolved in the water does)  the sensor can deduce in milliSiemens per meter (mS/m) the amount of moisture held by soil particles. It makes sense then that sand would have a low conductivity, silt a medium conductivity, and clay a high conductivity. More on soil conductivity here:  Measuring EC.

What units does Xiaomi use?

  • Moisture (%)
  • Temperature (ºC)
  • Conductivity (µS/cm)
  • Brightness (Lux)
  • Battery (%)

The sensor comes with a companion app – Plant care – which I would rate as OK. You can pair the sensor with your phone using bluetooth. The app has a database of plants and their required optimal conditions. I swapped the sensor around on my plants but with the app you cannot select more than one plant to monitor (as in if you change from monitoring a cucumber to a chilli pepper it doesn’t save the historical data from the time spent monitoring the cucumber.)

I also occasionally faced difficulty connecting to the sensor via bluetooth on my phone to sync the data. The app’s major shortcoming is that its analytics and time series graphs are not portable, meaning you can’t email them or download them. There are somehow full days of data missing from the graphs.

I took this screenshot in the evening when there was no sunlight in my room and I hadn’t watered the plant for a few days. I also compared the temperature reading to that of my Aqara temperature and humidity sensor and they were on par. So I’m happy to vouch for this little device in terms of accuracy. I’m also impressed with the battery life. I’ve been using it for over 6 months and the battery level is still at 98%.

RPi – time to boot up!

To overcome the limitations of this app and to collect data directly from the sensor it was time to boot up the raspberry pi and look for an API. I was delighted to find a python module online over on https://pypi.org/project/miflora

I downloaded it and used the following code to insert data into a postgresql database every 3 hours with the intention of making my own graphs.

Instructions

Install prerequisite modules

sudo apt-get install python3-pip
sudo pip3 install miflora
sudo pip3 install bluepy

To find the sensor’s MAC address

sudo hcitool lescan
LE Scan ...
C4:7C:8D:6A:59:EA Flower care

From reading miflora github I retrieved the methods and modules needed to connect to the sensor https://github.com/open-homeautomation/miflora

import psycopg2
import datetime
from miflora.miflora_poller import MiFloraPoller, MI_CONDUCTIVITY, MI_MOISTURE, MI_LIGHT, MI_TEMPERATURE, MI_BATTERY
from btlewrap.bluepy import BluepyBackend

def connectPlantcare(MAC):
     data = {}
     conn = MiFloraPoller(MAC, BluepyBackend)
     data["Entry_date"] = datetime.datetime.now()
     data["Light"] = poller.parameter_value(MI_LIGHT)
     data["Temperature"] = poller.parameter_value(MI_TEMPERATURE)
     data["Conductivity"] = poller.parameter_value(MI_CONDUCTIVITY)
     data["Moisture"] = poller.parameter_value(MI_MOISTURE)
     data["Battery"] = poller.parameter_value(MI_BATTERY)
     return data

def connectPsql():
     try:
         conn = psycopg2.connect('dbname=plantcare user=postgres host=localhost password=secret port=5432')
     except ConnectionError as e:
         return e
     return conn

def insertSensorData():
     try:
         conn = connect_psql()
         cur = conn.cursor()
         data = connect_plantcare("C4:7C:8D:6A:59:EA")
         query = u"INSERT INTO sensor_data(entry_date, temperature, moisture, light, conductivity, battery) VALUES (%s, %s, %s, %s, %s, %s)"
         vars= (data["Entry_date"], data["Temperature"], data["Conductivity"], data["Moisture"], data["Light"], data["Conductivity"])
         cur.execute(query, vars)
         conn.commit()
         conn.close()
         print("Data inserted into plantcare database ", datetime.datetime.now())

except ConnectionError as e:
         return e

finally:
         if(conn):
             conn.close()
             print("PostgresSQL connection closed")

if __name__ == "__main__":
     insertSensorData()

Originally I had used the above script, scheduled to run with a cronjob to insert data into the database every 3 hours. However, I fried my SD card by sticking it into my laptop and trying to read it when the SD adapter had a drop of water on it. Don’t ask! So I lost over 3 months of historical plant data of monitoring my chilli pepper from a seedling to a mature bush.

Fairly devastating, but motivation to port my database to a cloud solution instead of my local RPi. Part 2 of this series will include a time series visualization.

Advertisements

One Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.