m5stack co2 sensor

This commit is contained in:
Martin Bauer 2024-03-01 15:01:56 +01:00
parent 513cab32b7
commit 55ec47e3af
10 changed files with 3414 additions and 9 deletions

View File

@ -0,0 +1,59 @@
.
light:
- platform: monochromatic
name: "Livingroom Ceiling light"
id: light_0
output: light_0_out
# Example output entry
output:
- platform: esp8266_pwm
id: light_0_out
pin: D4
inverted: true
binary_sensor:
- platform: gpio
pin: D2
id: light_0_touch
on_click:
then:
- if:
condition:
light.is_off: light_0
then:
light.turn_on:
id: light_0
brightness: 1.0
else:
light.turn_off: light_0
on_press:
then:
- if:
condition:
light.is_off: light_0
then:
- delay: 0.5s
- while:
condition:
binary_sensor.is_on: light_0_touch
then:
- light.dim_relative:
id: light_0
relative_brightness: 5%
transition_length: 0.1s
- delay: 0.1s
else:
- delay: 0.5s
- while:
condition:
and:
- binary_sensor.is_on: light_0_touch
- light.is_on: light_0
then:
- light.dim_relative:
id: light_0
relative_brightness: -5%
transition_length: 0.1s
- delay: 0.1s

243
m5echo_voice_assistant.yaml Normal file
View File

@ -0,0 +1,243 @@
---
esphome:
name: m5atom1
friendly_name: M5Atom1
name_add_mac_suffix: true
project:
name: m5stack.atom-echo-voice-assistant
version: "1.0"
min_version: 2023.11.1
esp32:
board: m5stack-atom
framework:
type: esp-idf
logger:
api:
encryption:
key: !secret api_encryption_key
ota:
password: !secret ota_password
dashboard_import:
package_import_url: github://esphome/firmware/voice-assistant/m5stack-atom-echo.yaml@main
wifi:
on_connect:
- delay: 5s # Gives time for improv results to be transmitted
- ble.disable:
on_disconnect:
- ble.enable:
ssid: WLAN
password: !secret wifi_password
improv_serial:
esp32_improv:
authorizer: none
button:
- platform: factory_reset
id: factory_reset_btn
name: Factory reset
i2s_audio:
i2s_lrclk_pin: GPIO33
i2s_bclk_pin: GPIO19
microphone:
- platform: i2s_audio
id: echo_microphone
i2s_din_pin: GPIO23
adc_type: external
pdm: true
speaker:
- platform: i2s_audio
id: echo_speaker
i2s_dout_pin: GPIO22
dac_type: external
mode: mono
voice_assistant:
id: va
microphone: echo_microphone
speaker: echo_speaker
noise_suppression_level: 2
auto_gain: 31dBFS
volume_multiplier: 2.0
vad_threshold: 3
on_listening:
- light.turn_on:
id: led
blue: 100%
red: 0%
green: 0%
effect: "Slow Pulse"
on_stt_vad_end:
- light.turn_on:
id: led
blue: 100%
red: 0%
green: 0%
effect: "Fast Pulse"
on_tts_start:
- light.turn_on:
id: led
blue: 100%
red: 0%
green: 0%
brightness: 100%
effect: none
on_end:
- delay: 100ms
- wait_until:
not:
speaker.is_playing:
- script.execute: reset_led
on_error:
- light.turn_on:
id: led
red: 100%
green: 0%
blue: 0%
brightness: 100%
effect: none
- delay: 1s
- script.execute: reset_led
on_client_connected:
- if:
condition:
switch.is_on: use_wake_word
then:
- voice_assistant.start_continuous:
- script.execute: reset_led
on_client_disconnected:
- if:
condition:
switch.is_on: use_wake_word
then:
- voice_assistant.stop:
- light.turn_off: led
binary_sensor:
- platform: gpio
pin:
number: GPIO39
inverted: true
name: Button
disabled_by_default: true
entity_category: diagnostic
id: echo_button
on_multi_click:
- timing:
- ON for at least 250ms
- OFF for at least 50ms
then:
- if:
condition:
switch.is_off: use_wake_word
then:
- if:
condition: voice_assistant.is_running
then:
- voice_assistant.stop:
- script.execute: reset_led
else:
- voice_assistant.start:
else:
- voice_assistant.stop
- delay: 1s
- script.execute: reset_led
- script.wait: reset_led
- voice_assistant.start_continuous:
- timing:
- ON for at least 10s
then:
- button.press: factory_reset_btn
light:
- platform: esp32_rmt_led_strip
id: led
name: None
disabled_by_default: true
entity_category: config
pin: GPIO27
default_transition_length: 0s
chipset: SK6812
num_leds: 1
rgb_order: grb
rmt_channel: 0
effects:
- pulse:
name: "Slow Pulse"
transition_length: 250ms
update_interval: 250ms
min_brightness: 50%
max_brightness: 100%
- pulse:
name: "Fast Pulse"
transition_length: 100ms
update_interval: 100ms
min_brightness: 50%
max_brightness: 100%
script:
- id: reset_led
then:
- if:
condition:
- switch.is_on: use_wake_word
- switch.is_on: use_listen_light
then:
- light.turn_on:
id: led
red: 100%
green: 89%
blue: 71%
brightness: 60%
effect: none
else:
- light.turn_off: led
switch:
- platform: template
name: Use wake word
id: use_wake_word
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
entity_category: config
on_turn_on:
- lambda: id(va).set_use_wake_word(true);
- if:
condition:
not:
- voice_assistant.is_running
then:
- voice_assistant.start_continuous
- script.execute: reset_led
on_turn_off:
- voice_assistant.stop
- lambda: id(va).set_use_wake_word(false);
- script.execute: reset_led
- platform: template
name: Use Listen Light
id: use_listen_light
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
entity_category: config
on_turn_on:
- script.execute: reset_led
on_turn_off:
- script.execute: reset_led
external_components:
- source: github://pr#5230
components:
- esp_adf
refresh: 0s
esp_adf:

