Error message: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

Hi, im trying to use Arduino to program my ESP32's.
The code im using is from here: GitHub - RonnyWinkler/esp32.miflora: MiFlora adapter for ESP32 (MiFlora->BT->ESP32->Wifi->MQTT) and i have used this code before with success but not now.

The error message i get is this:

In file included from C:\Users\Niclas\Desktop\flora2\flora2.ino:38:
C:\Users\Temp\flora2\config.h:9:1: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
 };
 ^
C:\Users\Temp\flora2\config.h:9:1: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

The first 10 lines of config.h looks like this:


// array of different xiaomi flora MAC addresses
char* FLORA_DEVICES[] = {
    "C4:7C:8D:6D:EE:BA", 
    "C4:7C:8D:6E:1D:27",
    "C4:7C:8D:6E:02:A9",
    "C4:7C:8D:6E:01:09",    
    "C4:7C:8D:6E:1D:8A"
};

Why doesn't the code work anymore?
The ESP's boot fine and i can find them on the network, but nothing else works

Should be a const char*, because a string constant should be constant.
< edit >
Not sure why the code will not run properly, that warning does not stop the compilation. Do you attempt to alter the string constants in the code?

I am not trying to alter anything besides the contents of the file config.h.
I changed from char* to const char* and then i got these error messages instead:

C:\Users\Temp\flora2\flora2.ino: In function 'void setup()':
flora2:352:45: error: invalid conversion from 'const char*' to 'char*' [-fpermissive]
     char* deviceMacAddress = FLORA_DEVICES[i];
                              ~~~~~~~~~~~~~~~^
Multiple libraries were found for "WiFi.h"
 Used: C:\Users\***\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.3-RC1\libraries\WiFi
 Not used: C:\Program Files (x86)\Arduino\libraries\WiFi
exit status 1
invalid conversion from 'const char*' to 'char*' [-fpermissive]

You can't have a 'char *' variable that points to 'const char' data. It would defeat the purpose of 'const'.

Ok, so how would you write the code for config.h?

This is the code right now copied from the link below and used earlier:


// array of different xiaomi flora MAC addresses
char* FLORA_DEVICES[] = {
    "C4:7C:8D:6D:EE:BA", 
    "C4:7C:8D:6E:1D:27",
    "C4:7C:8D:6E:02:A9",
    "C4:7C:8D:6E:01:09",    
    "C4:7C:8D:6E:1D:8A"
};

#define SLEEP_DURATION 30 * 60
#define EMERGENCY_HIBERNATE 3 * 60
#define BATTERY_INTERVAL 6
#define RETRY 3

const char*   WIFI_SSID       = "redacted";
const char*   WIFI_PASSWORD   = "redacted";
const char*   MQTT_HOST       = "redacted";
const int     MQTT_PORT       = 1883;
const char*   MQTT_CLIENTID   = "miflora";
const char*   MQTT_USERNAME   = "redacted";
const char*   MQTT_PASSWORD   = "redacted";
const String  MQTT_BASE_TOPIC = "homeassistant/miflora"; 
const int     MQTT_RETRY_WAIT = 5000;

The code here: esp32.miflora/config.h.example at main · RonnyWinkler/esp32.miflora · GitHub used to work, what has changed?

