Skip to content
Brian Smith edited this page Mar 11, 2020 · 37 revisions

SensESP is a library of functions that makes it relatively simple to create a program that will run on an ESP8266 microcontroller ("MCU"), that collects data from various sensors, processes the data, and sends it along to a final "consumer", typically a Signal K server. It must be used with the PlatformIO IDE - it won't work with the Arduino IDE.

As the end user, you create a single main.cpp file, most of which will be boilerplate - all the code necessary to connect to your network and your SignalK server. Then, with only a few lines of code, you create an instance of a sensor, process the data from the sensor, then send the final result to the Signal K server.

The best way to learn how to use SensESP is by looking at a few examples. In the /examples folder, rpm_counter.cpp is a good one to start with, as it illustrates a simple implementation - reading the value of a sensor connected to a GPIO pin on the MCU, converting that value to hertz (the native unit of measurement for a tachometer in Signal K), then sending the value in hertz to the Signal K server.

Sensors

A Sensor is an object that knows how to get data from an actual, physical sensor and pass it along to a Transport. For example, an AnalogInput Sensor knows how to read the AnalogIn pin on the MCU, and a DigitalInputCounter Sensor knows how to read a GPIO pin on the MCU using an interrupt.

Transports

A Transport is an object that takes a value as input (from a Sensor, or from another Transport), does something with that value, then outputs the new value, either to another Transport, or to the Signal K server. In the rpm_counter.cpp example referred to above, the meat of the code is these three lines:

     auto* pSensor = new DigitalInputCounter(D5, INPUT_PULLUP, RISING, read_delay);
     
     pSensor->connectTo(new Frequency(multiplier, config_path_calibrate))
            ->connectTo(new SKOutputNumber(sk_path, config_path_skpath));

The first line instantiates a Sensor of type DigitalInputCounter. The second line is a Transport of type Frequency - it takes a raw number from the DigitalInputCounter Sensor and converts it to hertz, then passes it along to SKOutputNumber. SKOutputNumber is a special Transport whose purpose is to send a value to the Signal K server.

A much more complex example is temperature_sender.cpp, where the meat of the program is this:

auto* pAnalogInput = new AnalogInput();

pAnalogInput->connectTo(new AnalogVoltage()) -> 
              connectTo(new VoltageDividerR2(R1, Vin, "/gen/temp/sender")) -> 
              connectTo(new TemperatureInterpreter("/gen/temp/curve")) -> 
              connectTo(new Linear(1.0, 0.0, "/gen/temp/calibrate")) -> 
              connectTo(new SKOutputNumber(sk_path, "/gen/temp/sk"));

In this example, there is still only one Sensor (AnalogInput), but several Transports, all required to turn the raw value from the Analog Input pin on the MCU into a temperature that's sent to the Signal K server.

You can also include multiple Sensors, each with at least one Transport, in the same program, such as combining both of the examples above into the same main.cpp, one after the other. (TODO - add such an example to /examples.)

Configuration Paths

Some Sensors and Transports have parameters that can be configured "live", by accessing the MCU through its IP address, entered as a URL in any browser. For example, entering 192.168.1.236 (the IP address of the MCU I'm currently using as my example) in a browser will bring up the following web page that's hosted by the MCU itself:

image

"Device information" does not display any information at this time. "Configure device" will be explained below. "Restart device" will restart the MCU. "Reset device" will erase all the network information and all the Sensor and Transport configuration, and the program. The MCU will be returned to the way it came from the factory.

Click on "Configure device" to bring up the /setup page, which will look something like this: image

The only menu item that will always be present is "system", which has two sub-menus: "networking" (which lets you see and edit your wifi SSID and password, and the MCU's hostname), and "sk", which displays some networking info, but can't yet be used for editing that info, although it looks like it can.

The other menu items ("sensors" and "transports") are there only if you've provided a configuration path in the constructor of one or more of the Sensors and Transports in main.cpp. And the names - in this case, "sensors" and "transports", are completely up to you - you can call them "fred" and "barney" if you like.

Look at the three lines from the rpm_counter.cpp example above. There are three constructors - the one for the DigitalInputCounter Sensor, the one for the Frequency Transport, and the one for the SKOutputNumber Transport. The latter two have a configuration path included as the last item in their parameter list, but the first one doesn't. That means that the first one - the DigitalInputCounter - has no values that can be configured "live", but the latter two do. Actually, it's a little more complicated than that, because it's not entirely consistent among all Sensors and Transports.

As of this writing (March 2020), here are the ** possible ** configurable parameters for each Sensor and Transport:

** SENSORS **

  • AnalogInput: Read delay (Number of milliseconds between each analogRead(A0))
  • DigitalInputValue: none
  • DigitalInputCounter: Read delay (The time, in milliseconds, between each read of the input)
  • GPSInput: none
  • OneWireTemperature: OneWire address (the unique address of the specific sensor being used)

** TRANSFORMS **

  • AnalogVoltage: none
  • AngleCorrection: Constant offset (value to be added, in radians); Minimum angle value (pi, or 0)
  • ChangeFilter: Minimum delta (Minimum difference in change of value before forwarding); Maximum delta (maximum difference in change of value to allow forwarding); Max skips (maximum number of consecutive filtered values before one is allowed through)
  • CurveInterpolator: input, output (each pair is added to the table of samples)
  • Difference: Input #1 multiplier, Input #2 multiplier
  • Frequency: Multiplier
  • Integrator: Multiplier
  • Linear: Multiplier; Constant offset
  • Median: Sample size (number of samples to take before outputing a value)
  • MovingAverage: Number of samples in average; Multiplier
  • VoltageDivider: Voltage in; Resistance (ohms) of R1
  • SKOutputNumber, SKOutputInt, SKOutputBool, SKOutputString: Signal K data path

In every case of a configurable value, that value can be set in main.cpp, but by making it configurable, it's easier to make adjustments to your output based on what you're seeing in the real world. For example, the Median Transport is used to smooth output from a "noisy" sensor. By making the "Sample size" configurable, you can experiment with different sample sizes while the MCU is running and outputting data to Signal K, so you can decide when you have the sample size where you want it.

Even though a Sensor or Transport has the ability to be configurable, it won't be unless you provide a configuration path in the constructor when you use it in main.cpp. For example, auto* pAnalogInput = new AnalogInput(); creates an AnalogInput Sensor whose Read Delay will be the default 200 ms, and which can't be configured. On the other hand, auto* pAnalogInput = new AnalogInput(250, "/sensors/analogInput"); creates a Sensor with a 250 ms Read Delay that can be adjusted in real time, because of the presence of the config_path parameter ("/sensors/analogInput").

Clone this wiki locally