BLYNK
HOME       📲 GETTING STARTED       📗 DOCS       ❓HELP CENTER       👉 SKETCH BUILDER

Mi Flora / Flower Care + Blynk + ESP32

esp32
#1

Hi Blynkers !

I wanted to share with you the project I just finished. The goal of the project was to get the infos from de Xiaomi Flower Care and send them to Blynk from the ESP32.


Hardware

  • Xiaomi Flower Care is a 4 in 1 Bluetooth sensor to put in your flower pot. It measures soil moisture, light intensity, soil fertility and temperature. Also, it is self-sufficient with a small battery !
  • ESP32 is a “new” espressif chip that includes Wifi and Bluetooth, as ESP8266, developement boards including ESP32 are quite cheap, I personally use the LOLIN D32, and the Arduino IDE.

Tutorial

Here is a small tutorial to use the code provided in the last section:

Here are the settings I use for the LOLIN D32

  • Add the ESP32_BLE_Arduino library from nkolban : https://github.com/nkolban/ESP32_BLE_Arduino

  • Now you need to know the address of your Flower Care (i.e mine is c4:7c:8d:66:e9:11). To do so, load the sketch BLE_scan.ino from the nkolban’s library in File > Examples > ESP32_BLE_ARDUINO. Run it near to your Flower Care, and you will have its address.

  • Now you have the Flower Care’s address, you can fill it in the code, with your Blynk Token, SSID and Password. Don’t change any UUID ! Upload it and it should work !


What it does ?

It switchs ON Bluetooth, reads the values from the sensor, switchs OFF bluetooth, switchs ON wifi, connects to Blynk, sends Temperature and Moistures values to V6 and V7 pins, disconnects from Blynk and switchs the wifi OFF. The routine is done every 10 minutes (600000 ms).
Why switching ON and OFF BT and Wifi ? Because the ESP32 cannot manage Bluetooth and Wifi at the same time, and when Blynk.run() is in the loop, Wifi is “constantly” used. This is why the sketch does not look like a “standard Blynk sketch”.

Of course, you can change the sending period, and the vitrtual pins etc.


Edit 17/08 : I experienced heat issues that lead into an ESP32 crash when running during a “long” period, even with the use of WiFi/BT disconnect. I don’t really know the root cause, but I re-write a code deleting the timer and adding a DeepSleep. Both codes are below. Heat problem is solved now !

Code with Timer

Here is the code :

#include "BLEDevice.h"
#define FLORA_ADDR "c4:7c:8d:66:e9:11"
#define BLYNK_NO_BUILTIN   // Disable built-in analog & digital pin operations
#include <TimeLib.h>
#include "WiFi.h"
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>

BlynkTimer timer;

BLEClient* pClient;

//**********TOKEN (OK)**********//
char auth[] = "YOUR BLYNK TOKEN";

//**********WIFI (OK)**********//
char ssid[] = "YOUR WIFI SSID";
char pass[] = "YOUR WIFI PASSWORD";

// The remote service we wish to connect to.
static BLEUUID serviceUUID("00001204-0000-1000-8000-00805f9b34fb");

// The characteristic of the remote service we are interested in.
static BLEUUID uuid_sensor_data("00001a01-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_write_mode("00001a00-0000-1000-8000-00805f9b34fb");
static BLEAddress floraAddress(FLORA_ADDR);
static BLERemoteCharacteristic* pRemoteCharacteristic;
float temp;
int moisture;
int light;
int conductivity;
int freeHeapstart;
int freeHeapstop;
  
void getSensorData1(BLEAddress pAddress){
  btStart();
  //delay(500);
  Serial.print("============================ Forming a connection to Flora device ===============================");
  Serial.print(pAddress.toString().c_str());
  // Connect to the remove BLE Server.
  if (!pClient->connect(pAddress)){
  pClient->disconnect();
  return; 
  } else {
  Serial.println(" - Connected to Flora");
  }
  
  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
  } else {
    Serial.println(" - Found our service");
  }
 
  pRemoteCharacteristic = pRemoteService->getCharacteristic(uuid_write_mode);
  uint8_t buf[2] = {0xA0, 0x1F};
  pRemoteCharacteristic->writeValue(buf, 2, true);
  
  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(uuid_sensor_data);
  Serial.println(pRemoteService->toString().c_str());
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(uuid_sensor_data.toString().c_str());
  } else {
      Serial.println(" - Found our characteristic");
    }
    
  // Read the value of the characteristic.
  std::string value = pRemoteCharacteristic->readValue();
  Serial.print("The characteristic value was: ");
  const char *val = value.c_str();

  Serial.print("Hex: ");
  for (int i = 0; i < 16; i++) {
    Serial.print((int)val[i], HEX);
    Serial.print(" ");
  }
  Serial.println(" ");

  temp = (val[0] + val[1] * 256) / ((float)10.0);
  moisture = val[7];
  light = val[3] + val[4] * 256;
  conductivity = val[8] + val[9] * 256;
  
  char buffer[64];
  
  Serial.print("Temperature: ");
  Serial.println(temp);

  Serial.print("Moisture: ");
  Serial.println(moisture);
  
  Serial.print("Light: ");
  Serial.println(light);
  
  Serial.print("Conductivity: ");
  Serial.println(conductivity);
  pClient->disconnect();
  delay(500);
  btStop();
  timer.setTimeout(1000L,sendSensorData);
}