const char* FLORA_DEVICES[] = {
const char* deviceMacAddress = FLORA_DEVICES[i];

Changing to

const char* FLORA_DEVICES[] = {

results in the following error message:

C:\*\flora2.ino: In function 'void setup()':
flora2:352:45: error: invalid conversion from 'const char*' to 'char*' [-fpermissive]
     char* deviceMacAddress = FLORA_DEVICES[i];
                              ~~~~~~~~~~~~~~~^
Multiple libraries were found for "WiFi.h"
 Used: C:\Users\***\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.3-RC1\libraries\WiFi
 Not used: C:\Program Files (x86)\Arduino\libraries\WiFi
exit status 1
invalid conversion from 'const char*' to 'char*' [-fpermissive]

I dont really understand how i would use the code:
const char* deviceMacAddress = FLORA_DEVICES[i];

Where do i specify the device macadresses?

But, you didn't make the other change I mentioned:

The MAC address array FLORA_DEVICES is indexed by the variable 'i' inside the square brakets.

Ok, so i need both of the lines, that kinda makes sense. But where do i put the array of macadresses?
This code gives me new error messages:

// array of different xiaomi flora MAC addresses
const char* FLORA_DEVICES[] = {
        "C4:7C:8D:6D:EE:BA", 
        "C4:7C:8D:6E:1D:27",
        "C4:7C:8D:6E:02:A9",
        "C4:7C:8D:6E:01:09", 
        "C4:7C:8D:6E:1D:8A"
};
const char* deviceMacAddress = FLORA_DEVICES[i];

And the error message:

config.h:10:46: error: 'i' was not declared in this scope
 const char* deviceMacAddress = FLORA_DEVICES[i];

I really appreciate that you are helping me, thank you!
It is a steep learning curve for a beginner.

Ok, so i changed the code some more in both files after reading some info regarding c++ arrays.
The code now in config.h is as follows:

// array of different xiaomi flora MAC addresses
const char* FLORA_DEVICES[5] = {"C4:7C:8D:6D:EE:BA","C4:7C:8D:6E:1D:27","C4:7C:8D:6E:02:A9","C4:7C:8D:6E:01:09","C4:7C:8D:6E:1D:8A"};
const char* deviceMacAddress = FLORA_DEVICES[5];


#define SLEEP_DURATION 30 * 60
#define EMERGENCY_HIBERNATE 3 * 60
#define BATTERY_INTERVAL 6
#define RETRY 3

I dont get anymore errors and the esps boot up and connects to the mqttserver.
The problem is that they dont send any info, so something is wrong most likely with the macadresses.

Any idea what and why?

You have not shown enough code to make even an uneducated guess.

1 Like

Sorry, here we go: Config.h

// array of different xiaomi flora MAC addresses
const char* FLORA_DEVICES[5] = {"C4:7C:8D:6D:EE:BA","C4:7C:8D:6E:1D:27","C4:7C:8D:6E:02:A9","C4:7C:8D:6E:01:09","C4:7C:8D:6E:1D:8A"};
const char* deviceMacAddress = FLORA_DEVICES[5];


#define SLEEP_DURATION 30 * 60
#define EMERGENCY_HIBERNATE 3 * 60
#define BATTERY_INTERVAL 6
#define RETRY 3

const char*   WIFI_SSID       = "redacted";
const char*   WIFI_PASSWORD   = "redacted";
const char*   MQTT_HOST       = "redacted";
const int     MQTT_PORT       = 1883;
const char*   MQTT_CLIENTID   = "espmiflora2";
const char*   MQTT_USERNAME   = "redacted";
const char*   MQTT_PASSWORD   = "redacted";
const String  MQTT_BASE_TOPIC = "homeassistant/miflora"; 
const int     MQTT_RETRY_WAIT = 5000;

and flora2.ino:

/**
   A BLE client for the Xiaomi Mi Plant Sensor, pushing measurements to an MQTT server.

   See https://github.com/nkolban/esp32-snippets/blob/master/Documentation/BLE%20C%2B%2B%20Guide.pdf
   on how bluetooth low energy and the library used are working.

   See https://github.com/ChrisScheffler/miflora/wiki/The-Basics for details on how the 
   protocol is working.
   
   MIT License

   Copyright (c) 2017 Sven Henkel
   Multiple units reading by Grega Lebar 2018

   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.
*/

#include "BLEDevice.h"
#include <WiFi.h>
#include <PubSubClient.h>

#include "config.h"

// boot count used to check if battery status should be read
RTC_DATA_ATTR int bootCount = 0;

// device count
static int deviceCount = sizeof FLORA_DEVICES / sizeof FLORA_DEVICES[0];

// 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_version_battery("00001a02-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_sensor_data("00001a01-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_write_mode("00001a00-0000-1000-8000-00805f9b34fb");

TaskHandle_t hibernateTaskHandle = NULL;

WiFiClient espClient;
PubSubClient client(espClient);

void connectWifi() {
  Serial.println("Connecting to WiFi...");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("");
}

void disconnectWifi() {
  WiFi.disconnect(true);
  Serial.println("WiFi disonnected");
}

void connectMqtt() {
  Serial.println("Connecting to MQTT...");
  client.setServer(MQTT_HOST, MQTT_PORT);

  while (!client.connected()) {
    if (!client.connect(MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD)) {
      Serial.print("MQTT connection failed:");
      Serial.print(client.state());
      Serial.println("Retrying...");
      delay(MQTT_RETRY_WAIT);
    }
  }

  Serial.println("MQTT connected");
  Serial.println("");
}

void disconnectMqtt() {
  client.disconnect();
  Serial.println("MQTT disconnected");
}

BLEClient* getFloraClient(BLEAddress floraAddress) {
  BLEClient* floraClient = BLEDevice::createClient();

  if (!floraClient->connect(floraAddress)) {
    Serial.println("- Connection failed, skipping");
    return nullptr;
  }

  Serial.println("- Connection successful");
  return floraClient;
}

BLERemoteService* getFloraService(BLEClient* floraClient) {
  BLERemoteService* floraService = nullptr;

  try {
    floraService = floraClient->getService(serviceUUID);
  }
  catch (...) {
    // something went wrong
  }
  if (floraService == nullptr) {
    Serial.println("- Failed to find data service");
  }
  else {
    Serial.println("- Found data service");
  }

  return floraService;
}

bool forceFloraServiceDataMode(BLERemoteService* floraService) {
  BLERemoteCharacteristic* floraCharacteristic;
  
  // get device mode characteristic, needs to be changed to read data
  Serial.println("- Force device in data mode");
  floraCharacteristic = nullptr;
  try {
    floraCharacteristic = floraService->getCharacteristic(uuid_write_mode);
  }
  catch (...) {
    // something went wrong
  }
  if (floraCharacteristic == nullptr) {
    Serial.println("-- Failed, skipping device");
    return false;
  }

  // write the magic data
  uint8_t buf[2] = {0xA0, 0x1F};
  floraCharacteristic->writeValue(buf, 2, true);

  delay(500);
  return true;
}

bool readFloraDataCharacteristic(BLERemoteService* floraService, String baseTopic) {
  BLERemoteCharacteristic* floraCharacteristic = nullptr;

  // get the main device data characteristic
  Serial.println("- Access characteristic from device");
  try {
    floraCharacteristic = floraService->getCharacteristic(uuid_sensor_data);
  }
  catch (...) {
    // something went wrong
  }
  if (floraCharacteristic == nullptr) {
    Serial.println("-- Failed, skipping device");
    return false;
  }

  // read characteristic value
  Serial.println("- Read value from characteristic");
  std::string value;
  try{
    value = floraCharacteristic->readValue();
  }
  catch (...) {
    // something went wrong
    Serial.println("-- Failed, skipping device");
    return false;
  }
  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(" ");

  int16_t* temp_raw = (int16_t*)val;
  float temperature = (*temp_raw) / ((float)10.0);
  Serial.print("-- Temperature: ");
  Serial.println(temperature);

  int moisture = val[7];
  Serial.print("-- Moisture: ");
  Serial.println(moisture);

  int light = val[3] + val[4] * 256;
  Serial.print("-- Light: ");
  Serial.println(light);
 
  int conductivity = val[8] + val[9] * 256;
  Serial.print("-- Conductivity: ");
  Serial.println(conductivity);

  if (temperature > 200) {
    Serial.println("-- Unreasonable values received, skip publish");
    return false;
  }

  char buffer[64];

  snprintf(buffer, 64, "%f", temperature);
  client.publish((baseTopic + "temperature").c_str(), buffer); 
  snprintf(buffer, 64, "%d", moisture); 
  client.publish((baseTopic + "moisture").c_str(), buffer);
  snprintf(buffer, 64, "%d", light);
  client.publish((baseTopic + "light").c_str(), buffer);
  snprintf(buffer, 64, "%d", conductivity);
  client.publish((baseTopic + "conductivity").c_str(), buffer);

  return true;
}

bool readFloraBatteryCharacteristic(BLERemoteService* floraService, String baseTopic) {
  BLERemoteCharacteristic* floraCharacteristic = nullptr;

  // get the device battery characteristic
  Serial.println("- Access battery characteristic from device");
  try {
    floraCharacteristic = floraService->getCharacteristic(uuid_version_battery);
  }
  catch (...) {
    // something went wrong
  }
  if (floraCharacteristic == nullptr) {
    Serial.println("-- Failed, skipping battery level");
    return false;
  }

  // read characteristic value
  Serial.println("- Read value from characteristic");
  std::string value;
  try{
    value = floraCharacteristic->readValue();
  }
  catch (...) {
    // something went wrong
    Serial.println("-- Failed, skipping battery level");
    return false;
  }
  const char *val2 = value.c_str();
  int battery = val2[0];

  char buffer[64];

  Serial.print("-- Battery: ");
  Serial.println(battery);
  snprintf(buffer, 64, "%d", battery);
  client.publish((baseTopic + "battery").c_str(), buffer);

  return true;
}

bool processFloraService(BLERemoteService* floraService, const char* deviceMacAddress, bool readBattery) {
  // set device in data mode
  if (!forceFloraServiceDataMode(floraService)) {
    return false;
  }

  String baseTopic = MQTT_BASE_TOPIC + "/" + deviceMacAddress + "/";
  bool dataSuccess = readFloraDataCharacteristic(floraService, baseTopic);

  bool batterySuccess = true;
  if (readBattery) {
    batterySuccess = readFloraBatteryCharacteristic(floraService, baseTopic);
  }

  return dataSuccess && batterySuccess;
}

bool processFloraDevice(BLEAddress floraAddress, const char* deviceMacAddress, bool getBattery, int tryCount) {
  Serial.print("Processing Flora device at ");
  Serial.print(floraAddress.toString().c_str());
  Serial.print(" (try ");
  Serial.print(tryCount);
  Serial.println(")");

  // connect to flora ble server
  BLEClient* floraClient = getFloraClient(floraAddress);
  if (floraClient == nullptr) {
    return false;
  }

  // connect data service
  BLERemoteService* floraService = getFloraService(floraClient);
  if (floraService == nullptr) {
    floraClient->disconnect();
    return false;
  }

  // process devices data
  bool success = processFloraService(floraService, deviceMacAddress, getBattery);

  // disconnect from device
  floraClient->disconnect();

  return success;
}

void hibernate() {
  esp_sleep_enable_timer_wakeup(SLEEP_DURATION * 1000000ll);
  Serial.println("Going to sleep now.");
  delay(100);
  esp_deep_sleep_start();
}

void delayedHibernate(void *parameter) {
  delay(EMERGENCY_HIBERNATE*1000); // delay for five minutes
  Serial.println("Something got stuck, entering emergency hibernate...");
  hibernate();
}

void setup() {
  // all action is done when device is woken up
  Serial.begin(115200);
  delay(1000);

  // increase boot count
  bootCount++;

  // create a hibernate task in case something gets stuck
  xTaskCreate(delayedHibernate, "hibernate", 4096, NULL, 1, &hibernateTaskHandle);

  Serial.println("Initialize BLE client...");
  BLEDevice::init("");
  BLEDevice::setPower(ESP_PWR_LVL_P7);

  // connecting wifi and mqtt server
  connectWifi();
  connectMqtt();

  // check if battery status should be read - based on boot count
  bool readBattery = ((bootCount % BATTERY_INTERVAL) == 0);

  // process devices
  for (int i=0; i<deviceCount; i++) {
    int tryCount = 0;
    const char* deviceMacAddress = FLORA_DEVICES[5];
    BLEAddress floraAddress(deviceMacAddress);

    while (tryCount < RETRY) {
      tryCount++;
      if (processFloraDevice(floraAddress, deviceMacAddress, readBattery, tryCount)) {
        break;
      }
      delay(1000);
    }
    delay(1500);
  }

  // disconnect wifi and mqtt
  disconnectWifi();
  disconnectMqtt();

  // delete emergency hibernate task
  vTaskDelete(hibernateTaskHandle);

  // go to sleep now
  hibernate();
}

void loop() {
  /// we're not doing anything in the loop, only on device wakeup
  delay(10000);
}

The only thing i have altered in flora2 is char* to const char*

I still dont get why i could use the old code on esp's before and not now?

Likely either a change in the esp core, the compiler, or the compiler options set by the IDE. Those particular warnings usually do not stop the compiler or code from working, just point our that you are doing something that may be a problem - in this particular case a char* should point to something that can be altered by your code, while a const char* will generate an error if the compiler detects you are trying to alter it.

Remove the following line from Config.h

Change the 5 in:

in your void setup() function to i. You are forcing only the 6th mac address, which does not exist, to be used instead of cycling through the 5 specified addresses.

I changed the code in config.h to this:

// array of different xiaomi flora MAC addresses
const char* FLORA_DEVICES[] = {"C4:7C:8D:6D:EE:BA","C4:7C:8D:6E:1D:27","C4:7C:8D:6E:02:A9","C4:7C:8D:6E:01:09","C4:7C:8D:6E:1D:8A"};

#define SLEEP_DURATION 30 * 60
#define EMERGENCY_HIBERNATE 3 * 60
#define BATTERY_INTERVAL 6
#define RETRY 3

const char*   WIFI_SSID       = "redacted";
const char*   WIFI_PASSWORD   = "redacted";
const char*   MQTT_HOST       = "redacted";
const int     MQTT_PORT       = 1883;
const char*   MQTT_CLIENTID   = "redacted";
const char*   MQTT_USERNAME   = "redacted";
const char*   MQTT_PASSWORD   = "redacted";
const String  MQTT_BASE_TOPIC = "homeassistant/miflora"; 
const int     MQTT_RETRY_WAIT = 5000;

The esp still boots up and connects to the mqttserver fine, but it doesnt post any data. So something stills seems to be off.

Have you changed he second part, the 5 to i, in setup();

Yes, the code in setup is changed to this:

  // process devices
  for (int i=0; i<deviceCount; i++) {
    int tryCount = 0;
    const char* deviceMacAddress = FLORA_DEVICES[i];
    BLEAddress floraAddress(deviceMacAddress);

Variable definitions don't belong in .h files, only 'extern' declarations of those variables. Otherwise you will get errors for multiple definitions should the .h file be #included in more than one translation unit.

See my Post # 5 in This Thread for basic guidelines for the standard practices used to partition a project into multiple files.

Thank you for your answer. I don't get what i should do with the information, its all a bit too complicated for me. Any possibility that i could get a more hands-on type of help?

The code right now which results in a esp that boots and connects to the mqtt server but doesnt post any data is as follows:

config.h:

// array of different xiaomi flora MAC addresses
const char* FLORA_DEVICES[] = {
    "C4:7C:8D:6D:EE:BA", 
    "C4:7C:8D:6E:1D:27",
    "C4:7C:8D:6E:02:A9",
    "C4:7C:8D:6E:01:09", 
    "C4:7C:8D:6E:1D:8A"
};

#define SLEEP_DURATION 30 * 60
#define EMERGENCY_HIBERNATE 3 * 60
#define BATTERY_INTERVAL 6
#define RETRY 3

const char*   WIFI_SSID       = "redacted";
const char*   WIFI_PASSWORD   = "redacted";
const char*   MQTT_HOST       = "redacted";
const int     MQTT_PORT       = 1883;
const char*   MQTT_CLIENTID   = "miflora";
const char*   MQTT_USERNAME   = "redacted";
const char*   MQTT_PASSWORD   = "redacted";
const String  MQTT_BASE_TOPIC = "homeassistant/miflora"; 
const int     MQTT_RETRY_WAIT = 5000;

And the code in flora.ino looks like this:

/**
   A BLE client for the Xiaomi Mi Plant Sensor, pushing measurements to an MQTT server.

   See https://github.com/nkolban/esp32-snippets/blob/master/Documentation/BLE%20C%2B%2B%20Guide.pdf
   on how bluetooth low energy and the library used are working.

   See https://github.com/ChrisScheffler/miflora/wiki/The-Basics for details on how the 
   protocol is working.
   
   MIT License

   Copyright (c) 2021 Ronny Winkler
   Multiple units reading by Grega Lebar 2018

   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.
*/

#include "BLEDevice.h"
#include <WiFi.h>
#include <PubSubClient.h>

#include "config.h"

// boot count used to check if battery status should be read
RTC_DATA_ATTR int bootCount = 0;

// device count
static int deviceCount = sizeof FLORA_DEVICES / sizeof FLORA_DEVICES[0];

// 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_version_battery("00001a02-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_sensor_data("00001a01-0000-1000-8000-00805f9b34fb");
static BLEUUID uuid_write_mode("00001a00-0000-1000-8000-00805f9b34fb");

TaskHandle_t hibernateTaskHandle = NULL;

WiFiClient espClient;
PubSubClient client(espClient);

void connectWifi() {
  Serial.println("Connecting to WiFi...");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("");
}

void disconnectWifi() {
  WiFi.disconnect(true);
  Serial.println("WiFi disonnected");
}

void connectMqtt() {
  Serial.println("Connecting to MQTT...");
  client.setServer(MQTT_HOST, MQTT_PORT);

  while (!client.connected()) {
    if (!client.connect(MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD)) {
      Serial.print("MQTT connection failed:");
      Serial.print(client.state());
      Serial.println("Retrying...");
      delay(MQTT_RETRY_WAIT);
    }
  }

  Serial.println("MQTT connected");
}

void disconnectMqtt() {
  client.disconnect();
  Serial.println("MQTT disconnected");
}

BLEClient* getFloraClient(BLEAddress floraAddress) {
  BLEClient* floraClient = BLEDevice::createClient();

  if (!floraClient->connect(floraAddress)) {
    Serial.println("- Connection failed, skipping");
    return nullptr;
  }

  Serial.println("- Connection successful");
  return floraClient;
}

BLERemoteService* getFloraService(BLEClient* floraClient) {
  BLERemoteService* floraService = nullptr;

  try {
    floraService = floraClient->getService(serviceUUID);
  }
  catch (...) {
    // something went wrong
  }
  if (floraService == nullptr) {
    Serial.println("- Failed to find data service");
  }
  else {
    Serial.println("- Found data service");
  }

  return floraService;
}

bool forceFloraServiceDataMode(BLERemoteService* floraService) {
  BLERemoteCharacteristic* floraCharacteristic;
  
  // get device mode characteristic, needs to be changed to read data
  Serial.println("- Force device in data mode");
  floraCharacteristic = nullptr;
  try {
    floraCharacteristic = floraService->getCharacteristic(uuid_write_mode);
  }
  catch (...) {
    // something went wrong
  }
  if (floraCharacteristic == nullptr) {
    Serial.println("-- Failed, skipping device");
    return false;
  }

  // write the magic data
  uint8_t buf[2] = {0xA0, 0x1F};
  floraCharacteristic->writeValue(buf, 2, true);

  delay(500);
  return true;
}

bool readFloraDataCharacteristic(BLERemoteService* floraService, String baseTopic) {
  BLERemoteCharacteristic* floraCharacteristic = nullptr;

  // get the main device data characteristic
  Serial.println("- Access characteristic from device");
  try {
    floraCharacteristic = floraService->getCharacteristic(uuid_sensor_data);
  }
  catch (...) {
    // something went wrong
  }
  if (floraCharacteristic == nullptr) {
    Serial.println("-- Failed, skipping device");
    return false;
  }

  // read characteristic value
  Serial.println("- Read value from characteristic");
  std::string value;
  try{
    value = floraCharacteristic->readValue();
  }
  catch (...) {
    // something went wrong
    Serial.println("-- Failed, skipping device");
    return false;
  }
  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(" ");

  int16_t* temp_raw = (int16_t*)val;
  float temperature = (*temp_raw) / ((float)10.0);
  Serial.print("-- Temperature: ");
  Serial.println(temperature);

  int moisture = val[7];
  Serial.print("-- Moisture: ");
  Serial.println(moisture);

  int light = val[3] + val[4] * 256;
  Serial.print("-- Light: ");
  Serial.println(light);
 
  int conductivity = val[8] + val[9] * 256;
  Serial.print("-- Conductivity: ");
  Serial.println(conductivity);

  if (temperature > 200 || temperature < -30 || conductivity > 3000) {
    Serial.println("-- Unreasonable values received, skip publish");
    return false;
  }

  char buffer[64];

  connectMqtt();
  Serial.println("MQTT publish...");
  snprintf(buffer, 64, "%.2f", temperature);
  if (!client.publish((baseTopic + "temperature").c_str(), buffer, true)){
    Serial.println("MQTT publish error - temperature");
  }
  snprintf(buffer, 64, "%d", moisture); 
  if (!client.publish((baseTopic + "moisture").c_str(), buffer, true)){
    Serial.println("MQTT publish error - moisture");
  }
  snprintf(buffer, 64, "%d", light);
  if (!client.publish((baseTopic + "light").c_str(), buffer, true)){
    Serial.println("MQTT publish error - light");
  }
  snprintf(buffer, 64, "%d", conductivity);
  if (!client.publish((baseTopic + "conductivity").c_str(), buffer, true)){
    Serial.println("MQTT publish error - conductivity");
  }
  disconnectMqtt();

  return true;
}

bool readFloraBatteryCharacteristic(BLERemoteService* floraService, String baseTopic) {
  BLERemoteCharacteristic* floraCharacteristic = nullptr;

  // get the device battery characteristic
  Serial.println("- Access battery characteristic from device");
  try {
    floraCharacteristic = floraService->getCharacteristic(uuid_version_battery);
  }
  catch (...) {
    // something went wrong
  }
  if (floraCharacteristic == nullptr) {
    Serial.println("-- Failed, skipping battery level");
    return false;
  }

  // read characteristic value
  Serial.println("- Read value from characteristic");
  std::string value;
  try{
    value = floraCharacteristic->readValue();
  }
  catch (...) {
    // something went wrong
    Serial.println("-- Failed, skipping battery level");
    return false;
  }
  const char *val2 = value.c_str();
  int battery = val2[0];

  char buffer[64];
  
  connectMqtt();
  Serial.println("MQTT publish...");
  Serial.print("-- Battery: ");
  Serial.println(battery);
  snprintf(buffer, 64, "%d", battery);
  if (!client.publish((baseTopic + "battery").c_str(), buffer, true)){
    Serial.println("MQTT publish error - conductivity");
  }
  disconnectMqtt();
  
  return true;
}

bool processFloraService(BLERemoteService* floraService, const char* deviceMacAddress, bool readBattery) {
  // set device in data mode
  if (!forceFloraServiceDataMode(floraService)) {
    return false;
  }

  String baseTopic = MQTT_BASE_TOPIC + "/" + deviceMacAddress + "/";
  bool dataSuccess = readFloraDataCharacteristic(floraService, baseTopic);

  bool batterySuccess = true;
  if (readBattery) {
    batterySuccess = readFloraBatteryCharacteristic(floraService, baseTopic);
  }

  return dataSuccess && batterySuccess;
}

bool processFloraDevice(BLEAddress floraAddress, const char* deviceMacAddress, bool getBattery, int tryCount) {
  Serial.print("Processing Flora device at ");
  Serial.print(floraAddress.toString().c_str());
  Serial.print(" (try ");
  Serial.print(tryCount);
  Serial.println(")");

  // connect to flora ble server
  BLEClient* floraClient = getFloraClient(floraAddress);
  if (floraClient == nullptr) {
    return false;
  }

  // connect data service
  BLERemoteService* floraService = getFloraService(floraClient);
  if (floraService == nullptr) {
    floraClient->disconnect();
    return false;
  }

  // process devices data
  bool success = processFloraService(floraService, deviceMacAddress, getBattery);

  // disconnect from device
  floraClient->disconnect();

  return success;
}

void hibernate() {
  esp_sleep_enable_timer_wakeup(SLEEP_DURATION * 1000000ll);
  Serial.println("Going to sleep now.");
  delay(100);
  esp_deep_sleep_start();
}

void delayedHibernate(void *parameter) {
  delay(EMERGENCY_HIBERNATE*1000); // delay for five minutes
  Serial.println("Something got stuck, entering emergency hibernate...");
  hibernate();
}

void setup() {
  // all action is done when device is woken up
  Serial.begin(115200);
  delay(1000);

  // increase boot count
  bootCount++;

  // create a hibernate task in case something gets stuck
  xTaskCreate(delayedHibernate, "hibernate", 4096, NULL, 1, &hibernateTaskHandle);

  Serial.println("Initialize BLE client...");
  BLEDevice::init("");
  BLEDevice::setPower(ESP_PWR_LVL_P7);

  // connecting wifi and mqtt server
  connectWifi();
  connectMqtt();

  // check if battery status should be read - based on boot count
  bool readBattery = ((bootCount % BATTERY_INTERVAL) == 0);

  // process devices
  for (int i=0; i<deviceCount; i++) {
    int tryCount = 0;
    const char* deviceMacAddress = FLORA_DEVICES[i];
    BLEAddress floraAddress(deviceMacAddress);

    while (tryCount < RETRY) {
      tryCount++;
      if (processFloraDevice(floraAddress, deviceMacAddress, readBattery, tryCount)) {
        delay(1000);
        break;
      }
      delay(1000);
    }
    delay(1500);
  }

  // disconnect wifi and mqtt
  disconnectMqtt();
  disconnectWifi();

  // delete emergency hibernate task
  vTaskDelete(hibernateTaskHandle);

  // go to sleep now
  hibernate();
}

void loop() {
  /// we're not doing anything in the loop, only on device wakeup
  delay(10000);
}

Well to start, there's no real value added by having a config.h file since you're only using one source file (flora.ino).

But, if you wanted to do it per the standard technique .... and future-proof your code should you decide at a later date to partition it into multiple .cpp files, then it would look like this:
config.h:

#ifndef CONFIG_H
#define CONFIG_H

#include <Arduino.h>

#define SLEEP_DURATION 30 * 60
#define EMERGENCY_HIBERNATE 3 * 60
#define BATTERY_INTERVAL 6
#define RETRY 3

// array of different xiaomi flora MAC addresses
extern const char* FLORA_DEVICES[];
extern const size_t numDevices;



extern const char*   WIFI_SSID;
extern const char*   WIFI_PASSWORD;
extern const char*   MQTT_HOST;
extern const int     MQTT_PORT;
extern const char*   MQTT_CLIENTID;
extern const char*   MQTT_USERNAME;
extern const char*   MQTT_PASSWORD;
extern const String  MQTT_BASE_TOPIC;
extern const int     MQTT_RETRY_WAIT;

#endif

config.cpp:

#include "config.h"

const char* FLORA_DEVICES[] = {
    "C4:7C:8D:6D:EE:BA", 
    "C4:7C:8D:6E:1D:27",
    "C4:7C:8D:6E:02:A9",
    "C4:7C:8D:6E:01:09", 
    "C4:7C:8D:6E:1D:8A"
};

const size_t numDevices = sizeof(FLORA_DEVICES) / sizeof(FLORA_DEVICES[0]);

const char*   WIFI_SSID       = "redacted";
const char*   WIFI_PASSWORD   = "redacted";
const char*   MQTT_HOST       = "redacted";
const int     MQTT_PORT       = 1883;
const char*   MQTT_CLIENTID   = "miflora";
const char*   MQTT_USERNAME   = "redacted";
const char*   MQTT_PASSWORD   = "redacted";
const String  MQTT_BASE_TOPIC = "homeassistant/miflora"; 
const int     MQTT_RETRY_WAIT = 5000;

flora.ino:

#include "BLEDevice.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include "config.h"

Also, you might consider renaming variables like 'WIFI_SSID' using camelCase as you've done elsewhere in your program. That's the convention for all variable names.

1 Like