ESPhome Arduino Port Expander sketch error

error compiling the file in esphome, namely in the arduino_port_expander.h file
I took an example of a configuration here https://esphome.io/cookbook/arduino_port_extender.html

In file included from src/main.cpp:22:0:
src/arduino_port_expander.h:106:1: error: expected class-name before '{' token
 {
 ^
src/arduino_port_expander.h:108:36: error: expected ')' before '*' token
   ArduinoPortExpander(I2CComponent *parent, uint8_t address, bool vref_default = false) : I2CDevice(parent, address)
                                    ^
src/arduino_port_expander.h: In member function 'virtual void ArduinoPortExpander::loop()':
src/arduino_port_expander.h:134:17: error: 'class ArduinoPortExpander' has no member named 'read_bytes'
       if (this->read_bytes(APE_CMD_DIGITAL_READ, const_cast<uint8_t *>(this->read_buffer_), 3, 1))
                 ^
src/arduino_port_expander.h:142:17: error: 'class ArduinoPortExpander' has no member named 'write_byte'
           this->write_byte(CMD_SETUP_ANALOG_DEFAULT, 0); // 0: unused
                 ^
src/arduino_port_expander.h: In member function 'void ArduinoPortExpander::write_state(uint8_t, bool, bool)':
src/arduino_port_expander.h:273:11: error: 'class ArduinoPortExpander' has no member named 'write_byte'
     this->write_byte(state ? APE_CMD_WRITE_DIGITAL_HIGH : APE_CMD_WRITE_DIGITAL_LOW, pin);
           ^
src/arduino_port_expander.h:281:13: error: 'class ArduinoPortExpander' has no member named 'write_byte'
       this->write_byte(APE_CMD_SETUP_PIN_OUTPUT, pin);
             ^
src/main.cpp: In lambda function:
src/main.cpp:173:47: error: 'i2c_component' was not declared in this scope
       auto expander = new ArduinoPortExpander(i2c_component, 0x08, true);
                                               ^
src/main.cpp:174:23: error: could not convert '{expander}' from '<brace-enclosed initializer list>' to 'std::vector<esphome::Component*>'
       return {expander};
                       ^
src/main.cpp:175:3: warning: control reaches end of non-void function [-Wreturn-type]
   });
   ^
*** [/data/tastgarage/.pioenvs/tastgarage/src/main.cpp.o] Error 1
========================= [FAILED] Took 10.24 seconds =========================

I have the same problem at 108:36 in *.h file. Did you ever resolve this problem? Thanks, Doug

BTW, my esphome greenhouse yaml compiled a long time ago (18 months) with arduino_port_expander.h but now the compile fails. Something has changed. Can anyone help me resolve the problem and get back to updating my greenhouse controller that uses an esp32 and a nano with I2C communication. Any help greatly appreciated, Doug

no, I stopped doing it. it’s easier to switch to a port expander . but on the breadboard Arduino stands for experiments.

The full example compiles fine for me (ESPHome v2022.3.1).

However, I can reproduce the error if the configuration file doesn’t include an i2c declaration:

i2c:
  id: i2c_component

Thanks, I may have to do so also. Much complexity leads to more risk of failure.

Thanks, I have rechecked that part. I am using two I2C devices, but one is designated for the arduino link. My implementation works from a compile 18 months age, but now will not compile. Something has changed either something I did or a change in the arduino add-on. Still trying to analyze. Thank you for your reply, Doug

If you don’t post more info (or your config file) it might be difficult for others to help.

I have returned to working on this problem (I need to change my greenhouse control program. The YAML that worked last year, now fails.
###############################################
This is compile log:
################################################

INFO ESPHome 2023.10.6
INFO Reading configuration /config/esphome/ghc_esp32.yaml...
INFO Detected timezone 'America/New_York'
WARNING GPIO15 is a Strapping PIN and should be avoided.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
WARNING GPIO15 is a Strapping PIN and should be avoided.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
INFO Generating C++ source...
INFO Compiling app...
Processing ghc-esp32 (board: esp32doit-devkit-v1; framework: arduino; platform: platformio/[email protected])
--------------------------------------------------------------------------------
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
Dependency Graph
|-- AsyncTCP-esphome @ 2.0.1
|-- WiFi @ 2.0.0
|-- FS @ 2.0.0
|-- Update @ 2.0.0
|-- ESPAsyncWebServer-esphome @ 3.1.0
|-- DNSServer @ 2.0.0
|-- ESPmDNS @ 2.0.0
|-- Wire @ 2.0.0
|-- ArduinoJson @ 6.18.5
Compiling .pioenvs/ghc-esp32/src/main.cpp.o
Compiling .pioenvs/ghc-esp32/lib38e/ESPAsyncWebServer-esphome/WebServer.cpp.o
In file included from src/main.cpp:338:
src/arduino_port_expander.h:108:35: error: expected ')' before '*' token
   ArduinoPortExpander(I2CComponent *parent, uint8_t address, bool vref_default = false) : I2CDevice(parent, address)
                      ~            ^~
                                   )
src/arduino_port_expander.h: In member function 'virtual void ArduinoPortExpander::loop()':
src/arduino_port_expander.h:134:97: error: no matching function for call to 'ArduinoPortExpander::read_bytes(int, uint8_t*, int, int)'
       if (this->read_bytes(APE_CMD_DIGITAL_READ, const_cast<uint8_t *>(this->read_buffer_), 3, 1))
                                                                                                 ^
In file included from src/esphome/components/bh1750/bh1750.h:5,
                 from src/esphome.h:15,
                 from src/main.cpp:3:
