懒人创建智能家居之二——智能控制节点创建和连接


1 概述
      在完成基本Home Assistant 操作系统并创建路由服务器之后,就可以添加智能控制节点了。在这个Home Assistant 操作系统的控制面板中,已经有很多定制的工具了。这里需要添加并使用ESPhome插件ESPHome,这样才能够添加所述定制的开发板和节点,这里使用的是ESP32C3和Rasperberry Pico W。并同时连接温度湿度传感器ADH20和照度传感器,以及控制LED照度。
IMG_20231225_210605.jpg
2 所需硬件
2.1 ESP32C3模块
2.2 Rasperberry Pico W
2.3 温度湿度传感器ADH20和照度传感器,以及LED。

3 开始搞一下
3.1 首先进入控制台的ESPHome,可以见到已经创建的节点ESP32C3
HA_17.PNG
点击edit进入yaml编辑界面
HA_18.PNG
这个界面是一个文本编辑器,描述了这个节点是在ESPhome插件下的,使用arduino创建并下载代码的esp32开发板,并且定义了无线连接的ssid接入点和密码,便于无线连接
esphome:
  •   name: xiao-esp32c3
  •   platformio_options:
  •    board_build.flash_mode: dio

  • esp32:
  •   board: seeed_xiao_esp32c3
  •   variant: esp32c3
  •   framework:
  •     type: arduino
  •     platform_version: 5.4.0

  • logger:
  • hardware_uart: UART0

  • api:

  • ota:

  • wifi:
  •   ssid: "UMASS fried chicken"
  •   password: "Zacharyloveschicken"

  •   # Enable fallback hotspot (captive portal) in case wifi connection fails
  •   ap:
  •     ssid: "Xiao-Esp32C3 Fallback Hotspot"
  •     password: "MoLTqZUvHwWI"
  • 复制代码
    3.2 开始安装
    然后点击INSTALL安装,确实,就是在这里,用这个文本就算完成编程了。随后立刻跳出了这个硬件的选择,可以看出都是ESP系列的各种模块,以及PICO W.
    HA_20.PNG
    接着把开发板模块连接在树莓派上,选择设备,就跳出了连接的串口,
    HA_21.PNG
    选择这个串口,就开始如下显示,编译安装了。具体其中的魔法,在后面详述。
    HA_22.PNG
    同时这里还提供了另一种安装方式,就是通过网页安装用esphome网页GUI,进入这个选项,这个是在后台自己独自跑圈,形成可下载的bin文件,
    HA_23.PNG
    然后进入如下esphome gui直接上传并安装到连接到串口的开发板
    HA_25.PNG
    下载安装成功后继续撒花。
    HA_26.PNG
    这个过程其实还有另一个用处,就是用命令行创建并生成独立的bin文件,自行下载。
    具体的这个操作所执行的过程就如下
    HA_24.PNG
    如果一切顺利,就可以继续启动这个节点的运行,随后继续在这个页面显示log的过程。
    HA_27.PNG
    这样这个节点就创建成功并成功运行了。现在可以安全关闭这个页面,进入原先的页面,看起来没没啥变换啊。
    HA_30.PNG
    貌似没变化,但是注意这个右上角的字符已经显示时online了,这个节点已经被识别并上线了。
    3.3 添加节点到控制台面板
    回到主页,就可以看到已经发现了新的esphome节点的esp32
    HA_31.PNG
    添加配置这个已经发现的设备
    HA_32.PNG
    选择安装的位置,是在哪个房间
    HA_36.PNG
    确认后,就可以成功添加到控制面板。
    HA_33.PNG
    3.4 添加传感器节点
        这个节点添加后,就需要安装对于传感器的采集,对于风机,照明等的控制。按照前面的连接方法连接传感器并修改前面提到的yaml文件,添加下述i2c和adc定义
    captive_portal:

  • i2c:
  •   sda: GPIO6
  •   scl: GPIO7
  •   scan: True
  •   id: bus_0x38

  • sensor:
  •   - platform: aht10
  •     temperature:
  •       name: "Living Room Temperature"
  •     humidity:
  •       name: "Living Room Humidity"
  •     update_interval: 60s

  •   - platform: adc
  •     pin: GPIO2
  •     name: "Light Sensor"
  •     update_interval: 6s   
  • 复制代码
    上面的定义了节点的性质和位置,其中对于引脚的访问。重复上面的过程,再次编译并安装
    HA_34.PNG
    成功下载
    HA_35.PNG
    随后按照功能加节点的传感器读数
    IMG_20231225_210515.jpg
    这里找到了照度传感器的读数,现在显示0.61V,这个添加到控制面板的控制台如下,这时是在暗处,只有0.08V,每隔6秒采样一下。
    IMG_20231225_210533.jpg
    4 面向文本的低代码开发解密
    4.1 采用pico w为例,先如前述在esphome中添加设备,并安装下载yaml
    PICOW-01.PNG
    这样就在esphome中注册成功了
    4.2 其实,上述的过程就是一个创建工程并安装的过程。那么具体可以在计算机的命令行来实现
    首先使用
    pip install esphome

    来安装开发工具,随后补充安装缺失的插件,这里platformio因为版本不兼容,需要卸载原有包并新安装
    PICOW-02.PNG
    然后选择
    esphome picow.yaml compile
    执行编译,成功后如下
    PICOW-03.PNG
    编译成功的是一个uf2格式的文件
    PICOW-04.PNG
    成功以后把这个文件安装到pico w就可以自行启动了。不用再重复上面的过程。并且同样可以添加接点,设计功能。
    4.3 解密时间
         这个HomeAssistant操作系统中esphome的使用方法,是首先把yaml文件转换成了一个arduino程序。
    // Auto generated code by esphome
  • // ========== AUTO GENERATED INCLUDE BLOCK BEGIN ===========
  • #include "esphome.h"
  • using namespace esphome;
  • using std::isnan;
  • using std::min;
  • using std::max;
  • using namespace light;
  • wifi::WiFiComponent *wifi_wificomponent;
  • mdns::MDNSComponent *mdns_mdnscomponent;
  • StartupTrigger *startuptrigger;
  • Automation<> *automation;
  • using namespace output;
  • gpio::GPIOBinaryOutput *LED;
  • rp2040::RP2040GPIOPin *rp2040_rp2040gpiopin;
  • rp2040_pwm::RP2040PWM *led;
  • rp2040::RP2040GPIOPin *rp2040_rp2040gpiopin_2;
  • interval::IntervalTrigger *interval_intervaltrigger;
  • Automation<> *automation_2;
  • output::TurnOnAction<> *output_turnonaction;
  • DelayAction<> *delayaction;
  • output::TurnOffAction<> *output_turnoffaction;
  • monochromatic::MonochromaticLightOutput *monochromatic_monochromaticlightoutput;
  • light::LightState *pulsating_led;
  • light::PulseLightEffect *light_pulselighteffect;
  • light::LightControlAction<> *light_lightcontrolaction;
  • #define yield() esphome::yield()
  • #define millis() esphome::millis()
  • #define micros() esphome::micros()
  • #define delay(x) esphome::delay(x)
  • #define delayMicroseconds(x) esphome::delayMicroseconds(x)
  • // ========== AUTO GENERATED INCLUDE BLOCK END ==========="

  • void setup() {
  •   // ========== AUTO GENERATED CODE BEGIN ===========
  •   // rp2040:
  •   //   board: rpipicow
  •   //   framework:
  •   //     platform_version: https:github.com/maxgerhardt/platform-raspberrypi.git
  •   rp2040::setup_preferences();
  •   // esphome:
  •   //   name: rpi-pico
  •   //   friendly_name: Raspberry Pi Pico W
  •   //   on_boot:
  •   //   - then:
  •   //     - light.turn_on:
  •   //         id: pulsating_led
  •   //         effect: Slow pulse
  •   //         state: true
  •   //       type_id: light_lightcontrolaction
  •   //     automation_id: automation
  •   //     trigger_id: startuptrigger
  •   //     priority: 600.0
  •   //   build_path: build/rpi-pico
  •   //   area: ''
  •   //   platformio_options: {}
  •   //   includes: []
  •   //   libraries: []
  •   //   name_add_mac_suffix: false
  •   //   min_version: 2023.12.3
  •   App.pre_setup("rpi-pico", "Raspberry Pi Pico W", "", "", __DATE__ ", " __TIME__, false);
  •   // light:
  •   // wifi:
  •   //   ap:

  •   wifi_wificomponent = new wifi::WiFiComponent();
  •   wifi_wificomponent->set_use_address("rpi-pico.local");
  •   {
  •   wifi::WiFiAP wifi_wifiap_2 = wifi::WiFiAP();
  •   wifi_wifiap_2.set_ssid("TP-LINK_2.4GHz_FFF1EC");
  •   wifi_wifiap_2.set_password("13810963660");
  •   wifi_wifiap_2.set_priority(0.0f);
  •   wifi_wificomponent->add_sta(wifi_wifiap_2);
  •   }
  •   {
  •   wifi::WiFiAP wifi_wifiap = wifi::WiFiAP();
  •   wifi_wifiap.set_ssid("PICOW Fallback Hotspot");
  •   wifi_wifiap.set_password("Checwifi0617");
  •   wifi_wificomponent->set_ap(wifi_wifiap);
  •   }
  •   wifi_wificomponent->set_ap_timeout(60000);
  •   wifi_wificomponent->set_reboot_timeout(900000);
  •   wifi_wificomponent->set_power_save_mode(wifi::WIFI_POWER_SAVE_LIGHT);
  •   wifi_wificomponent->set_fast_connect(false);
  •   wifi_wificomponent->set_passive_scan(false);
  •   wifi_wificomponent->set_enable_on_boot(true);
  •   wifi_wificomponent->set_component_source("wifi");
  •   App.register_component(wifi_wificomponent);
  •   // mdns:
  •   //   id: mdns_mdnscomponent
  •   //   disabled: false
  •   //   services: []
  •   mdns_mdnscomponent = new mdns::MDNSComponent();
  •   mdns_mdnscomponent->set_component_source("mdns");
  •   App.register_component(mdns_mdnscomponent);
  •   startuptrigger = new StartupTrigger(600.0f);
  •   startuptrigger->set_component_source("esphome.coroutine");
  •   App.register_component(startuptrigger);
  •   automation = new Automation<>(startuptrigger);
  •   // output:
  •   // output.gpio:
  •   //   platform: gpio
  •   //   pin:
  •   //     number: 25
  •   //     mode:
  •   //       output: true
  •   //       input: false
  •   //       open_drain: false
  •   //       pullup: false
  •   //       pulldown: false
  •   //       analog: false
  •   //     id: rp2040_rp2040gpiopin
  •   //     inverted: false
  •   //   id: LED
  •   LED = new gpio::GPIOBinaryOutput();
  •   LED->set_component_source("gpio.output");
  •   App.register_component(LED);
  •   rp2040_rp2040gpiopin = new rp2040::RP2040GPIOPin();
  •   rp2040_rp2040gpiopin->set_pin(25);
  •   rp2040_rp2040gpiopin->set_inverted(false);
  •   rp2040_rp2040gpiopin->set_flags(gpio::Flags::FLAG_OUTPUT);
  •   LED->set_pin(rp2040_rp2040gpiopin);
  •   // output.rp2040_pwm:
  •   //   platform: rp2040_pwm
  •   //   pin:
  •   //     number: 15
  •   //     mode:
  •   //       output: true
  •   //       input: false
  •   //       open_drain: false
  •   //       pullup: false
  •   //       pulldown: false
  •   //       analog: false
  •   //     id: rp2040_rp2040gpiopin_2
  •   //     inverted: false
  •   //   id: led
  •   //   zero_means_zero: false
  •   //   frequency: 1000.0
  •   led = new rp2040_pwm::RP2040PWM();
  •   led->set_component_source("rp2040_pwm.output");
  •   App.register_component(led);
  •   led->set_zero_means_zero(false);
  •   rp2040_rp2040gpiopin_2 = new rp2040::RP2040GPIOPin();
  •   rp2040_rp2040gpiopin_2->set_pin(15);
  •   rp2040_rp2040gpiopin_2->set_inverted(false);
  •   rp2040_rp2040gpiopin_2->set_flags(gpio::Flags::FLAG_OUTPUT);
  •   led->set_pin(rp2040_rp2040gpiopin_2);
  •   led->set_frequency(1000.0f);
  •   // interval:
  •   //   - interval: 500ms
  •   //     then:
  •   //     - output.turn_on:
  •   //         id: LED
  •   //       type_id: output_turnonaction
  •   //     - delay: 250ms
  •   //       type_id: delayaction
  •   //     - output.turn_off:
  •   //         id: LED
  •   //       type_id: output_turnoffaction
  •   //     trigger_id: trigger
  •   //     automation_id: automation_2
  •   //     id: interval_intervaltrigger
  •   //     startup_delay: 0s
  •   interval_intervaltrigger = new interval::IntervalTrigger();
  •   interval_intervaltrigger->set_component_source("interval");
  •   App.register_component(interval_intervaltrigger);
  •   automation_2 = new Automation<>(interval_intervaltrigger);
  •   output_turnonaction = new output::TurnOnAction<>(LED);
  •   delayaction = new DelayAction<>();
  •   delayaction->set_component_source("interval");
  •   App.register_component(delayaction);
  •   delayaction->set_delay(250);
  •   output_turnoffaction = new output::TurnOffAction<>(LED);
  •   automation_2->add_actions({output_turnonaction, delayaction, output_turnoffaction});
  •   interval_intervaltrigger->set_update_interval(500);
  •   interval_intervaltrigger->set_startup_delay(0);
  •   // light.monochromatic:
  •   //   platform: monochromatic
  •   //   output: led
  •   //   id: pulsating_led
  •   //   effects:
  •   //   - pulse:
  •   //       name: Slow pulse
  •   //       transition_length: 2s
  •   //       update_interval: 2s
  •   //       min_brightness: 0.0
  •   //       max_brightness: 1.0
  •   //     type_id: light_pulselighteffect
  •   //   disabled_by_default: false
  •   //   restore_mode: ALWAYS_OFF
  •   //   gamma_correct: 2.8
  •   //   default_transition_length: 1s
  •   //   flash_transition_length: 0s
  •   //   output_id: monochromatic_monochromaticlightoutput
  •   //   name: pulsating_led
  •   //   internal: true
  •   monochromatic_monochromaticlightoutput = new monochromatic::MonochromaticLightOutput();
  •   pulsating_led = new light::LightState(monochromatic_monochromaticlightoutput);
  •   App.register_light(pulsating_led);
  •   pulsating_led->set_component_source("light");
  •   App.register_component(pulsating_led);
  •   pulsating_led->set_name("pulsating_led");
  •   pulsating_led->set_object_id("pulsating_led");
  •   pulsating_led->set_disabled_by_default(false);
  •   pulsating_led->set_internal(true);
  •   pulsating_led->set_restore_mode(light::LIGHT_ALWAYS_OFF);
  •   pulsating_led->set_default_transition_length(1000);
  •   pulsating_led->set_flash_transition_length(0);
  •   pulsating_led->set_gamma_correct(2.8f);
  •   light_pulselighteffect = new light::PulseLightEffect("Slow pulse");
  •   light_pulselighteffect->set_transition_on_length(2000);
  •   light_pulselighteffect->set_transition_off_length(2000);
  •   light_pulselighteffect->set_update_interval(2000);
  •   light_pulselighteffect->set_min_max_brightness(0.0f, 1.0f);
  •   pulsating_led->add_effects({light_pulselighteffect});
  •   monochromatic_monochromaticlightoutput->set_output(led);
  •   // network:
  •   //   enable_ipv6: false
  •   light_lightcontrolaction = new light::LightControlAction<>(pulsating_led);
  •   light_lightcontrolaction->set_state(true);
  •   light_lightcontrolaction->set_effect("Slow pulse");
  •   automation->add_actions({light_lightcontrolaction});
  •   // =========== AUTO GENERATED CODE END ============
  •   App.setup();
  • }

  • void loop() {
  •   App.loop();
  • }
  • 复制代码
    仔细研究,虽然代码长,但仍然是包括初始化部分
    setup()
    以及
    void loop() {
  •   App.loop();
  • }
  • 复制代码
    这样的标准循环过程。第一行导入的头文件
    #include "esphome.h"
    复制代码
    就一步把代码带入了esphome的开发环境,后面的编译完全借用了arduino的开发工具,整个这个都是开源代码,提供开发者更多的选择空间。并且具备工业化生产的条件,因为ESP32是一个开发者的大众情人,因为性价比好广泛采用,这样的玩法,可以让独立开发者直接接入home assistant的开发生态,提供良好的接入和访问环境。