void sendSensorData() {
  Blynk.begin(auth, ssid, pass);
  //Blynk.run();
  Blynk.connect();
  Blynk.virtualWrite(V6, temp);
  Blynk.virtualWrite(V7, moisture);
  Blynk.disconnect();
  WiFi.mode(WIFI_OFF);
  freeHeapstop = ESP.getFreeHeap();
  Serial.print("FREEHEAP END : ");
  Serial.println(freeHeapstop);
}

void getSensorData() {
  freeHeapstart = ESP.getFreeHeap();
  Serial.print("FREEHEAP START : ");
  Serial.println(freeHeapstart);
  getSensorData1(floraAddress);
}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Flora client...");
  BLEDevice::init("");
  pClient  = BLEDevice::createClient();
  timer.setInterval(600000L,getSensorData);
}

void loop() {
  timer.run();
}

Code with DeepSleep

#include "BLEDevice.h"
#define FLORA_ADDR "c4:7c:8d:66:e9:11"
#define BLYNK_NO_BUILTIN   // Disable built-in analog & digital pin operations
#include <TimeLib.h>
#include "WiFi.h"
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  600        /* Time ESP32 will go to sleep (in seconds) */

BLEClient* pClient;

//**********TOKEN (OK)**********//
char auth[] = "YOUR TOKEN";

//**********WIFI (OK)**********//
char ssid[] = "YOUR WIFI SSID";
char pass[] = "YOUR WIFI PASSWORD";

// The remote service we wish to connect to.
static BLEUUID serviceUUID("00001204-0000-1000-8000-00805f9b34fb");

// The characteristic of the remote service we are interested in.
static BLEUUID uuid_sensor_data("00001a01-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_write_mode("00001a00-0000-1000-8000-00805f9b34fb");
static BLEAddress floraAddress(FLORA_ADDR);
static BLERemoteCharacteristic* pRemoteCharacteristic;
float temp;
int moisture;
int light;
int conductivity;
int freeHeapstart;
int freeHeapstop;
  
void getSensorData(BLEAddress pAddress){
  btStart();
  //delay(500);
  Serial.print("============================ Forming a connection to Flora device ===============================");
  Serial.print(pAddress.toString().c_str());
  // Connect to the remove BLE Server.
  if (!pClient->connect(pAddress)){
  pClient->disconnect();
  ESP.restart();
  } else {
  Serial.println(" - Connected to Flora");
  }
  
  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
  } else {
    Serial.println(" - Found our service");
  }
 
  pRemoteCharacteristic = pRemoteService->getCharacteristic(uuid_write_mode);
  uint8_t buf[2] = {0xA0, 0x1F};
  pRemoteCharacteristic->writeValue(buf, 2, true);
  
  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(uuid_sensor_data);
  Serial.println(pRemoteService->toString().c_str());
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(uuid_sensor_data.toString().c_str());
  } else {
      Serial.println(" - Found our characteristic");
    }
    
  // Read the value of the characteristic.
  std::string value = pRemoteCharacteristic->readValue();
  Serial.print("The characteristic value was: ");
  const char *val = value.c_str();

  Serial.print("Hex: ");
  for (int i = 0; i < 16; i++) {
    Serial.print((int)val[i], HEX);
    Serial.print(" ");
  }
  Serial.println(" ");

  temp = (val[0] + val[1] * 256) / ((float)10.0);
  moisture = val[7];
  light = val[3] + val[4] * 256;
  conductivity = val[8] + val[9] * 256;
  
  char buffer[64];
  
  Serial.print("Temperature: ");
  Serial.println(temp);

  Serial.print("Moisture: ");
  Serial.println(moisture);
  
  Serial.print("Light: ");
  Serial.println(light);
  
  Serial.print("Conductivity: ");
  Serial.println(conductivity);
  pClient->disconnect();
  delay(500);
  btStop();
  delay(500);
  sendSensorData();
}