src/esphome/components/i2c/i2c.h:78:8: note: candidate: 'bool esphome::i2c::I2CDevice::read_bytes(uint8_t, uint8_t*, uint8_t)'
   bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) {
        ^~~~~~~~~~
src/esphome/components/i2c/i2c.h:78:8: note:   candidate expects 3 arguments, 4 provided
src/esphome/components/i2c/i2c.h:83:55: note: candidate: 'template<unsigned int N> esphome::optional<std::array<unsigned char, N> > esphome::i2c::I2CDevice::read_bytes(uint8_t)'
   template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) {
                                                       ^~~~~~~~~~
src/esphome/components/i2c/i2c.h:83:55: note:   template argument deduction/substitution failed:
In file included from src/main.cpp:338:
src/arduino_port_expander.h:134:97: note:   candidate expects 1 argument, 4 provided
       if (this->read_bytes(APE_CMD_DIGITAL_READ, const_cast<uint8_t *>(this->read_buffer_), 3, 1))
                                                                                                 ^
src/arduino_port_expander.h:198:96: error: no matching function for call to 'ArduinoPortExpander::read_bytes(int, uint8_t*, int, int)'
     if (!this->read_bytes(APE_CMD_DIGITAL_READ, const_cast<uint8_t *>(this->read_buffer_), 3, 1))
                                                                                                ^
In file included from src/esphome/components/bh1750/bh1750.h:5,
                 from src/esphome.h:15,
                 from src/main.cpp:3:
src/esphome/components/i2c/i2c.h:78:8: note: candidate: 'bool esphome::i2c::I2CDevice::read_bytes(uint8_t, uint8_t*, uint8_t)'
   bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) {
        ^~~~~~~~~~
src/esphome/components/i2c/i2c.h:78:8: note:   candidate expects 3 arguments, 4 provided
src/esphome/components/i2c/i2c.h:83:55: note: candidate: 'template<unsigned int N> esphome::optional<std::array<unsigned char, N> > esphome::i2c::I2CDevice::read_bytes(uint8_t)'
   template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) {
                                                       ^~~~~~~~~~
src/esphome/components/i2c/i2c.h:83:55: note:   template argument deduction/substitution failed:
In file included from src/main.cpp:338:
src/arduino_port_expander.h:198:96: note:   candidate expects 1 argument, 4 provided
     if (!this->read_bytes(APE_CMD_DIGITAL_READ, const_cast<uint8_t *>(this->read_buffer_), 3, 1))
                                                                                                ^
src/arduino_port_expander.h: In member function 'uint16_t ArduinoPortExpander::analogRead(uint8_t)':
src/arduino_port_expander.h:233:116: error: no matching function for call to 'ArduinoPortExpander::read_bytes(uint8_t, uint8_t*, int, int)'
     bool ok = this->read_bytes((uint8_t)(CMD_ANALOG_READ_A0 + pin), const_cast<uint8_t *>(this->read_buffer_), 2, 1);
                                                                                                                    ^
In file included from src/esphome/components/bh1750/bh1750.h:5,
                 from src/esphome.h:15,
                 from src/main.cpp:3:
src/esphome/components/i2c/i2c.h:78:8: note: candidate: 'bool esphome::i2c::I2CDevice::read_bytes(uint8_t, uint8_t*, uint8_t)'
   bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) {
        ^~~~~~~~~~
src/esphome/components/i2c/i2c.h:78:8: note:   candidate expects 3 arguments, 4 provided
src/esphome/components/i2c/i2c.h:83:55: note: candidate: 'template<unsigned int N> esphome::optional<std::array<unsigned char, N> > esphome::i2c::I2CDevice::read_bytes(uint8_t)'
   template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) {
                                                       ^~~~~~~~~~
src/esphome/components/i2c/i2c.h:83:55: note:   template argument deduction/substitution failed:
In file included from src/main.cpp:338:
src/arduino_port_expander.h:233:116: note:   candidate expects 1 argument, 4 provided
     bool ok = this->read_bytes((uint8_t)(CMD_ANALOG_READ_A0 + pin), const_cast<uint8_t *>(this->read_buffer_), 2, 1);
                                                                                                                    ^
/config/esphome/ghc_esp32.yaml: In lambda function:
/config/esphome/ghc_esp32.yaml:239:75: error: no matching function for call to 'ArduinoPortExpander::ArduinoPortExpander(esphome::i2c::ArduinoI2CBus*&, int, bool)'
             auto ape_component = new ArduinoPortExpander(i2c_arduino, 0x08, true);
                                                                           ^
In file included from src/main.cpp:338:
src/arduino_port_expander.h:105:7: note: candidate: 'ArduinoPortExpander::ArduinoPortExpander()'
 class ArduinoPortExpander : public Component, public I2CDevice
       ^~~~~~~~~~~~~~~~~~~
src/arduino_port_expander.h:105:7: note:   candidate expects 0 arguments, 3 provided
src/arduino_port_expander.h:105:7: note: candidate: 'ArduinoPortExpander::ArduinoPortExpander(const ArduinoPortExpander&)'
src/arduino_port_expander.h:105:7: note:   candidate expects 1 argument, 3 provided
src/arduino_port_expander.h:105:7: note: candidate: 'ArduinoPortExpander::ArduinoPortExpander(ArduinoPortExpander&&)'
src/arduino_port_expander.h:105:7: note:   candidate expects 1 argument, 3 provided
/config/esphome/ghc_esp32.yaml:240:28: error: could not convert '{ape_component}' from '<brace-enclosed initializer list>' to 'std::vector<esphome::Component*>'
             return {ape_component};
                            ^
Compiling .pioenvs/ghc-esp32/lib333/DNSServer/DNSServer.cpp.o
Compiling .pioenvs/ghc-esp32/lib915/ESPmDNS/ESPmDNS.cpp.o
Compiling .pioenvs/ghc-esp32/lib4fc/Wire/Wire.cpp.o
Compiling .pioenvs/ghc-esp32/FrameworkArduino/Esp.cpp.o
*** [.pioenvs/ghc-esp32/src/main.cpp.o] Error 1
========================= [FAILED] Took 14.65 seconds =========================

#####################################
I will post the YAML separately because adding it here exceeds the size limit of a post.
######################################
Any suggestion are greatly appreciated. Any test, reading suggestions, and advise would be appreciated very much, Doug

This is the YAML that fails to compile with compile log in last post.

################################################################################
# File = /home/dbasberg/Documents/Platformio/Projects/ghc_esp32/ghc_esp32.yaml
#
#  This ESP32 Do-It MAC: XX-XX-XX-XX-XX-XX is on 10.0.1.51 and this will be
#  mounted on a post in the greenhouse near a 120vac outlet - it provides
#  control of greenhouse system.
#
# CHANGE LOG:
#
# 04/23/2020 DSB -- started writing this program
#
# 04/26/2020 DSB -- used gedit on Linux PC to input this file for use on 10.0.1.100
#       RPI4 using command line esphome tools
# 04/27/2020 DSB -- move pin assignments to 'substitutions'
#
#  05/09/2020 DSB -- does not compile w/ error in lambda in script (early end of
#    block) -- fixed # comments not C compatible removed - review naming errors
#
# 05/12/2020 DSB -- cleaning up compile errors in quest for binary file to
#    flash on ESP32 chip -- got "VALID" ready for compile.
#  05/14/2020 DSB -- compile says mqtt payload must be a string, so convert all
#   numeric data to strings - I have decided to use mqtt.publish_json instead
#   so that numerical values are sent in jason key-value format.
# 05/15/2020 DSB -- 'btn_state' becomes global variable used with toggle binary
#	sensor.
# 05/15/2020 DSB -- file currently in hassio and providing node-red output on
#      gh/esp32/# mqtt topic
# 05/19/20 DSB -- BME280 address is 0x77 - edit on Acer and compile on linuxPC.
#
##########################
#  08/01/2020 DSB -- change program name to ghc_esp32.yaml and develop a greenhouse
#  controller using an Arduino DCCduino Nano as a port expander which provides
#  enough I/O to handle the vents, analog moisture sensors and sensor requiring
#  5vdc logic (ultrasonic distance sensors for vent control).
#
#  08/03/2020 DSB -- convert i/o from previous program to support NANO expanded
#           I/O in espHome.
#   08/04/2020 DSB -- adding moisture sensors and irrigation and update
#      ultrasonic
#   08/04/2020 DSB -- 8:52pm first version that compiles [print out]
#  08/13/2020 DSB -- first OTA upload of S/W -- 
#  08/16/2020 DSB -- arduino A0=0, A1=1, etc.
#  12/01/2021 DSB -- cleanup indents
########
#  04/13/22 DSB -- does not compile due to arduino_port_expander.h problem -
#       investigate problem is arduino_port_expander file too old?
#
## 03/20/23 DSB -- device name dash
#
## 11/14/23 DSB -- ArduinoPortExpander is still creating compile errors
#
#
##############################################################################
##

substitutions:
# values for specific devices
    device_name: ghc-esp32
    friendly_name: greenhouse_control
    ip_address: !secret ghc_esp32_ip
    my_mqtt_broker: !secret mqtt_broker


# Pin Assignments
#    pin_motion_sensor: nano_d4
#   pin_select_vent: nano_d3
#    pin_open_close_toggle: GPIO18
    pin_dist_ceiling_trigger: GPIO25
    pin_dist_ceiling_echo: GPIO26
    pin_dist_floor_trigger: GPIO32
    pin_dist_floor_echo: GPIO33
#    pin_select_vent_in: GPIO15
#    pin_vent_fan: Sonoff_POW02  
#    pin_vent_open_sw: nano_d10
#    pin_vent_close_sw: nano_d11
#    pin_vent_power: nano_d9
#    pin_water_pump: nano_d8



# CONSTANTS for Program

# Vent constants
    VENT_CEILING_CLOSED_LIMIT: "10.0" # vent closed in centimeters
    VENT_CEILING_OPEN_LIMIT: "100.0"
    VENT_FLOOR_CLOSED_LIMIT: "10.0"
    VENT_FLOOR_OPEN_LIMIT: "100.0"
    VENT_CEILING_OPEN_TEMP: "90.0"    # Farenheit to open vent
    VENT_CEILING_CLOSE_TEMP: "80.0"
    VENT_FLOOR_OPEN_TEMP: "90.0"      # floor vent open
    VENT_FLOOR_CLOSE_TEMP: "80.0"
    VENT_FAN_ON_TEMP: "95.0"          # fan on greenhouse wall
    VENT_FAN_OFF_TEMP: "85.0"
# Moisture Sensor constants
    MOISTURE_IRRIGATE: "500"
    MOISTURE_OFF: "400"
    MOISTURE_AIR: "700"
    MOISTURE_WATER: "300"

##############################################################
## GLOBAL VARIABLES
##############################################################
#
globals:

    -   id: watering_count
        type: int
        restore_value: no
        initial_value: "0"
        


    -   id: btn_state   # state of vent_op_btn toggling
        type: int
        restore_value: no
        initial_value: "0"



esphome:
  name: ghc-esp32
  includes:
    - arduino_port_expander.h 
  #  - arduino_port_expander.c

    

esp32:
  board: esp32doit-devkit-v1
  framework:
    type: arduino



wifi:
    ssid: !secret wifi_ssid
    password: !secret wifi_pw
    manual_ip:
        static_ip: !secret gh_esp32_ip
        gateway: !secret wifi_gateway
        subnet: !secret wifi_subnet
        dns1: !secret wifi_dns1
#        dns2: 8.8.8.8

# Enable fallback hotspot (captive portal) in case wifi connection fails
    ap:
        ssid: "Ghc Esp32 Fallback Hotspot"
        password: "3othynlAefNJ"
        
web_server:
    port: 80

captive_portal:

# Enable logging
#logger:
#    baud_rate: 0

# Enable Home Assistant API
api:
  password: "Aa234567@"

ota:
    password: "Aa234567@"



#############################################
#  MQTT for HA communication
#############################################
#
mqtt:
    broker: !secret mqtt_broker
    username: !secret mqtt_user
    password: !secret mqtt_pw
    topic_prefix: ghc/esp32
    log_topic: !secret ghc_esp32_log



##############################################
###  Time  ###
##############################################
#
time:
    -   platform: sntp
        id: sntp_time
        on_time:
            # every minute
            -   seconds: 5
                minutes: /1
                then:
                    -   if:
                            condition:
                                -   switch.is_on: watering_pump
                            then:
                                -   lambda: |-
                                        id(watering_count) += 1;
            # every 10 minutes
            -   seconds: 10
                minutes: /10
                then:
                    -   mqtt.publish_json:
                            topic: "watering_since_sunday"
                            payload: |-
                                root["state"] = id(watering_count);
            # at Midnight
            #Sunday only (once a week)
            -   seconds: 2
                minutes: 0
                hours: 0
                days_of_week: 1  
                # minutes of watering for week
                then:
                    -   lambda: |-
                            id(watering_count) = 0; 
                            
##############################################




##############################################
##  I2C bus ###
##############################################
#
i2c:
    -   id: i2c_local
        sda: 13
        scl: 16
        scan: True
    -   id: i2c_arduino
        sda: 14
        scl: 15
        scan: True

    
custom_component:
    -   id: ape
        lambda: |-
            auto ape_component = new ArduinoPortExpander(i2c_arduino, 0x08, true);
            return {ape_component};
            
################################################
#  BINARY OUTPUTS
#################################################
#
output:
    -   platform: custom
        type: binary
        lambda: |-
            return {ape_binary_output(ape, 3),
                    ape_binary_output(ape, 8),
                    ape_binary_output(ape, 9),
                    ape_binary_output(ape, 10),
                    ape_binary_output(ape, 11),
                    ape_binary_output(ape, 12),
                    ape_binary_output(ape, 13)
                    };
                    
        outputs:
            -   id: vent_select
            -   id: ghc_relay_1
                inverted: true
            -   id: ghc_relay_2
                inverted: true
            -   id: r_pwm_vents
            -   id: l_pwm_vents
            -   id: ghc_relay_3
                inverted: true
            -   id: ghc_relay_4
                inverted: true


##############################################
##  BINARY SENSORS
##############################################
#
binary_sensor:

    -   platform: custom 
        lambda: |-
            return {ape_binary_sensor(ape, 7),
                ape_binary_sensor(ape, 4),
                ape_binary_sensor(ape, 5)
                };
        binary_sensors:
            -   id: ghc_motion
                name: greenhouse_motion
                device_class: motion
                # Vent Selection Switch Input where 1=ceiling & 0=floor
            -   id: vent_sel_btn
                name: vent_select_button
                internal: true  # do not show on HA


            -   id: vent_op_btn
                name: vent_operation_button
                internal:   true  #do not show on HA
                on_press:
                    then:
                    # Button toggle state: 0=close, 1=stop, 2=open, 3=stop & loop
                        -   if:
                                condition:
                                    -   lambda: return (id(btn_state) > 3);
                                then:
                                    -   lambda: |-
                                            id(btn_state) = 0;
                        -   if:
                                condition:
                                    -   lambda: return (id(btn_state) == 3);
                                then:
                                    -   script.execute: stop_all_moving_vents_script
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(btn_state) == 2);
                                        -   lambda: return (id(vent_sel_btn).state ==1);
                                then:
                                    -   script.execute: vent_ceiling_open_script
                                
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(btn_state) == 2);
                                        -   lambda: return (id(vent_sel_btn).state == 0);
                                then:
                                    -   script.execute: vent_floor_open_script
                                    
                        -   if:
                                condition:
                                    -   lambda: return (id(btn_state) == 1);
                                then:
                                    -   script.execute: stop_all_moving_vents_script
                
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(btn_state) == 0);
                                        -   lambda: return (id(vent_sel_btn).state == 1);
                                then:
                                    -   script.execute: vent_ceiling_close_script
                                    
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(btn_state) == 0);
                                        -   lambda: return (id(vent_sel_btn).state == 0);
                                then:
                                    -   script.execute: vent_floor_close_script

                        
                        -   lambda: |-
                                id(btn_state) += 1;
                                



                            
                        



