Custom sensor guide

This commit is contained in:
Otto Winter 2018-06-04 13:46:40 +02:00
parent 4f184c4baa
commit 9de6037c7c
No known key found for this signature in database
GPG Key ID: DB66C0BE6013F97E
7 changed files with 274 additions and 5 deletions

View File

@ -21,16 +21,20 @@ minifyhtml: html
doxyg:
ESPHOMELIB_PATH=$(ESPHOMELIB_PATH) doxygen Doxygen
pdf:
-yes '' | make latexpdf
cp $(BUILDDIR)/latex/$(SPHINXPROJ).pdf $(BUILDDIR)/html/_static/esphomelib.pdf
deploy: cleanhtml doxyg html pdf minifyhtml
fixdeploy: cleanhtml doxyg html minifyhtml
touch "$(BUILDDIR)/html/.nojekyll"
echo "esphomelib.com" >"$(BUILDDIR)/html/CNAME"
cd "$(BUILDDIR)/html" && git add --all && git commit -m "Deploy to gh-pages"
@printf "Run \033[0;36mcd $(BUILDDIR)/html && git push origin gh-pages\033[0m to deploy\n"
releasedeploy: cleanhtml doxyg html minifyhtml
touch "$(BUILDDIR)/html/.nojekyll"
echo "esphomelib.com" >"$(BUILDDIR)/html/CNAME"
-yes '' | make latexpdf
cp $(BUILDDIR)/latex/$(SPHINXPROJ).pdf $(BUILDDIR)/html/_static/esphomelib.pdf
cd "$(BUILDDIR)/html" && git add --all && git commit -m "Deploy to gh-pages"
@printf "Run \033[0;36mcd $(BUILDDIR)/html && git push origin gh-pages\033[0m to deploy\n"
help:
$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -0,0 +1,252 @@
Custom Sensor Component
=======================
.. warning::
While I do try to keep the esphomeyaml configuration options as stable as possible
and backport them, the esphomelib API is less stable. If something in the APIs needs
to be changed in order for something else to work, I will do so.
So, you just set up esphomelib for your ESP32/ESP8266, but sadly esphomelib is missing a sensor integration
you'd really like to have 😕. It's pretty much impossible to support every single sensor, as there are simply too many.
That's why esphomelib has a really simple API for you to create your own **custom sensors** 🎉
In this guide, we will go through creating a custom sensor component for the
`BMP180 <https://www.adafruit.com/product/1603>`__ pressure sensor (we will only do the pressure part,
temperature is more or less the same). During this guide, you will learn how to 1. define a custom sensor
esphomelib can use 2. go over how to register the sensor so that it will be shown inside Home Assistant and
3. leverage an existing arduino library for the BMP180 with esphomelib.
.. note::
Since the creation of this guide, the BMP180 has been officially supported by the :doc:`BMP085 component
<bmp085>`. The code still applies though.
This guide will require at least a bit of knowledge of C++, so be prepared for that. If you have any problems,
I'm here to help :) https://discord.gg/KhAMKrd
Step 1: Custom Sensor Definition
--------------------------------
At this point, you might have a main source file like this:
.. code:: cpp
// ...
using namespace esphomelib;
void setup() {
// ===== DO NOT EDIT ANYTHING BELOW THIS LINE =====
// ========== AUTO GENERATED CODE BEGIN ===========
App.set_name("livingroom");
App.init_log();
// ...
// =========== AUTO GENERATED CODE END ============
// ========= YOU CAN EDIT AFTER THIS LINE =========
App.setup();
}
void loop() {
App.loop();
}
To create your own custom sensor, you just have define a C++ class that extends ``Component`` and ``Sensor`` like this:
.. code:: cpp
using namespace esphomelib;
class CustomSensor : public Component, public sensor::Sensor {
public:
void setup() override {
// This will be called by App.setup()
}
void loop() override {
// This will be called by App.loop()
}
};
void setup() {
// ...
You've just created your first esphomelib sensor 🎉. It doesn't do very much right now and is never registered,
but it's a first step.
Let's now take a look at how a sensor works in esphomelib: A sensor is some hardware device (like a BMP180)
that sends out new values like temperatures.
Like any Component in esphomelib, if it's registered in the Application, ``setup()`` will be called for you when
``App.setup()`` is run. ``setup()`` is also the place where you should do hardware initialization like setting
``pinMode()``. Next, every time ``App.loop()`` is called, your component will also receive a ``loop()`` call.
This is the place where you should do stuff like querying a sensor for a new value like you might be used
to do in an Arduino sketch.
Let's now also take a closer look at this line, which you might not be too used to when writing pure C code:
.. code:: cpp
class CustomSensor : public Component, public sensor::Sensor {
What this line is essentially saying is that we're defining our own class that's called ``CustomSensor``
which is also a subclass of ``Component`` and ``Sensor`` (in the namespace ``sensor::``).
``Component`` is there so that we can register it in our application and so that we will receive ``setup()``
and ``loop()`` calls. We're also inheriting from the ``Sensor`` class so that our custom sensor can be used
by the MQTT sensor to automatically display it in the Home Assistant frontend.
As most sensors really just setup some pins and then check the sensor every x seconds,
there's another abstraction that we'll use to simplify our code: ``PollingSensorComponent``.
.. code:: cpp
class CustomSensor : public sensor::PollingSensorComponent {
public:
CustomSensor(uint32_t update_interval) : sensor::PollingSensorComponent(update_interval) {}
void setup() override {
// This will be called by App.setup()
}
void update() override {
// This will be called every `update_interval` milliseconds.
}
};
What ``PollingSensorComponent`` (and ``PollingComponent``) does is essentially
just replace the ``loop()`` method and will call ``update()`` instead every ``update_interval`` milliseconds.
Because with most sensors, you really don't need to get the latest values with every single ``loop()``
call (which can be called many times per second). If we forward the ``update_interval`` in our *constructor*
(line 3), ``PollingSensorComponent`` will call ``update()`` for us every ``update_interval``
milliseconds, so that we don't have to do time checking ourself.
You don't really need to know about constructors for now, but I would definitely recommend it.
Let's also now make our sensor actually *output* something (42 for now):
.. code:: cpp
// class CustomSensor ...
// ... previous code
void update() override {
push_new_value(42.0); // 42°C
}
std::string unit_of_measurement() override { return "°C"; }
int8_t accuracy_decimals() override { return 2; } // 2 decimal places of accuracy.
};
Every time ``update`` is called we will now **push** a new value to the MQTT sensor component that's
listening to our events. Additionally, we created a function that tells the sensor what unit of measurement the
value is in, this is not strictly required and only used for a nice output in Home Assistant.
Step 2: Registering the custom sensor
-------------------------------------
Now we have our Custom Sensor set up, but unfortunately it doesn't do much right now.
Actually ... it does nothing because it's never registered in the App,
so esphomelib can't know about it. Let's change that.
In your global ``setup()`` method, after you've setup all other components, do the following:
.. code:: cpp
void setup() {
// ...
App.init_mqtt(...);
// ... other stuff
auto *custom_sensor = new CustomSensor(5000); // update every 5000ms or every 5 seconds.
App.register_component(custom_sensor);
App.make_mqtt_sensor_for(custom_sensor, "Custom Sensor Example");
App.setup();
}
If you have Home Assistant MQTT discovery setup, it will even automatically show up in the frontend 🎉
(with the entity id ``sensor.custom_sensor_example``)
.. figure:: images/custom-ui.png
:align: center
:width: 60%
Let's go through the code for registering our custom sensor. First, we're creating a new CustomSensor
instance with the update interval of 5000ms using the ``new`` C++ syntax (important!) and assigning it to a
variable ``custom_sensor`` (using C++11 ``auto`` type specifier to make it simpler).
.. code:: cpp
auto *custom_sensor = new CustomSensor(5000);
Next, we *register* the component in esphomelib's Application instance so that it can call the component's
``setup()`` and ``loop()``.
.. code:: cpp
App.register_component(custom_sensor);
// you could also write this, it's a bit shorter and works the same way.
// auto *custom_sensor = App.register_component(new CustomSensor(5000));
Lastly, we're setting up a ``MQTTSensorComponent`` for our sensor, this mqtt component will automatically set up a
bunch of callbacks so that it can publish state changes to MQTT when you call ``publish_new_value()``,
create automatic MQTT discovery messages and setup a moving average over the sensor values
(adjust these as you would with any other sensor).
Step 3: BMP180 support
----------------------
Let's finally make this custom sensor useful by adding the BMP180 aspect into it! A great feature of
esphomelib is that you can just use all existing arduino libraries, amazing right? Now for this example we'll
use the `Adafruit BMP085 Library <https://platformio.org/lib/show/525/Adafruit%20BMP085%20Library>`__
library by Adafruit.
First we'll need to add the library to our platformio dependencies. To do so, put the following in
the ``common`` section of your ``platformio.ini``:
.. code:: ini
[common]
lib_deps = Adafruit BMP085 Library
build_flags =
upload_flags =
Next, include the library in your main sketch file:
.. code:: cpp
#include "esphomelib/application.h"
#include <Adafruit_BMP085.h>
using namespace esphomelib;
Then update our sensor for BMP180 support:
.. code:: cpp
class BMP180Sensor : public sensor::PollingSensorComponent {
public:
Adafruit_BMP085 bmp;
BMP180Sensor(uint32_t update_interval) : sensor::PollingSensorComponent(update_interval) {}
void setup() override {
bmp.begin();
}
void update() override {
int pressure = bmp.readPressure(); // in Pa, or 1/100 hPa
push_new_value(pressure / 100.0); // convert to hPa
}
std::string unit_of_measurement() override { return "hPa"; }
int8_t accuracy_decimals() override { return 2; } // 2 decimal places of accuracy.
};
There's not too much going on there. First, we define the variable ``bmp`` of type ``Adafruit_BMP085``
inside our class as a class member. In ``setup()`` we initialize the library and in ``update()`` we read the
pressure and send it out to MQTT.
You've now successfully created your first custom sensor component 🎉 Happy coding!
See Also
--------
- `Full source code <https://github.com/OttoWinter/esphomelib/blob/master/examples/custom-bmp180-sensor.cpp>`__

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -290,3 +290,4 @@ See Also
template.rst
max6675.rst
esp32_hall.rst
custom.rst

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path clip-rule="evenodd" fill="none" d="M0 0h24v24H0z"/>
<path d="M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@ -200,6 +200,10 @@ Sensor Components
|Template Sensor|_ |TSL2561|_ |Ultrasonic Sensor|_
-------------------------------------------------- -------------------------------------------------- --------------------------------------------------
`Template Sensor`_ `TSL2561`_ `Ultrasonic Sensor`_
-------------------------------------------------- -------------------------------------------------- --------------------------------------------------
|Custom Sensor|_
-------------------------------------------------- -------------------------------------------------- --------------------------------------------------
`Custom Sensor`_
================================================== ================================================== ==================================================
.. |Sensor Core| image:: /esphomeyaml/images/folder-open.svg
@ -265,6 +269,9 @@ Sensor Components
.. |Ultrasonic Sensor| image:: /esphomeyaml/images/ultrasonic.jpg
:class: component-image
.. _Ultrasonic Sensor: /esphomeyaml/components/sensor/ultrasonic.html
.. |Custom Sensor| image:: /esphomeyaml/images/build.svg
:class: component-image
.. _Custom Sensor: /esphomeyaml/components/sensor/custom.html
Binary Sensor Components

View File

@ -19,3 +19,4 @@ SHT3X-D, components/sensor/sht3xd, sht3xd.jpg
Template Sensor, components/sensor/template, description.svg
TSL2561, components/sensor/tsl2561, tsl2561.jpg
Ultrasonic Sensor, components/sensor/ultrasonic, ultrasonic.jpg
Custom Sensor, components/sensor/custom, build.svg

1 Sensor Core components/sensor/index folder-open.svg
19 Template Sensor components/sensor/template description.svg
20 TSL2561 components/sensor/tsl2561 tsl2561.jpg
21 Ultrasonic Sensor components/sensor/ultrasonic ultrasonic.jpg
22 Custom Sensor components/sensor/custom build.svg