void sendSensorData() {
  Blynk.begin(auth, ssid, pass);
  Blynk.connect();
  Blynk.virtualWrite(V6, temp);
  Blynk.virtualWrite(V7, moisture);
  delay(500);
  Blynk.disconnect();
  WiFi.mode(WIFI_OFF);
//  freeHeapstop = ESP.getFreeHeap();
//  Serial.print("FREEHEAP END : ");
//  Serial.println(freeHeapstop);
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); // Setup wake up interval
  delay(500);
  esp_deep_sleep_start(); // Start deep sleep
}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Flora client...");
  BLEDevice::init("");
  pClient  = BLEDevice::createClient();
  delay(500);
  getSensorData(floraAddress);
}

void loop() {
}

I hope it will be useful to someone, I struggled a bit before finding the way to manage wifi and bluetooth with this ESP32 :slight_smile:

7 Likes

#2

Nice project! :ok_hand::ok_hand::ok_hand:

Could you share a photo of the full setup with the flowers/plants?

0 Likes

#3

Hi @Pavel !

Here is a photo of the setup ! I use it for a Bonsaï Tree. ESP32 is only USB powered, nothing more. The sprinklers are triggered with Blynk, with a “classic” ESP8266 + relay setup (not detailed in my original post).

5 Likes

#4

hy ,
great work …

I’ve try to connect to my Xiaomi , but do you have a special memory partition setup ?
because i ve got a cpu reset with your code…

Have you got a idea to submit …?

0 Likes

#5

Hi @gilles99,

You have to change the Partition Scheme to “No OTA (Large APP)” as you can see it in my first post screen.
But in case your partition setup is not good, you shouldn’t be able to upload the sketch (so you shouldn’t even have a CPU restart).

I noticed some crash events, or device stuck issues while using the codes from the first post.
You will find below the last version code I use, it is working well for 1 week now without any problem. All you need to do is to use BLEClient.cpp from Chegewara which fixes connection issues : https://gist.github.com/chegewara/d3ed0e3365407f6d3abcffc486b2c462

#define BLYNK_PRINT Serial // Defines the object that is used for printing
#define BLYNK_DEBUG        // Optional, this enables more detailed prints
#include "BLEDevice.h"
#define FLORA_ADDR "c4:7c:8d:66:e9:11"
#define BLYNK_NO_BUILTIN   // Disable built-in analog & digital pin operations
#include <Time.h>
#include <TimeLib.h>
#include "WiFi.h"
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  120        /* Time ESP32 will go to sleep (in seconds) */

BlynkTimer timer;
BLEClient* pClient;
WidgetBridge bridge1(V10); //Initiating Bridge Widget on V1 of Device LolinD32

//**********TOKEN (OK)**********//
char auth[] = "****************************";

//**********WIFI (OK)**********//
char ssid[] = "*************************";
char pass[] = "************************";

// The remote service we wish to connect to.
static BLEUUID serviceUUID("00001204-0000-1000-8000-00805f9b34fb");

// The characteristic of the remote service we are interested in.
static BLEUUID uuid_sensor_data("00001a01-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_write_mode("00001a00-0000-1000-8000-00805f9b34fb");
static BLEAddress floraAddress(FLORA_ADDR);
static BLERemoteCharacteristic* pRemoteCharacteristic;
float temp;
float moisture;
float light;
int conductivity;
int freeHeapstart;
int freeHeapstop;
int c;
 
void getSensorData(BLEAddress pAddress){ 
  btStart();
  timer.setTimeout(30000L, deepsleep);
  Serial.print("=================== Forming a connection to Flora device ====================");
  Serial.println(pAddress.toString().c_str());
  Serial.println(" - Connection to Flora");
  if (!pClient->connect(pAddress)){
      pClient->disconnect();
      delay(500);
      Serial.println(" - Cannot connect to Flora");
      ESP.restart();
  } else {
    Serial.println(" - Connected to Flora");
  }
  
  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
  } else {
    Serial.println(" - Found our service");
  }
 
  pRemoteCharacteristic = pRemoteService->getCharacteristic(uuid_write_mode);
  uint8_t buf[2] = {0xA0, 0x1F};
  pRemoteCharacteristic->writeValue(buf, 2, true);
  
  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(uuid_sensor_data);
  Serial.println(pRemoteService->toString().c_str());
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(uuid_sensor_data.toString().c_str());
  } else {
      Serial.println(" - Found our characteristic");
    }
    
  // Read the value of the characteristic.
  std::string value = pRemoteCharacteristic->readValue();
  Serial.print("The characteristic value was: ");
  const char *val = value.c_str();

  Serial.print("Hex: ");
  for (int i = 0; i < 16; i++) {
    Serial.print((int)val[i], HEX);
    Serial.print(" ");
  }
  Serial.println(" ");

  temp = (val[0] + val[1] * 256) / ((float)10.0);
  moisture = val[7];
  light = val[3] + val[4] * 256;
  conductivity = val[8] + val[9] * 256;
  
  char buffer[64];

  temp = float(temp);
  moisture = float(moisture);
  light = float(light);
  conductivity = float(conductivity);
  
  Serial.print("Temperature: ");
  Serial.println(temp);

  Serial.print("Moisture: ");
  Serial.println(moisture);
  
  Serial.print("Light: ");
  Serial.println(light);
  
  Serial.print("Conductivity: ");
  Serial.println(conductivity);
  pClient->disconnect();
  delay(500);
  btStop();
  delay(500);
  sendSensorData();
}