###############################################
#### SENSORS   ################################
###############################################
#
sensor:
    -   platform: custom
        lambda: |-
            return { ape_analog_input(ape, 0),
                    ape_analog_input(ape, 1),
                    ape_analog_input(ape, 2),
                    ape_analog_input(ape, 3)
                    };
        sensors:
            -   name: Moisture_1
                id: moisture_1
                filters:
                    -   throttle: 20s
                on_value:
                    then:
                        -   mqtt.publish_json:
                                topic: "moisture_1"
                                payload: |-
                                    root["moisture"] = id(moisture_1).state;
                                    root["percent"] = 100 * (id(moisture_1).state - ${MOISTURE_WATER}/${MOISTURE_AIR} - ${MOISTURE_WATER});
                                    
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_1).state > ${MOISTURE_IRRIGATE});
                                        -   switch.is_off: watering_pump
                                then:
                                    -   switch.turn_on: watering_pump
                                    -   lambda: |-
                                          id(watering_count) += 1;
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_1).state < ${MOISTURE_OFF});
                                        -   switch.is_on: watering_pump
                                then:
                                    -   switch.turn_off: watering_pump
                                
            -   name: Moisture_2
                id: moisture_2
                filters:
                    -   throttle: 20s
                on_value:
                    then:
                        -   mqtt.publish_json:
                                topic: "moisture_2"
                                payload: |-
                                    root["moisture"] = id(moisture_2).state;
                                    root["percent"] = 100 * (id(moisture_2).state - ${MOISTURE_WATER}/${MOISTURE_AIR} - ${MOISTURE_WATER});
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_2).state > ${MOISTURE_IRRIGATE});
                                        -   switch.is_off: watering_pump
                                then:
                                    -   switch.turn_on: watering_pump
                                    -   lambda: |-
                                            id(watering_count) += 1;
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_2).state < ${MOISTURE_OFF});
                                        -   switch.is_on: watering_pump
                                then:
                                    -   switch.turn_off: watering_pump
            -   name: Moisture_3
                id: moisture_3
                filters:
                    -   throttle: 20s
                on_value:
                    then:
                        -   mqtt.publish_json:
                                topic: "moisture_3"
                                payload: |-
                                    root["moisture"] = id(moisture_3).state;
                                    root["percent"] = 100 * (id(moisture_3).state - ${MOISTURE_WATER}/${MOISTURE_AIR} - ${MOISTURE_WATER});
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_3).state > ${MOISTURE_IRRIGATE});
                                        -   switch.is_off: watering_pump
                                then:
                                    -   switch.turn_on: watering_pump
                                    -   lambda: |-
                                            id(watering_count) += 1;
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_3).state < ${MOISTURE_OFF});
                                        -   switch.is_on: watering_pump
                                then:
                                    -   switch.turn_off: watering_pump
            -   name: Moisture_4
                id: moisture_4
                filters:
                    -   throttle: 20s
                on_value:
                    then:
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_4).state > ${MOISTURE_IRRIGATE});
                                        -   switch.is_off: watering_pump
                                then:
                                    -   switch.turn_on: watering_pump
                                    -   lambda: |-
                                            id(watering_count) += 1;
                        -   if:
                                condition:
                                    and:
                                        -   lambda: return (id(moisture_4).state < ${MOISTURE_OFF});
                                        -   switch.is_on: watering_pump
                                then:
                                    -   switch.turn_off: watering_pump
                        -   mqtt.publish_json:
                                topic: "moisture_4"
                                payload: |-
                                    root["moisture"] = id(moisture_4).state;
                                    root["percent"] = 100 * (id(moisture_4).state - ${MOISTURE_WATER}/${MOISTURE_AIR} - ${MOISTURE_WATER});