BIN
m5stack_display.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

185
m5stack_display.svg Normal file
View File

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="320mm"
height="240mm"
viewBox="0 0 320 240"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="m5stack_display.svg"
inkscape:export-filename="/home/martin/code/esphome/m5stack_display.png"
inkscape:export-xdpi="25.4"
inkscape:export-ydpi="25.4"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="1.1474186"
inkscape:cx="722.05556"
inkscape:cy="440.11836"
inkscape:window-width="3840"
inkscape:window-height="2063"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" />
<defs
id="defs2">
<rect
x="-263.52751"
y="328.9208"
width="56.257952"
height="42.873549"
id="rect29523" />
<rect
x="81.093522"
y="296.73663"
width="416.73771"
height="44.334986"
id="rect4969" />
</defs>
<g
inkscape:label="Background"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#000000;fill-opacity:0.96470606;stroke:none;stroke-width:2.23467;stroke-linecap:round;stroke-opacity:0.968627"
id="rect848"
width="320"
height="240"
x="0"
y="0"
ry="2.5911958e-13" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Elements">
<rect
style="fill:#0076ff;fill-opacity:0.96470606;stroke:none;stroke-width:1.32314;stroke-linecap:round;stroke-opacity:0.968627"
id="rect1122"
width="320"
height="30"
x="0"
y="0"
ry="9.4221316e-14" />
<rect
style="fill:#0076ff;fill-opacity:0.964706;stroke:none;stroke-width:1.3777;stroke-linecap:round;stroke-opacity:0.968627"
id="rect1122-3"
width="130"
height="30"
x="15"
y="63.852001"
ry="9.8846529e-14" />
<rect
style="fill:#0076ff;fill-opacity:0.964706;stroke:none;stroke-width:1.3777;stroke-linecap:round;stroke-opacity:0.968627"
id="rect1122-3-6"
width="130"
height="30"
x="15"
y="122.02885"
ry="9.8846529e-14" />
<rect
style="fill:#0076ff;fill-opacity:0.964706;stroke:none;stroke-width:1.26316;stroke-linecap:round;stroke-opacity:0.968627"
id="rect1122-3-7"
width="130"
height="30"
x="15"
y="180.2054"
ry="9.9489962e-14" />
<rect
style="fill:#009600;fill-opacity:0.96470606;stroke:none;stroke-width:1.3777;stroke-linecap:round;stroke-opacity:0.968627"
id="rect1122-3-8"
width="130"
height="30"
x="168.81218"
y="63.851997"
ry="9.8846529e-14" />
<rect
style="fill:#009600;fill-opacity:0.96470606;stroke:none;stroke-width:1.3777;stroke-linecap:round;stroke-opacity:0.968627"
id="rect1122-3-6-8"
width="130"
height="30"
x="168.81218"
y="122.02884"
ry="9.8846529e-14" />
<rect
style="fill:#009600;fill-opacity:0.96470606;stroke:none;stroke-width:1.26316;stroke-linecap:round;stroke-opacity:0.968627"
id="rect1122-3-7-6"
width="130"
height="30"
x="168.81218"
y="180.2054"
ry="9.9489962e-14" />
<text
xml:space="preserve"
transform="scale(0.26458333)"
id="text4967"
style="font-size:24px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect4969);fill:#ffd5d5" />
<text
xml:space="preserve"
style="font-size:16.9333px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="20.979563"
y="84.87159"
id="text14105"><tspan
sodipodi:role="line"
id="tspan14103"
style="font-size:16.9333px;fill:#e8e8e8;fill-opacity:1;stroke-width:0.264583"
x="20.979563"
y="84.87159">CO </tspan></text>
<text
xml:space="preserve"
style="font-size:16.9333px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="21.558338"
y="142.9158"
id="text14105-0"><tspan
sodipodi:role="line"
id="tspan14103-29"
style="font-size:16.9333px;fill:#e8e8e8;fill-opacity:1;stroke-width:0.264583"
x="21.558338"
y="142.9158">Temperatur</tspan></text>
<text
xml:space="preserve"
style="font-size:16.9333px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="20.574421"
y="200.93526"
id="text14105-0-0"><tspan
sodipodi:role="line"
id="tspan14103-29-9"
style="font-size:16.9333px;fill:#e8e8e8;fill-opacity:1;stroke-width:0.264583"
x="20.574421"
y="200.93526">Luftfeuchtigkeit</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="46.06287"
y="88.702797"
id="text14105-5"><tspan
sodipodi:role="line"
id="tspan14103-2"
style="font-size:14.1111px;fill:#e8e8e8;fill-opacity:1;stroke-width:0.264583"
x="46.06287"
y="88.702797">2</tspan></text>
<text
xml:space="preserve"
transform="scale(0.26458333)"
id="text29521"
style="font-family:Roboto;font-size:24px;line-height:1.25;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Roboto;white-space:pre;shape-inside:url(#rect29523)" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Text" />
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -46,12 +46,12 @@ ip5306:
# Buttons
binary_sensor:
- platform: gpio
id: M5_BtnA
id: m5stackfire_button_left
pin:
number: 39
inverted: true
- platform: gpio
id: M5_BtnB
id: m5stackfire_button_middle
pin:
number: 38
inverted: true
@ -59,11 +59,14 @@ binary_sensor:
then:
- switch.toggle: backlight
- platform: gpio
id: M5_BtnC
id: m5stackfire_button_right
pin:
number: 37
inverted: true
- platform: gpio
pin: 36
name: m5stackfire-motion
device_class: motion
# GPIO pin of the display backlight
switch:
- platform: gpio
@ -88,6 +91,19 @@ light:
restore_mode: ALWAYS_OFF
id: side_light
default_transition_length: 0s
- platform: partition
name: "${friendly_name} Side Light Right"
segments:
- id: side_light
from: 0
to: 4
- platform: partition
name: "${friendly_name} Side Light Left"
segments:
- id: side_light
from: 5
to: 9
i2c:
sda: 21
@ -103,7 +119,13 @@ spi:
font:
- file: "gfonts://Roboto"
id: roboto
size: 24
size: 18
image:
- file: m5stack_display.png
id: img_background
type: RGB24
display:
- platform: ili9xxx
@ -113,9 +135,10 @@ display:
reset_pin: 33
id: my_display
lambda: |-
it.printf(80, 0, id(roboto), Color(255, 255, 255), TextAlign::TOP_CENTER, "CO2 Value %.1f", id(co2value).state );
it.printf(80, 30, id(roboto), Color(255, 255, 255), TextAlign::TOP_CENTER, "CO2 Temp %.1f", id(co2temp).state );
it.image(0, 0, id(img_background));
it.printf(240, 67, id(roboto), Color(255, 255, 255), TextAlign::TOP_CENTER, "%.0f ppm", id(co2value).state );
it.printf(240, 125, id(roboto), Color(255, 255, 255), TextAlign::TOP_CENTER, "%.1f °C", id(co2temp).state );
it.printf(240, 184, id(roboto), Color(255, 255, 255), TextAlign::TOP_CENTER, "%.1f %%", id(m5stackfire_dht22_humidity).state );
#display:
# - platform: ili9xxx
@ -176,4 +199,15 @@ sensor:
update_interval: 5s
automatic_baseline_calibration: false
uart_id: uart_co2
id: my_mhz19
id: my_mhz19
- platform: dht
pin: 26
temperature:
id: m5stackfire_dht22_temperature
name: "DHT22 Temperature"
humidity:
id: m5stackfire_dht22_humidity
name: "DHT22 humidity"