void sendSensorData() {
  Blynk.begin(auth, ssid, pass);
  do
{
  Blynk.connect();
} while (!Blynk.connected());
    bridge1.setAuthToken("****************************");
    Blynk.virtualWrite(V6, temp);
    Blynk.virtualWrite(V7, moisture);
    bridge1.virtualWrite(V11, temp);
    bridge1.virtualWrite(V12, moisture);
    delay(1000);
    Blynk.disconnect();
    WiFi.mode(WIFI_OFF);
    freeHeapstop = ESP.getFreeHeap();
    Serial.print("FREEHEAP END : ");
    Serial.println(freeHeapstop);
    ESP.restart();
}

void getSensorData1(){
  getSensorData(floraAddress);
}
void deepsleep(){
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  delay(200);
  esp_deep_sleep_start();
}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Flora client...");
  delay(500);
  BLEDevice::init("");
  pClient  = BLEDevice::createClient();
  delay(500);
  WiFi.mode(WIFI_OFF);
  timer.setTimeout(102000L, getSensorData1);
}

void loop() {
  timer.run();
}
0 Likes

#6

So many thanks for your share , and your support… So usefull !

1 Like

#7

Hi.
Very interest project for me. I was tryin to do like show post autor. But…
I have WeMos ESP32 Wi-Fi&Bluetooth Battery device. Mi Flora (Regular) sensor 4in1.
Arduino IDE the normal functionaly by wemos…
First, I must to see MAC of my mi flora sensor! Set ESP32_BLE_ARDUINO. Go to File/ Examples /ESP32_BLE_ARDUINO and run BLE_scan.ino. Compiling is ok. Doard is wemos esp32 wi-fi&Bluetoth battery. Boud 115200. …_____… (push “BOOT”). This is ok now…
Serial monitor 115200… And that i see…
ESP BLE_ERR 259
Device found: 0

Why it is?

Help pls.
Scan done.

0 Likes

#8

Hi @Jon1, here is what I found when looking into nkolban/esp32-snippets issues : https://github.com/nkolban/esp32-snippets/issues/592

It might be related to you Arduino IDE version. Try to update your Arduino IDE and use the last nkolban’s library version.

0 Likes

#9

Thank you [kaosss] for your answer! I will trying to night to do like you were writing. I used old version Arduino IDE 1.6.5.:frowning:

0 Likes

#10

Good time for you Kaoss.
A little bit from me in this. One more, I have WeMos esp32 wi-fi & Bluetoth battery. Version Arduino IDE 1.8.8. Handy setuping, esp32 in Arduino IDE like in manual, not a have had really effect. I’m use for setup a boards manager so: https://dl.espressif.com/dl/package_esp32_index.json.
What I see on this step from mi flora sensor by a newest libs ncolban ESP32_BLE_ARDUINO:
mac:
c4:7c:8d:63:13:9d
serviceUUID: 0000fe95-0000-1000-8000-00805f9b34fb

But, I need in uuid_sensor_data and uuid_write_mode (like in you simle up where is

static BLEUUID uuid_sensor_data("00001a01-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_write_mode("00001a00-0000-1000-8000-00805f9b34fb");
``` )
So differention data this in (a0.) uuid_sensor_data 00001(a00) and uuid_write_mode 00001(a00)

Me seems, I must scanning parameters uuid_sensor_data and  uuid_write_mode with 
other scanners (BLE_Scan for iOS e.t.c.) or ESP32_BLE_ARDUINO show me uuid_write_mode and uuid_write_mode? It isn't were I can to see parameters of uuid_write_mode and uuid_write_mode?
Thank you!
0 Likes

#11

Hi @Jon1, I took inspiration on Sidddy code here to write mine : https://github.com/sidddy/flora/blob/master/flora/flora.ino

As you can see, serviceUUID, uuid_sensor_data and uuid_write_mode are the same for him and for me. I guess every Mi Flora have the same uuid for sensor data, write mode and service. So you can try with these uuid.

0 Likes

#12

Ok! I’ll trying.

0 Likes

#14

Your last Code works great with my Lolin32 , thank for sharing!
Only the ESP.restart(); at the end i removed, because my device loops endless.
Are the bridge commands neccesary? I dont understand the meaning of it.
What is the best way to request more than one Flora device? I tried with a simple counter at the end and another getSensorData() call with another adress, but that didnt work.

0 Likes