####################################
#  Template sensors
####################################
#
#    # Vent State map: 0=stopped, 1=closed, 2=opening, 3=open, 4=closing
    -   platform: template
        name: "Vent Ceiling State"
        id: vent_ceiling_state
        on_value:
            then:
                -   mqtt.publish_json:
                        topic: "vent_ceiling_state"
                        payload: |-
                            root["state"] = id(vent_ceiling_state).state; 
                            
    -   platform: template
        name: "Vent Floor State"
        id: vent_floor_state
        on_value:
            then:
                
                -   mqtt.publish_json:
                        topic: "vent_floor_state"
                        payload: |-
                            root["state"] = id(vent_floor_state).state; 
                            
    -   platform: template
        name: "Vent Fan State"
        id: vent_fan_state
        on_value:
            then:
                -   mqtt.publish_json:
                        topic: "vent_fan_state"
                        payload: |-
                            root["state"] = id(vent_fan_state).state;

#################################
# Ultrasonic Distance Sensors
#################################
#

########
# Distance sensor for Ceiling Vent
########
    -   platform: ultrasonic
        trigger_pin: ${pin_dist_ceiling_trigger}
        echo_pin: ${pin_dist_ceiling_echo}
        name: "Distance Ceiling Vent"
        update_interval: 3s
        id: distance_ceiling_vent
        on_value:
            then:
                
                -   mqtt.publish_json:
                        topic: "distance_ceiling_vent"
                        payload: |-
                            root["state"] = id(distance_ceiling_vent).state; 
        on_value_range:
            -   below: ${VENT_CEILING_CLOSED_LIMIT}
                then:
