Using RepeatSensor to Interface a Hardware Sensor Library: BMP280

Although SensESP can read virtually any hardware sensor that can interface with the ESP32 and have an Arduino framework compatible library available, there are only two types of sensor that SensESP can read without using at least one external library: those that can be read with AnalogRead() or DigitalRead(). Every other kind of sensor require at least one external library to work with SensESP.

Most sensors that use an external library can easily be used with SensESP with just a few lines of code, as illustrated in this Tutorial. It uses the Adafruit BMP280 temperature and pressure sensor as an example, but you can easily use almost any other Arduino-compatible sensor by modifying this Tutorial’s main.cpp and platformio.ini files to work with the other sensor’s library. Specifics of what to modify for a different sensor are explained in Part 2 of this tutorial.

Downloading and Opening the Files

Clone or download this GitHub repo.

platformio.ini for BMP280

Look at the lib_deps section of the platformio.ini that you should now have open in PlatformIO. It lists two libraries:

lib_deps =
  SignalK/SensESP @ ^3.0.0
  adafruit/Adafruit BMP280 Library @ ^2.5.0

Every SensESP project will include Signalk/SensESP, and depending on the version of SensESP being used, it may or may not include the @ ^3.0.0, or some variation of that.

This Tutorial reads a physical sensor that is read with a library that’s not part of the SensESP core code - an Adafruit BMP280 Temperature and Pressure sensor. So we have to add that library to the lib_deps section, so that PlatformIO will know to download it before attempting to build this project.

In the second part of this tutorial, we’ll explain how to determine what external libraries you’ll need for other physical sensors. For this one, the lesson is simply that you’ll be adding one or more external libraries to your platformio.ini file for any physical sensor that isn’t read with AnalogRead() or DigitalRead().

main.cpp for BMP280

The lines in the main.cpp source file that are specific to the BMP280 are explained below:

// Sensor-specific #includes:
#include <Adafruit_BMP280.h>

This #include line is obviously very specific to the Adafruit BMP280.

// Create an instance of the sensor using its I2C interface
Adafruit_BMP280 bmp280;

This line creates an instance of an Adafruit_BMP280, using the constructor from the Adafruit BMP280 library header file, which we have included above. Note that it’s done before the beginning of the setup() function.

// Define the function that will be called every time we want
// an updated temperature value from the sensor. The sensor reads degrees
// Celsius, but all temps in Signal K are in Kelvin, so add 273.15.
float read_temp_callback() { return (bmp280.readTemperature() + 273.15); }

Below, inside the setup() function, we’re going to use the function that actually reads the data from the sensor. Since C++ doesn’t support nested functions, we need to define this function before the beginning of setup(). (A more concise alternative, shown below, is to use a lambda expression to define the function.) This line declares and defines that function. It’s called read_temp_callback(), which returns a float, the value of which is the result of the Adafruit_BMP280’s readTemperature() function, after adding 273.15 to it.

// Initialize the BMP280 using the default address
  bmp280.begin();

The above line is also a function from the Adafruit_BMP280 library - it starts, or initializes, the sensor.

auto* engine_room_temp =
      new RepeatSensor<float>(read_interval, read_temp_callback);

The above line creates a pointer (called engine_room_temp) to a RepeatSensor, which is a special kind of Sensor in SensESP that needs to know only two things: how often to execute a function, and what IS the function? The function, of course, is the one we defined above - the one that reads the temperature from a BMP280. So this line creates the “event” that reads the temperature from the BMP280 every read_interval ms.

Another way to define the callback function is to place it inline in the setup() function as a lambda function, as shown below:

auto* engine_room_temp =
      new RepeatSensor<float>(read_interval, []() {
        return (bmp280.readTemperature() + 273.15);
      });

This is equivalent to referring to the named function.

  // Set the Signal K Path for the output
  const char* sk_path = "propulsion.engineRoom.temperature";

  // Send the temperature to the Signal K server as a Float
  engine_room_temp->connect_to(new SKOutputFloat(sk_path));

These two lines pass the value of engine_room_temp to Signal K as a Float, to the path propulsion.engineRoom.temperature.

Summary

  • Almost any sensor can be used in SensESP by using a special Sensor called a RepeatSensor.
  • You are responsible for writing a function that calls the function from the sensor’s library that actually reads the data.
  • You are responsible for instantiating the sensor, then starting / initializing it, along with any setup or configuration of it that may be necessary before it’s ready to be read.
  • You’ll use the RepeatSensor constructor to create the event that reads the sensor every XXX ms.
  • You can make minor modifications to this Tutorial’s platformio.ini and main.cpp to work with almost any other kind of sensor. (This is detailed in part 2.)