View File

@ -0,0 +1,80 @@
from esphome.components import select
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ID, CONF_ADDRESS
from esphome.components.modbus_controller import (
add_modbus_base_properties,
modbus_controller_ns,
modbus_calc_properties,
validate_modbus_register,
ModbusItemBaseSchema,
SensorItem,
MODBUS_REGISTER_TYPE,
)
from ..const import (
CONF_BITMASK,
CONF_FORCE_NEW_RANGE,
CONF_MODBUS_CONTROLLER_ID,
CONF_REGISTER_TYPE,
CONF_SKIP_UPDATES,
CONF_USE_WRITE_MULTIPLE,
CONF_WRITE_LAMBDA,
)
DEPENDENCIES = ["modbus_controller"]
CODEOWNERS = ["@mabau"]
NeopoolFiltrationMode = modbus_controller_ns.class_(
"NeopoolFiltrationMode", cg.Component, select.Select, SensorItem
)
CONFIG_SCHEMA = cv.All(
select.select_schema(NeopoolFiltrationMode)
.extend(cv.COMPONENT_SCHEMA)
#.extend(ModbusItemBaseSchema)
#.extend(
# {
# cv.Required(CONF)
# cv.Optional(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE),
# cv.Optional(CONF_USE_WRITE_MULTIPLE, default=False): cv.boolean,
# cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda,
# }
#),
#validate_modbus_register,
)
async def to_code(config):
byte_offset, _ = modbus_calc_properties(config)
var = cg.new_Pvariable(
config[CONF_ID],
config[CONF_REGISTER_TYPE],
config[CONF_ADDRESS],
byte_offset,
config[CONF_BITMASK],
config[CONF_SKIP_UPDATES],
config[CONF_FORCE_NEW_RANGE],
)
await cg.register_component(var, config)
await switch.register_switch(var, config)
paren = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID])
cg.add(var.set_parent(paren))
cg.add(var.set_use_write_mutiple(config[CONF_USE_WRITE_MULTIPLE]))
cg.add(paren.add_sensor_item(var))
if CONF_WRITE_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_WRITE_LAMBDA],
[
(ModbusSwitch.operator("ptr"), "item"),
(cg.bool_, "x"),
(cg.std_vector.template(cg.uint8).operator("ref"), "payload"),
],
return_type=cg.optional.template(bool),
)
cg.add(var.set_write_template(template_))
await add_modbus_base_properties(var, config, ModbusSwitch, bool, bool)