# Closed, so stop vents
                    -   script.execute: stop_all_moving_vents_script
                    -   sensor.template.publish:
                            id: vent_ceiling_state
                            state: 1
                    -   delay: 2s
                    -   switch.turn_off: vent_power_sw
        
            -   above: ${VENT_CEILING_OPEN_LIMIT}
                then:
# Fully Open, so stop vent
                    -   script.execute: stop_all_moving_vents_script
                    -   sensor.template.publish:
                            id: vent_ceiling_state
                            state: 3

########
# Distance sensor for Floor vents
########
    -   platform: ultrasonic
        trigger_pin: ${pin_dist_floor_trigger}
        echo_pin: ${pin_dist_floor_echo}
        name: "Distance Floor Vent"
        update_interval: 3s
        id: distance_floor_vent
        on_value:
            then:
                
                -   mqtt.publish_json:
                        topic: "distance_floor_vent"
                        payload: |-
                            root["state"] = id(distance_floor_vent).state;
        on_value_range:
            -   below: ${VENT_FLOOR_CLOSED_LIMIT}
                then:
# Closed, so stop vents
                    -   script.execute: stop_all_moving_vents_script
                    -   sensor.template.publish:
                            id: vent_floor_state
                            state: 1
                    -   delay: 2s
                    -   switch.turn_off: vent_power_sw

            -   above: ${VENT_FLOOR_OPEN_LIMIT}
                then:
