Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
koenvervloesem committed Dec 13, 2020
0 parents commit 7ccd630
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
name: Build

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
- name: Install requirements
run: make requirements
- name: Build code
run: make build
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.esphome/
m5stack_air_quality/
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2020 Koen Vervloesem

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.
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
BUILD_DIR = m5stack_air_quality
YAML_FILE = $(BUILD_DIR).yaml

.PHONY: build clean requirements run

build:
esphome $(YAML_FILE) compile

clean:
rm -r $(BUILD_DIR)

requirements:
pip3 install https://github.com/esphome/esphome/archive/dev.zip

run:
esphome $(YAML_FILE) run
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# ESPHome configuration for the M5Stack PM2.5 Air Quality Kit

[![Build status](https://github.com/koenvervloesem/M5Stack-Air-Quality-ESPHome/workflows/Build/badge.svg)](https://github.com/koenvervloesem/M5Stack-Air-Quality-ESPHome/actions)
[![GitHub license](https://img.shields.io/github/license/koenvervloesem/M5Stack-Air-Quality-ESPHome.svg)](https://github.com/koenvervloesem/M5Stack-Air-Quality-ESPHome/blob/main/LICENSE)

This [ESPHome](https://esphome.io) configuration builds firmware for M5Stack's [PM2.5 Air Quality Kit (PMSA003 + SHT20)](https://m5stack.com/products/pm-2-5-sensor-usb-power-sht20). It shows:

* The amount of particulate matter (PM1, PM2.5 and PM10).
* The temperature and humidity, as well as the computed vapour-pressure deficit and dew point.

![ESPHome configuration for the M5Stack air quality kit](https://github.com/koenvervloesem/M5Stack-Air-Quality-ESPHome/raw/main/m5stack-air-quality-kit-esphome.jpg)

## Requirements

* M5Stack's [PM2.5 Air Quality Kit (PMSA003 + SHT20)](https://m5stack.com/products/pm-2-5-sensor-usb-power-sht20)
* The development version of ESPHome. The 1.15.3 release doesn't have support for the M5Stack Core's [ili9341 display](https://github.com/esphome/esphome/pull/1233) yet. See the [installation instructions for ESPHome's bleeding edge version](https://esphome.io/guides/faq.html#how-do-i-use-the-latest-bleeding-edge-version).

The SHT20 sensor is also not supported by ESPHome, but this repository adds a custom component that's using the Arduino library [uFire_SHT20](https://github.com/u-fire/uFire_SHT20) which supports the sensor.

## Usage

Compile and upload the firmware to your M5Stack air quality kit with:

```shell
esphome m5stack_air_quality.yaml run
```

The YAML file doesn't configure any Wi-Fi connection, MQTT broker or native API for Home Assistant. You can add this yourself. The configuration has been tested extensively with MQTT. If you have set up Home Assistant MQTT discovery, the sensors of the device even automatically show up in Home Assistant.

The middle button is used to toggle the display's backlight on and off.

## Remarks about the temperature and humidity readings

The SHT20 sensor shows a much too high temperature and much too low humidity. This doesn't seem to be a software problem: I tried a couple of other libraries for the SHT20 with the M5Stack air quality kit and they all have the same result.

According to Sensirion's [data sheet of the SHT20](https://www.sensirion.com/SHT20), you shouldn't do more than two measurements per second at 12-bit accuracy, to prevent self-heating of the sensor. In my custom sensor component, I configured polling every second to stay on the safe side.

But even this doesn't solve the problem. I suspect M5Stack's hardware design is faulty and the sensor really reads the heat produced by the other components in the M5Stack Core case.

## TODO

* Change the interface to something nicer. I'm not a designer, so I welcome any suggestions or pull requests.
* Use the left and right buttons for other tasks.
* Find a solution for the faulty SHT20.

## License

This project is provided by [Koen Vervloesem](mailto:[email protected]) as open source software with the MIT license. See the [LICENSE](LICENSE) file for more information.

The included Roboto font is licensed under the [Apache License, Version 2.0](https://fonts.google.com/specimen/Roboto#license).

The uFire_SHT20 library is licensed under the MIT license.

The C++/runtime codebase of the ESPHome project (file extensions .c, .cpp, .h, .hpp, .tcc, .ino) are published under the GPLv3 license. The Python codebase and all other parts of the ESPHome codebase are published under the MIT license. See the [ESPHome License](https://github.com/esphome/esphome/blob/dev/LICENSE) for more information.
Binary file added fonts/Roboto-Medium.ttf
Binary file not shown.
37 changes: 37 additions & 0 deletions include/sht20.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "esphome.h"
#include "uFire_SHT20.h"

class SHT20 : public PollingComponent {
public:
uFire_SHT20 sht20;
Sensor *temperature_sensor = new Sensor();
Sensor *humidity_sensor = new Sensor();
Sensor *vpd_sensor = new Sensor();
Sensor *dew_point_sensor = new Sensor();

// To prevent self-heating, the datasheet warns to do
// maximum two measurements per second at 12-bit accuracy.
// Stay on the safe side by polling every second.
SHT20() : PollingComponent(1000) { }

void setup() override {
if(!sht20.begin()) {
ESP_LOGE("SHT20", "Sensor is not connected");
}
}

void update() override {
float temperature = sht20.temperature();
temperature_sensor->publish_state(temperature);

int humidity = sht20.humidity();
humidity_sensor->publish_state(humidity);

int vpd = sht20.vpd();
vpd_sensor->publish_state(vpd);

int dew_point = sht20.dew_point();
dew_point_sensor->publish_state(dew_point);

}
};
Binary file added m5stack-air-quality-kit-esphome.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
145 changes: 145 additions & 0 deletions m5stack_air_quality.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
esphome:
name: m5stack_air_quality
platform: ESP32
board: m5stack-core-esp32
libraries:
- "uFire SHT20"
- "ArduinoJson"
includes:
- include/sht20.h

logger:

# Define I/O: UART, I²C and SPI
uart:
rx_pin: 16
baud_rate: 9600

i2c:
sda: 21
scl: 22
scan: true
frequency: 200kHz

spi:
clk_pin: 18
mosi_pin: 23
miso_pin: 19

# Particulate matter sensor and temperature sensor
sensor:
- platform: pmsx003
type: PMSX003
pm_1_0:
name: "PM1"
id: sensor_pm1
filters:
- sliding_window_moving_average:
window_size: 180
send_every: 60
pm_2_5:
name: "PM2.5"
id: sensor_pm25
filters:
- sliding_window_moving_average:
window_size: 180
send_every: 60
pm_10_0:
name: "PM10"
id: sensor_pm10
filters:
- sliding_window_moving_average:
window_size: 180
send_every: 60
- platform: custom
lambda: |-
auto sht20 = new SHT20();
App.register_component(sht20);
return {sht20->temperature_sensor, sht20->humidity_sensor, sht20->vpd_sensor, sht20->dew_point_sensor};
sensors:
- name: "Temperature"
id: sensor_temperature
unit_of_measurement: °C
accuracy_decimals: 2
- name: "Humidity"
id: sensor_humidity
unit_of_measurement: "%"
accuracy_decimals: 2
- name: "Vapour-pressure deficit"
id: sensor_vpd
unit_of_measurement: "kPa"
accuracy_decimals: 2
- name: "Dew point"
id: sensor_dew_point
unit_of_measurement: °C
accuracy_decimals: 2

# Button to toggle the display backlight
binary_sensor:
- platform: gpio
id: M5_BtnB
pin:
number: 38
inverted: true
on_click:
then:
- switch.toggle: backlight

# GPIO pin of the display backlight
switch:
- platform: gpio
pin: 32
name: "Backlight"
id: backlight
restore_mode: ALWAYS_ON

# Download Roboto font from https://fonts.google.com/specimen/Roboto
font:
- file: "fonts/Roboto-Medium.ttf"
id: font_roboto_medium22
size: 22
glyphs: '!"%()+,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz/³µ'

display:
- platform: ili9341
id: m5stack_display
model: M5Stack
cs_pin: 14
dc_pin: 27
led_pin: 32
reset_pin: 33
rotation: 0
lambda: |-
Color RED(1,0,0);
Color BLUE(0,0,1);
Color WHITE(1,1,1);
it.rectangle(0, 0, it.get_width(), it.get_height(), BLUE);
it.rectangle(0, 22, it.get_width(), it.get_height(), BLUE); // header bar
it.print(it.get_width() / 2, 11, id(font_roboto_medium22), RED, TextAlign::CENTER, "Particulate matter");
it.print(11, 33, id(font_roboto_medium22), WHITE, TextAlign::LEFT, "PM1");
it.print(11, 55, id(font_roboto_medium22), WHITE, TextAlign::LEFT, "PM2.5");
it.print(11, 77, id(font_roboto_medium22), WHITE, TextAlign::LEFT, "PM10");
it.printf(it.get_width() - 11, 33, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%.0f µg/m³", id(sensor_pm1).state);
it.printf(it.get_width() - 11, 55, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%.0f µg/m³", id(sensor_pm25).state);
it.printf(it.get_width() - 11, 77, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%.0f µg/m³", id(sensor_pm10).state);
it.rectangle(0, 110, it.get_width(), 22, BLUE); // header bar
it.print(it.get_width() / 2, 121, id(font_roboto_medium22), RED, TextAlign::CENTER, "Environment");
it.print(11, 143, id(font_roboto_medium22), WHITE, TextAlign::LEFT, "T");
it.print(11, 165, id(font_roboto_medium22), WHITE, TextAlign::LEFT, "H");
it.print(11, 187, id(font_roboto_medium22), WHITE, TextAlign::LEFT, "VPD");
it.print(11, 209, id(font_roboto_medium22), WHITE, TextAlign::LEFT, "Tdp");
it.printf(it.get_width() - 77, 143, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%.0f", id(sensor_temperature).state);
it.printf(it.get_width() - 77, 165, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%.0f", id(sensor_humidity).state);
it.printf(it.get_width() - 77, 187, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%.0f", id(sensor_vpd).state);
it.printf(it.get_width() - 77, 209, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%.0f", id(sensor_dew_point).state);
it.printf(it.get_width() - 11, 143, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "°C");
it.printf(it.get_width() - 11, 165, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "%%");
it.printf(it.get_width() - 11, 187, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "kPa");
it.printf(it.get_width() - 11, 209, id(font_roboto_medium22), WHITE, TextAlign::RIGHT, "°C");

0 comments on commit 7ccd630

Please sign in to comment.