Close

Logging to the cloud

A project log for ESP32 Soil Moisture Sensor with Flip-Dot

In this project I develop a small, battery-powered soil moisture sensor using the ESP32 with a status indicator and logging to the cloud!

maakbaasMaakbaas 04/18/2021 at 18:040 Comments

In this final post I will dive into the connectivity and WiFi functions for the ESP32 soil moisture sensor. The goal is to be able to log the sensor data to the cloud and pull it into a home automation system. The data will first be buffered locally, and then pushed to the cloud in a batch. This is needed because WiFi is significantly more power hungry compared to other activity. With this approach the device should theoretically have a battery life of two years.


Before we dive into the software, a quick visual update. The PCB was still missing a case, so I designed one in Fusion 360 and printed it in garden green😉.

The 3D printed case and the final assembled sensor in action in our mini jungle.

Looking back at the battery calculations in the first post on this project, I assumed an hourly sensor reading and a daily push to the cloud. Logging more than hourly does not make sense given the slow dynamics of soil moisture levels. This means that the ESP32 must remember the last 24 sensor measurements locally in a buffer, and send them to the cloud periodically.

To be able to push historical timestamped data to the cloud I settled on using InfluxDB. It is quite easy to push a batch of data to the database with a single HTTP post request. It also helps that I already had an instance of InfluxDB running on my NAS 😉. But in any case this type of database is specifically designed for time series data, so it makes sense to use it for that purpose.

Connect to WiFi and NTP

During the first wake of the device, it will connect to WiFi and get the current time from a NTP server:

//Connect to WiFi
WiFi.mode(WIFI_STA);
WiFi.begin("SSID", "password");
while (WiFi.status() != WL_CONNECTED) {    delay(1000); }

//set real time clock
configTime(0, 0, "pool.ntp.org");   struct tm timeinfo;
getLocalTime(&timeinfo);

Filling the measurement buffer

Every hour the ESP32 is woken from deep sleep by the timer, the new sensor measurement is added to a buffer. There are three buffers. For the timestamp, the actual sensor value, and one for the threshold value, since it can be changed by the user by pressing the calibration button.

//write buffers
time_t now;
time(&now);
timestampBuffer[bufferIndex] = now;
thresholdBuffer[bufferIndex] = threshold;
measBuffer[bufferIndex] = output;
bufferIndex++;

Writing to InfluxDB

Once the buffer is full (after 24 measurements), the complete content is written to the InfluxDB database. However, there is one additional situation that triggers a write. This is when the sensor value crosses the threshold. This means the plant needs to be watered, and we don't want to wait for up to 23 hours to push this information to the cloud. Therefore this will also trigger a write to the database.

//write to DB daily or when watering is needed 
if (bufferIndex == 24 || (output > threshold && status!=RED))
{               
    initWiFi();
    HTTPClient http;    
    http.begin("http://192.168.1.90:8086/write?db=plant&u=username&p=pass");    http.addHeader("Content-Type", "application/binary");
    
    char payload[2000], *pos = payload;
    for(int i=0; i<bufferIndex; i++)    
    {        
        pos += sprintf(pos,"moisture level=%d,threshold=%d %d000000000\n", 
                       measBuffer[i],thresholdBuffer[i],timestampBuffer[i]);
    }        
    
    http.POST((uint8_t *)payload, strlen(payload));    
    http.end();

    bufferIndex=0;     
}

In the for loop a char array is filled with the payload of the Post request. This is in what is called the InfluxDB line protocol, which is an intuitive way to format your data. After the data is sent to the database, bufferIndex is reset to zero to start filling the buffer for a new cycle.

Connecting to Home Assistant

The main reason I did not push the data to home assistant directly is that I did not find a way to push historical timestamped data, but of course the fact that the data is now in a database is not enough. It would be nice to integrate this with other measurements around the house, and to set up automations such that a low moisture level can trigger notifications. This is all possible with home assistant, so therefore we need to pull the InfluxDB data into home assistant.

sensor:    
  - platform: influxdb    
    host: 192.168.1.90    
    username: username    
    password: pass    
    queries:      
    - name: Moisture        
      value_template: '{{ (100 - float(value) / 11) | round(1)}}'        
      unit_of_measurement: '%'        
      group_function: last        
      where: '"time" > 0'        
      measurement: 'moisture'        
      field: level        
      database: plant      
    - name: Threshold        
      value_template: '{{ (100 - float(value) / 11) | round(1)}}'        
      unit_of_measurement: '%'        
      group_function: last        
      where: '"time" > 0'        
      measurement: 'moisture'        
      field: threshold        
      database: plant
}

By adding this code to configuration.yaml it is possible to use InfluxDB as a sensor source, and pull in both the actual moisture level and the user defined critical threshold. The value_template fields are used to transform the sensor reading (where 0 is very wet and 1100 is the maximum value in open air) into a moisture percentage between 0 and 100%.

The plant showing up in home assistant, it is thirsty!

That's it! Thanks for following along for this project.


Discussions