# Fully Open, so stop vent motor
                    -   script.execute: stop_all_moving_vents_script
                    -   sensor.template.publish:
                            id: vent_floor_state
                            state: 3

############################################################
# BME280 Temperature, Humidity, and Pressure Sensor on I2C
############################################################
#
    -   platform: bme280
        i2c_id: i2c_local
        address: 0x76
        update_interval: 30s
        temperature:
            name: "GHC BME280 Temperature"
            oversampling: 16x
            id: ghc_bme280_temp
            unit_of_measurement: "F"
            icon: "mdi:temperature-fahrenheit"
            on_value:
                then:
                    
                    -   mqtt.publish_json:
                            topic: "ghc_bme280_temp"
                            payload: |-
                                root["temp"] = id(ghc_bme280_temp).state * (9.0/5.0) + 32.0;
                                
                    -   if:
                            condition:
                                and:
                                    -   lambda: return (id(ghc_bme280_temp).state > ${VENT_CEILING_OPEN_TEMP});
                                    -   lambda: return id(vent_ceiling_state).state < 3;
# Too Hot, then open ceiling vent
                            then:
                                -   script.execute: vent_ceiling_open_script
                    -   if:
                            condition:
                                and:
                                    -   lambda: return (id(ghc_bme280_temp).state < ${VENT_CEILING_CLOSE_TEMP});
                                    -   lambda: return id(vent_ceiling_state).state > 1;
# Too cold, then close ceiling vent
                            then:
                                -   script.execute: vent_ceiling_close_script

                    -   if:
                            condition:
                                and:
                                    -   lambda: return (id(ghc_bme280_temp).state > ${VENT_FLOOR_OPEN_TEMP});
                                    -   lambda: return id(vent_floor_state).state < 3;
# Too Hot, then open floor vent
                            then:
                                -   script.execute: vent_floor_open_script

                    -   if:
                            condition:
                                and:
                                    -   lambda: return (id(ghc_bme280_temp).state < ${VENT_FLOOR_CLOSE_TEMP});
                                    -   lambda: return id(vent_floor_state).state > 1;
# Too cold, then close floor vents
                            then:
                                -   script.execute: vent_floor_close_script

# Too Hot, then turn on vent fan (mqtt to HA for POW02 vent fan sw)
                    -   if:
                            condition:
                                and:
                                    -   lambda: return (id(ghc_bme280_temp).state > ${VENT_FAN_ON_TEMP});
                                    -   lambda: return (id(vent_fan_state).state == 0);
                            then:
                            # turn on vent fan thru HA POW02
                                -   lambda: |-
                                        id(vent_fan_state).state = 1;  

# Too cold, then turn off vent fan
                    -   if:
                            condition:
                                and:
                                    -   lambda: return (id(ghc_bme280_temp).state < ${VENT_FAN_OFF_TEMP});
                                    -   lambda: return (id(vent_fan_state).state == 1);
                            then:
                            # turn off vent fan thru HA POW02
                                -   lambda: |-
                                        id(vent_fan_state).state = 0;  
        humidity:
            name: "GHC BME280 Humidity"
            oversampling: 16x
            id: ghc_bme280_humidity
            unit_of_measurement: "%"
            icon: "mdi:water-percent"
            on_value:
                then:
                    -   mqtt.publish_json:
                            topic: "ghc_bme280_humidity"
                            payload: |-
                                root["humidity"] = id(ghc_bme280_humidity).state;
            

        pressure:
            name: "GHC BME280 Pressure"
            oversampling: 16x
            id: ghc_bme280_pressure
            unit_of_measurement: "hPa"
            icon: "mdi:waves"
            on_value:
                then:
                    
                    -   mqtt.publish_json:
                            topic: "ghc_bme280_pressure"
                            payload: |-
                                root["pressure"] = id(ghc_bme280_pressure).state;
                                




#####################################
# bh1750 light level sensor on I2C ##
#####################################
#
    -   platform: bh1750
        name: "GHC Light Sensor"
        i2c_id: i2c_local
        id: ghc_light_sensor
        address: 0x23
        update_interval: 30s
        unit_of_measurement: "lumens"
        icon: "mdi:lightbulb-on"
        on_value:
            then:
                
                -   mqtt.publish_json:
                            topic: "ghc_bh1750_light_sensor"
                            payload: |-
                                root["light"] = id(ghc_light_sensor).state;
                                
###################################
# Light Color Sensor on local I2C #
###################################
#
    -   platform: tcs34725
        i2c_id: i2c_local
        address: 0x29
        update_interval: 60s
        red_channel:
            name: "TCS Red"
        green_channel:
            name: "TCS Green"
        blue_channel:
            name: "TCS_Blue"
        clear_channel:
            name: "TCS_Clear"
        illuminance:
            name: "TCS_Illum"
        color_temperature:
            name: "TCS_Color_Temp"
        gain: 1x
        integration_time: 2.4ms
        