View File

@ -0,0 +1,89 @@
#include "filtration_mode.h"
#include "esphome/core/log.h"
namespace esphome {
namespace neopool {
void FiltrationSelect::dump_config() { LOG_SELECT(TAG, "Modbus Neopool Filtration Select", this); }
void FiltrationSelect::parse_and_publish(const std::vector<uint8_t> &data)
{
int64_t value = payload_to_number(data, this->sensor_value_type, this->offset, this->bitmask);
ESP_LOGD(TAG, "New select value %lld from payload", value);
optional<std::string> new_state;
if (this->transform_func_.has_value()) {
auto val = (*this->transform_func_)(this, value, data);
if (val.has_value()) {
new_state = *val;
ESP_LOGV(TAG, "lambda returned option %s", new_state->c_str());
}
}
if (!new_state.has_value()) {
auto map_it = std::find(this->mapping_.cbegin(), this->mapping_.cend(), value);
if (map_it != this->mapping_.cend()) {
size_t idx = std::distance(this->mapping_.cbegin(), map_it);
new_state = this->traits.get_options()[idx];
ESP_LOGV(TAG, "Found option %s for value %lld", new_state->c_str(), value);
} else {
ESP_LOGE(TAG, "No option found for mapping %lld", value);
}
}
if (new_state.has_value()) {
this->publish_state(new_state.value());
}
}
void FiltrationSelect::control(const std::string &value) {
auto options = this->traits.get_options();
auto opt_it = std::find(options.cbegin(), options.cend(), value);
size_t idx = std::distance(options.cbegin(), opt_it);
optional<int64_t> mapval = this->mapping_[idx];
ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, value.c_str());
std::vector<uint16_t> data;
if (this->write_transform_func_.has_value()) {
auto val = (*this->write_transform_func_)(this, value, *mapval, data);
if (val.has_value()) {
mapval = *val;
ESP_LOGV(TAG, "write_lambda returned mapping value %lld", *mapval);
} else {
ESP_LOGD(TAG, "Communication handled by write_lambda - exiting control");
return;
}
}
if (data.empty()) {
number_to_payload(data, *mapval, this->sensor_value_type);
} else {
ESP_LOGV(TAG, "Using payload from write lambda");
}
if (data.empty()) {
ESP_LOGW(TAG, "No payload was created for updating select");
return;
}
const uint16_t write_address = this->start_address + this->offset / 2;
ModbusCommandItem write_cmd;
if ((this->register_count == 1) && (!this->use_write_multiple_)) {
write_cmd = ModbusCommandItem::create_write_single_command(parent_, write_address, data[0]);
} else {
write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, write_address, this->register_count, data);
}
parent_->queue_command(write_cmd);
if (this->optimistic_)
this->publish_state(value);
}
}
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <utility>
#include <vector>
#include "esphome/components/modbus_controller/modbus_controller.h"
#include "esphome/components/select/select.h"
#include "esphome/core/component.h"
namespace esphome {
namespace neopool {
/**
* @brief Controls the filtration mode of the Neopool device connected via modbus
*
* Possible states:
* - automatic
* - off
* - slow
* - medium
* - fast
*
* To read the state:
* reads relay register 0x010E, with bitmasks
* - 0x0100 slow
* - 0x0200 medium
* - 0x0400 high
* - 0x0010 filtering
* auto / non-auto: 0x0411:
* - MBV_PAR_FILT_MANUAL = 0, // This mode allows to turn the filtration (and all other systems that depend on it) on and off manually.
* MBV_PAR_FILT_AUTO = 1, // This mode allows filtering to be turned on and off according to the settings of the TIMER1, TIMER2 and TIMER3 timers.
*
* To set the manual state:
* set filtration speed to addr 0x050F (MBF_PAR_FILTRATION_CONF), register needs to be read first to be correctly modified
* set MBF_EXEC to 1
* set filtration mode to manual (=0) via MBF_PAR_FILT_MODE (0x0411)
* set manual mode filtration to on 0x0413 to 0, 1, 2, ?
*
*/
class FiltrationMode : public Component, public select::Select {
public:
void set_parent(ModbusController *const parent) { this->parent_ = parent; }
void dump_config() override;
void control(const std::string &value) override;
protected:
ModbusController *parent_;
};
} // namespace modbus_controller
} // namespace esphome