##########################
# Switches
##########################
#
switch:
    -   platform: output
        name: watering_pump
        id: watering_pump
        output: ghc_relay_1
    -   platform: output
        name: vent_power_sw
        id: vent_power_sw
        output: ghc_relay_2
    -   platform: output
        name: circulation_fan
        id: circ_fan
        output: ghc_relay_3
    -   platform: output
        name: relay_4
        id: relay4
        output: ghc_relay_4
    -   platform: output
        name: vent_open_sw
        id: vent_open_sw
        output: r_pwm_vents
    -   platform: output
        name: vent_close_sw
        id: vent_close_sw
        output: l_pwm_vents
    -   platform: output
        name: vent_motor_select
        id: vent_motor_select # 0=floor & 1=ceiling
        output: vent_select


################################################################
## SCRIPTS   ###################################################
################################################################
#
script:
    -   id: vent_ceiling_open_script
        then:
            -   switch.turn_off: vent_close_sw
            -   switch.turn_off: vent_open_sw
            -   switch.turn_on: vent_power_sw
            -   delay: 5s
            -   switch.turn_on: vent_motor_select   # 1=ceiling
            -   switch.turn_on: vent_open_sw
            -   sensor.template.publish:
                    id: vent_ceiling_state
                    state: 2
    -   id: vent_ceiling_close_script
        then:
            -   switch.turn_off: vent_open_sw
            -   switch.turn_off: vent_close_sw
            -   switch.turn_on: vent_power_sw
            -   delay: 5s
            -   switch.turn_on: vent_motor_select  # 1=ceiling 0=floor
            -   switch.turn_on: vent_close_sw
            -   sensor.template.publish:
                    id: vent_ceiling_state
                    state: 4

    -   id: vent_floor_open_script
        then: 
            -   switch.turn_off: vent_close_sw
            -   switch.turn_off: vent_open_sw
            -   switch.turn_on: vent_power_sw
            -   delay: 5s
            -   switch.turn_off: vent_motor_select  # 0=floor
            -   switch.turn_on: vent_open_sw
            -   sensor.template.publish:
                    id: vent_floor_state
                    state: 2

    -   id: vent_floor_close_script
        then:
            -   switch.turn_off: vent_open_sw
            -   switch.turn_off: vent_close_sw
            -   switch.turn_on: vent_power_sw
            -   delay: 5s
            -   switch.turn_off: vent_motor_select  # 0=floor
            -   switch.turn_on: vent_close_sw
            -   sensor.template.publish:
                    id: vent_floor_state
                    state: 4

    -   id: stop_all_moving_vents_script
        then:
            -   switch.turn_off: vent_open_sw
            -   switch.turn_off: vent_close_sw
            -   switch.turn_off: vent_power_sw
            -   delay: 1s
            -   sensor.template.publish:
                    id: vent_ceiling_state
                    state: 0
            -   sensor.template.publish:
                    id: vent_floor_state
                    state: 0



# ############################  End of Scripts  #############################

#############################################

THANKS FOR ANY HELP/SUGGESTIONS, Doug

#############################################

crazy question... what directory did you put the arduino_port_expander.h file?

1 Like

After updating ESP Home, I cannot compile the Sketch Arduino expander.


I don’t know what has changed, I rarely update ESP Home because it annoys me to upload a new build to each ESP every week. Now I have updated ESP Home and unfortunately Expander doesn’t want to update due to some compilation errors. Do you see something I don’t?

substitutions:
  device_name: esp-rozdzielnia
  friendly_name: esp32-rozdzielnia
  device_description: "Sterownik oƛwietlenia w domu"
  #min_press: 400ms
  #max_press: 2000ms
  
esphome:
  name: esp32-rozdzielnia
  includes: 
    - arduino_port_expander.h
  
esp8266:
  board: d1_mini

i2c:
  sda: 4
  scl: 5
  scan: True
  id: i2c_component
  #frequency: 50kHz
  
# define the port expander hub, here we define one with id 'expander1',
# but you can define many

custom_component:
  - id: expander1
    lambda: |-
      auto expander = new ArduinoPortExpander(i2c_component, 0x08, true);
      return {expander};
  
# Logowanie www
web_server:
  port: 80
  
#Ustawienia wifi
wifi:
  ssid: 
  password: 
  
# Enable logging
logger:
  #level: DEBUG
  level: INFO

# Enable Home Assistant API
api:

ota:
  platform: esphome

text_sensor:
  - platform: template
    name: uptime ${device_name}
    id: uptime_human
    icon: mdi:clock-start

sensor:
#Uptime urządzeni
  - platform: uptime
    name: Uptime Sensor
    id: uptime_sensor
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
              int seconds = round(id(uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? to_string(days) + "d " : "") +
                (hours ? to_string(hours) + "h " : "") +
                (minutes ? to_string(minutes) + "m " : "") +
                (to_string(seconds) + "s")
              ).c_str();


captive_portal:

switch:
  - platform: restart
    name: "Restart esp32-rozdzielnia"
    
#Konfiguracja przekaĆșnikĂłw    

# define binary outputs, here we have 4, as the relays are inverse logic
# (a path to ground turns the relay ON), we defined the inverted: true
# option of ESPHome outputs.
output:
- platform: custom
  type: binary
  lambda: |-
    return {ape_binary_output(expander1, 22),
            ape_binary_output(expander1, 23),
            ape_binary_output(expander1, 24),
            ape_binary_output(expander1, 25),
            ape_binary_output(expander1, 26),
            ape_binary_output(expander1, 28),
            ape_binary_output(expander1, 30),
            ape_binary_output(expander1, 32),
            ape_binary_output(expander1, 34),
            ape_binary_output(expander1, 35),
            ape_binary_output(expander1, 36),
            ape_binary_output(expander1, 38),
            ape_binary_output(expander1, 39),
            ape_binary_output(expander1, 40),
            ape_binary_output(expander1, 42),
            ape_binary_output(expander1, 44),
            ape_binary_output(expander1, 45)};

  outputs:
    - id: kuchnia
      inverted: False
    - id: kuchnia_blat
      inverted: False
    - id: korytarz_parter
      inverted: False
    - id: lazienka_parter
      inverted: False
    - id: lazienka_pietro
      inverted: False
    - id: pralnia
      inverted: False
    - id: jadalnia
      inverted: False
    - id: schody
      inverted: False
    - id: ganek
      inverted: False
    - id: wiatrolap
      inverted: False
    - id: salon
      inverted: False
    - id: szafa
      inverted: False
    - id: bojler
      inverted: False
    - id: szafki_kuchnia
      inverted: False
    - id: taras
      inverted: False
    - id: garaz
      inverted: False
    - id: pokoj_parter
      inverted: False

# connect lights to the relays
light:
  - platform: binary
    id: kuchnia1
    name: kuchnia
    output: kuchnia
  - platform: binary
    id: kuchnia_blat1
    name: kuchnia_blat
    output: kuchnia_blat
  - platform: binary
    id: korytarz_parter1
    name: korytarz_parter
    output: korytarz_parter
  - platform: binary
    id: lazienka_parter1
    name: lazienka_parter
    output: lazienka_parter
  - platform: binary
    id: lazienka_pietro1
    name: lazienka_pietro
    output: lazienka_pietro
  - platform: binary
    id: pralnia1
    name: pralnia
    output: pralnia
  - platform: binary
    id: jadalnia1
    name: jadalnia
    output: jadalnia
  - platform: binary
    id: schody1
    name: schody
    output: schody
  - platform: binary
    id: ganek1
    name: ganek
    output: ganek
  - platform: binary
    id: wiatrolap1
    name: wiatrolap
    output: wiatrolap
  - platform: binary
    id: salon1
    name: salon
    output: salon
  - platform: binary
    id: szafa1
    name: szafa
    output: szafa
  - platform: binary
    id: bojler1
    name: bojler
    output: bojler
  - platform: binary
    id: szafki_kuchnia1
    name: szafki_kuchnia
    output: szafki_kuchnia
  - platform: binary
    id: taras1
    name: taras
    output: taras
  - platform: binary
    id: garaz1
    name: garaz
    output: garaz
  - platform: binary
    id: pokoj_parter1
    name: pokoj_parter
    output: pokoj_parter

# define binary sensors, use the Arduino PIN number for digital pins (0 0 .. 13 13 .. 53 53) and
# for analog use 54 for A0, 55 for A1 and so on...
binary_sensor:
  - platform: custom
    lambda: |-
      return {ape_binary_sensor(expander1, 4),
              ape_binary_sensor(expander1, 5),
              ape_binary_sensor(expander1, 6),
              ape_binary_sensor(expander1, 7),
              ape_binary_sensor(expander1, 8),
              ape_binary_sensor(expander1, 10),
              ape_binary_sensor(expander1, 54),
              ape_binary_sensor(expander1, 56), 
              ape_binary_sensor(expander1, 58),
              ape_binary_sensor(expander1, 59),
              ape_binary_sensor(expander1, 60),
              ape_binary_sensor(expander1, 62),
              ape_binary_sensor(expander1, 63),
              ape_binary_sensor(expander1, 64),
              ape_binary_sensor(expander1, 66),
              ape_binary_sensor(expander1, 68),
              ape_binary_sensor(expander1, 69)};

    binary_sensors:
      - id: kuchnia2
        internal: true # don't show on HA
        on_press:
          - light.toggle: kuchnia1
      - id: kuchnia_blat2
        internal: true
        on_press:
          - light.toggle: kuchnia_blat1
      - id: korytarz_parter2
        internal: true
        on_press:
          - light.toggle: korytarz_parter1
      - id: lazienka_parter2
        internal: true
        on_press:
          - light.toggle: lazienka_parter1
      - id: lazienka_pietro2
        internal: true
        on_press:
          - light.toggle: lazienka_pietro1
      - id: pralnia2
        internal: true
        on_press:
          - light.toggle: pralnia1
      - id: jadalnia2
        internal: true
        on_press:
          - light.toggle: jadalnia1
      - id: schody2
        internal: true
        on_press:
          - light.toggle: schody1
      - id: ganek2
        internal: true
        on_press:
          - light.toggle: ganek1
      - id: wiatrolap2
        internal: true
        on_press:
          - light.toggle: wiatrolap1
      - id: salon2
        internal: true
        on_press:
          - light.toggle: salon1
      - id: szafa2
        internal: true
        on_press:
          - light.toggle: szafa1
      - id: bojler2
        internal: true
        on_press:
          - light.toggle: bojler1
      - id: szafki_kuchnia2
        internal: true
        on_press:
          - light.toggle: szafki_kuchnia1
      - id: taras2
        internal: true
        on_press:
          - light.toggle: taras1
      - id: garaz2
        internal: true
        on_press:
          - light.toggle: garaz1
      - id: pokoj_parter2
        internal: true
        on_press:
          - light.toggle: pokoj_parter1

Custom components aren’t supported anymore in ESPHome 2025.x. You can bring back support using this: GitHub - robertklep/esphome-custom-component: Brings back support for custom ESPHome components

1 Like

Working, very thanks:)

1 Like