2593
neopool.cpp Normal file

File diff suppressed because it is too large Load Diff

66
test_dmx.yaml Normal file
View File

@ -0,0 +1,66 @@
esphome:
name: test-dmx
esp32:
board: wt32-eth01
framework:
type: arduino
api:
encryption:
key: !secret api_encryption_key
# Enable logging
logger:
wifi:
ssid: WLAN
password: !secret wifi_password
external_components:
- source:
type: git
url: "https://github.com/andyboeh/esphome-dmx512"
ref: master
uart:
- id: "uart_dmx1"
rx_pin: 2
tx_pin: 4 # the pin where the transmission happens connected to tx (not reversed)
stop_bits: 2
baud_rate: 250000
dmx512:
- id: dmx1
uart_id: uart_dmx1
tx_pin: 4
uart_num: 1
periodic_update: true
force_full_frames: true
#update_interval: 100
#custom_break_len: 176
output:
- platform: dmx512
channel: 1
universe: dmx1
id: test_licht_r
- platform: dmx512
channel: 2
universe: dmx1
id: test_licht_g
- platform: dmx512
channel: 3
universe: dmx1
id: test_licht_b
light:
- platform: rgb
name: test_licht
red: test_licht_r
green: test_licht_g
blue: test_licht_b
restore_mode: RESTORE_DEFAULT_OFF