From ac0d921413c3884752193fe568fa82853f0f99e9 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 11:47:51 +0200 Subject: [PATCH] ESP-IDF support and generic target platforms (#2303) * Socket refactor and SSL * esp-idf temp * Fixes * Echo component and noise * Add noise API transport support * Updates * ESP-IDF * Complete * Fixes * Fixes * Versions update * New i2c APIs * Complete i2c refactor * SPI migration * Revert ESP Preferences migration, too complex for now * OTA support * Remove echo again * Remove ssl again * GPIOFlags updates * Rename esphal and ICACHE_RAM_ATTR * Make ESP32 arduino compilable again * Fix GPIO flags * Complete pin registry refactor and fixes * Fixes to make test1 compile * Remove sdkconfig file * Ignore sdkconfig file * Fixes in reviewing * Make test2 compile * Make test4 compile * Make test5 compile * Run clang-format * Fix lint errors * Use esp-idf APIs instead of btStart * Another round of fixes * Start implementing ESP8266 * Make test3 compile * Guard esp8266 code * Lint * Reformat * Fixes * Fixes v2 * more fixes * ESP-IDF tidy target * Convert ARDUINO_ARCH_ESPxx * Update WiFiSignalSensor * Update time ifdefs * OTA needs millis from hal * RestartSwitch needs delay from hal * ESP-IDF Uart * Fix OTA blank password * Allow setting sdkconfig * Fix idf partitions and allow setting sdkconfig from yaml * Re-add read/write compat APIs and fix esp8266 uart * Fix esp8266 store log strings in flash * Fix ESP32 arduino preferences not initialized * Update ifdefs * Change how sdkconfig change is detected * Add checks to ci-custom and fix them * Run clang-format * Add esp-idf clang-tidy target and fix errors * Fixes from clang-tidy idf round 2 * Fixes from compiling tests with esp-idf * Run clang-format * Switch test5.yaml to esp-idf * Implement ESP8266 Preferences * Lint * Re-do PIO package version selection a bit * Fix arduinoespressif32 package version * Fix unit tests * Lint * Lint fixes * Fix readv/writev not defined * Fix graphing component * Re-add all old options from core/config.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +- .gitignore | 2 + CODEOWNERS | 3 + esphome/__main__.py | 2 +- esphome/codegen.py | 3 + esphome/components/a4988/a4988.h | 2 +- esphome/components/ac_dimmer/ac_dimmer.cpp | 47 +- esphome/components/ac_dimmer/ac_dimmer.h | 20 +- esphome/components/ac_dimmer/output.py | 25 +- esphome/components/adc/adc_sensor.cpp | 29 +- esphome/components/adc/adc_sensor.h | 14 +- esphome/components/adc/sensor.py | 37 +- esphome/components/ade7953/ade7953.cpp | 33 +- esphome/components/ade7953/ade7953.h | 75 +- esphome/components/ade7953/sensor.py | 5 +- esphome/components/ads1115/ads1115.cpp | 3 +- esphome/components/aht10/aht10.cpp | 44 +- .../airthings_ble/airthings_listener.cpp | 2 +- .../airthings_ble/airthings_listener.h | 3 +- .../airthings_wave_plus.cpp | 4 +- .../airthings_wave_plus/airthings_wave_plus.h | 4 +- .../components/airthings_wave_plus/sensor.py | 6 +- esphome/components/am2320/am2320.cpp | 3 +- esphome/components/am43/am43.cpp | 3 +- esphome/components/am43/am43.h | 2 +- esphome/components/am43/cover/am43_cover.cpp | 2 +- esphome/components/am43/cover/am43_cover.h | 2 +- esphome/components/anova/anova.cpp | 2 +- esphome/components/anova/anova.h | 2 +- esphome/components/apds9960/apds9960.cpp | 1 + esphome/components/api/api_connection.cpp | 5 +- esphome/components/api/api_frame_helper.cpp | 1 + esphome/components/api/api_pb2.cpp | 1 + esphome/components/api/api_server.cpp | 4 +- esphome/components/api/proto.h | 1 + esphome/components/as3935/as3935.h | 2 +- esphome/components/as3935_i2c/as3935_i2c.cpp | 8 +- esphome/components/async_tcp/__init__.py | 6 + .../atc_mithermometer/atc_mithermometer.cpp | 2 +- .../atc_mithermometer/atc_mithermometer.h | 2 +- esphome/components/b_parasite/b_parasite.cpp | 4 +- esphome/components/b_parasite/b_parasite.h | 4 +- .../bang_bang/bang_bang_climate.cpp | 3 +- esphome/components/bh1750/bh1750.cpp | 3 +- esphome/components/binary_sensor/automation.h | 1 + esphome/components/ble_client/automation.h | 2 +- esphome/components/ble_client/ble_client.cpp | 2 +- esphome/components/ble_client/ble_client.h | 2 +- .../components/ble_client/sensor/automation.h | 2 +- .../ble_client/sensor/ble_sensor.cpp | 2 +- .../components/ble_client/sensor/ble_sensor.h | 2 +- .../ble_client/switch/ble_switch.cpp | 2 +- .../components/ble_client/switch/ble_switch.h | 2 +- .../ble_presence/ble_presence_device.cpp | 2 +- .../ble_presence/ble_presence_device.h | 2 +- .../components/ble_rssi/ble_rssi_sensor.cpp | 2 +- esphome/components/ble_rssi/ble_rssi_sensor.h | 2 +- .../components/ble_scanner/ble_scanner.cpp | 2 +- esphome/components/ble_scanner/ble_scanner.h | 2 +- esphome/components/bme280/bme280.cpp | 2 +- esphome/components/bme680/bme680.cpp | 1 + .../components/bme680_bsec/bme680_bsec.cpp | 2 +- esphome/components/bmp280/bmp280.cpp | 2 +- esphome/components/captive_portal/__init__.py | 19 +- .../captive_portal/captive_portal.cpp | 14 +- .../captive_portal/captive_portal.h | 4 + esphome/components/ccs811/ccs811.cpp | 5 +- esphome/components/climate/climate.cpp | 16 +- esphome/components/climate_ir/climate_ir.cpp | 2 +- esphome/components/cover/cover.cpp | 2 +- .../components/ct_clamp/ct_clamp_sensor.cpp | 2 +- esphome/components/ct_clamp/ct_clamp_sensor.h | 2 +- .../components/dallas/dallas_component.cpp | 2 +- esphome/components/dallas/esp_one_wire.cpp | 38 +- esphome/components/dallas/esp_one_wire.h | 3 +- esphome/components/debug/debug_component.cpp | 32 +- esphome/components/deep_sleep/__init__.py | 2 +- .../deep_sleep/deep_sleep_component.cpp | 30 +- .../deep_sleep/deep_sleep_component.h | 16 +- esphome/components/demo/demo_sensor.h | 2 +- esphome/components/dht/dht.cpp | 6 +- esphome/components/dht/dht.h | 6 +- esphome/components/display/display_buffer.cpp | 23 +- esphome/components/display/display_buffer.h | 1 + esphome/components/dsmr/__init__.py | 19 +- esphome/components/dsmr/dsmr.cpp | 7 +- esphome/components/dsmr/dsmr.h | 4 + .../duty_cycle/duty_cycle_sensor.cpp | 6 +- .../components/duty_cycle/duty_cycle_sensor.h | 8 +- esphome/components/duty_cycle/sensor.py | 4 +- esphome/components/e131/__init__.py | 17 +- esphome/components/e131/e131.cpp | 8 +- esphome/components/e131/e131.h | 4 + .../e131/e131_addressable_light_effect.cpp | 4 + .../e131/e131_addressable_light_effect.h | 4 + esphome/components/e131/e131_packet.cpp | 14 +- esphome/components/endstop/endstop_cover.cpp | 1 + esphome/components/esp32/__init__.py | 379 ++++++++ esphome/{ => components/esp32}/boards.py | 225 ----- esphome/components/esp32/const.py | 21 + esphome/components/esp32/core.cpp | 89 ++ esphome/components/esp32/gpio.py | 201 ++++ esphome/components/esp32/gpio_arduino.cpp | 107 +++ esphome/components/esp32/gpio_arduino.h | 36 + esphome/components/esp32/gpio_idf.cpp | 49 + esphome/components/esp32/gpio_idf.h | 96 ++ esphome/components/esp32/preferences.cpp | 99 ++ esphome/components/esp32/preferences.h | 12 + esphome/components/esp32_ble/__init__.py | 9 +- esphome/components/esp32_ble/ble.cpp | 36 +- esphome/components/esp32_ble/ble.h | 2 +- .../components/esp32_ble/ble_advertising.cpp | 5 +- .../components/esp32_ble/ble_advertising.h | 2 +- esphome/components/esp32_ble/ble_uuid.cpp | 6 +- esphome/components/esp32_ble/ble_uuid.h | 4 +- esphome/components/esp32_ble/queue.h | 8 +- .../components/esp32_ble_beacon/__init__.py | 9 +- .../esp32_ble_beacon/esp32_ble_beacon.cpp | 33 +- .../esp32_ble_beacon/esp32_ble_beacon.h | 2 +- .../components/esp32_ble_server/__init__.py | 9 +- .../components/esp32_ble_server/ble_2901.cpp | 2 +- .../components/esp32_ble_server/ble_2901.h | 2 +- .../components/esp32_ble_server/ble_2902.cpp | 4 +- .../components/esp32_ble_server/ble_2902.h | 2 +- .../esp32_ble_server/ble_characteristic.cpp | 2 +- .../esp32_ble_server/ble_characteristic.h | 4 +- .../esp32_ble_server/ble_descriptor.cpp | 5 +- .../esp32_ble_server/ble_descriptor.h | 2 +- .../esp32_ble_server/ble_server.cpp | 2 +- .../components/esp32_ble_server/ble_server.h | 2 +- .../esp32_ble_server/ble_service.cpp | 2 +- .../components/esp32_ble_server/ble_service.h | 2 +- .../components/esp32_ble_tracker/__init__.py | 8 +- .../components/esp32_ble_tracker/automation.h | 2 +- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 41 +- .../esp32_ble_tracker/esp32_ble_tracker.h | 2 +- esphome/components/esp32_ble_tracker/queue.h | 7 +- esphome/components/esp32_camera/__init__.py | 31 +- .../components/esp32_camera/esp32_camera.cpp | 7 +- .../components/esp32_camera/esp32_camera.h | 4 +- esphome/components/esp32_dac/esp32_dac.cpp | 19 +- esphome/components/esp32_dac/esp32_dac.h | 8 +- esphome/components/esp32_dac/output.py | 4 +- esphome/components/esp32_hall/esp32_hall.cpp | 10 +- esphome/components/esp32_hall/esp32_hall.h | 2 +- esphome/components/esp32_hall/sensor.py | 3 +- esphome/components/esp32_improv/__init__.py | 5 +- .../esp32_improv/esp32_improv_component.cpp | 2 +- .../esp32_improv/esp32_improv_component.h | 2 +- esphome/components/esp32_touch/__init__.py | 3 +- .../components/esp32_touch/binary_sensor.py | 8 +- .../components/esp32_touch/esp32_touch.cpp | 5 +- esphome/components/esp32_touch/esp32_touch.h | 9 +- esphome/components/esp8266/__init__.py | 213 +++++ esphome/components/esp8266/boards.py | 266 ++++++ esphome/components/esp8266/const.py | 8 + esphome/components/esp8266/core.cpp | 37 + esphome/components/esp8266/gpio.cpp | 96 ++ esphome/components/esp8266/gpio.h | 38 + esphome/components/esp8266/gpio.py | 170 ++++ esphome/components/esp8266/preferences.cpp | 263 +++++ esphome/components/esp8266/preferences.h | 14 + .../components/esp8266_pwm/esp8266_pwm.cpp | 4 +- esphome/components/esp8266_pwm/esp8266_pwm.h | 8 +- esphome/components/esp8266_pwm/output.py | 3 +- esphome/components/ethernet/__init__.py | 20 +- .../ethernet/ethernet_component.cpp | 24 +- .../components/ethernet/ethernet_component.h | 21 +- .../exposure_notifications.cpp | 2 +- .../exposure_notifications.h | 2 +- esphome/components/ezo/ezo.cpp | 5 +- esphome/components/fan/fan_state.cpp | 2 +- .../components/fastled_base/fastled_light.cpp | 4 + .../components/fastled_base/fastled_light.h | 4 + esphome/components/fastled_clockless/light.py | 3 +- esphome/components/fastled_spi/light.py | 17 +- .../fingerprint_grow/fingerprint_grow.cpp | 2 +- esphome/components/globals/__init__.py | 12 +- .../components/globals/globals_component.h | 40 +- .../gpio/binary_sensor/gpio_binary_sensor.h | 2 +- .../gpio/output/gpio_binary_output.h | 2 +- esphome/components/gpio/switch/gpio_switch.h | 2 +- esphome/components/gps/__init__.py | 5 +- esphome/components/gps/gps.cpp | 4 + esphome/components/gps/gps.h | 4 + esphome/components/gps/time/gps_time.cpp | 4 + esphome/components/gps/time/gps_time.h | 4 + esphome/components/graph/graph.cpp | 30 +- esphome/components/hdc1080/hdc1080.cpp | 17 +- esphome/components/hlw8012/hlw8012.h | 10 +- esphome/components/hlw8012/sensor.py | 8 +- .../hm3301/abstract_aqi_calculator.h | 5 + esphome/components/hm3301/aqi_calculator.h | 4 + .../hm3301/aqi_calculator_factory.h | 4 + esphome/components/hm3301/caqi_calculator.h | 4 + esphome/components/hm3301/hm3301.cpp | 4 + esphome/components/hm3301/hm3301.h | 4 + esphome/components/hm3301/sensor.py | 1 + esphome/components/http_request/__init__.py | 50 +- .../components/http_request/http_request.cpp | 10 +- .../components/http_request/http_request.h | 10 +- esphome/components/htu21d/htu21d.cpp | 17 +- esphome/components/hx711/hx711.h | 2 +- esphome/components/i2c/__init__.py | 71 +- esphome/components/i2c/i2c.cpp | 291 +----- esphome/components/i2c/i2c.h | 284 ++---- esphome/components/i2c/i2c_bus.h | 46 + esphome/components/i2c/i2c_bus_arduino.cpp | 97 ++ esphome/components/i2c/i2c_bus_arduino.h | 37 + esphome/components/i2c/i2c_bus_esp_idf.cpp | 147 +++ esphome/components/i2c/i2c_bus_esp_idf.h | 41 + .../components/ili9341/ili9341_display.cpp | 5 +- esphome/components/improv/improv.cpp | 2 + esphome/components/improv/improv.h | 5 + esphome/components/ina219/ina219.cpp | 10 +- esphome/components/ina226/ina226.cpp | 10 +- esphome/components/ina3221/ina3221.cpp | 5 +- .../inkbird_ibsth1_mini.cpp | 6 +- .../inkbird_ibsth1_mini/inkbird_ibsth1_mini.h | 2 +- esphome/components/inkplate6/display.py | 5 +- esphome/components/inkplate6/inkplate.cpp | 162 ++-- esphome/components/inkplate6/inkplate.h | 67 +- .../integration/integration_sensor.cpp | 3 +- .../integration/integration_sensor.h | 1 + esphome/components/json/__init__.py | 6 + esphome/components/json/json_util.cpp | 4 + esphome/components/json/json_util.h | 4 + esphome/components/lcd_base/lcd_display.cpp | 1 + .../components/lcd_gpio/gpio_lcd_display.cpp | 11 +- .../components/lcd_gpio/gpio_lcd_display.h | 2 +- .../lcd_pcf8574/pcf8574_display.cpp | 1 + esphome/components/ledc/ledc_output.cpp | 107 ++- esphome/components/ledc/ledc_output.h | 8 +- esphome/components/ledc/output.py | 3 +- esphome/components/light/light_state.cpp | 2 +- esphome/components/light/light_transformer.h | 3 + esphome/components/logger/__init__.py | 2 +- esphome/components/logger/logger.cpp | 74 +- esphome/components/logger/logger.h | 27 +- esphome/components/max31856/max31856.cpp | 4 +- esphome/components/max31856/max31856.h | 4 +- esphome/components/max31865/max31865.cpp | 6 +- esphome/components/max31865/max31865.h | 6 +- esphome/components/max7219/max7219.cpp | 3 +- .../components/max7219digit/max7219digit.cpp | 3 +- .../components/max7219digit/max7219digit.h | 4 +- esphome/components/max7219digit/max7219font.h | 4 +- esphome/components/mcp23008/mcp23008.h | 2 +- esphome/components/mcp23016/__init__.py | 61 +- esphome/components/mcp23016/mcp23016.cpp | 27 +- esphome/components/mcp23016/mcp23016.h | 23 +- esphome/components/mcp23017/mcp23017.h | 2 +- esphome/components/mcp23s08/mcp23s08.h | 2 +- esphome/components/mcp23s17/mcp23s17.h | 2 +- .../mcp23x08_base/mcp23x08_base.cpp | 22 +- .../components/mcp23x08_base/mcp23x08_base.h | 4 +- .../mcp23x17_base/mcp23x17_base.cpp | 22 +- .../components/mcp23x17_base/mcp23x17_base.h | 4 +- esphome/components/mcp23xxx_base/__init__.py | 79 +- .../mcp23xxx_base/mcp23xxx_base.cpp | 15 +- .../components/mcp23xxx_base/mcp23xxx_base.h | 26 +- esphome/components/mcp3008/mcp3008.h | 2 +- esphome/components/mcp4725/mcp4725.cpp | 7 +- esphome/components/mcp9808/mcp9808.cpp | 10 +- esphome/components/mdns/__init__.py | 25 + esphome/components/mdns/mdns_component.cpp | 74 ++ esphome/components/mdns/mdns_component.h | 37 + .../components/mdns/mdns_esp32_arduino.cpp | 27 + esphome/components/mdns/mdns_esp8266.cpp | 32 + esphome/components/mdns/mdns_esp_idf.cpp | 52 + esphome/components/midea/adapter.cpp | 4 + esphome/components/midea/adapter.h | 5 + esphome/components/midea/air_conditioner.cpp | 4 + esphome/components/midea/air_conditioner.h | 5 + esphome/components/midea/appliance_base.h | 14 +- esphome/components/midea/automations.h | 5 + esphome/components/midea/climate.py | 3 +- esphome/components/midea/midea_ir.h | 3 + esphome/components/mpr121/mpr121.cpp | 1 + esphome/components/mqtt/__init__.py | 1 + .../components/mqtt/custom_mqtt_device.cpp | 5 + esphome/components/mqtt/custom_mqtt_device.h | 5 + .../components/mqtt/mqtt_binary_sensor.cpp | 2 + esphome/components/mqtt/mqtt_binary_sensor.h | 3 +- esphome/components/mqtt/mqtt_client.cpp | 44 +- esphome/components/mqtt/mqtt_client.h | 12 +- esphome/components/mqtt/mqtt_climate.cpp | 4 +- esphome/components/mqtt/mqtt_climate.h | 2 + esphome/components/mqtt/mqtt_component.cpp | 5 + esphome/components/mqtt/mqtt_component.h | 6 + esphome/components/mqtt/mqtt_cover.cpp | 2 + esphome/components/mqtt/mqtt_cover.h | 2 + esphome/components/mqtt/mqtt_fan.cpp | 2 + esphome/components/mqtt/mqtt_fan.h | 2 + esphome/components/mqtt/mqtt_light.cpp | 2 + esphome/components/mqtt/mqtt_light.h | 2 + esphome/components/mqtt/mqtt_number.cpp | 2 + esphome/components/mqtt/mqtt_number.h | 2 + esphome/components/mqtt/mqtt_select.cpp | 2 + esphome/components/mqtt/mqtt_select.h | 2 + esphome/components/mqtt/mqtt_sensor.cpp | 2 + esphome/components/mqtt/mqtt_sensor.h | 2 + esphome/components/mqtt/mqtt_switch.cpp | 2 + esphome/components/mqtt/mqtt_switch.h | 2 + esphome/components/mqtt/mqtt_text_sensor.cpp | 2 + esphome/components/mqtt/mqtt_text_sensor.h | 2 + .../sensor/mqtt_subscribe_sensor.cpp | 5 + .../sensor/mqtt_subscribe_sensor.h | 6 + .../mqtt_subscribe_text_sensor.cpp | 4 + .../text_sensor/mqtt_subscribe_text_sensor.h | 6 + esphome/components/ms5611/ms5611.cpp | 1 + esphome/components/my9231/my9231.h | 3 +- esphome/components/neopixelbus/light.py | 7 +- .../neopixelbus/neopixelbus_light.h | 6 +- esphome/components/network/__init__.py | 12 +- esphome/components/network/ip_address.h | 45 + esphome/components/network/util.cpp | 54 ++ esphome/components/network/util.h | 16 + esphome/components/nextion/display.py | 4 +- esphome/components/nextion/nextion.h | 19 +- esphome/components/nextion/nextion_upload.cpp | 32 +- .../nextion/sensor/nextion_sensor.cpp | 2 +- esphome/components/ntc/ntc.cpp | 2 +- esphome/components/number/automation.cpp | 10 +- esphome/components/number/automation.h | 4 +- esphome/components/number/number.cpp | 2 +- esphome/components/ota/__init__.py | 18 +- esphome/components/ota/ota_component.cpp | 432 ++++++--- esphome/components/ota/ota_component.h | 27 +- esphome/components/pca9685/pca9685_output.h | 4 +- esphome/components/pcf8574/__init__.py | 61 +- esphome/components/pcf8574/pcf8574.cpp | 36 +- esphome/components/pcf8574/pcf8574.h | 23 +- esphome/components/pid/pid_autotuner.cpp | 2 +- esphome/components/pid/pid_climate.cpp | 2 +- esphome/components/pid/pid_controller.h | 6 +- esphome/components/pipsolar/pipsolar.cpp | 2 +- esphome/components/pipsolar/pipsolar.h | 2 +- esphome/components/pmsa003i/pmsa003i.cpp | 1 + esphome/components/pn532/pn532.cpp | 1 + esphome/components/pn532_i2c/pn532_i2c.cpp | 3 +- .../components/power_supply/power_supply.h | 2 +- .../prometheus/prometheus_handler.cpp | 8 +- .../prometheus/prometheus_handler.h | 4 + .../pulse_counter/pulse_counter_sensor.cpp | 14 +- .../pulse_counter/pulse_counter_sensor.h | 24 +- .../pulse_meter/pulse_meter_sensor.cpp | 6 +- .../pulse_meter/pulse_meter_sensor.h | 8 +- .../components/pulse_width/pulse_width.cpp | 4 +- esphome/components/pulse_width/pulse_width.h | 12 +- esphome/components/pulse_width/sensor.py | 4 +- .../pvvx_mithermometer/pvvx_mithermometer.cpp | 2 +- .../pvvx_mithermometer/pvvx_mithermometer.h | 2 +- esphome/components/qmc5883l/qmc5883l.cpp | 7 +- esphome/components/rc522/rc522.cpp | 10 +- esphome/components/rc522/rc522.h | 2 +- esphome/components/rc522_i2c/rc522_i2c.cpp | 5 +- esphome/components/remote_base/__init__.py | 7 +- .../components/remote_base/midea_protocol.h | 7 +- .../components/remote_base/remote_base.cpp | 2 +- esphome/components/remote_base/remote_base.h | 14 +- .../remote_base/samsung_protocol.cpp | 1 + .../remote_base/toshiba_ac_protocol.cpp | 1 + .../components/remote_receiver/__init__.py | 4 +- .../remote_receiver/remote_receiver.h | 16 +- .../remote_receiver/remote_receiver_esp32.cpp | 2 +- .../remote_receiver_esp8266.cpp | 10 +- .../remote_transmitter/remote_transmitter.h | 8 +- .../remote_transmitter_esp32.cpp | 2 +- .../remote_transmitter_esp8266.cpp | 2 +- .../resistance/resistance_sensor.cpp | 2 +- esphome/components/restart/restart_switch.cpp | 1 + .../rotary_encoder/rotary_encoder.cpp | 23 +- .../rotary_encoder/rotary_encoder.h | 14 +- esphome/components/rotary_encoder/sensor.py | 8 +- esphome/components/ruuvi_ble/ruuvi_ble.cpp | 2 +- esphome/components/ruuvi_ble/ruuvi_ble.h | 2 +- esphome/components/ruuvitag/ruuvitag.cpp | 2 +- esphome/components/ruuvitag/ruuvitag.h | 2 +- esphome/components/scd30/scd30.cpp | 17 +- esphome/components/sdp3x/sdp3x.cpp | 14 +- esphome/components/sensor/automation.h | 14 +- esphome/components/sensor/filter.cpp | 19 +- esphome/components/servo/servo.cpp | 1 + esphome/components/servo/servo.h | 2 +- esphome/components/sgp30/sgp30.cpp | 9 +- esphome/components/sgp40/sgp40.cpp | 15 +- esphome/components/sht3xd/sht3xd.cpp | 2 +- esphome/components/sht4x/sht4x.cpp | 4 +- esphome/components/shtcx/shtcx.cpp | 3 +- .../components/shutdown/shutdown_switch.cpp | 11 +- esphome/components/slow_pwm/slow_pwm_output.h | 2 +- esphome/components/sm16716/sm16716.h | 3 +- esphome/components/sm2135/sm2135.h | 3 +- esphome/components/sn74hc595/__init__.py | 33 +- esphome/components/sn74hc595/sn74hc595.cpp | 26 +- esphome/components/sn74hc595/sn74hc595.h | 16 +- esphome/components/sntp/sntp_component.cpp | 8 +- .../components/socket/bsd_sockets_impl.cpp | 13 +- esphome/components/socket/headers.h | 8 +- .../components/socket/lwip_raw_tcp_impl.cpp | 3 +- esphome/components/spi/spi.cpp | 73 +- esphome/components/spi/spi.h | 43 +- esphome/components/sps30/sps30.cpp | 2 +- .../components/ssd1306_base/ssd1306_base.h | 2 +- .../components/ssd1306_i2c/ssd1306_i2c.cpp | 4 +- .../components/ssd1322_base/ssd1322_base.h | 2 +- .../components/ssd1325_base/ssd1325_base.h | 2 +- .../components/ssd1327_base/ssd1327_base.h | 2 +- .../components/ssd1327_i2c/ssd1327_i2c.cpp | 4 +- .../components/ssd1331_base/ssd1331_base.h | 2 +- .../components/ssd1351_base/ssd1351_base.h | 2 +- esphome/components/st7735/st7735.cpp | 17 +- .../status/status_binary_sensor.cpp | 4 +- .../status_led/light/status_led_light.h | 2 +- esphome/components/status_led/status_led.h | 2 +- esphome/components/stepper/stepper.cpp | 1 + esphome/components/sts3x/sts3x.cpp | 2 +- esphome/components/sun/sun.h | 4 +- esphome/components/switch/switch.cpp | 2 +- esphome/components/sx1509/__init__.py | 68 +- .../sx1509/output/sx1509_float_output.cpp | 3 +- esphome/components/sx1509/sx1509.cpp | 51 +- esphome/components/sx1509/sx1509.h | 22 +- esphome/components/sx1509/sx1509_gpio_pin.cpp | 13 +- esphome/components/sx1509/sx1509_gpio_pin.h | 13 +- esphome/components/tca9548a/__init__.py | 37 +- esphome/components/tca9548a/tca9548a.cpp | 43 +- esphome/components/tca9548a/tca9548a.h | 24 +- esphome/components/tcs34725/tcs34725.cpp | 1 + .../template/number/template_number.cpp | 4 +- .../template/select/template_select.cpp | 2 +- .../template/sensor/template_sensor.cpp | 3 +- .../thermostat/thermostat_climate.cpp | 14 +- esphome/components/time/automation.cpp | 1 + esphome/components/time/real_time_clock.cpp | 2 +- .../time_based/time_based_cover.cpp | 1 + .../components/tlc59208f/tlc59208f_output.cpp | 2 +- .../components/tlc59208f/tlc59208f_output.h | 1 + esphome/components/tlc5947/tlc5947.h | 3 +- esphome/components/tm1637/tm1637.cpp | 29 +- esphome/components/tm1637/tm1637.h | 2 +- esphome/components/tm1651/__init__.py | 15 +- esphome/components/tm1651/tm1651.cpp | 4 + esphome/components/tm1651/tm1651.h | 14 +- esphome/components/tmp102/tmp102.cpp | 9 +- .../components/tof10120/tof10120_sensor.cpp | 8 +- esphome/components/toshiba/toshiba.cpp | 2 +- .../total_daily_energy/total_daily_energy.cpp | 4 +- .../total_daily_energy/total_daily_energy.h | 1 + esphome/components/tsl2591/tsl2591.cpp | 3 +- esphome/components/ttp229_bsf/ttp229_bsf.h | 2 +- esphome/components/ttp229_lsf/ttp229_lsf.cpp | 6 +- .../components/tuya/climate/tuya_climate.cpp | 2 +- esphome/components/tuya/tuya.cpp | 5 +- esphome/components/tx20/sensor.py | 4 +- esphome/components/tx20/tx20.cpp | 8 +- esphome/components/tx20/tx20.h | 8 +- esphome/components/uart/__init__.py | 45 +- esphome/components/uart/uart.cpp | 50 +- esphome/components/uart/uart.h | 147 +-- esphome/components/uart/uart_component.cpp | 24 + esphome/components/uart/uart_component.h | 67 ++ ...2.cpp => uart_component_esp32_arduino.cpp} | 103 +- .../uart/uart_component_esp32_arduino.h | 40 + ...esp8266.cpp => uart_component_esp8266.cpp} | 163 ++-- .../components/uart/uart_component_esp8266.h | 79 ++ .../uart/uart_component_esp_idf.cpp | 201 ++++ .../components/uart/uart_component_esp_idf.h | 39 + esphome/components/uln2003/uln2003.h | 2 +- .../ultrasonic/ultrasonic_sensor.cpp | 20 +- .../components/ultrasonic/ultrasonic_sensor.h | 7 +- esphome/components/uptime/uptime_sensor.cpp | 1 + esphome/components/vl53l0x/vl53l0x_sensor.cpp | 6 +- esphome/components/vl53l0x/vl53l0x_sensor.h | 2 +- esphome/components/web_server/web_server.cpp | 7 +- esphome/components/web_server/web_server.h | 4 + .../web_server_base/web_server_base.cpp | 12 +- .../web_server_base/web_server_base.h | 4 + esphome/components/wifi/__init__.py | 23 +- esphome/components/wifi/wifi_component.cpp | 81 +- esphome/components/wifi/wifi_component.h | 57 +- ...2.cpp => wifi_component_esp32_arduino.cpp} | 80 +- .../wifi/wifi_component_esp8266.cpp | 69 +- .../wifi/wifi_component_esp_idf.cpp | 901 ++++++++++++++++++ .../wifi_info/wifi_info_text_sensor.h | 18 +- .../wifi_signal/wifi_signal_sensor.h | 2 +- esphome/components/wled/__init__.py | 2 +- esphome/components/wled/wled_light_effect.cpp | 8 +- esphome/components/wled/wled_light_effect.h | 4 + esphome/components/xiaomi_ble/xiaomi_ble.cpp | 2 +- esphome/components/xiaomi_ble/xiaomi_ble.h | 2 +- .../components/xiaomi_cgd1/xiaomi_cgd1.cpp | 2 +- esphome/components/xiaomi_cgd1/xiaomi_cgd1.h | 2 +- .../components/xiaomi_cgdk2/xiaomi_cgdk2.cpp | 2 +- .../components/xiaomi_cgdk2/xiaomi_cgdk2.h | 2 +- .../components/xiaomi_cgg1/xiaomi_cgg1.cpp | 2 +- esphome/components/xiaomi_cgg1/xiaomi_cgg1.h | 2 +- .../components/xiaomi_cgpr1/xiaomi_cgpr1.cpp | 2 +- .../components/xiaomi_cgpr1/xiaomi_cgpr1.h | 2 +- .../xiaomi_gcls002/xiaomi_gcls002.cpp | 2 +- .../xiaomi_gcls002/xiaomi_gcls002.h | 2 +- .../xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp | 2 +- .../xiaomi_hhccjcy01/xiaomi_hhccjcy01.h | 2 +- .../xiaomi_hhccpot002/xiaomi_hhccpot002.cpp | 2 +- .../xiaomi_hhccpot002/xiaomi_hhccpot002.h | 2 +- .../xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp | 2 +- .../xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h | 2 +- .../xiaomi_lywsd02/xiaomi_lywsd02.cpp | 2 +- .../xiaomi_lywsd02/xiaomi_lywsd02.h | 2 +- .../xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp | 2 +- .../xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h | 2 +- .../xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp | 2 +- .../xiaomi_lywsdcgq/xiaomi_lywsdcgq.h | 2 +- .../xiaomi_mhoc401/xiaomi_mhoc401.cpp | 2 +- .../xiaomi_mhoc401/xiaomi_mhoc401.h | 2 +- .../xiaomi_miscale/xiaomi_miscale.cpp | 2 +- .../xiaomi_miscale/xiaomi_miscale.h | 2 +- .../xiaomi_miscale2/xiaomi_miscale2.cpp | 2 +- .../xiaomi_miscale2/xiaomi_miscale2.h | 2 +- .../xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp | 2 +- .../xiaomi_mjyd02yla/xiaomi_mjyd02yla.h | 2 +- .../xiaomi_mue4094rt/xiaomi_mue4094rt.cpp | 2 +- .../xiaomi_mue4094rt/xiaomi_mue4094rt.h | 2 +- .../xiaomi_wx08zm/xiaomi_wx08zm.cpp | 2 +- .../components/xiaomi_wx08zm/xiaomi_wx08zm.h | 2 +- esphome/components/zyaura/sensor.py | 8 +- esphome/components/zyaura/zyaura.cpp | 14 +- esphome/components/zyaura/zyaura.h | 16 +- esphome/config.py | 724 ++++++++------ esphome/config_validation.py | 127 ++- esphome/const.py | 32 +- esphome/core/__init__.py | 82 +- esphome/core/application.cpp | 21 +- esphome/core/application.h | 1 - esphome/core/application_esp32.cpp | 21 - esphome/core/application_esp8266.cpp | 14 - esphome/core/component.cpp | 4 +- esphome/core/component.h | 2 +- esphome/core/config.py | 229 ++--- esphome/core/defines.h | 43 +- esphome/core/esphal.cpp | 303 ------ esphome/core/esphal.h | 128 --- esphome/core/gpio.h | 98 ++ esphome/core/hal.h | 47 + esphome/core/helpers.cpp | 52 +- esphome/core/helpers.h | 12 +- esphome/core/log.cpp | 2 +- esphome/core/log.h | 12 +- esphome/core/macros.h | 4 +- esphome/core/preferences.cpp | 303 ------ esphome/core/preferences.h | 127 +-- esphome/core/scheduler.cpp | 3 +- esphome/core/util.cpp | 105 -- esphome/core/util.h | 17 - esphome/cpp_generator.py | 4 + esphome/cpp_helpers.py | 15 +- esphome/cpp_types.py | 4 +- esphome/dashboard/dashboard.py | 10 +- esphome/final_validate.py | 24 - esphome/helpers.py | 12 +- esphome/loader.py | 47 +- esphome/pins.py | 443 +++------ esphome/platformio_api.py | 4 +- esphome/storage_json.py | 31 +- esphome/wizard.py | 16 +- esphome/writer.py | 114 +-- platformio.ini | 77 +- requirements.txt | 4 + script/ci-custom.py | 27 +- script/clang-tidy | 9 +- script/helpers.py | 25 +- .../binary_sensor/test_binary_sensor.py | 2 +- tests/dummy_main.cpp | 6 - tests/test1.yaml | 72 +- tests/test2.yaml | 6 +- tests/test3.yaml | 2 +- tests/test4.yaml | 19 +- tests/test5.yaml | 24 +- tests/unit_tests/test_core.py | 4 +- tests/unit_tests/test_cpp_helpers.py | 10 - tests/unit_tests/test_pins.py | 346 ------- tests/unit_tests/test_wizard.py | 2 +- 583 files changed, 9008 insertions(+), 5420 deletions(-) create mode 100644 esphome/components/esp32/__init__.py rename esphome/{ => components/esp32}/boards.py (77%) create mode 100644 esphome/components/esp32/const.py create mode 100644 esphome/components/esp32/core.cpp create mode 100644 esphome/components/esp32/gpio.py create mode 100644 esphome/components/esp32/gpio_arduino.cpp create mode 100644 esphome/components/esp32/gpio_arduino.h create mode 100644 esphome/components/esp32/gpio_idf.cpp create mode 100644 esphome/components/esp32/gpio_idf.h create mode 100644 esphome/components/esp32/preferences.cpp create mode 100644 esphome/components/esp32/preferences.h create mode 100644 esphome/components/esp8266/__init__.py create mode 100644 esphome/components/esp8266/boards.py create mode 100644 esphome/components/esp8266/const.py create mode 100644 esphome/components/esp8266/core.cpp create mode 100644 esphome/components/esp8266/gpio.cpp create mode 100644 esphome/components/esp8266/gpio.h create mode 100644 esphome/components/esp8266/gpio.py create mode 100644 esphome/components/esp8266/preferences.cpp create mode 100644 esphome/components/esp8266/preferences.h create mode 100644 esphome/components/i2c/i2c_bus.h create mode 100644 esphome/components/i2c/i2c_bus_arduino.cpp create mode 100644 esphome/components/i2c/i2c_bus_arduino.h create mode 100644 esphome/components/i2c/i2c_bus_esp_idf.cpp create mode 100644 esphome/components/i2c/i2c_bus_esp_idf.h create mode 100644 esphome/components/mdns/__init__.py create mode 100644 esphome/components/mdns/mdns_component.cpp create mode 100644 esphome/components/mdns/mdns_component.h create mode 100644 esphome/components/mdns/mdns_esp32_arduino.cpp create mode 100644 esphome/components/mdns/mdns_esp8266.cpp create mode 100644 esphome/components/mdns/mdns_esp_idf.cpp create mode 100644 esphome/components/network/ip_address.h create mode 100644 esphome/components/network/util.cpp create mode 100644 esphome/components/network/util.h create mode 100644 esphome/components/uart/uart_component.cpp create mode 100644 esphome/components/uart/uart_component.h rename esphome/components/uart/{uart_esp32.cpp => uart_component_esp32_arduino.cpp} (64%) create mode 100644 esphome/components/uart/uart_component_esp32_arduino.h rename esphome/components/uart/{uart_esp8266.cpp => uart_component_esp8266.cpp} (60%) create mode 100644 esphome/components/uart/uart_component_esp8266.h create mode 100644 esphome/components/uart/uart_component_esp_idf.cpp create mode 100644 esphome/components/uart/uart_component_esp_idf.h rename esphome/components/wifi/{wifi_component_esp32.cpp => wifi_component_esp32_arduino.cpp} (89%) create mode 100644 esphome/components/wifi/wifi_component_esp_idf.cpp delete mode 100644 esphome/core/application_esp32.cpp delete mode 100644 esphome/core/application_esp8266.cpp delete mode 100644 esphome/core/esphal.cpp delete mode 100644 esphome/core/esphal.h create mode 100644 esphome/core/gpio.h create mode 100644 esphome/core/hal.h delete mode 100644 esphome/core/preferences.cpp delete mode 100644 tests/unit_tests/test_pins.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 875db0f9fd..c2c434a9b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: name: Run script/clang-format - id: clang-tidy name: Run script/clang-tidy for ESP8266 - options: --environment esp8266-tidy --grep ARDUINO_ARCH_ESP8266 + options: --environment esp8266-tidy --grep USE_ESP8266 pio_cache_key: tidyesp8266 - id: clang-tidy name: Run script/clang-tidy for ESP32 1/4 @@ -64,6 +64,10 @@ jobs: name: Run script/clang-tidy for ESP32 4/4 options: --environment esp32-tidy --split-num 4 --split-at 4 pio_cache_key: tidyesp32 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 esp-idf + options: --environment esp32-idf-tidy --grep USE_ESP_IDF + pio_cache_key: tidyesp32-idf steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index cd7822bded..c52909f7c9 100644 --- a/.gitignore +++ b/.gitignore @@ -124,3 +124,5 @@ tests/.esphome/ /.temp-clang-tidy.cpp /.temp/ .pio/ + +sdkconfig.* diff --git a/CODEOWNERS b/CODEOWNERS index ab3a0815ce..385ec4d892 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -44,9 +44,11 @@ esphome/components/dfplayer/* @glmnet esphome/components/dht/* @OttoWinter esphome/components/ds1307/* @badbadc0ffee esphome/components/dsmr/* @glmnet @zuidwijk +esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @jesserockz esphome/components/esp32_ble_server/* @jesserockz esphome/components/esp32_improv/* @jesserockz +esphome/components/esp8266/* @esphome/core esphome/components/exposure_notifications/* @OttoWinter esphome/components/ezo/* @ssieb esphome/components/fastled_base/* @OttoWinter @@ -81,6 +83,7 @@ esphome/components/mcp23x17_base/* @jesserockz esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn +esphome/components/mdns/* @esphome/core esphome/components/midea/* @dudanov esphome/components/mitsubishi/* @RubyBailey esphome/components/network/* @esphome/core diff --git a/esphome/__main__.py b/esphome/__main__.py index ba1019869d..6bc7e065ce 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -245,7 +245,7 @@ def upload_program(config, args, host): ota_conf = config[CONF_OTA] remote_port = ota_conf[CONF_PORT] - password = ota_conf[CONF_PASSWORD] + password = ota_conf.get(CONF_PASSWORD, "") return espota2.run_ota(host, remote_port, password, CORE.firmware_bin) diff --git a/esphome/codegen.py b/esphome/codegen.py index c05cc5efca..1b38fd9ed2 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -30,6 +30,7 @@ from esphome.cpp_generator import ( # noqa add_library, add_build_flag, add_define, + add_platformio_option, get_variable, get_variable_with_full_id, process_lambda, @@ -78,4 +79,6 @@ from esphome.cpp_types import ( # noqa JsonObjectConstRef, Controller, GPIOPin, + InternalGPIOPin, + gpio_Flags, ) diff --git a/esphome/components/a4988/a4988.h b/esphome/components/a4988/a4988.h index 5be0f3ce69..0fe7891110 100644 --- a/esphome/components/a4988/a4988.h +++ b/esphome/components/a4988/a4988.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/stepper/stepper.h" namespace esphome { diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index e86703bfe1..a7f1e6f3a9 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -1,10 +1,16 @@ +#ifdef USE_ARDUINO + #include "ac_dimmer.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #endif +#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#include +#endif namespace esphome { namespace ac_dimmer { @@ -25,7 +31,7 @@ static const uint32_t GATE_ENABLE_TIME = 50; /// Function called from timer interrupt /// Input is current time in microseconds (micros()) /// Returns when next "event" is expected in µs, or 0 if no such event known. -uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { +uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { // If no ZC signal received yet. if (this->crossed_zero_at == 0) return 0; @@ -37,13 +43,13 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) { this->enable_time_us = 0; - this->gate_pin->digital_write(true); + this->gate_pin.digital_write(true); // Prevent too short pulses - this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME); + this->disable_time_us = std::max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME); } if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) { this->disable_time_us = 0; - this->gate_pin->digital_write(false); + this->gate_pin.digital_write(false); } if (time_since_zc < this->enable_time_us) @@ -63,7 +69,7 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { } /// Run timer interrupt code and return in how many µs the next event is expected -uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() { +uint32_t IRAM_ATTR HOT timer_interrupt() { // run at least with 1kHz uint32_t min_dt_us = 1000; uint32_t now = micros(); @@ -80,7 +86,7 @@ uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() { } /// GPIO interrupt routine, called when ZC pin triggers -void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() { +void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() { uint32_t prev_crossed = this->crossed_zero_at; // 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms @@ -97,7 +103,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() { if (this->value == 65535) { // fully on, enable output immediately - this->gate_pin->digital_write(true); + this->gate_pin.digital_write(true); } else if (this->init_cycle) { // send a full cycle this->init_cycle = false; @@ -105,29 +111,29 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() { this->disable_time_us = cycle_time_us; } else if (this->value == 0) { // fully off, disable output immediately - this->gate_pin->digital_write(false); + this->gate_pin.digital_write(false); } else { if (this->method == DIM_METHOD_TRAILING) { this->enable_time_us = 1; // cannot be 0 - this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535); + this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535); } else { // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic // also take into account min_power auto min_us = this->cycle_time_us * this->min_power / 1000; - this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); + this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); if (this->method == DIM_METHOD_LEADING_PULSE) { // Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone // this is for brightness near 99% - this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10); + this->disable_time_us = std::max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10); } else { - this->gate_pin->digital_write(false); + this->gate_pin.digital_write(false); this->disable_time_us = this->cycle_time_us; } } } } -void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) { +void IRAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) { // Attaching pin interrupts on the same pin will override the previous interrupt // However, the user expects that multiple dimmers sharing the same ZC pin will work. // We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers @@ -141,11 +147,11 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store } } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 // ESP32 implementation, uses basically the same code but needs to wrap // timer_interrupt() function to auto-reschedule static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); } +void IRAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); } #endif void AcDimmer::setup() { @@ -174,15 +180,16 @@ void AcDimmer::setup() { if (setup_zero_cross_pin) { this->zero_cross_pin_->setup(); this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr(); - this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING); + this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, + gpio::INTERRUPT_FALLING_EDGE); } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 // Uses ESP8266 waveform (soft PWM) class // PWM and AcDimmer can even run at the same time this way setTimer1Callback(&timer_interrupt); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 // 80 Divider -> 1 count=1µs dimmer_timer = timerBegin(0, 80, true); timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); @@ -218,3 +225,5 @@ void AcDimmer::dump_config() { } // namespace ac_dimmer } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/ac_dimmer/ac_dimmer.h b/esphome/components/ac_dimmer/ac_dimmer.h index 00da061cfd..fd1bbc28db 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.h +++ b/esphome/components/ac_dimmer/ac_dimmer.h @@ -1,7 +1,9 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" namespace esphome { @@ -11,11 +13,11 @@ enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TR struct AcDimmerDataStore { /// Zero-cross pin - ISRInternalGPIOPin *zero_cross_pin; + ISRInternalGPIOPin zero_cross_pin; /// Zero-cross pin number - used to share ZC pin across multiple dimmers uint8_t zero_cross_pin_number; /// Output pin to write to - ISRInternalGPIOPin *gate_pin; + ISRInternalGPIOPin gate_pin; /// Value of the dimmer - 0 to 65535. uint16_t value; /// Minimum power for activation @@ -37,7 +39,7 @@ struct AcDimmerDataStore { void gpio_intr(); static void s_gpio_intr(AcDimmerDataStore *store); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 static void s_timer_intr(); #endif }; @@ -47,16 +49,16 @@ class AcDimmer : public output::FloatOutput, public Component { void setup() override; void dump_config() override; - void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; } - void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; } + void set_gate_pin(InternalGPIOPin *gate_pin) { gate_pin_ = gate_pin; } + void set_zero_cross_pin(InternalGPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; } void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; } void set_method(DimMethod method) { method_ = method; } protected: void write_state(float state) override; - GPIOPin *gate_pin_; - GPIOPin *zero_cross_pin_; + InternalGPIOPin *gate_pin_; + InternalGPIOPin *zero_cross_pin_; AcDimmerDataStore store_; bool init_with_half_cycle_; DimMethod method_; @@ -64,3 +66,5 @@ class AcDimmer : public output::FloatOutput, public Component { } // namespace ac_dimmer } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/ac_dimmer/output.py b/esphome/components/ac_dimmer/output.py index 2c37d325eb..c39fc382b6 100644 --- a/esphome/components/ac_dimmer/output.py +++ b/esphome/components/ac_dimmer/output.py @@ -19,17 +19,20 @@ DIM_METHODS = { CONF_GATE_PIN = "gate_pin" CONF_ZERO_CROSS_PIN = "zero_cross_pin" CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle" -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( - { - cv.Required(CONF_ID): cv.declare_id(AcDimmer), - cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, - cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, - cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum( - DIM_METHODS, upper=True, space="_" - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.All( + output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(AcDimmer), + cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, + cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum( + DIM_METHODS, upper=True, space="_" + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_arduino, +) async def to_code(config): diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 4106a0c49b..9a05c1d66b 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #ifdef USE_ADC_SENSOR_VCC +#include ADC_MODE(ADC_VCC) #endif @@ -10,7 +11,7 @@ namespace adc { static const char *const TAG = "adc"; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; } inline adc1_channel_t gpio_to_adc1(uint8_t pin) { @@ -57,28 +58,28 @@ inline adc1_channel_t gpio_to_adc1(uint8_t pin) { void ADCSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); #ifndef USE_ADC_SENSOR_VCC - GPIOPin(this->pin_, INPUT).setup(); + pin_->setup(); #endif -#ifdef ARDUINO_ARCH_ESP32 - adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_); +#ifdef USE_ESP32 + adc1_config_channel_atten(gpio_to_adc1(pin_->get_pin()), attenuation_); adc1_config_width(ADC_WIDTH_BIT_12); #if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 - adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_)); + adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_->get_pin())); #endif #endif } void ADCSensor::dump_config() { LOG_SENSOR("", "ADC Sensor", this); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #ifdef USE_ADC_SENSOR_VCC ESP_LOGCONFIG(TAG, " Pin: VCC"); #else - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); + LOG_PIN(" Pin: ", pin_); #endif #endif -#ifdef ARDUINO_ARCH_ESP32 - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); +#ifdef USE_ESP32 + LOG_PIN(" Pin: ", pin_); switch (this->attenuation_) { case ADC_ATTEN_DB_0: ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)"); @@ -105,8 +106,8 @@ void ADCSensor::update() { this->publish_state(value_v); } float ADCSensor::sample() { -#ifdef ARDUINO_ARCH_ESP32 - int raw = adc1_get_raw(gpio_to_adc1(pin_)); +#ifdef USE_ESP32 + int raw = adc1_get_raw(gpio_to_adc1(pin_->get_pin())); float value_v = raw / 4095.0f; #if CONFIG_IDF_TARGET_ESP32 switch (this->attenuation_) { @@ -146,15 +147,15 @@ float ADCSensor::sample() { return value_v; #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #ifdef USE_ADC_SENSOR_VCC return ESP.getVcc() / 1024.0f; // NOLINT(readability-static-accessed-through-instance) #else - return analogRead(this->pin_) / 1024.0f; // NOLINT + return analogRead(this->pin_->get_pin()) / 1024.0f; // NOLINT #endif #endif } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } #endif diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 4591ed758d..b8c702be4e 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -1,12 +1,12 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/defines.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "driver/adc.h" #endif @@ -15,7 +15,7 @@ namespace adc { class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { public: -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 /// Set the attenuation for this pin. Only available on the ESP32. void set_attenuation(adc_atten_t attenuation); #endif @@ -27,17 +27,17 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage void dump_config() override; /// `HARDWARE_LATE` setup priority. float get_setup_priority() const override; - void set_pin(uint8_t pin) { this->pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } float sample() override; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::string unique_id() override; #endif protected: - uint8_t pin_; + InternalGPIOPin *pin_; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 adc_atten_t attenuation_{ADC_ATTEN_DB_0}; #endif }; diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 7c32e4a923..9a0407d0f4 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -5,11 +5,13 @@ from esphome.components import sensor, voltage_sampler from esphome.const import ( CONF_ATTENUATION, CONF_ID, + CONF_INPUT, CONF_PIN, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, UNIT_VOLT, ) +from esphome.core import CORE AUTO_LOAD = ["voltage_sampler"] @@ -23,10 +25,34 @@ ATTENUATION_MODES = { def validate_adc_pin(value): - vcc = str(value).upper() - if vcc == "VCC": - return cv.only_on_esp8266(vcc) - return pins.analog_pin(value) + if str(value).upper() == "VCC": + return cv.only_on_esp8266("VCC") + + if CORE.is_esp32: + from esphome.components.esp32 import is_esp32c3 + + value = pins.internal_gpio_input_pin_number(value) + if is_esp32c3(): + if not (0 <= value <= 4): # ADC1 + raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.") + if not (32 <= value <= 39): # ADC1 + raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.") + elif CORE.is_esp8266: + from esphome.components.esp8266.gpio import CONF_ANALOG + + value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})( + value + ) + + if value != 17: # A0 + raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.") + return pins.gpio_pin_schema( + {CONF_ANALOG: True, CONF_INPUT: True}, internal=True + )(value) + else: + raise NotImplementedError + + return pins.internal_gpio_input_pin_schema(value) adc_ns = cg.esphome_ns.namespace("adc") @@ -62,7 +88,8 @@ async def to_code(config): if config[CONF_PIN] == "VCC": cg.add_define("USE_ADC_SENSOR_VCC") else: - cg.add(var.set_pin(config[CONF_PIN])) + pin = await cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) if CONF_ATTENUATION in config: cg.add(var.set_attenuation(config[CONF_ATTENUATION])) diff --git a/esphome/components/ade7953/ade7953.cpp b/esphome/components/ade7953/ade7953.cpp index 0e6d5624c1..2c61fc6a44 100644 --- a/esphome/components/ade7953/ade7953.cpp +++ b/esphome/components/ade7953/ade7953.cpp @@ -8,9 +8,7 @@ static const char *const TAG = "ade7953"; void ADE7953::dump_config() { ESP_LOGCONFIG(TAG, "ADE7953:"); - if (this->has_irq_) { - ESP_LOGCONFIG(TAG, " IRQ Pin: GPIO%u", this->irq_pin_number_); - } + LOG_PIN(" IRQ Pin: ", irq_pin_); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_); @@ -20,27 +18,28 @@ void ADE7953::dump_config() { LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_); } -#define ADE_PUBLISH_(name, factor) \ - if ((name) && this->name##_sensor_) { \ - float value = *(name) / (factor); \ +#define ADE_PUBLISH_(name, val, factor) \ + if (err == i2c::ERROR_OK && this->name##_sensor_) { \ + float value = (val) / (factor); \ this->name##_sensor_->publish_state(value); \ } -#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor) +#define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor) void ADE7953::update() { if (!this->is_setup_) return; - auto active_power_a = this->ade_read_(0x0312); - ADE_PUBLISH(active_power_a, 154.0f); - auto active_power_b = this->ade_read_(0x0313); - ADE_PUBLISH(active_power_b, 154.0f); - auto current_a = this->ade_read_(0x031A); - ADE_PUBLISH(current_a, 100000.0f); - auto current_b = this->ade_read_(0x031B); - ADE_PUBLISH(current_b, 100000.0f); - auto voltage = this->ade_read_(0x031C); - ADE_PUBLISH(voltage, 26000.0f); + uint32_t val; + i2c::ErrorCode err = ade_read_32_(0x0312, &val); + ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f); + err = ade_read_32_(0x0313, &val); + ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f); + err = ade_read_32_(0x031A, &val); + ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f); + err = ade_read_32_(0x031B, &val); + ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f); + err = ade_read_32_(0x031C, &val); + ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f); // auto apparent_power_a = this->ade_read_(0x0310); // auto apparent_power_b = this->ade_read_(0x0311); diff --git a/esphome/components/ade7953/ade7953.h b/esphome/components/ade7953/ade7953.h index 5a582a991a..c6fb383ed8 100644 --- a/esphome/components/ade7953/ade7953.h +++ b/esphome/components/ade7953/ade7953.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" #include "esphome/components/sensor/sensor.h" @@ -10,10 +10,7 @@ namespace ade7953 { class ADE7953 : public i2c::I2CDevice, public PollingComponent { public: - void set_irq_pin(uint8_t irq_pin) { - has_irq_ = true; - irq_pin_number_ = irq_pin; - } + void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; } void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; } void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; } @@ -25,15 +22,13 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent { } void setup() override { - if (this->has_irq_) { - auto pin = GPIOPin(this->irq_pin_number_, INPUT); - this->irq_pin_ = &pin; + if (this->irq_pin_ != nullptr) { this->irq_pin_->setup(); } this->set_timeout(100, [this]() { - this->ade_write_(0x0010, 0x04); - this->ade_write_(0x00FE, 0xAD); - this->ade_write_(0x0120, 0x0030); + this->ade_write_8_(0x0010, 0x04); + this->ade_write_8_(0x00FE, 0xAD); + this->ade_write_16_(0x0120, 0x0030); this->is_setup_ = true; }); } @@ -43,31 +38,51 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent { void update() override; protected: - template bool ade_write_(uint16_t reg, T value) { + i2c::ErrorCode ade_write_8_(uint16_t reg, uint8_t value) { std::vector data; data.push_back(reg >> 8); data.push_back(reg >> 0); - for (int i = sizeof(T) - 1; i >= 0; i--) - data.push_back(value >> (i * 8)); - return this->write_bytes_raw(data); + data.push_back(value); + return write(data.data(), data.size()); } - template optional ade_read_(uint16_t reg) { - uint8_t hi = reg >> 8; - uint8_t lo = reg >> 0; - if (!this->write_bytes_raw({hi, lo})) - return {}; - auto ret = this->read_bytes_raw(); - if (!ret.has_value()) - return {}; - T result = 0; - for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--) - result |= T((*ret)[i]) << (j * 8); - return result; + i2c::ErrorCode ade_write_16_(uint16_t reg, uint16_t value) { + std::vector data; + data.push_back(reg >> 8); + data.push_back(reg >> 0); + data.push_back(value >> 8); + data.push_back(value >> 0); + return write(data.data(), data.size()); + } + i2c::ErrorCode ade_write_32_(uint16_t reg, uint32_t value) { + std::vector data; + data.push_back(reg >> 8); + data.push_back(reg >> 0); + data.push_back(value >> 24); + data.push_back(value >> 16); + data.push_back(value >> 8); + data.push_back(value >> 0); + return write(data.data(), data.size()); + } + i2c::ErrorCode ade_read_32_(uint16_t reg, uint32_t *value) { + uint8_t reg_data[2]; + reg_data[0] = reg >> 8; + reg_data[1] = reg >> 0; + i2c::ErrorCode err = write(reg_data, 2); + if (err != i2c::ERROR_OK) + return err; + uint8_t recv[4]; + err = read(recv, 4); + if (err != i2c::ERROR_OK) + return err; + *value = 0; + *value |= ((uint32_t) recv[0]) << 24; + *value |= ((uint32_t) recv[1]) << 24; + *value |= ((uint32_t) recv[2]) << 24; + *value |= ((uint32_t) recv[3]) << 24; + return i2c::ERROR_OK; } - bool has_irq_ = false; - uint8_t irq_pin_number_; - GPIOPin *irq_pin_{nullptr}; + InternalGPIOPin *irq_pin_ = nullptr; bool is_setup_{false}; sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_a_sensor_{nullptr}; diff --git a/esphome/components/ade7953/sensor.py b/esphome/components/ade7953/sensor.py index 80dafe2417..d02f466091 100644 --- a/esphome/components/ade7953/sensor.py +++ b/esphome/components/ade7953/sensor.py @@ -29,7 +29,7 @@ CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(ADE7953), - cv.Optional(CONF_IRQ_PIN): pins.input_pin, + cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema, cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, @@ -73,7 +73,8 @@ async def to_code(config): await i2c.register_i2c_device(var, config) if CONF_IRQ_PIN in config: - cg.add(var.set_irq_pin(config[CONF_IRQ_PIN])) + irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN]) + cg.add(var.set_irq_pin(irq_pin)) for key in [ CONF_VOLTAGE, diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index 92f0ffbe3a..beb379db93 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -1,5 +1,6 @@ #include "ads1115.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ads1115 { @@ -159,7 +160,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); } void ADS1115Sensor::update() { float v = this->parent_->request_measurement(this); - if (!isnan(v)) { + if (!std::isnan(v)) { ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); this->publish_state(v); } diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index da8c6e844e..713199212c 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -14,6 +14,7 @@ #include "aht10.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace aht10 { @@ -33,8 +34,19 @@ void AHT10Component::setup() { this->mark_failed(); return; } - uint8_t data; - if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) { + uint8_t data = 0; + if (this->write(&data, 1) != i2c::ERROR_OK) { + ESP_LOGD(TAG, "Communication with AHT10 failed!"); + this->mark_failed(); + return; + } + delay(AHT10_DEFAULT_DELAY); + if (this->read(&data, 1) != i2c::ERROR_OK) { + ESP_LOGD(TAG, "Communication with AHT10 failed!"); + this->mark_failed(); + return; + } + if (this->read(&data, 1) != i2c::ERROR_OK) { ESP_LOGD(TAG, "Communication with AHT10 failed!"); this->mark_failed(); return; @@ -55,15 +67,26 @@ void AHT10Component::update() { return; } uint8_t data[6]; - uint8_t delay = AHT10_DEFAULT_DELAY; + uint8_t delay_ms = AHT10_DEFAULT_DELAY; if (this->humidity_sensor_ != nullptr) - delay = AHT10_HUMIDITY_DELAY; + delay_ms = AHT10_HUMIDITY_DELAY; + bool success = false; for (int i = 0; i < AHT10_ATTEMPTS; ++i) { - ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis()); + ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis()); delay_microseconds_accurate(4); - if (!this->read_bytes(0, data, 6, delay)) { + + uint8_t reg = 0; + if (this->write(®, 1) != i2c::ERROR_OK) { ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); - } else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy + continue; + } + delay(delay_ms); + if (this->read(data, 6) != i2c::ERROR_OK) { + ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); + continue; + } + + if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy ESP_LOGD(TAG, "AHT10 is busy, waiting..."); } else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) { // Unrealistic humidity (0x0) @@ -80,11 +103,12 @@ void AHT10Component::update() { } } else { // data is valid, we can break the loop - ESP_LOGVV(TAG, "Answer at %6ld", millis()); + ESP_LOGVV(TAG, "Answer at %6u", millis()); + success = true; break; } } - if ((data[0] & 0x80) == 0x80) { + if (!success || (data[0] & 0x80) == 0x80) { ESP_LOGE(TAG, "Measurements reading timed-out!"); this->status_set_warning(); return; @@ -105,7 +129,7 @@ void AHT10Component::update() { this->temperature_sensor_->publish_state(temperature); } if (this->humidity_sensor_ != nullptr) { - if (isnan(humidity)) + if (std::isnan(humidity)) ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum"); this->humidity_sensor_->publish_state(humidity); } diff --git a/esphome/components/airthings_ble/airthings_listener.cpp b/esphome/components/airthings_ble/airthings_listener.cpp index a843bcb145..951961cb1b 100644 --- a/esphome/components/airthings_ble/airthings_listener.cpp +++ b/esphome/components/airthings_ble/airthings_listener.cpp @@ -1,7 +1,7 @@ #include "airthings_listener.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace airthings_ble { diff --git a/esphome/components/airthings_ble/airthings_listener.h b/esphome/components/airthings_ble/airthings_listener.h index cd240ac1ba..52f69ea970 100644 --- a/esphome/components/airthings_ble/airthings_listener.h +++ b/esphome/components/airthings_ble/airthings_listener.h @@ -1,10 +1,9 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#include namespace esphome { namespace airthings_ble { diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index 6b4780726d..a8153ae87a 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -1,6 +1,6 @@ #include "airthings_wave_plus.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO namespace esphome { namespace airthings_wave_plus { @@ -141,4 +141,4 @@ void AirthingsWavePlus::setup() {} } // namespace airthings_wave_plus } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.h b/esphome/components/airthings_wave_plus/airthings_wave_plus.h index 88d7a7a01b..5677f05a62 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.h +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.h @@ -1,6 +1,6 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #include @@ -72,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientN } // namespace airthings_wave_plus } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/airthings_wave_plus/sensor.py b/esphome/components/airthings_wave_plus/sensor.py index 4109fca700..8b902ea81c 100644 --- a/esphome/components/airthings_wave_plus/sensor.py +++ b/esphome/components/airthings_wave_plus/sensor.py @@ -34,7 +34,7 @@ AirthingsWavePlus = airthings_wave_plus_ns.class_( ) -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(AirthingsWavePlus), @@ -83,7 +83,9 @@ CONFIG_SCHEMA = ( } ) .extend(cv.polling_component_schema("5mins")) - .extend(ble_client.BLE_CLIENT_SCHEMA) + .extend(ble_client.BLE_CLIENT_SCHEMA), + # Until BLEUUID reference removed + cv.only_with_arduino, ) diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp index 7e8795dd30..b53eb69464 100644 --- a/esphome/components/am2320/am2320.cpp +++ b/esphome/components/am2320/am2320.cpp @@ -5,6 +5,7 @@ #include "am2320.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace am2320 { @@ -77,7 +78,7 @@ bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len if (conversion > 0) delay(conversion); - return this->parent_->raw_receive(this->address_, data, len); + return this->read(data, len) == i2c::ERROR_OK; } bool AM2320Component::read_data_(uint8_t *data) { diff --git a/esphome/components/am43/am43.cpp b/esphome/components/am43/am43.cpp index 94be194a1d..2130b334be 100644 --- a/esphome/components/am43/am43.cpp +++ b/esphome/components/am43/am43.cpp @@ -1,7 +1,8 @@ #include "am43.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace am43 { diff --git a/esphome/components/am43/am43.h b/esphome/components/am43/am43.h index 025d075b68..8dfe83e3a3 100644 --- a/esphome/components/am43/am43.h +++ b/esphome/components/am43/am43.h @@ -6,7 +6,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/am43/am43_base.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index 1067ce9ebb..fd337ba17b 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -1,7 +1,7 @@ #include "am43_cover.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace am43 { diff --git a/esphome/components/am43/cover/am43_cover.h b/esphome/components/am43/cover/am43_cover.h index dba1391b63..f33f2d1734 100644 --- a/esphome/components/am43/cover/am43_cover.h +++ b/esphome/components/am43/cover/am43_cover.h @@ -6,7 +6,7 @@ #include "esphome/components/cover/cover.h" #include "esphome/components/am43/am43_base.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp index d0ea818669..8e0724ad13 100644 --- a/esphome/components/anova/anova.cpp +++ b/esphome/components/anova/anova.cpp @@ -1,7 +1,7 @@ #include "anova.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace anova { diff --git a/esphome/components/anova/anova.h b/esphome/components/anova/anova.h index e033513013..554024e389 100644 --- a/esphome/components/anova/anova.h +++ b/esphome/components/anova/anova.h @@ -6,7 +6,7 @@ #include "esphome/components/climate/climate.h" #include "anova_base.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/apds9960/apds9960.cpp b/esphome/components/apds9960/apds9960.cpp index 15b0cb39f8..9ee873ac64 100644 --- a/esphome/components/apds9960/apds9960.cpp +++ b/esphome/components/apds9960/apds9960.cpp @@ -1,5 +1,6 @@ #include "apds9960.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace apds9960 { diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 0fa4ca6397..450375f7cd 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,7 +1,8 @@ #include "api_connection.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include "esphome/core/version.h" +#include "esphome/core/hal.h" #include #ifdef USE_DEEP_SLEEP @@ -48,7 +49,7 @@ void APIConnection::loop() { if (this->remove_) return; - if (!network_is_connected()) { + if (!network::is_connected()) { // when network is disconnected force disconnect immediately // don't wait for timeout this->on_fatal_error(); diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 15014b7937..00f28457ae 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -3,6 +3,7 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" #include "proto.h" +#include namespace esphome { namespace api { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index d6b85d257c..580e147245 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -2,6 +2,7 @@ // See scripts/api_protobuf/api_protobuf.py #include "api_pb2.h" #include "esphome/core/log.h" +#include namespace esphome { namespace api { diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index d84a35747c..8728597537 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -5,6 +5,8 @@ #include "esphome/core/log.h" #include "esphome/core/util.h" #include "esphome/core/version.h" +#include "esphome/core/hal.h" +#include "esphome/components/network/util.h" #include #ifdef USE_LOGGER @@ -130,7 +132,7 @@ void APIServer::loop() { } void APIServer::dump_config() { ESP_LOGCONFIG(TAG, "API Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); } bool APIServer::uses_password() const { return !this->password_.empty(); } bool APIServer::check_password(const std::string &password) const { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index dd054bab7e..edbc916b01 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -246,6 +246,7 @@ class ProtoWriteBuffer { class ProtoMessage { public: + virtual ~ProtoMessage() = default; virtual void encode(ProtoWriteBuffer buffer) const = 0; void decode(const uint8_t *buffer, size_t length); #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/as3935/as3935.h b/esphome/components/as3935/as3935.h index 76c7697d38..2e65aab4d1 100644 --- a/esphome/components/as3935/as3935.h +++ b/esphome/components/as3935/as3935.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/binary_sensor/binary_sensor.h" diff --git a/esphome/components/as3935_i2c/as3935_i2c.cpp b/esphome/components/as3935_i2c/as3935_i2c.cpp index 1a1cd8fe82..3a7fa7bf84 100644 --- a/esphome/components/as3935_i2c/as3935_i2c.cpp +++ b/esphome/components/as3935_i2c/as3935_i2c.cpp @@ -25,8 +25,12 @@ void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t I2CAS3935Component::read_register(uint8_t reg) { uint8_t value; - if (!this->read_byte(reg, &value, 2)) { - ESP_LOGW(TAG, "Read failed!"); + if (write(®, 1) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "Writing register failed!"); + return 0; + } + if (read(&value, 1) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "Reading register failed!"); return 0; } return value; diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 22d4bb1fcb..8789448792 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -1,9 +1,15 @@ # Dummy integration to allow relying on AsyncTCP import esphome.codegen as cg +import esphome.config_validation as cv from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@OttoWinter"] +CONFIG_SCHEMA = cv.All( + cv.Schema({}), + cv.only_with_arduino, +) + @coroutine_with_priority(200.0) async def to_code(config): diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.cpp b/esphome/components/atc_mithermometer/atc_mithermometer.cpp index 1dba259143..b04d634103 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.cpp +++ b/esphome/components/atc_mithermometer/atc_mithermometer.cpp @@ -1,7 +1,7 @@ #include "atc_mithermometer.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace atc_mithermometer { diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.h b/esphome/components/atc_mithermometer/atc_mithermometer.h index eff14811be..291c1d96cd 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.h +++ b/esphome/components/atc_mithermometer/atc_mithermometer.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace atc_mithermometer { diff --git a/esphome/components/b_parasite/b_parasite.cpp b/esphome/components/b_parasite/b_parasite.cpp index 3098d462d2..bc1463fd1c 100644 --- a/esphome/components/b_parasite/b_parasite.cpp +++ b/esphome/components/b_parasite/b_parasite.cpp @@ -1,7 +1,7 @@ #include "b_parasite.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace b_parasite { @@ -79,4 +79,4 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { } // namespace b_parasite } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32 diff --git a/esphome/components/b_parasite/b_parasite.h b/esphome/components/b_parasite/b_parasite.h index 04f648ab63..bdd9a01b83 100644 --- a/esphome/components/b_parasite/b_parasite.h +++ b/esphome/components/b_parasite/b_parasite.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace b_parasite { @@ -37,4 +37,4 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene } // namespace b_parasite } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32 diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index 4b41684707..5645f46f1c 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -69,7 +69,8 @@ void BangBangClimate::compute_state_() { this->switch_to_action_(climate::CLIMATE_ACTION_OFF); return; } - if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) { + if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature_low) || + std::isnan(this->target_temperature_high)) { // if any control parameters are nan, go to OFF action (not IDLE!) this->switch_to_action_(climate::CLIMATE_ACTION_OFF); return; diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index 43af116c9e..3645a45bf9 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -71,10 +71,11 @@ void BH1750Sensor::update() { float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; } void BH1750Sensor::read_data_() { uint16_t raw_value; - if (!this->parent_->raw_receive_16(this->address_, &raw_value, 1)) { + if (!this->read(reinterpret_cast(&raw_value), 2)) { this->status_set_warning(); return; } + raw_value = i2c::i2ctohs(raw_value); float lx = float(raw_value) / 1.2f; lx *= 69.0f / this->measurement_duration_; diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index 0c1e80afba..31bf1a5565 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -4,6 +4,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/hal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 2db609de55..d015d4019c 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -3,7 +3,7 @@ #include "esphome/core/automation.h" #include "esphome/components/ble_client/ble_client.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 0398113f83..f584cdf79e 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -4,7 +4,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "ble_client.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index b9d16ddacd..a69460e8b6 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/ble_client/sensor/automation.h b/esphome/components/ble_client/sensor/automation.h index a528493947..2255a5ac55 100644 --- a/esphome/components/ble_client/sensor/automation.h +++ b/esphome/components/ble_client/sensor/automation.h @@ -3,7 +3,7 @@ #include "esphome/core/automation.h" #include "esphome/components/ble_client/sensor/ble_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 270822be9d..4459163389 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/sensor/ble_sensor.h b/esphome/components/ble_client/sensor/ble_sensor.h index 25e996b6ee..52c9e9d5ca 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.h +++ b/esphome/components/ble_client/sensor/ble_sensor.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include namespace esphome { diff --git a/esphome/components/ble_client/switch/ble_switch.cpp b/esphome/components/ble_client/switch/ble_switch.cpp index 669984d705..00593da9d6 100644 --- a/esphome/components/ble_client/switch/ble_switch.cpp +++ b/esphome/components/ble_client/switch/ble_switch.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/switch/ble_switch.h b/esphome/components/ble_client/switch/ble_switch.h index f91af533f1..2e19c8aeef 100644 --- a/esphome/components/ble_client/switch/ble_switch.h +++ b/esphome/components/ble_client/switch/ble_switch.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/switch/switch.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include namespace esphome { diff --git a/esphome/components/ble_presence/ble_presence_device.cpp b/esphome/components/ble_presence/ble_presence_device.cpp index 1355c2bcc3..e482bb9a78 100644 --- a/esphome/components/ble_presence/ble_presence_device.cpp +++ b/esphome/components/ble_presence/ble_presence_device.cpp @@ -1,7 +1,7 @@ #include "ble_presence_device.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_presence { diff --git a/esphome/components/ble_presence/ble_presence_device.h b/esphome/components/ble_presence/ble_presence_device.h index dfc36d68cb..40cda89e62 100644 --- a/esphome/components/ble_presence/ble_presence_device.h +++ b/esphome/components/ble_presence/ble_presence_device.h @@ -4,7 +4,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/binary_sensor/binary_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_presence { diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.cpp b/esphome/components/ble_rssi/ble_rssi_sensor.cpp index 096259d8d1..4b37fcc6ef 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.cpp +++ b/esphome/components/ble_rssi/ble_rssi_sensor.cpp @@ -1,7 +1,7 @@ #include "ble_rssi_sensor.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_rssi { diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.h b/esphome/components/ble_rssi/ble_rssi_sensor.h index 2082a52469..c6acae2593 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.h +++ b/esphome/components/ble_rssi/ble_rssi_sensor.h @@ -4,7 +4,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_rssi { diff --git a/esphome/components/ble_scanner/ble_scanner.cpp b/esphome/components/ble_scanner/ble_scanner.cpp index 798824bb4e..f2cda227bb 100644 --- a/esphome/components/ble_scanner/ble_scanner.cpp +++ b/esphome/components/ble_scanner/ble_scanner.cpp @@ -1,7 +1,7 @@ #include "ble_scanner.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_scanner { diff --git a/esphome/components/ble_scanner/ble_scanner.h b/esphome/components/ble_scanner/ble_scanner.h index 194494144c..542d5047ec 100644 --- a/esphome/components/ble_scanner/ble_scanner.h +++ b/esphome/components/ble_scanner/ble_scanner.h @@ -7,7 +7,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/text_sensor/text_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_scanner { diff --git a/esphome/components/bme280/bme280.cpp b/esphome/components/bme280/bme280.cpp index fe11b69945..18386430a2 100644 --- a/esphome/components/bme280/bme280.cpp +++ b/esphome/components/bme280/bme280.cpp @@ -186,7 +186,7 @@ void BME280Component::update() { this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() { int32_t t_fine = 0; float temperature = this->read_temperature_(&t_fine); - if (isnan(temperature)) { + if (std::isnan(temperature)) { ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values."); this->status_set_warning(); return; diff --git a/esphome/components/bme680/bme680.cpp b/esphome/components/bme680/bme680.cpp index ee7db3c65f..99e0b6f860 100644 --- a/esphome/components/bme680/bme680.cpp +++ b/esphome/components/bme680/bme680.cpp @@ -1,5 +1,6 @@ #include "bme680.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace bme680 { diff --git a/esphome/components/bme680_bsec/bme680_bsec.cpp b/esphome/components/bme680_bsec/bme680_bsec.cpp index e2cb7491a6..0a8ca7f3c3 100644 --- a/esphome/components/bme680_bsec/bme680_bsec.cpp +++ b/esphome/components/bme680_bsec/bme680_bsec.cpp @@ -381,7 +381,7 @@ void BME680BSECComponent::delay_ms(uint32_t period) { void BME680BSECComponent::load_state_() { uint32_t hash = fnv1_hash("bme680_bsec_state_" + to_string(this->address_)); - this->bsec_state_ = global_preferences.make_preference(hash, true); + this->bsec_state_ = global_preferences->make_preference(hash, true); uint8_t state[BSEC_MAX_STATE_BLOB_SIZE]; if (this->bsec_state_.load(&state)) { diff --git a/esphome/components/bmp280/bmp280.cpp b/esphome/components/bmp280/bmp280.cpp index d04a357ca2..b4348e8a74 100644 --- a/esphome/components/bmp280/bmp280.cpp +++ b/esphome/components/bmp280/bmp280.cpp @@ -139,7 +139,7 @@ void BMP280Component::update() { this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() { int32_t t_fine = 0; float temperature = this->read_temperature_(&t_fine); - if (isnan(temperature)) { + if (std::isnan(temperature)) { ESP_LOGW(TAG, "Invalid temperature, cannot read pressure values."); this->status_set_warning(); return; diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index 102bfd370e..a1cc5734c1 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -12,14 +12,17 @@ CODEOWNERS = ["@OttoWinter"] captive_portal_ns = cg.esphome_ns.namespace("captive_portal") CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CaptivePortal), - cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( - web_server_base.WebServerBase - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CaptivePortal), + cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( + web_server_base.WebServerBase + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_arduino, +) @coroutine_with_priority(64.0) diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 3341769956..9e00adae3d 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "captive_portal.h" #include "esphome/core/log.h" #include "esphome/core/application.h" @@ -78,14 +80,14 @@ void CaptivePortal::start() { this->dns_server_ = make_unique(); this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); - IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); - this->dns_server_->start(53, "*", ip); + network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); + this->dns_server_->start(53, "*", (uint32_t) ip); this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { bool not_found = false; if (!this->active_) { not_found = true; - } else if (req->host() == wifi::global_wifi_component->wifi_soft_ap_ip().toString()) { + } else if (req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) { not_found = true; } @@ -94,8 +96,8 @@ void CaptivePortal::start() { return; } - auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().toString(); - req->redirect(url); + auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().str(); + req->redirect(url.c_str()); }); this->initialized_ = true; @@ -151,3 +153,5 @@ CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avo } // namespace captive_portal } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index 399ffaabd3..b308de42b7 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include #include #include "esphome/core/component.h" @@ -73,3 +75,5 @@ extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid- } // namespace captive_portal } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 08df6f7774..6bdb4739cf 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -1,5 +1,6 @@ #include "ccs811.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ccs811 { @@ -124,12 +125,12 @@ void CCS811Component::send_env_data_() { float humidity = NAN; if (this->humidity_ != nullptr) humidity = this->humidity_->state; - if (isnan(humidity) || humidity < 0 || humidity > 100) + if (std::isnan(humidity) || humidity < 0 || humidity > 100) humidity = 50; float temperature = NAN; if (this->temperature_ != nullptr) temperature = this->temperature_->state; - if (isnan(temperature) || temperature < -25 || temperature > 50) + if (std::isnan(temperature) || temperature < -25 || temperature > 50) temperature = 25; // temperature has a 25° offset to allow negative temperatures temperature += 25; diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 383103b014..a365b933bb 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -95,7 +95,7 @@ void ClimateCall::validate_() { ESP_LOGW(TAG, " Cannot set target temperature for climate device " "with two-point target temperature!"); this->target_temperature_.reset(); - } else if (isnan(target)) { + } else if (std::isnan(target)) { ESP_LOGW(TAG, " Target temperature must not be NAN!"); this->target_temperature_.reset(); } @@ -107,11 +107,11 @@ void ClimateCall::validate_() { this->target_temperature_high_.reset(); } } - if (this->target_temperature_low_.has_value() && isnan(*this->target_temperature_low_)) { + if (this->target_temperature_low_.has_value() && std::isnan(*this->target_temperature_low_)) { ESP_LOGW(TAG, " Target temperature low must not be NAN!"); this->target_temperature_low_.reset(); } - if (this->target_temperature_high_.has_value() && isnan(*this->target_temperature_high_)) { + if (this->target_temperature_high_.has_value() && std::isnan(*this->target_temperature_high_)) { ESP_LOGW(TAG, " Target temperature low must not be NAN!"); this->target_temperature_high_.reset(); } @@ -318,17 +318,23 @@ void Climate::add_on_state_callback(std::function &&callback) { static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL; optional Climate::restore_state_() { - this->rtc_ = - global_preferences.make_preference(this->get_object_id_hash() ^ RESTORE_STATE_VERSION); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash() ^ + RESTORE_STATE_VERSION); ClimateDeviceRestoreState recovered{}; if (!this->rtc_.load(&recovered)) return {}; return recovered; } void Climate::save_state_() { +#if defined(USE_ESP_IDF) && !defined(CLANG_TIDY) +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif ClimateDeviceRestoreState state{}; // initialize as zero to prevent random data on stack triggering erase memset(&state, 0, sizeof(ClimateDeviceRestoreState)); +#if USE_ESP_IDF && !defined(CLANG_TIDY) +#pragma GCC diagnostic pop +#endif state.mode = this->mode; auto traits = this->get_traits(); diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index 8d5ae6053d..b47d9b0141 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -52,7 +52,7 @@ void ClimateIR::setup() { this->swing_mode = climate::CLIMATE_SWING_OFF; } // Never send nan to HA - if (isnan(this->target_temperature)) + if (std::isnan(this->target_temperature)) this->target_temperature = 24; } diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index 2b4452f5b7..e1ce211b64 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -180,7 +180,7 @@ void Cover::publish_state(bool save) { } } optional Cover::restore_state_() { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); CoverRestoreState recovered{}; if (!this->rtc_.load(&recovered)) return {}; diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.cpp b/esphome/components/ct_clamp/ct_clamp_sensor.cpp index 130cdfefba..0052b1426d 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.cpp +++ b/esphome/components/ct_clamp/ct_clamp_sensor.cpp @@ -52,7 +52,7 @@ void CTClampSensor::loop() { // Perform a single sample float value = this->source_->sample(); - if (isnan(value)) + if (std::isnan(value)) return; this->sample_sum_ += value; diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.h b/esphome/components/ct_clamp/ct_clamp_sensor.h index 2f201c11a0..10601ab852 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.h +++ b/esphome/components/ct_clamp/ct_clamp_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index 847d630e7c..0fc4108687 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -161,7 +161,7 @@ const std::string &DallasTemperatureSensor::get_address_name() { return this->address_name_; } -bool ICACHE_RAM_ATTR DallasTemperatureSensor::read_scratch_pad() { +bool IRAM_ATTR DallasTemperatureSensor::read_scratch_pad() { ESPOneWire *wire = this->parent_->one_wire_; if (!wire->reset()) { return false; diff --git a/esphome/components/dallas/esp_one_wire.cpp b/esphome/components/dallas/esp_one_wire.cpp index 702d1eddc2..9278b83f7f 100644 --- a/esphome/components/dallas/esp_one_wire.cpp +++ b/esphome/components/dallas/esp_one_wire.cpp @@ -12,11 +12,11 @@ const int ONE_WIRE_ROM_SEARCH = 0xF0; ESPOneWire::ESPOneWire(GPIOPin *pin) : pin_(pin) {} -bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() { +bool HOT IRAM_ATTR ESPOneWire::reset() { uint8_t retries = 125; // Wait for communication to clear - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); do { if (--retries == 0) return false; @@ -24,12 +24,12 @@ bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() { } while (!this->pin_->digital_read()); // Send 480µs LOW TX reset pulse - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); delayMicroseconds(480); // Switch into RX mode, letting the pin float - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); // after 15µs-60µs wait time, responder pulls low for 60µs-240µs // let's have 70µs just in case delayMicroseconds(70); @@ -39,9 +39,9 @@ bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() { return r; } -void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) { +void HOT IRAM_ATTR ESPOneWire::write_bit(bool bit) { // Initiate write/read by pulling low. - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); // bus sampled within 15µs and 60µs after pulling LOW. @@ -60,14 +60,14 @@ void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) { } } -bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() { +bool HOT IRAM_ATTR ESPOneWire::read_bit() { // Initiate read slot by pulling LOW for at least 1µs - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); delayMicroseconds(3); // release bus, we have to sample within 15µs of pulling low - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); delayMicroseconds(10); bool r = this->pin_->digital_read(); @@ -76,43 +76,43 @@ bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() { return r; } -void ICACHE_RAM_ATTR ESPOneWire::write8(uint8_t val) { +void IRAM_ATTR ESPOneWire::write8(uint8_t val) { for (uint8_t i = 0; i < 8; i++) { this->write_bit(bool((1u << i) & val)); } } -void ICACHE_RAM_ATTR ESPOneWire::write64(uint64_t val) { +void IRAM_ATTR ESPOneWire::write64(uint64_t val) { for (uint8_t i = 0; i < 64; i++) { this->write_bit(bool((1ULL << i) & val)); } } -uint8_t ICACHE_RAM_ATTR ESPOneWire::read8() { +uint8_t IRAM_ATTR ESPOneWire::read8() { uint8_t ret = 0; for (uint8_t i = 0; i < 8; i++) { ret |= (uint8_t(this->read_bit()) << i); } return ret; } -uint64_t ICACHE_RAM_ATTR ESPOneWire::read64() { +uint64_t IRAM_ATTR ESPOneWire::read64() { uint64_t ret = 0; for (uint8_t i = 0; i < 8; i++) { ret |= (uint64_t(this->read_bit()) << i); } return ret; } -void ICACHE_RAM_ATTR ESPOneWire::select(uint64_t address) { +void IRAM_ATTR ESPOneWire::select(uint64_t address) { this->write8(ONE_WIRE_ROM_SELECT); this->write64(address); } -void ICACHE_RAM_ATTR ESPOneWire::reset_search() { +void IRAM_ATTR ESPOneWire::reset_search() { this->last_discrepancy_ = 0; this->last_device_flag_ = false; this->last_family_discrepancy_ = 0; this->rom_number_ = 0; } -uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() { +uint64_t HOT IRAM_ATTR ESPOneWire::search() { if (this->last_device_flag_) { return 0u; } @@ -196,7 +196,7 @@ uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() { return this->rom_number_; } -std::vector ICACHE_RAM_ATTR ESPOneWire::search_vec() { +std::vector IRAM_ATTR ESPOneWire::search_vec() { std::vector res; this->reset_search(); @@ -206,12 +206,12 @@ std::vector ICACHE_RAM_ATTR ESPOneWire::search_vec() { return res; } -void ICACHE_RAM_ATTR ESPOneWire::skip() { +void IRAM_ATTR ESPOneWire::skip() { this->write8(0xCC); // skip ROM } GPIOPin *ESPOneWire::get_pin() { return this->pin_; } -uint8_t ICACHE_RAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast(&this->rom_number_); } +uint8_t IRAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast(&this->rom_number_); } } // namespace dallas } // namespace esphome diff --git a/esphome/components/dallas/esp_one_wire.h b/esphome/components/dallas/esp_one_wire.h index 68bcc0c193..728fa127d3 100644 --- a/esphome/components/dallas/esp_one_wire.h +++ b/esphome/components/dallas/esp_one_wire.h @@ -1,7 +1,8 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" +#include namespace esphome { namespace dallas { diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 11590a0978..7fd8956148 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -4,8 +4,18 @@ #include "esphome/core/defines.h" #include "esphome/core/version.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include +#include +#endif + +#ifdef USE_ARDUINO +#include +#endif + +#ifdef USE_ESP_IDF +#include +#include #endif namespace esphome { @@ -21,9 +31,14 @@ void DebugComponent::dump_config() { #endif ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION); +#ifdef USE_ARDUINO this->free_heap_ = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP_IDF) + this->free_heap_ = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); +#endif ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); +#ifdef USE_ARDUINO const char *flash_mode; switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance) case FM_QIO: @@ -38,7 +53,7 @@ void DebugComponent::dump_config() { case FM_DOUT: flash_mode = "DOUT"; break; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 case FM_FAST_READ: flash_mode = "FAST_READ"; break; @@ -52,8 +67,9 @@ void DebugComponent::dump_config() { // NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", ESP.getFlashChipSize() / 1024, ESP.getFlashChipSpeed() / 1000000, flash_mode); +#endif // USE_ARDUINO -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_chip_info_t info; esp_chip_info(&info); const char *model; @@ -88,7 +104,9 @@ void DebugComponent::dump_config() { ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); - std::string mac = uint64_to_string(ESP.getEfuseMac()); // NOLINT(readability-static-accessed-through-instance) + uint64_t chip_mac = 0LL; + esp_efuse_mac_get_default((uint8_t *) (&chip_mac)); + std::string mac = uint64_to_string(chip_mac); ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str()); const char *reset_reason; @@ -187,7 +205,7 @@ void DebugComponent::dump_config() { ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason); #endif -#if defined(ARDUINO_ARCH_ESP8266) && !defined(CLANG_TIDY) +#if defined(USE_ESP8266) && !defined(CLANG_TIDY) ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId()); ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion()); ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str()); @@ -199,7 +217,11 @@ void DebugComponent::dump_config() { #endif } void DebugComponent::loop() { +#ifdef USE_ARDUINO uint32_t new_free_heap = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP_IDF) + uint32_t new_free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); +#endif if (new_free_heap < this->free_heap_ / 2) { this->free_heap_ = new_free_heap; ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 1e07b75173..f47888b8eb 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -62,7 +62,7 @@ CONFIG_SCHEMA = cv.Schema( cv.Schema( { cv.Required(CONF_PINS): cv.ensure_list( - pins.shorthand_input_pin, validate_pin_number + pins.internal_gpio_input_pin_schema, validate_pin_number ), cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), } diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 9c354c8513..e4b1edfb7b 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -2,6 +2,10 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" +#ifdef USE_ESP8266 +#include +#endif + namespace esphome { namespace deep_sleep { @@ -25,9 +29,9 @@ void DeepSleepComponent::dump_config() { if (this->run_duration_.has_value()) { ESP_LOGCONFIG(TAG, " Run Duration: %u ms", *this->run_duration_); } -#ifdef ARDUINO_ARCH_ESP32 - if (this->wakeup_pin_.has_value()) { - LOG_PIN(" Wakeup Pin: ", *this->wakeup_pin_); +#ifdef USE_ESP32 + if (wakeup_pin_ != nullptr) { + LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_); } #endif } @@ -39,7 +43,7 @@ float DeepSleepComponent::get_loop_priority() const { return -100.0f; // run after everything else is ready } void DeepSleepComponent::set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) { this->wakeup_pin_mode_ = wakeup_pin_mode; } @@ -52,9 +56,9 @@ void DeepSleepComponent::begin_sleep(bool manual) { this->next_enter_deep_sleep_ = true; return; } -#ifdef ARDUINO_ARCH_ESP32 - if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_.has_value() && - !this->sleep_duration_.has_value() && (*this->wakeup_pin_)->digital_read()) { +#ifdef USE_ESP32 + if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr && + !this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) { // Defer deep sleep until inactive if (!this->next_enter_deep_sleep_) { this->status_set_warning(); @@ -69,14 +73,14 @@ void DeepSleepComponent::begin_sleep(bool manual) { App.run_safe_shutdown_hooks(); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (this->sleep_duration_.has_value()) esp_sleep_enable_timer_wakeup(*this->sleep_duration_); - if (this->wakeup_pin_.has_value()) { - bool level = !(*this->wakeup_pin_)->is_inverted(); - if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && (*this->wakeup_pin_)->digital_read()) + if (this->wakeup_pin_ != nullptr) { + bool level = this->wakeup_pin_->is_inverted(); + if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) level = !level; - esp_sleep_enable_ext0_wakeup(gpio_num_t((*this->wakeup_pin_)->get_pin()), level); + esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level); } if (this->ext1_wakeup_.has_value()) { esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode); @@ -90,7 +94,7 @@ void DeepSleepComponent::begin_sleep(bool manual) { esp_deep_sleep_start(); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance) #endif } diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 575f7be72b..d7969ba999 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -3,12 +3,16 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/automation.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" + +#ifdef USE_ESP32 +#include +#endif namespace esphome { namespace deep_sleep { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 /** The values of this enum define what should be done if deep sleep is set up with a wakeup pin on the ESP32 * and the scenario occurs that the wakeup pin is already in the wakeup state. @@ -44,11 +48,11 @@ class DeepSleepComponent : public Component { public: /// Set the duration in ms the component should sleep once it's in deep sleep mode. void set_sleep_duration(uint32_t time_ms); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 /** Set the pin to wake up to on the ESP32 once it's in deep sleep mode. * Use the inverted property to set the wakeup level. */ - void set_wakeup_pin(GPIOPin *pin) { this->wakeup_pin_ = pin; } + void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; } void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode); @@ -72,8 +76,8 @@ class DeepSleepComponent : public Component { protected: optional sleep_duration_; -#ifdef ARDUINO_ARCH_ESP32 - optional wakeup_pin_; +#ifdef USE_ESP32 + InternalGPIOPin *wakeup_pin_; WakeupPinMode wakeup_pin_mode_{WAKEUP_PIN_MODE_IGNORE}; optional ext1_wakeup_; optional touch_wakeup_; diff --git a/esphome/components/demo/demo_sensor.h b/esphome/components/demo/demo_sensor.h index 9a35674124..b4afa03e11 100644 --- a/esphome/components/demo/demo_sensor.h +++ b/esphome/components/demo/demo_sensor.h @@ -13,7 +13,7 @@ class DemoSensor : public sensor::Sensor, public PollingComponent { float val = random_float(); bool increasing = this->get_state_class() == sensor::STATE_CLASS_TOTAL_INCREASING; if (increasing) { - float base = isnan(this->state) ? 0.0f : this->state; + float base = std::isnan(this->state) ? 0.0f : this->state; this->publish_state(base + val * 10); } else { if (val < 0.1) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 734dde20a8..2539bfe5ee 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -71,7 +71,7 @@ void DHT::set_dht_model(DHTModel model) { this->model_ = model; this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT; } -bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { +bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { *humidity = NAN; *temperature = NAN; @@ -83,7 +83,7 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, InterruptLock lock; this->pin_->digital_write(false); - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); if (this->model_ == DHT_MODEL_DHT11) { @@ -99,7 +99,7 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, } else { delayMicroseconds(800); } - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); // Host pull up 20-40us then DHT response 80us // Start waiting for initial rising edge at the center when we diff --git a/esphome/components/dht/dht.h b/esphome/components/dht/dht.h index 9b848cb119..f3a29f9ce9 100644 --- a/esphome/components/dht/dht.h +++ b/esphome/components/dht/dht.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -36,7 +36,7 @@ class DHT : public PollingComponent { */ void set_dht_model(DHTModel model); - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_model(DHTModel model) { model_ = model; } void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } @@ -52,7 +52,7 @@ class DHT : public PollingComponent { protected: bool read_sensor_(float *temperature, float *humidity, bool report_errors); - GPIOPin *pin_; + InternalGPIOPin *pin_; DHTModel model_{DHT_MODEL_AUTO_DETECT}; bool is_auto_detect_{false}; sensor::Sensor *temperature_sensor_{nullptr}; diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index bbb7444cb5..2ee06e379f 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -4,6 +4,7 @@ #include "esphome/core/application.h" #include "esphome/core/color.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace display { @@ -372,7 +373,7 @@ bool Glyph::get_pixel(int x, int y) const { return false; const uint32_t width_8 = ((this->glyph_data_->width + 7u) / 8u) * 8u; const uint32_t pos = x_data + y_data * width_8; - return pgm_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u)); + return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u)); } const char *Glyph::get_char() const { return this->glyph_data_->a_char; } bool Glyph::compare_to(const char *str) const { @@ -464,22 +465,22 @@ bool Image::get_pixel(int x, int y) const { return false; const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; const uint32_t pos = x + y * width_8; - return pgm_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); + return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); } Color Image::get_color_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return Color::BLACK; const uint32_t pos = (x + y * this->width_) * 3; - const uint32_t color32 = (pgm_read_byte(this->data_start_ + pos + 2) << 0) | - (pgm_read_byte(this->data_start_ + pos + 1) << 8) | - (pgm_read_byte(this->data_start_ + pos + 0) << 16); + const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) | + (progmem_read_byte(this->data_start_ + pos + 1) << 8) | + (progmem_read_byte(this->data_start_ + pos + 0) << 16); return Color(color32); } Color Image::get_grayscale_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return Color::BLACK; const uint32_t pos = (x + y * this->width_); - const uint8_t gray = pgm_read_byte(this->data_start_ + pos); + const uint8_t gray = progmem_read_byte(this->data_start_ + pos); return Color(gray | gray << 8 | gray << 16 | gray << 24); } int Image::get_width() const { return this->width_; } @@ -496,7 +497,7 @@ bool Animation::get_pixel(int x, int y) const { if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_) return false; const uint32_t pos = x + y * width_8 + frame_index; - return pgm_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); + return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); } Color Animation::get_color_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) @@ -505,9 +506,9 @@ Color Animation::get_color_pixel(int x, int y) const { if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_) return Color::BLACK; const uint32_t pos = (x + y * this->width_ + frame_index) * 3; - const uint32_t color32 = (pgm_read_byte(this->data_start_ + pos + 2) << 0) | - (pgm_read_byte(this->data_start_ + pos + 1) << 8) | - (pgm_read_byte(this->data_start_ + pos + 0) << 16); + const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) | + (progmem_read_byte(this->data_start_ + pos + 1) << 8) | + (progmem_read_byte(this->data_start_ + pos + 0) << 16); return Color(color32); } Color Animation::get_grayscale_pixel(int x, int y) const { @@ -517,7 +518,7 @@ Color Animation::get_grayscale_pixel(int x, int y) const { if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_) return Color::BLACK; const uint32_t pos = (x + y * this->width_ + frame_index); - const uint8_t gray = pgm_read_byte(this->data_start_ + pos); + const uint8_t gray = progmem_read_byte(this->data_start_ + pos); return Color(gray | gray << 8 | gray << 16 | gray << 24); } Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type) diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 3f89d3f8d2..54488f18f7 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -4,6 +4,7 @@ #include "esphome/core/defines.h" #include "esphome/core/automation.h" #include "display_color_utils.h" +#include #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/dsmr/__init__.py b/esphome/components/dsmr/__init__.py index 1f1a2f980e..dd6e6051aa 100644 --- a/esphome/components/dsmr/__init__.py +++ b/esphome/components/dsmr/__init__.py @@ -39,14 +39,17 @@ def _validate_key(value): return "".join(f"{part:02X}" for part in parts_int) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(Dsmr), - cv.Optional(CONF_DECRYPTION_KEY): _validate_key, - cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean, - cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_, - } -).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(Dsmr), + cv.Optional(CONF_DECRYPTION_KEY): _validate_key, + cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean, + cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_, + } + ).extend(uart.UART_DEVICE_SCHEMA), + cv.only_with_arduino, +) async def to_code(config): diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index 7c1b406d42..54c4343cfe 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "dsmr.h" #include "esphome/core/log.h" @@ -128,8 +130,9 @@ void Dsmr::receive_encrypted_() { delay(4); // Wait for data } } - if (buffer_length > 0) + if (buffer_length > 0) { ESP_LOGW(TAG, "Timeout while waiting for encrypted data or invalid data received."); + } } bool Dsmr::parse_telegram() { @@ -186,3 +189,5 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) { } // namespace dsmr } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/dsmr/dsmr.h b/esphome/components/dsmr/dsmr.h index dcb8f5f73d..dfee3b338a 100644 --- a/esphome/components/dsmr/dsmr.h +++ b/esphome/components/dsmr/dsmr.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/text_sensor/text_sensor.h" @@ -103,3 +105,5 @@ class Dsmr : public Component, public uart::UARTDevice { }; } // namespace dsmr } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.cpp b/esphome/components/duty_cycle/duty_cycle_sensor.cpp index c989421948..3d7f731d5d 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.cpp +++ b/esphome/components/duty_cycle/duty_cycle_sensor.cpp @@ -15,7 +15,7 @@ void DutyCycleSensor::setup() { this->last_update_ = micros(); this->store_.last_interrupt = micros(); - this->pin_->attach_interrupt(DutyCycleSensorStore::gpio_intr, &this->store_, CHANGE); + this->pin_->attach_interrupt(DutyCycleSensorStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void DutyCycleSensor::dump_config() { LOG_SENSOR("", "Duty Cycle Sensor", this); @@ -44,8 +44,8 @@ void DutyCycleSensor::update() { float DutyCycleSensor::get_setup_priority() const { return setup_priority::DATA; } -void ICACHE_RAM_ATTR DutyCycleSensorStore::gpio_intr(DutyCycleSensorStore *arg) { - const bool new_level = arg->pin->digital_read(); +void IRAM_ATTR DutyCycleSensorStore::gpio_intr(DutyCycleSensorStore *arg) { + const bool new_level = arg->pin.digital_read(); if (new_level == arg->last_level) return; arg->last_level = new_level; diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.h b/esphome/components/duty_cycle/duty_cycle_sensor.h index e168f20eff..22d3588fb7 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.h +++ b/esphome/components/duty_cycle/duty_cycle_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -12,14 +12,14 @@ struct DutyCycleSensorStore { volatile uint32_t last_interrupt{0}; volatile uint32_t on_time{0}; volatile bool last_level{false}; - ISRInternalGPIOPin *pin; + ISRInternalGPIOPin pin; static void gpio_intr(DutyCycleSensorStore *arg); }; class DutyCycleSensor : public sensor::Sensor, public PollingComponent { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void setup() override; float get_setup_priority() const override; @@ -27,7 +27,7 @@ class DutyCycleSensor : public sensor::Sensor, public PollingComponent { void update() override; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; DutyCycleSensorStore store_{}; uint32_t last_update_; diff --git a/esphome/components/duty_cycle/sensor.py b/esphome/components/duty_cycle/sensor.py index 3537cb0973..6a367328e6 100644 --- a/esphome/components/duty_cycle/sensor.py +++ b/esphome/components/duty_cycle/sensor.py @@ -25,9 +25,7 @@ CONFIG_SCHEMA = ( .extend( { cv.GenerateID(): cv.declare_id(DutyCycleSensor), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), } ) .extend(cv.polling_component_schema("60s")) diff --git a/esphome/components/e131/__init__.py b/esphome/components/e131/__init__.py index 5eb823064d..bb662e0989 100644 --- a/esphome/components/e131/__init__.py +++ b/esphome/components/e131/__init__.py @@ -4,6 +4,8 @@ from esphome.components.light.types import AddressableLightEffect from esphome.components.light.effects import register_addressable_effect from esphome.const import CONF_ID, CONF_NAME, CONF_METHOD, CONF_CHANNELS +DEPENDENCIES = ["network"] + e131_ns = cg.esphome_ns.namespace("e131") E131AddressableLightEffect = e131_ns.class_( "E131AddressableLightEffect", AddressableLightEffect @@ -21,11 +23,16 @@ CHANNELS = { CONF_UNIVERSE = "universe" CONF_E131_ID = "e131_id" -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(E131Component), - cv.Optional(CONF_METHOD, default="MULTICAST"): cv.one_of(*METHODS, upper=True), - } +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(E131Component), + cv.Optional(CONF_METHOD, default="MULTICAST"): cv.one_of( + *METHODS, upper=True + ), + } + ), + cv.only_with_arduino, ) diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index 7694d039e5..35510fe204 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -1,12 +1,14 @@ +#ifdef USE_ARDUINO + #include "e131.h" #include "e131_addressable_light_effect.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #include #endif @@ -104,3 +106,5 @@ bool E131Component::process_(int universe, const E131Packet &packet) { } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index 3f647edbf1..3819e522a5 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include @@ -55,3 +57,5 @@ class E131Component : public esphome::Component { } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131_addressable_light_effect.cpp b/esphome/components/e131/e131_addressable_light_effect.cpp index f0f165b25f..371f3b9cbf 100644 --- a/esphome/components/e131/e131_addressable_light_effect.cpp +++ b/esphome/components/e131/e131_addressable_light_effect.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "e131.h" #include "e131_addressable_light_effect.h" #include "esphome/core/log.h" @@ -90,3 +92,5 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131_addressable_light_effect.h b/esphome/components/e131/e131_addressable_light_effect.h index 1ab5d43164..e78f6bb0e0 100644 --- a/esphome/components/e131/e131_addressable_light_effect.h +++ b/esphome/components/e131/e131_addressable_light_effect.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/light/addressable_light_effect.h" @@ -46,3 +48,5 @@ class E131AddressableLightEffect : public light::AddressableLightEffect { } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index 14fdc084a6..b20eb9f666 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -1,8 +1,14 @@ +#ifdef USE_ARDUINO + #include "e131.h" #include "esphome/core/log.h" #include "esphome/core/util.h" +#include "esphome/components/network/ip_address.h" +#include +#include #include +#include #include namespace esphome { @@ -63,8 +69,8 @@ bool E131Component::join_igmp_groups_() { if (!universe.second) continue; - ip4_addr_t multicast_addr = { - static_cast(IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; + ip4_addr_t multicast_addr = {static_cast( + network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); @@ -98,7 +104,7 @@ void E131Component::leave_(int universe) { if (listen_method_ == E131_MULTICAST) { ip4_addr_t multicast_addr = { - static_cast(IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))}; + static_cast(network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))}; igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr); } @@ -134,3 +140,5 @@ bool E131Component::packet_(const std::vector &data, int &universe, E13 } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/endstop/endstop_cover.cpp b/esphome/components/endstop/endstop_cover.cpp index cbc4b334d9..67c6a4ebd3 100644 --- a/esphome/components/endstop/endstop_cover.cpp +++ b/esphome/components/endstop/endstop_cover.cpp @@ -1,5 +1,6 @@ #include "endstop_cover.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace endstop { diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py new file mode 100644 index 0000000000..c86087cc25 --- /dev/null +++ b/esphome/components/esp32/__init__.py @@ -0,0 +1,379 @@ +from dataclasses import dataclass +from typing import Union +from pathlib import Path +import logging + +from esphome.helpers import write_file_if_changed +from esphome.const import ( + CONF_BOARD, + CONF_FRAMEWORK, + CONF_TYPE, + CONF_VARIANT, + CONF_VERSION, + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, +) +from esphome.core import CORE, HexInt +import esphome.config_validation as cv +import esphome.codegen as cg + +from .const import ( + KEY_BOARD, + KEY_ESP32, + KEY_SDKCONFIG_OPTIONS, + KEY_VARIANT, + VARIANT_ESP32C3, + VARIANTS, +) + +# force import gpio to register pin schema +from .gpio import esp32_pin_to_code # noqa + + +_LOGGER = logging.getLogger(__name__) +CODEOWNERS = ["@esphome/core"] + + +def set_core_data(config): + CORE.data[KEY_ESP32] = {} + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp32" + conf = config[CONF_FRAMEWORK] + if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf" + CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] = {} + elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" + CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( + config[CONF_FRAMEWORK][CONF_VERSION_HINT] + ) + CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD] + CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT] + return config + + +def get_esp32_variant(): + return CORE.data[KEY_ESP32][KEY_VARIANT] + + +def is_esp32c3(): + return get_esp32_variant() == VARIANT_ESP32C3 + + +@dataclass +class RawSdkconfigValue: + """An sdkconfig value that won't be auto-formatted""" + + value: str + + +SdkconfigValueType = Union[bool, int, HexInt, str, RawSdkconfigValue] + + +def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType): + """Set an esp-idf sdkconfig value.""" + if not CORE.using_esp_idf: + raise ValueError("Not an esp-idf project") + CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS][name] = value + + +def _format_framework_arduino_version(ver: cv.Version) -> str: + # format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to + # a PIO platformio/framework-arduinoespressif32 value + # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32 + if ver <= cv.Version(1, 0, 3): + return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + + +# NOTE: Keep this in mind when updating the recommended version: +# * New framework historically have had some regressions, especially for WiFi. +# The new version needs to be thoroughly validated before changing the +# recommended version as otherwise a bunch of devices could be bricked +# * For all constants below, update platformio.ini (in this repo) +# and platformio.ini/platformio-lint.ini in the esphome-docker-base repository + +# The default/recommended arduino framework version +# - https://github.com/espressif/arduino-esp32/releases +# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32 +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(1, 0, 6) +# The platformio/espressif32 version to use for arduino frameworks +# - https://github.com/platformio/platform-espressif32/releases +# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 +ARDUINO_PLATFORM_VERSION = cv.Version(3, 3, 2) + +# The default/recommended esp-idf framework version +# - https://github.com/espressif/esp-idf/releases +# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 3, 0) +# The platformio/espressif32 version to use for esp-idf frameworks +# - https://github.com/platformio/platform-espressif32/releases +# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 +ESP_IDF_PLATFORM_VERSION = cv.Version(3, 3, 2) + + +def _arduino_check_versions(value): + value = value.copy() + lookups = { + "dev": ("https://github.com/espressif/arduino-esp32.git", cv.Version(2, 0, 0)), + "latest": ("", cv.Version(1, 0, 3)), + "recommended": ( + _format_framework_arduino_version(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION), + RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, + ), + } + ver_value = value[CONF_VERSION] + default_ver_hint = None + if ver_value.lower() in lookups: + default_ver_hint = str(lookups[ver_value.lower()][1]) + ver_value = lookups[ver_value.lower()][0] + else: + with cv.suppress_invalid(): + ver = cv.Version.parse(cv.version_number(value)) + if ver <= cv.Version(1, 0, 3): + ver_value = f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + else: + ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + default_ver_hint = str(ver) + value[CONF_VERSION] = ver_value + + if CONF_VERSION_HINT not in value and default_ver_hint is None: + raise cv.Invalid("Needs a version hint to understand the framework version") + + ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) + value[CONF_VERSION_HINT] = ver_hint_s + plat_ver = value.get(CONF_PLATFORM_VERSION, ARDUINO_PLATFORM_VERSION) + value[CONF_PLATFORM_VERSION] = str(plat_ver) + + if cv.Version.parse(ver_hint_s) != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: + _LOGGER.warning( + "The selected arduino framework version is not the recommended one" + ) + _LOGGER.warning( + "If there are connectivity or build issues please remove the manual version" + ) + + return value + + +def _format_framework_espidf_version(ver: cv.Version) -> str: + # format the given arduino (https://github.com/espressif/esp-idf/releases) version to + # a PIO platformio/framework-espidf value + # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf + return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + + +def _esp_idf_check_versions(value): + value = value.copy() + lookups = { + "dev": ("https://github.com/espressif/esp-idf.git", cv.Version(4, 3, 1)), + "latest": ("", cv.Version(4, 3, 0)), + "recommended": ( + _format_framework_espidf_version(RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION), + RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, + ), + } + ver_value = value[CONF_VERSION] + default_ver_hint = None + if ver_value.lower() in lookups: + default_ver_hint = str(lookups[ver_value.lower()][1]) + ver_value = lookups[ver_value.lower()][0] + else: + with cv.suppress_invalid(): + ver = cv.Version.parse(cv.version_number(value)) + ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + default_ver_hint = str(ver) + value[CONF_VERSION] = ver_value + + if CONF_VERSION_HINT not in value and default_ver_hint is None: + raise cv.Invalid("Needs a version hint to understand the framework version") + + ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) + value[CONF_VERSION_HINT] = ver_hint_s + if cv.Version.parse(ver_hint_s) < cv.Version(4, 0, 0): + raise cv.Invalid("Only ESP-IDF 4.0+ is supported") + if cv.Version.parse(ver_hint_s) != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION: + _LOGGER.warning( + "The selected esp-idf framework version is not the recommended one" + ) + _LOGGER.warning( + "If there are connectivity or build issues please remove the manual version" + ) + + plat_ver = value.get(CONF_PLATFORM_VERSION, ESP_IDF_PLATFORM_VERSION) + value[CONF_PLATFORM_VERSION] = str(plat_ver) + + return value + + +CONF_VERSION_HINT = "version_hint" +CONF_PLATFORM_VERSION = "platform_version" +ARDUINO_FRAMEWORK_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, + } + ), + _arduino_check_versions, +) +CONF_SDKCONFIG_OPTIONS = "sdkconfig_options" +ESP_IDF_FRAMEWORK_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { + cv.string_strict: cv.string_strict + }, + cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, + } + ), + _esp_idf_check_versions, +) + + +FRAMEWORK_ESP_IDF = "esp-idf" +FRAMEWORK_ARDUINO = "arduino" +FRAMEWORK_SCHEMA = cv.typed_schema( + { + FRAMEWORK_ESP_IDF: ESP_IDF_FRAMEWORK_SCHEMA, + FRAMEWORK_ARDUINO: ARDUINO_FRAMEWORK_SCHEMA, + }, + lower=True, + space="-", + default_type=FRAMEWORK_ARDUINO, +) + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_BOARD): cv.string_strict, + cv.Optional(CONF_VARIANT, default="ESP32"): cv.one_of( + *VARIANTS, upper=True + ), + cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, + } + ), + set_core_data, +) + + +async def to_code(config): + cg.add_platformio_option("board", config[CONF_BOARD]) + cg.add_build_flag("-DUSE_ESP32") + cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) + cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") + + conf = config[CONF_FRAMEWORK] + if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: + cg.add_platformio_option( + "platform", f"espressif32 @ {conf[CONF_PLATFORM_VERSION]}" + ) + cg.add_platformio_option("framework", "espidf") + cg.add_build_flag("-DUSE_ESP_IDF") + cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ESP_IDF") + cg.add_build_flag("-Wno-nonnull-compare") + cg.add_platformio_option( + "platform_packages", + [f"platformio/framework-espidf @ {conf[CONF_VERSION]}"], + ) + add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False) + add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True) + add_idf_sdkconfig_option( + "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME", "partitions.csv" + ) + add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) + add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True) + + cg.add_platformio_option("board_build.partitions", "partitions.csv") + + for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): + add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + + elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: + cg.add_platformio_option( + "platform", f"espressif32 @ {conf[CONF_PLATFORM_VERSION]}" + ) + cg.add_platformio_option("framework", "arduino") + cg.add_build_flag("-DUSE_ARDUINO") + cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO") + cg.add_platformio_option( + "platform_packages", + [f"platformio/framework-arduinoespressif32 @ {conf[CONF_VERSION]}"], + ) + + cg.add_platformio_option("board_build.partitions", "partitions.csv") + + +ARDUINO_PARTITIONS_CSV = """\ +nvs, data, nvs, 0x009000, 0x005000, +otadata, data, ota, 0x00e000, 0x002000, +app0, app, ota_0, 0x010000, 0x1C0000, +app1, app, ota_1, 0x1D0000, 0x1C0000, +eeprom, data, 0x99, 0x390000, 0x001000, +spiffs, data, spiffs, 0x391000, 0x00F000 +""" + + +IDF_PARTITIONS_CSV = """\ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, , 0x4000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +app0, app, ota_0, , 0x1C0000, +app1, app, ota_1, , 0x1C0000, +""" + + +def _format_sdkconfig_val(value: SdkconfigValueType) -> str: + if isinstance(value, bool): + return "y" if value else "n" + if isinstance(value, int): + return str(value) + if isinstance(value, str): + return f'"{value}"' + if isinstance(value, RawSdkconfigValue): + return value.value + raise ValueError + + +def _write_sdkconfig(): + # sdkconfig.{name} stores the real sdkconfig (modified by esp-idf with default) + # sdkconfig.{name}.esphomeinternal stores what esphome last wrote + # we use the internal one to detect if there were any changes, and if so write them to the + # real sdkconfig + sdk_path = Path(CORE.relative_build_path(f"sdkconfig.{CORE.name}")) + internal_path = Path( + CORE.relative_build_path(f"sdkconfig.{CORE.name}.esphomeinternal") + ) + + want_opts = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] + contents = ( + "\n".join( + f"{name}={_format_sdkconfig_val(value)}" + for name, value in sorted(want_opts.items()) + ) + + "\n" + ) + if write_file_if_changed(internal_path, contents): + # internal changed, update real one + write_file_if_changed(sdk_path, contents) + + +# Called by writer.py +def copy_files(): + if CORE.using_arduino: + write_file_if_changed( + CORE.relative_build_path("partitions.csv"), + ARDUINO_PARTITIONS_CSV, + ) + if CORE.using_esp_idf: + _write_sdkconfig() + write_file_if_changed( + CORE.relative_build_path("partitions.csv"), + IDF_PARTITIONS_CSV, + ) diff --git a/esphome/boards.py b/esphome/components/esp32/boards.py similarity index 77% rename from esphome/boards.py rename to esphome/components/esp32/boards.py index ba6fe889ea..ddf4bf2026 100644 --- a/esphome/boards.py +++ b/esphome/components/esp32/boards.py @@ -1,212 +1,3 @@ -ESP8266_BASE_PINS = { - "A0": 17, - "SS": 15, - "MOSI": 13, - "MISO": 12, - "SCK": 14, - "SDA": 4, - "SCL": 5, - "RX": 3, - "TX": 1, -} - -ESP8266_BOARD_PINS = { - "d1": { - "D0": 3, - "D1": 1, - "D2": 16, - "D3": 5, - "D4": 4, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 0, - "D9": 2, - "D10": 15, - "D11": 13, - "D12": 14, - "D13": 14, - "D14": 4, - "D15": 5, - "LED": 2, - }, - "d1_mini": { - "D0": 16, - "D1": 5, - "D2": 4, - "D3": 0, - "D4": 2, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 15, - "LED": 2, - }, - "d1_mini_lite": "d1_mini", - "d1_mini_pro": "d1_mini", - "esp01": {}, - "esp01_1m": {}, - "esp07": {}, - "esp12e": {}, - "esp210": {}, - "esp8285": {}, - "esp_wroom_02": {}, - "espduino": {"LED": 16}, - "espectro": {"LED": 15, "BUTTON": 2}, - "espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0}, - "espinotee": {"LED": 16}, - "espmxdevkit": {}, - "espresso_lite_v1": {"LED": 16}, - "espresso_lite_v2": {"LED": 2}, - "gen4iod": {}, - "heltec_wifi_kit_8": "d1_mini", - "huzzah": { - "LED": 0, - "LED_RED": 0, - "LED_BLUE": 2, - "D4": 4, - "D5": 5, - "D12": 12, - "D13": 13, - "D14": 14, - "D15": 15, - "D16": 16, - }, - "inventone": {}, - "modwifi": {}, - "nodemcu": { - "D0": 16, - "D1": 5, - "D2": 4, - "D3": 0, - "D4": 2, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 15, - "D9": 3, - "D10": 1, - "LED": 16, - }, - "nodemcuv2": "nodemcu", - "oak": { - "P0": 2, - "P1": 5, - "P2": 0, - "P3": 3, - "P4": 1, - "P5": 4, - "P6": 15, - "P7": 13, - "P8": 12, - "P9": 14, - "P10": 16, - "P11": 17, - "LED": 5, - }, - "phoenix_v1": {"LED": 16}, - "phoenix_v2": {"LED": 2}, - "sonoff_basic": {}, - "sonoff_s20": {}, - "sonoff_sv": {}, - "sonoff_th": {}, - "sparkfunBlynk": "thing", - "thing": {"LED": 5, "SDA": 2, "SCL": 14}, - "thingdev": "thing", - "wifi_slot": {"LED": 2}, - "wifiduino": { - "D0": 3, - "D1": 1, - "D2": 2, - "D3": 0, - "D4": 4, - "D5": 5, - "D6": 16, - "D7": 14, - "D8": 12, - "D9": 13, - "D10": 15, - "D11": 13, - "D12": 12, - "D13": 14, - }, - "wifinfo": { - "LED": 12, - "D0": 16, - "D1": 5, - "D2": 4, - "D3": 0, - "D4": 2, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 15, - "D9": 3, - "D10": 1, - }, - "wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0}, - "wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0}, - "xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13}, -} - -FLASH_SIZE_1_MB = 2 ** 20 -FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2 -FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB -FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB -FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB - -ESP8266_FLASH_SIZES = { - "d1": FLASH_SIZE_4_MB, - "d1_mini": FLASH_SIZE_4_MB, - "d1_mini_lite": FLASH_SIZE_1_MB, - "d1_mini_pro": FLASH_SIZE_16_MB, - "esp01": FLASH_SIZE_512_KB, - "esp01_1m": FLASH_SIZE_1_MB, - "esp07": FLASH_SIZE_4_MB, - "esp12e": FLASH_SIZE_4_MB, - "esp210": FLASH_SIZE_4_MB, - "esp8285": FLASH_SIZE_1_MB, - "esp_wroom_02": FLASH_SIZE_2_MB, - "espduino": FLASH_SIZE_4_MB, - "espectro": FLASH_SIZE_4_MB, - "espino": FLASH_SIZE_4_MB, - "espinotee": FLASH_SIZE_4_MB, - "espmxdevkit": FLASH_SIZE_1_MB, - "espresso_lite_v1": FLASH_SIZE_4_MB, - "espresso_lite_v2": FLASH_SIZE_4_MB, - "gen4iod": FLASH_SIZE_512_KB, - "heltec_wifi_kit_8": FLASH_SIZE_4_MB, - "huzzah": FLASH_SIZE_4_MB, - "inventone": FLASH_SIZE_4_MB, - "modwifi": FLASH_SIZE_2_MB, - "nodemcu": FLASH_SIZE_4_MB, - "nodemcuv2": FLASH_SIZE_4_MB, - "oak": FLASH_SIZE_4_MB, - "phoenix_v1": FLASH_SIZE_4_MB, - "phoenix_v2": FLASH_SIZE_4_MB, - "sonoff_basic": FLASH_SIZE_1_MB, - "sonoff_s20": FLASH_SIZE_1_MB, - "sonoff_sv": FLASH_SIZE_1_MB, - "sonoff_th": FLASH_SIZE_1_MB, - "sparkfunBlynk": FLASH_SIZE_4_MB, - "thing": FLASH_SIZE_512_KB, - "thingdev": FLASH_SIZE_512_KB, - "wifi_slot": FLASH_SIZE_1_MB, - "wifiduino": FLASH_SIZE_4_MB, - "wifinfo": FLASH_SIZE_1_MB, - "wio_link": FLASH_SIZE_4_MB, - "wio_node": FLASH_SIZE_4_MB, - "xinabox_cw01": FLASH_SIZE_4_MB, -} - -ESP8266_LD_SCRIPTS = { - FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), - FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), - FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"), - FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"), - FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"), -} - ESP32_BASE_PINS = { "TX": 1, "RX": 3, @@ -1134,19 +925,3 @@ ESP32_BOARD_PINS = { }, "xinabox_cw02": {"LED": 27}, } - -ESP32_C3_BASE_PINS = { - "TX": 21, - "RX": 20, - "ADC1_0": 0, - "ADC1_1": 1, - "ADC1_2": 2, - "ADC1_3": 3, - "ADC1_4": 4, - "ADC2_0": 5, -} - -ESP32_C3_BOARD_PINS = { - "esp32-c3-devkitm-1": {"LED": 8}, - "esp32-c3-devkitc-02": "esp32-c3-devkitm-1", -} diff --git a/esphome/components/esp32/const.py b/esphome/components/esp32/const.py new file mode 100644 index 0000000000..b82f03bf68 --- /dev/null +++ b/esphome/components/esp32/const.py @@ -0,0 +1,21 @@ +import esphome.codegen as cg + +KEY_ESP32 = "esp32" +KEY_BOARD = "board" +KEY_VARIANT = "variant" +KEY_SDKCONFIG_OPTIONS = "sdkconfig_options" + +VARIANT_ESP32 = "ESP32" +VARIANT_ESP32S2 = "ESP32S2" +VARIANT_ESP32S3 = "ESP32S3" +VARIANT_ESP32C3 = "ESP32C3" +VARIANT_ESP32H2 = "ESP32H2" +VARIANTS = [ + VARIANT_ESP32, + VARIANT_ESP32S2, + VARIANT_ESP32S3, + VARIANT_ESP32C3, + VARIANT_ESP32H2, +] + +esp32_ns = cg.esphome_ns.namespace("esp32") diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp new file mode 100644 index 0000000000..96047df535 --- /dev/null +++ b/esphome/components/esp32/core.cpp @@ -0,0 +1,89 @@ +#ifdef USE_ESP32 + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "preferences.h" +#include +#include +#include +#include + +#if ESP_IDF_VERSION_MAJOR >= 4 +#include +#endif + +void setup(); +void loop(); + +namespace esphome { + +void IRAM_ATTR HOT yield() { vPortYield(); } +uint32_t IRAM_ATTR HOT millis() { return (uint32_t)(esp_timer_get_time() / 1000ULL); } +void IRAM_ATTR HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } +uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); } +void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { + auto start = (uint64_t) esp_timer_get_time(); + while (((uint64_t) esp_timer_get_time()) - start < us) + ; +} +void arch_restart() { + esp_restart(); + // restart() doesn't always end execution + while (true) { // NOLINT(clang-diagnostic-unreachable-code) + yield(); + } +} +void IRAM_ATTR HOT arch_feed_wdt() { +#ifdef USE_ARDUINO +#if CONFIG_ARDUINO_RUNNING_CORE == 0 +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 + // ESP32 uses "Task Watchdog" which is hooked to the FreeRTOS idle task. + // To cause the Watchdog to be triggered we need to put the current task + // to sleep to get the idle task scheduled. + delay(1); +#endif +#endif +#endif // USE_ARDUINO + +#ifdef USE_ESP_IDF +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 + delay(1); +#endif +#endif // USE_ESP_IDF +} + +uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } +uint32_t arch_get_cpu_cycle_count() { +#if ESP_IDF_VERSION_MAJOR >= 4 + return cpu_hal_get_cycle_count(); +#else + uint32_t ccount; + __asm__ __volatile__("esync; rsr %0,ccount" : "=a"(ccount)); + return ccount; +#endif +} +uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); } + +#ifdef USE_ESP_IDF +TaskHandle_t loop_task_handle = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +void loop_task(void *pv_params) { + setup(); + while (true) { + loop(); + } +} + +extern "C" void app_main() { + esp32::setup_preferences(); + xTaskCreate(loop_task, "loopTask", 8192, nullptr, 1, &loop_task_handle); +} +#endif // USE_ESP_IDF + +#ifdef USE_ARDUINO +extern "C" void init() { esp32::setup_preferences(); } +#endif // USE_ARDUINO + +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32/gpio.py b/esphome/components/esp32/gpio.py new file mode 100644 index 0000000000..93ab17db22 --- /dev/null +++ b/esphome/components/esp32/gpio.py @@ -0,0 +1,201 @@ +import logging + +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_INVERTED, + CONF_MODE, + CONF_NUMBER, + CONF_OPEN_DRAIN, + CONF_OUTPUT, + CONF_PULLDOWN, + CONF_PULLUP, +) +from esphome import pins +from esphome.core import CORE +import esphome.config_validation as cv +import esphome.codegen as cg + +from . import boards +from .const import KEY_BOARD, KEY_ESP32, esp32_ns + + +_LOGGER = logging.getLogger(__name__) + + +IDFInternalGPIOPin = esp32_ns.class_("IDFInternalGPIOPin", cg.InternalGPIOPin) +ArduinoInternalGPIOPin = esp32_ns.class_("ArduinoInternalGPIOPin", cg.InternalGPIOPin) + + +def _lookup_pin(value): + board = CORE.data[KEY_ESP32][KEY_BOARD] + board_pins = boards.ESP32_BOARD_PINS.get(board, {}) + + # Resolved aliased board pins (shorthand when two boards have the same pin configuration) + while isinstance(board_pins, str): + board_pins = boards.ESP32_BOARD_PINS[board_pins] + + if value in board_pins: + return board_pins[value] + if value in boards.ESP32_BASE_PINS: + return boards.ESP32_BASE_PINS[value] + raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.") + + +def _translate_pin(value): + if isinstance(value, dict) or value is None: + raise cv.Invalid( + "This variable only supports pin numbers, not full pin schemas " + "(with inverted and mode)." + ) + if isinstance(value, int): + return value + try: + return int(value) + except ValueError: + pass + if value.startswith("GPIO"): + return cv.int_(value[len("GPIO") :].strip()) + return _lookup_pin(value) + + +_ESP_SDIO_PINS = { + 6: "Flash Clock", + 7: "Flash Data 0", + 8: "Flash Data 1", + 11: "Flash Command", +} + + +def validate_gpio_pin(value): + value = _translate_pin(value) + if value < 0 or value > 39: + raise cv.Invalid(f"Invalid pin number: {value} (must be 0-39)") + if value in _ESP_SDIO_PINS: + raise cv.Invalid( + f"This pin cannot be used on ESP32s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" + ) + if 9 <= value <= 10: + _LOGGER.warning( + "Pin %s (9-10) might already be used by the " + "flash interface in QUAD IO flash mode.", + value, + ) + if value in (20, 24, 28, 29, 30, 31): + # These pins are not exposed in GPIO mux (reason unknown) + # but they're missing from IO_MUX list in datasheet + raise cv.Invalid(f"The pin GPIO{value} is not usable on ESP32s.") + return value + + +def validate_supports(value): + num = value[CONF_NUMBER] + mode = value[CONF_MODE] + is_input = mode[CONF_INPUT] + is_output = mode[CONF_OUTPUT] + is_open_drain = mode[CONF_OPEN_DRAIN] + is_pullup = mode[CONF_PULLUP] + is_pulldown = mode[CONF_PULLDOWN] + + if is_input: + # All ESP32 pins support input mode + pass + if is_output and 34 <= num <= 39: + raise cv.Invalid( + f"GPIO{num} (34-39) does not support output pin mode.", + [CONF_MODE, CONF_OUTPUT], + ) + if is_open_drain and not is_output: + raise cv.Invalid( + "Open-drain only works with output mode", [CONF_MODE, CONF_OPEN_DRAIN] + ) + if is_pullup and 34 <= num <= 39: + raise cv.Invalid( + f"GPIO{num} (34-39) does not support pullups.", [CONF_MODE, CONF_PULLUP] + ) + if is_pulldown and 34 <= num <= 39: + raise cv.Invalid( + f"GPIO{num} (34-39) does not support pulldowns.", [CONF_MODE, CONF_PULLDOWN] + ) + + if CORE.using_arduino: + # (input, output, open_drain, pullup, pulldown) + supported_modes = { + # INPUT + (True, False, False, False, False), + # OUTPUT + (False, True, False, False, False), + # INPUT_PULLUP + (True, False, False, True, False), + # INPUT_PULLDOWN + (True, False, False, False, True), + # OUTPUT_OPEN_DRAIN + (False, True, True, False, False), + } + key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown) + if key not in supported_modes: + raise cv.Invalid( + "This pin mode is not supported on ESP32 for arduino frameworks", + [CONF_MODE], + ) + + return value + + +# https://docs.espressif.com/projects/esp-idf/en/v3.3.5/api-reference/peripherals/gpio.html#_CPPv416gpio_drive_cap_t +gpio_drive_cap_t = cg.global_ns.enum("gpio_drive_cap_t") +DRIVE_STRENGTHS = { + 5.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_0, + 10.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_1, + 20.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_2, + 40.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_3, +} +gpio_num_t = cg.global_ns.enum("gpio_num_t") + + +def _choose_pin_declaration(value): + if CORE.using_esp_idf: + return cv.declare_id(IDFInternalGPIOPin)(value) + if CORE.using_arduino: + return cv.declare_id(ArduinoInternalGPIOPin)(value) + raise NotImplementedError + + +CONF_DRIVE_STRENGTH = "drive_strength" +ESP32_PIN_SCHEMA = cv.All( + { + cv.GenerateID(): _choose_pin_declaration, + cv.Required(CONF_NUMBER): validate_gpio_pin, + cv.Optional(CONF_MODE, default={}): cv.Schema( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_PULLDOWN, default=False): cv.boolean, + } + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.SplitDefault(CONF_DRIVE_STRENGTH, esp32_idf="20mA"): cv.All( + cv.only_with_esp_idf, + cv.float_with_unit("current", "mA", optional_unit=True), + cv.enum(DRIVE_STRENGTHS), + ), + }, + validate_supports, +) + + +@pins.PIN_SCHEMA_REGISTRY.register("esp32", ESP32_PIN_SCHEMA) +async def esp32_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + num = config[CONF_NUMBER] + if CORE.using_esp_idf: + cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}"))) + else: + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + if CONF_DRIVE_STRENGTH in config: + cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/esp32/gpio_arduino.cpp b/esphome/components/esp32/gpio_arduino.cpp new file mode 100644 index 0000000000..11de1e13e6 --- /dev/null +++ b/esphome/components/esp32/gpio_arduino.cpp @@ -0,0 +1,107 @@ +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include "gpio_arduino.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace esp32 { + +static const char *const TAG = "esp32"; + +struct ISRPinArg { + uint8_t pin; + bool inverted; +}; + +ISRInternalGPIOPin ArduinoInternalGPIOPin::to_isr() const { + auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) + arg->pin = pin_; + arg->inverted = inverted_; + return ISRInternalGPIOPin((void *) arg); +} + +void ArduinoInternalGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const { + uint8_t arduino_mode = DISABLED; + switch (type) { + case gpio::INTERRUPT_RISING_EDGE: + arduino_mode = inverted_ ? FALLING : RISING; + break; + case gpio::INTERRUPT_FALLING_EDGE: + arduino_mode = inverted_ ? RISING : FALLING; + break; + case gpio::INTERRUPT_ANY_EDGE: + arduino_mode = CHANGE; + break; + case gpio::INTERRUPT_LOW_LEVEL: + arduino_mode = inverted_ ? ONHIGH : ONLOW; + break; + case gpio::INTERRUPT_HIGH_LEVEL: + arduino_mode = inverted_ ? ONLOW : ONHIGH; + break; + } + + attachInterruptArg(pin_, func, arg, arduino_mode); +} +void ArduinoInternalGPIOPin::pin_mode(gpio::Flags flags) { + uint8_t mode; + if (flags == gpio::FLAG_INPUT) { + mode = INPUT; + } else if (flags == gpio::FLAG_OUTPUT) { + mode = OUTPUT; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + mode = INPUT_PULLUP; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) { + mode = INPUT_PULLDOWN; + } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + mode = OUTPUT_OPEN_DRAIN; + } else { + return; + } + pinMode(pin_, mode); // NOLINT +} + +std::string ArduinoInternalGPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "GPIO%u", pin_); + return buffer; +} + +bool ArduinoInternalGPIOPin::digital_read() { + return bool(digitalRead(pin_)) != inverted_; // NOLINT +} +void ArduinoInternalGPIOPin::digital_write(bool value) { + digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT +} +void ArduinoInternalGPIOPin::detach_interrupt() const { + detachInterrupt(pin_); // NOLINT +} + +} // namespace esp32 + +using namespace esp32; + +bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { + auto *arg = reinterpret_cast(arg_); + return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { + auto *arg = reinterpret_cast(arg_); + digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { + auto *arg = reinterpret_cast(arg_); +#ifdef CONFIG_IDF_TARGET_ESP32C3 + GPIO.status_w1tc.val = 1UL << arg->pin; +#else + if (arg->pin < 32) { + GPIO.status_w1tc = 1UL << arg->pin; + } else { + GPIO.status1_w1tc.intr_st = 1UL << (arg->pin - 32); + } +#endif +} + +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/esp32/gpio_arduino.h b/esphome/components/esp32/gpio_arduino.h new file mode 100644 index 0000000000..a077723075 --- /dev/null +++ b/esphome/components/esp32/gpio_arduino.h @@ -0,0 +1,36 @@ +#pragma once + +#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#include "esphome/core/hal.h" + +namespace esphome { +namespace esp32 { + +class ArduinoInternalGPIOPin : public InternalGPIOPin { + public: + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + + void setup() override { pin_mode(flags_); } + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; + std::string dump_summary() const override; + void detach_interrupt() const override; + ISRInternalGPIOPin to_isr() const override; + uint8_t get_pin() const override { return pin_; } + bool is_inverted() const override { return inverted_; } + + protected: + void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override; + + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; +}; + +} // namespace esp32 +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/esp32/gpio_idf.cpp b/esphome/components/esp32/gpio_idf.cpp new file mode 100644 index 0000000000..d662d5519a --- /dev/null +++ b/esphome/components/esp32/gpio_idf.cpp @@ -0,0 +1,49 @@ +#ifdef USE_ESP32_FRAMEWORK_ESP_IDF + +#include "gpio_idf.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace esp32 { + +static const char *const TAG = "esp32"; + +bool IDFInternalGPIOPin::isr_service_installed_ = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +struct ISRPinArg { + gpio_num_t pin; + bool inverted; +}; + +ISRInternalGPIOPin IDFInternalGPIOPin::to_isr() const { + auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) + arg->pin = pin_; + arg->inverted = inverted_; + return ISRInternalGPIOPin((void *) arg); +} + +std::string IDFInternalGPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast(pin_)); + return buffer; +} + +} // namespace esp32 + +using namespace esp32; + +bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { + auto *arg = reinterpret_cast(arg_); + return bool(gpio_get_level(arg->pin)) != arg->inverted; +} +void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { + auto *arg = reinterpret_cast(arg_); + gpio_set_level(arg->pin, value != arg->inverted ? 1 : 0); +} +void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { + // not supported +} + +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ESP_IDF diff --git a/esphome/components/esp32/gpio_idf.h b/esphome/components/esp32/gpio_idf.h new file mode 100644 index 0000000000..6a383afcae --- /dev/null +++ b/esphome/components/esp32/gpio_idf.h @@ -0,0 +1,96 @@ +#pragma once + +#ifdef USE_ESP32_FRAMEWORK_ESP_IDF +#include "esphome/core/hal.h" +#include + +namespace esphome { +namespace esp32 { + +class IDFInternalGPIOPin : public InternalGPIOPin { + public: + void set_pin(gpio_num_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_drive_strength(gpio_drive_cap_t drive_strength) { drive_strength_ = drive_strength; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + + void setup() override { + pin_mode(flags_); + gpio_set_drive_capability(pin_, drive_strength_); + } + void pin_mode(gpio::Flags flags) override { + gpio_config_t conf{}; + conf.pin_bit_mask = 1 << static_cast(pin_); + conf.mode = flags_to_mode_(flags); + conf.pull_up_en = flags & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; + conf.pull_down_en = flags & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE; + conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&conf); + } + bool digital_read() override { return bool(gpio_get_level(pin_)) != inverted_; } + void digital_write(bool value) override { gpio_set_level(pin_, value != inverted_ ? 1 : 0); } + std::string dump_summary() const override; + void detach_interrupt() const override { gpio_intr_disable(pin_); } + ISRInternalGPIOPin to_isr() const override; + uint8_t get_pin() const override { return (uint8_t) pin_; } + bool is_inverted() const override { return inverted_; } + + protected: + static gpio_mode_t flags_to_mode_(gpio::Flags flags) { + flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); + if (flags == gpio::FLAG_NONE) { + return GPIO_MODE_DISABLE; + } else if (flags == gpio::FLAG_INPUT) { + return GPIO_MODE_INPUT; + } else if (flags == gpio::FLAG_OUTPUT) { + return GPIO_MODE_OUTPUT; + } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + return GPIO_MODE_OUTPUT_OD; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + return GPIO_MODE_INPUT_OUTPUT_OD; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) { + return GPIO_MODE_INPUT_OUTPUT; + } else { + // unsupported + return GPIO_MODE_DISABLE; + } + } + void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override { + gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE; + switch (type) { + case gpio::INTERRUPT_RISING_EDGE: + idf_type = inverted_ ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE; + break; + case gpio::INTERRUPT_FALLING_EDGE: + idf_type = inverted_ ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE; + break; + case gpio::INTERRUPT_ANY_EDGE: + idf_type = GPIO_INTR_ANYEDGE; + break; + case gpio::INTERRUPT_LOW_LEVEL: + idf_type = inverted_ ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL; + break; + case gpio::INTERRUPT_HIGH_LEVEL: + idf_type = inverted_ ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL; + break; + } + gpio_set_intr_type(pin_, idf_type); + gpio_intr_enable(pin_); + if (!isr_service_installed_) { + gpio_install_isr_service(ESP_INTR_FLAG_LEVEL5); + isr_service_installed_ = true; + } + gpio_isr_handler_add(pin_, func, arg); + } + + gpio_num_t pin_; + bool inverted_; + gpio_drive_cap_t drive_strength_; + gpio::Flags flags_; + static bool isr_service_installed_; +}; + +} // namespace esp32 +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ESP_IDF diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp new file mode 100644 index 0000000000..639e6434d6 --- /dev/null +++ b/esphome/components/esp32/preferences.cpp @@ -0,0 +1,99 @@ +#ifdef USE_ESP32 + +#include "esphome/core/preferences.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace esp32 { + +static const char *const TAG = "esp32.preferences"; + +class ESP32PreferenceBackend : public ESPPreferenceBackend { + public: + std::string key; + uint32_t nvs_handle; + bool save(const uint8_t *data, size_t len) override { + esp_err_t err = nvs_set_blob(nvs_handle, key.c_str(), data, len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err)); + return false; + } + err = nvs_commit(nvs_handle); + if (err != 0) { + ESP_LOGV(TAG, "nvs_commit('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err)); + return false; + } + return true; + } + bool load(uint8_t *data, size_t len) override { + size_t actual_len; + esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err)); + return false; + } + if (actual_len != len) { + ESP_LOGVV(TAG, "NVS length does not match (%u!=%u)", actual_len, len); + return false; + } + err = nvs_get_blob(nvs_handle, key.c_str(), data, &len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err)); + return false; + } + return true; + } +}; + +class ESP32Preferences : public ESPPreferences { + public: + uint32_t nvs_handle; + uint32_t current_offset = 0; + + void open() { + esp_err_t err = nvs_open("esphome", NVS_READWRITE, &nvs_handle); + if (err == 0) + return; + + ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err)); + nvs_flash_deinit(); + nvs_flash_erase(); + nvs_flash_init(); + + err = nvs_open("esphome", NVS_READWRITE, &nvs_handle); + if (err != 0) { + nvs_handle = 0; + } + } + ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { + return make_preference(length, type); + } + ESPPreferenceObject make_preference(size_t length, uint32_t type) override { + auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) + pref->nvs_handle = nvs_handle; + current_offset += length; + + uint32_t keyval = current_offset ^ type; + char keybuf[16]; + snprintf(keybuf, sizeof(keybuf), "%d", keyval); + pref->key = keybuf; // copied to std::string + + return ESPPreferenceObject(pref); + } +}; + +void setup_preferences() { + auto *prefs = new ESP32Preferences(); // NOLINT(cppcoreguidelines-owning-memory) + prefs->open(); + global_preferences = prefs; +} + +} // namespace esp32 + +ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32/preferences.h b/esphome/components/esp32/preferences.h new file mode 100644 index 0000000000..e44213e4cf --- /dev/null +++ b/esphome/components/esp32/preferences.h @@ -0,0 +1,12 @@ +#pragma once +#ifdef USE_ESP32 + +namespace esphome { +namespace esp32 { + +void setup_preferences(); + +} // namespace esp32 +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index ccf1f6cafe..4b5c741ad9 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,8 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz"] CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"] @@ -20,3 +22,6 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 2d710a6ef7..143be06e3b 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -1,17 +1,21 @@ -#include "ble.h" +#ifdef USE_ESP32 +#include "ble.h" #include "esphome/core/application.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 - #include #include #include #include +#include #include #include +#ifdef USE_ARDUINO +#include +#endif + namespace esphome { namespace esp32_ble { @@ -52,9 +56,29 @@ bool ESP32BLE::ble_setup_() { return false; } - if (!btStart()) { - ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); - return false; + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + // start bt controller + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + err = esp_bt_controller_init(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); + return false; + } + while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) + ; + } + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { + err = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); + return false; + } + } + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + ESP_LOGE(TAG, "esp bt controller enable failed"); + return false; + } } esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 149f0008a4..008eba3235 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -11,7 +11,7 @@ #include "esphome/components/esp32_ble_server/ble_server.h" #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index bcfb0d1a1b..31b1f4c383 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -1,8 +1,11 @@ #include "ble_advertising.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "ble_uuid.h" +#include +#include +#include "esphome/core/log.h" namespace esphome { namespace esp32_ble { diff --git a/esphome/components/esp32_ble/ble_advertising.h b/esphome/components/esp32_ble/ble_advertising.h index d86089f333..01e2ba1295 100644 --- a/esphome/components/esp32_ble/ble_advertising.h +++ b/esphome/components/esp32_ble/ble_advertising.h @@ -2,7 +2,7 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp index b33275110d..8556aa87df 100644 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ b/esphome/components/esp32_ble/ble_uuid.cpp @@ -1,6 +1,10 @@ #include "ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 + +#include +#include +#include "esphome/core/log.h" namespace esphome { namespace esp32_ble { diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index 89082e19fa..f953f9fede 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -1,9 +1,9 @@ #pragma once #include "esphome/core/helpers.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index cd123d5469..8fb2803237 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -1,15 +1,19 @@ #pragma once + +#ifdef USE_ESP32 + #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include #include - -#ifdef ARDUINO_ARCH_ESP32 +#include #include #include #include +#include +#include /* * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index cd0cb4bed6..d6cbb15dd2 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -1,8 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] CONFLICTS_WITH = ["esp32_ble_tracker"] esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon") @@ -29,3 +31,6 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_major(config[CONF_MAJOR])) cg.add(var.set_minor(config[CONF_MINOR])) + + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index c9e00a1093..96afadd19a 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -1,14 +1,16 @@ #include "esp32_ble_beacon.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include -#include +#include #include #include #include #include +#include +#include "esphome/core/hal.h" namespace esphome { namespace esp32_ble_beacon { @@ -59,6 +61,7 @@ void ESP32BLEBeacon::ble_core_task(void *params) { delay(1000); // NOLINT } } + void ESP32BLEBeacon::ble_setup() { // Initialize non-volatile storage for the bluetooth controller esp_err_t err = nvs_flash_init(); @@ -67,9 +70,29 @@ void ESP32BLEBeacon::ble_setup() { return; } - if (!btStart()) { - ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); - return; + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + // start bt controller + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + err = esp_bt_controller_init(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); + return; + } + while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) + ; + } + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { + err = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); + return; + } + } + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + ESP_LOGE(TAG, "esp bt controller enable failed"); + return; + } } esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h index aba02830b3..d0ef73899c 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 0dcbcadc50..2fcc5c7743 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -1,12 +1,14 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_MODEL, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID, CONF_MODEL from esphome.components import esp32_ble +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option AUTO_LOAD = ["esp32_ble"] CODEOWNERS = ["@jesserockz"] CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] CONF_MANUFACTURER = "manufacturer" CONF_BLE_ID = "ble_id" @@ -37,3 +39,6 @@ async def to_code(config): cg.add_define("USE_ESP32_BLE_SERVER") cg.add(parent.set_server(var)) + + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) diff --git a/esphome/components/esp32_ble_server/ble_2901.cpp b/esphome/components/esp32_ble_server/ble_2901.cpp index 962ba3ffbe..ee0808d2c4 100644 --- a/esphome/components/esp32_ble_server/ble_2901.cpp +++ b/esphome/components/esp32_ble_server/ble_2901.cpp @@ -1,7 +1,7 @@ #include "ble_2901.h" #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_2901.h b/esphome/components/esp32_ble_server/ble_2901.h index 3bb23ae69d..60f53e55b2 100644 --- a/esphome/components/esp32_ble_server/ble_2901.h +++ b/esphome/components/esp32_ble_server/ble_2901.h @@ -2,7 +2,7 @@ #include "ble_descriptor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_2902.cpp b/esphome/components/esp32_ble_server/ble_2902.cpp index 0a87b239f9..2f34573c37 100644 --- a/esphome/components/esp32_ble_server/ble_2902.cpp +++ b/esphome/components/esp32_ble_server/ble_2902.cpp @@ -1,7 +1,9 @@ #include "ble_2902.h" #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 + +#include namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_2902.h b/esphome/components/esp32_ble_server/ble_2902.h index 024eec755e..64605924ad 100644 --- a/esphome/components/esp32_ble_server/ble_2902.h +++ b/esphome/components/esp32_ble_server/ble_2902.h @@ -2,7 +2,7 @@ #include "ble_descriptor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index d8ff39cc94..fae8c13934 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -4,7 +4,7 @@ #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_characteristic.h b/esphome/components/esp32_ble_server/ble_characteristic.h index bc5033f2ae..d2467dd176 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.h +++ b/esphome/components/esp32_ble_server/ble_characteristic.h @@ -5,13 +5,15 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include #include #include #include +#include +#include namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_descriptor.cpp b/esphome/components/esp32_ble_server/ble_descriptor.cpp index 5262087ad1..bfb6224335 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.cpp +++ b/esphome/components/esp32_ble_server/ble_descriptor.cpp @@ -1,10 +1,11 @@ #include "ble_descriptor.h" #include "ble_characteristic.h" #include "ble_service.h" - #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#include + +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_descriptor.h b/esphome/components/esp32_ble_server/ble_descriptor.h index 1a72cb2b54..4b8fb345c3 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.h +++ b/esphome/components/esp32_ble_server/ble_descriptor.h @@ -2,7 +2,7 @@ #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index 5777e99e23..0b91c238c3 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -5,7 +5,7 @@ #include "esphome/core/application.h" #include "esphome/core/version.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index 9d955dda79..7df44b4c61 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -12,7 +12,7 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_server/ble_service.cpp b/esphome/components/esp32_ble_server/ble_service.cpp index 5cfc53a397..281164f0f5 100644 --- a/esphome/components/esp32_ble_server/ble_service.cpp +++ b/esphome/components/esp32_ble_server/ble_service.cpp @@ -2,7 +2,7 @@ #include "ble_server.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_service.h b/esphome/components/esp32_ble_server/ble_service.h index 9fdb95bde5..16cc897238 100644 --- a/esphome/components/esp32_ble_server/ble_service.h +++ b/esphome/components/esp32_ble_server/ble_service.h @@ -3,7 +3,7 @@ #include "ble_characteristic.h" #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 162a4dee89..e3d52f345a 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -4,7 +4,6 @@ import esphome.config_validation as cv from esphome import automation from esphome.const import ( CONF_ID, - ESP_PLATFORM_ESP32, CONF_INTERVAL, CONF_DURATION, CONF_TRIGGER_ID, @@ -15,8 +14,10 @@ from esphome.const import ( CONF_ON_BLE_SERVICE_DATA_ADVERTISE, CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, ) +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] AUTO_LOAD = ["xiaomi_ble", "ruuvi_ble"] CONF_ESP32_BLE_ID = "esp32_ble_id" @@ -216,6 +217,9 @@ async def to_code(config): cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) + async def register_ble_device(var, config): paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) diff --git a/esphome/components/esp32_ble_tracker/automation.h b/esphome/components/esp32_ble_tracker/automation.h index 9df2587ede..3505e9c26d 100644 --- a/esphome/components/esp32_ble_tracker/automation.h +++ b/esphome/components/esp32_ble_tracker/automation.h @@ -3,7 +3,7 @@ #include "esphome/core/automation.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_tracker { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 358fe50605..5568884b9a 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -1,18 +1,24 @@ +#ifdef USE_ESP32 + #include "esp32_ble_tracker.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" - -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/hal.h" #include #include #include #include +#include #include #include #include +#ifdef USE_ARDUINO +#include +#endif + // bt_trace.h #undef TAG @@ -126,14 +132,33 @@ bool ESP32BLETracker::ble_setup() { return false; } - esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); - - // Initialize the bluetooth controller with the default configuration - if (!btStart()) { - ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); - return false; + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + // start bt controller + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + err = esp_bt_controller_init(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); + return false; + } + while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) + ; + } + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { + err = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); + return false; + } + } + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + ESP_LOGE(TAG, "esp bt controller enable failed"); + return false; + } } + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + err = esp_bluedroid_init(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 0594c4a811..40955d39cf 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "queue.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_tracker/queue.h b/esphome/components/esp32_ble_tracker/queue.h index f0f6ab9f17..3d38c17584 100644 --- a/esphome/components/esp32_ble_tracker/queue.h +++ b/esphome/components/esp32_ble_tracker/queue.h @@ -1,14 +1,17 @@ #pragma once + +#ifdef USE_ESP32 #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include #include - -#ifdef ARDUINO_ARCH_ESP32 +#include #include #include +#include +#include /* * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index e3c7383953..de61ab43cf 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -9,16 +9,16 @@ from esphome.const import ( CONF_PIN, CONF_SCL, CONF_SDA, - ESP_PLATFORM_ESP32, CONF_DATA_PINS, CONF_RESET_PIN, CONF_RESOLUTION, CONF_BRIGHTNESS, CONF_CONTRAST, ) +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -DEPENDENCIES = ["api"] +DEPENDENCIES = ["esp32", "api"] esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.Nameable) @@ -68,13 +68,15 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(): cv.declare_id(ESP32Camera), cv.Required(CONF_NAME): cv.string, cv.Optional(CONF_DISABLED_BY_DEFAULT, default=False): cv.boolean, - cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)), - cv.Required(CONF_VSYNC_PIN): pins.input_pin, - cv.Required(CONF_HREF_PIN): pins.input_pin, - cv.Required(CONF_PIXEL_CLOCK_PIN): pins.input_pin, + cv.Required(CONF_DATA_PINS): cv.All( + [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) + ), + cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( { - cv.Required(CONF_PIN): pins.output_pin, + cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( cv.frequency, cv.one_of(20e6, 10e6) ), @@ -82,12 +84,12 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Required(CONF_I2C_PINS): cv.Schema( { - cv.Required(CONF_SDA): pins.output_pin, - cv.Required(CONF_SCL): pins.output_pin, + cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number, + cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number, } ), - cv.Optional(CONF_RESET_PIN): pins.output_pin, - cv.Optional(CONF_POWER_DOWN_PIN): pins.output_pin, + cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( cv.framerate, cv.Range(min=0, min_included=False, max=60) ), @@ -146,3 +148,8 @@ async def to_code(config): cg.add_define("USE_ESP32_CAMERA") cg.add_build_flag("-DBOARD_HAS_PSRAM") + + if CORE.using_esp_idf: + cg.add_library("espressif/esp32-camera", "1.0.0") + add_idf_sdkconfig_option("CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC", True) + add_idf_sdkconfig_option("CONFIG_ESP32_SPIRAM_SUPPORT", True) diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 072cf41460..05445f024b 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -1,7 +1,10 @@ +#ifdef USE_ESP32 + #include "esp32_camera.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" -#ifdef ARDUINO_ARCH_ESP32 +#include namespace esphome { namespace esp32_camera { @@ -42,7 +45,9 @@ void ESP32Camera::dump_config() { auto conf = this->config_; ESP_LOGCONFIG(TAG, "ESP32 Camera:"); ESP_LOGCONFIG(TAG, " Name: %s", this->name_.c_str()); +#ifdef USE_ARDUINO ESP_LOGCONFIG(TAG, " Board Has PSRAM: %s", YESNO(psramFound())); +#endif // USE_ARDUINO ESP_LOGCONFIG(TAG, " Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d", conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3, conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7); ESP_LOGCONFIG(TAG, " VSYNC Pin: %d", conf.pin_vsync); diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 03272d3b32..430391aa76 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -1,10 +1,12 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include +#include +#include namespace esphome { namespace esp32_camera { diff --git a/esphome/components/esp32_dac/esp32_dac.cpp b/esphome/components/esp32_dac/esp32_dac.cpp index 20a047f0ba..7f37e2ce47 100644 --- a/esphome/components/esp32_dac/esp32_dac.cpp +++ b/esphome/components/esp32_dac/esp32_dac.cpp @@ -2,9 +2,14 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 +#ifdef USE_ARDUINO #include +#endif +#ifdef USE_ESP_IDF +#include +#endif namespace esphome { namespace esp32_dac { @@ -15,6 +20,11 @@ void ESP32DAC::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP32 DAC Output..."); this->pin_->setup(); this->turn_off(); + +#ifdef USE_ESP_IDF + auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; + dac_output_enable(channel); +#endif } void ESP32DAC::dump_config() { @@ -28,7 +38,14 @@ void ESP32DAC::write_state(float state) { state = 1.0f - state; state = state * 255; + +#ifdef USE_ESP_IDF + auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; + dac_output_voltage(channel, (uint8_t) state); +#endif +#ifdef USE_ARDUINO dacWrite(this->pin_->get_pin(), state); +#endif } } // namespace esp32_dac diff --git a/esphome/components/esp32_dac/esp32_dac.h b/esphome/components/esp32_dac/esp32_dac.h index 648efcfe4f..0fb1ddebf0 100644 --- a/esphome/components/esp32_dac/esp32_dac.h +++ b/esphome/components/esp32_dac/esp32_dac.h @@ -1,18 +1,18 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/output/float_output.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_dac { class ESP32DAC : public output::FloatOutput, public Component { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } /// Initialize pin void setup() override; @@ -23,7 +23,7 @@ class ESP32DAC : public output::FloatOutput, public Component { protected: void write_state(float state) override; - GPIOPin *pin_; + InternalGPIOPin *pin_; }; } // namespace esp32_dac diff --git a/esphome/components/esp32_dac/output.py b/esphome/components/esp32_dac/output.py index 8534a1bae1..f119198618 100644 --- a/esphome/components/esp32_dac/output.py +++ b/esphome/components/esp32_dac/output.py @@ -2,9 +2,9 @@ from esphome import pins from esphome.components import output import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] def valid_dac_pin(value): diff --git a/esphome/components/esp32_hall/esp32_hall.cpp b/esphome/components/esp32_hall/esp32_hall.cpp index 4bbf65e048..762497aedc 100644 --- a/esphome/components/esp32_hall/esp32_hall.cpp +++ b/esphome/components/esp32_hall/esp32_hall.cpp @@ -1,8 +1,8 @@ +#ifdef USE_ESP32 #include "esp32_hall.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" - -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/hal.h" +#include namespace esphome { namespace esp32_hall { @@ -10,7 +10,9 @@ namespace esp32_hall { static const char *const TAG = "esp32_hall"; void ESP32HallSensor::update() { - float value = (hallRead() / 4095.0f) * 10000.0f; + adc1_config_width(ADC_WIDTH_BIT_12); + int value_int = hall_sensor_read(); + float value = (value_int / 4095.0f) * 10000.0f; ESP_LOGD(TAG, "'%s': Got reading %.0f µT", this->name_.c_str(), value); this->publish_state(value); } diff --git a/esphome/components/esp32_hall/esp32_hall.h b/esphome/components/esp32_hall/esp32_hall.h index 040280fff3..8db50c4667 100644 --- a/esphome/components/esp32_hall/esp32_hall.h +++ b/esphome/components/esp32_hall/esp32_hall.h @@ -3,7 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_hall { diff --git a/esphome/components/esp32_hall/sensor.py b/esphome/components/esp32_hall/sensor.py index 4ba79de714..a752da2c97 100644 --- a/esphome/components/esp32_hall/sensor.py +++ b/esphome/components/esp32_hall/sensor.py @@ -3,13 +3,12 @@ import esphome.config_validation as cv from esphome.components import sensor from esphome.const import ( CONF_ID, - ESP_PLATFORM_ESP32, STATE_CLASS_MEASUREMENT, UNIT_MICROTESLA, ICON_MAGNET, ) -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] esp32_hall_ns = cg.esphome_ns.namespace("esp32_hall") ESP32HallSensor = esp32_hall_ns.class_( diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 4c337055cb..0b0214c63e 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,14 +1,13 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor, output, esp32_ble_server -from esphome.const import CONF_ID, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID AUTO_LOAD = ["binary_sensor", "output", "improv", "esp32_ble_server"] CODEOWNERS = ["@jesserockz"] CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"] -DEPENDENCIES = ["wifi"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["wifi", "esp32"] CONF_AUTHORIZED_DURATION = "authorized_duration" CONF_AUTHORIZER = "authorizer" diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index b584d2e8b9..fc58fbd264 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -5,7 +5,7 @@ #include "esphome/core/application.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_improv { diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 262124f983..af39ae4748 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -10,7 +10,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_improv { diff --git a/esphome/components/esp32_touch/__init__.py b/esphome/components/esp32_touch/__init__.py index 1564476ecf..cdf6aa3abd 100644 --- a/esphome/components/esp32_touch/__init__.py +++ b/esphome/components/esp32_touch/__init__.py @@ -9,12 +9,11 @@ from esphome.const import ( CONF_SETUP_MODE, CONF_SLEEP_DURATION, CONF_VOLTAGE_ATTENUATION, - ESP_PLATFORM_ESP32, ) from esphome.core import TimePeriod AUTO_LOAD = ["binary_sensor"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] esp32_touch_ns = cg.esphome_ns.namespace("esp32_touch") ESP32TouchComponent = esp32_touch_ns.class_("ESP32TouchComponent", cg.Component) diff --git a/esphome/components/esp32_touch/binary_sensor.py b/esphome/components/esp32_touch/binary_sensor.py index 198a43a738..93640334cd 100644 --- a/esphome/components/esp32_touch/binary_sensor.py +++ b/esphome/components/esp32_touch/binary_sensor.py @@ -5,14 +5,12 @@ from esphome.const import ( CONF_NAME, CONF_PIN, CONF_THRESHOLD, - ESP_PLATFORM_ESP32, CONF_ID, ) -from esphome.pins import validate_gpio_pin +from esphome.components.esp32 import gpio from . import esp32_touch_ns, ESP32TouchComponent -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -DEPENDENCIES = ["esp32_touch"] +DEPENDENCIES = ["esp32_touch", "esp32"] CONF_ESP32_TOUCH_ID = "esp32_touch_id" CONF_WAKEUP_THRESHOLD = "wakeup_threshold" @@ -32,7 +30,7 @@ TOUCH_PADS = { def validate_touch_pad(value): - value = validate_gpio_pin(value) + value = gpio.validate_gpio_pin(value) if value not in TOUCH_PADS: raise cv.Invalid(f"Pin {value} does not support touch pads.") return value diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index 9ae671a4a9..801106ab6c 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -1,7 +1,8 @@ +#ifdef USE_ESP32 + #include "esp32_touch.h" #include "esphome/core/log.h" - -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/hal.h" namespace esphome { namespace esp32_touch { diff --git a/esphome/components/esp32_touch/esp32_touch.h b/esphome/components/esp32_touch/esp32_touch.h index 8b92a482c0..c584a6d9bc 100644 --- a/esphome/components/esp32_touch/esp32_touch.h +++ b/esphome/components/esp32_touch/esp32_touch.h @@ -1,9 +1,16 @@ #pragma once +#ifdef USE_ESP32 + #include "esphome/core/component.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include -#ifdef ARDUINO_ARCH_ESP32 +#if ESP_IDF_VERSION_MAJOR >= 4 +#include +#else +#include +#endif namespace esphome { namespace esp32_touch { diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py new file mode 100644 index 0000000000..592de06440 --- /dev/null +++ b/esphome/components/esp8266/__init__.py @@ -0,0 +1,213 @@ +import logging + +from esphome.const import ( + CONF_BOARD, + CONF_BOARD_FLASH_MODE, + CONF_FRAMEWORK, + CONF_VERSION, + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, +) +from esphome.core import CORE +import esphome.config_validation as cv +import esphome.codegen as cg + +from .const import CONF_RESTORE_FROM_FLASH, KEY_BOARD, KEY_ESP8266 +from .boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS + +# force import gpio to register pin schema +from .gpio import esp8266_pin_to_code # noqa + + +CODEOWNERS = ["@esphome/core"] +_LOGGER = logging.getLogger(__name__) + + +def set_core_data(config): + CORE.data[KEY_ESP8266] = {} + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp8266" + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" + CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( + config[CONF_FRAMEWORK][CONF_VERSION_HINT] + ) + CORE.data[KEY_ESP8266][KEY_BOARD] = config[CONF_BOARD] + return config + + +def _format_framework_arduino_version(ver: cv.Version) -> str: + # format the given arduino (https://github.com/esp8266/Arduino/releases) version to + # a PIO platformio/framework-arduinoespressif8266 value + # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif8266 + if ver <= cv.Version(2, 4, 1): + return f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + if ver <= cv.Version(2, 6, 2): + return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + + +# NOTE: Keep this in mind when updating the recommended version: +# * New framework historically have had some regressions, especially for WiFi. +# The new version needs to be thoroughly validated before changing the +# recommended version as otherwise a bunch of devices could be bricked +# * For all constants below, update platformio.ini (in this repo) +# and platformio.ini/platformio-lint.ini in the esphome-docker-base repository + +# The default/recommended arduino framework version +# - https://github.com/esp8266/Arduino/releases +# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif8266 +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 7, 4) +# The platformio/espressif8266 version to use for arduino 2 framework versions +# - https://github.com/platformio/platform-espressif8266/releases +# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif8266 +ARDUINO_2_PLATFORM_VERSION = cv.Version(2, 6, 2) +# for arduino 3 framework versions +ARDUINO_3_PLATFORM_VERSION = cv.Version(3, 0, 2) + + +def _arduino_check_versions(value): + value = value.copy() + lookups = { + "dev": ("https://github.com/esp8266/Arduino.git", cv.Version(3, 0, 2)), + "latest": ("", cv.Version(3, 0, 2)), + "recommended": ( + _format_framework_arduino_version(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION), + RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, + ), + } + ver_value = value[CONF_VERSION] + default_ver_hint = None + if ver_value.lower() in lookups: + default_ver_hint = str(lookups[ver_value.lower()][1]) + ver_value = lookups[ver_value.lower()][0] + else: + with cv.suppress_invalid(): + ver = cv.Version.parse(cv.version_number(value)) + if ver <= cv.Version(2, 4, 1): + ver_value = f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + elif ver <= cv.Version(2, 6, 2): + ver_value = f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + else: + ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + default_ver_hint = str(ver) + + value[CONF_VERSION] = ver_value + + if CONF_VERSION_HINT not in value and default_ver_hint is None: + raise cv.Invalid("Needs a version hint to understand the framework version") + + ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) + value[CONF_VERSION_HINT] = ver_hint_s + plat_ver = value.get(CONF_PLATFORM_VERSION) + + if plat_ver is None: + ver_hint = cv.Version.parse(ver_hint_s) + if ver_hint >= cv.Version(3, 0, 0): + plat_ver = ARDUINO_3_PLATFORM_VERSION + elif ver_hint >= cv.Version(2, 5, 0): + plat_ver = ARDUINO_2_PLATFORM_VERSION + else: + plat_ver = cv.Version(1, 8, 0) + value[CONF_PLATFORM_VERSION] = str(plat_ver) + + if cv.Version.parse(ver_hint_s) != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: + _LOGGER.warning( + "The selected arduino framework version is not the recommended one" + ) + _LOGGER.warning( + "If there are connectivity or build issues please remove the manual version" + ) + + return value + + +CONF_VERSION_HINT = "version_hint" +CONF_PLATFORM_VERSION = "platform_version" +ARDUINO_FRAMEWORK_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, + } + ), + _arduino_check_versions, +) + + +BUILD_FLASH_MODES = ["qio", "qout", "dio", "dout"] +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_BOARD): cv.string_strict, + cv.Optional(CONF_FRAMEWORK, default={}): ARDUINO_FRAMEWORK_SCHEMA, + cv.Optional(CONF_RESTORE_FROM_FLASH, default=False): cv.boolean, + cv.Optional(CONF_BOARD_FLASH_MODE, default="dout"): cv.one_of( + *BUILD_FLASH_MODES, lower=True + ), + } + ), + set_core_data, +) + + +async def to_code(config): + cg.add_platformio_option("board", config[CONF_BOARD]) + cg.add_build_flag("-DUSE_ESP8266") + cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) + + conf = config[CONF_FRAMEWORK] + cg.add_platformio_option("framework", "arduino") + cg.add_build_flag("-DUSE_ARDUINO") + cg.add_build_flag("-DUSE_ESP8266_FRAMEWORK_ARDUINO") + cg.add_platformio_option( + "platform_packages", + [f"platformio/framework-arduinoespressif8266 @ {conf[CONF_VERSION]}"], + ) + cg.add_platformio_option( + "platform", f"platformio/espressif8266 @ {conf[CONF_PLATFORM_VERSION]}" + ) + + # Default for platformio is LWIP2_LOW_MEMORY with: + # - MSS=536 + # - LWIP_FEATURES enabled + # - this only adds some optional features like IP incoming packet reassembly and NAPT + # see also: + # https://github.com/esp8266/Arduino/blob/master/tools/sdk/lwip2/include/lwipopts.h + + # Instead we use LWIP2_HIGHER_BANDWIDTH_LOW_FLASH with: + # - MSS=1460 + # - LWIP_FEATURES disabled (because we don't need them) + # Other projects like Tasmota & ESPEasy also use this + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH") + + if config[CONF_RESTORE_FROM_FLASH]: + cg.add_define("USE_ESP8266_PREFERENCES_FLASH") + + # Arduino 2 has a non-standards conformant new that returns a nullptr instead of failing when + # out of memory and exceptions are disabled. Since Arduino 2.6.0, this flag can be used to make + # new abort instead. Use it so that OOM fails early (on allocation) instead of on dereference of + # a NULL pointer (so the stacktrace makes more sense), and for consistency with Arduino 3, + # which always aborts if exceptions are disabled. + # For cases where nullptrs can be handled, use nothrow: `new (std::nothrow) T;` + cg.add_build_flag("-DNEW_OOM_ABORT") + + cg.add_platformio_option("board_build.flash_mode", config[CONF_BOARD_FLASH_MODE]) + + if config[CONF_BOARD] in ESP8266_FLASH_SIZES: + flash_size = ESP8266_FLASH_SIZES[config[CONF_BOARD]] + ld_scripts = ESP8266_LD_SCRIPTS[flash_size] + ver = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + + if ver <= cv.Version(2, 3, 0): + # No ld script support + ld_script = None + if ver <= cv.Version(2, 4, 2): + # Old ld script path + ld_script = ld_scripts[0] + else: + ld_script = ld_scripts[1] + + if ld_script is not None: + cg.add_platformio_option("board_build.ldscript", ld_script) diff --git a/esphome/components/esp8266/boards.py b/esphome/components/esp8266/boards.py new file mode 100644 index 0000000000..c49aae4ffa --- /dev/null +++ b/esphome/components/esp8266/boards.py @@ -0,0 +1,266 @@ +FLASH_SIZE_1_MB = 2 ** 20 +FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2 +FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB +FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB +FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB + +ESP8266_FLASH_SIZES = { + "d1": FLASH_SIZE_4_MB, + "d1_mini": FLASH_SIZE_4_MB, + "d1_mini_lite": FLASH_SIZE_1_MB, + "d1_mini_pro": FLASH_SIZE_16_MB, + "esp01": FLASH_SIZE_512_KB, + "esp01_1m": FLASH_SIZE_1_MB, + "esp07": FLASH_SIZE_4_MB, + "esp12e": FLASH_SIZE_4_MB, + "esp210": FLASH_SIZE_4_MB, + "esp8285": FLASH_SIZE_1_MB, + "esp_wroom_02": FLASH_SIZE_2_MB, + "espduino": FLASH_SIZE_4_MB, + "espectro": FLASH_SIZE_4_MB, + "espino": FLASH_SIZE_4_MB, + "espinotee": FLASH_SIZE_4_MB, + "espmxdevkit": FLASH_SIZE_1_MB, + "espresso_lite_v1": FLASH_SIZE_4_MB, + "espresso_lite_v2": FLASH_SIZE_4_MB, + "gen4iod": FLASH_SIZE_512_KB, + "heltec_wifi_kit_8": FLASH_SIZE_4_MB, + "huzzah": FLASH_SIZE_4_MB, + "inventone": FLASH_SIZE_4_MB, + "modwifi": FLASH_SIZE_2_MB, + "nodemcu": FLASH_SIZE_4_MB, + "nodemcuv2": FLASH_SIZE_4_MB, + "oak": FLASH_SIZE_4_MB, + "phoenix_v1": FLASH_SIZE_4_MB, + "phoenix_v2": FLASH_SIZE_4_MB, + "sonoff_basic": FLASH_SIZE_1_MB, + "sonoff_s20": FLASH_SIZE_1_MB, + "sonoff_sv": FLASH_SIZE_1_MB, + "sonoff_th": FLASH_SIZE_1_MB, + "sparkfunBlynk": FLASH_SIZE_4_MB, + "thing": FLASH_SIZE_512_KB, + "thingdev": FLASH_SIZE_512_KB, + "wifi_slot": FLASH_SIZE_1_MB, + "wifiduino": FLASH_SIZE_4_MB, + "wifinfo": FLASH_SIZE_1_MB, + "wio_link": FLASH_SIZE_4_MB, + "wio_node": FLASH_SIZE_4_MB, + "xinabox_cw01": FLASH_SIZE_4_MB, +} + +ESP8266_LD_SCRIPTS = { + FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), + FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), + FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"), + FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"), + FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"), +} + +ESP8266_BASE_PINS = { + "A0": 17, + "SS": 15, + "MOSI": 13, + "MISO": 12, + "SCK": 14, + "SDA": 4, + "SCL": 5, + "RX": 3, + "TX": 1, +} + +ESP8266_BOARD_PINS = { + "d1": { + "D0": 3, + "D1": 1, + "D2": 16, + "D3": 5, + "D4": 4, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 0, + "D9": 2, + "D10": 15, + "D11": 13, + "D12": 14, + "D13": 14, + "D14": 4, + "D15": 5, + "LED": 2, + }, + "d1_mini": { + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "LED": 2, + }, + "d1_mini_lite": "d1_mini", + "d1_mini_pro": "d1_mini", + "esp01": {}, + "esp01_1m": {}, + "esp07": {}, + "esp12e": {}, + "esp210": {}, + "esp8285": {}, + "esp_wroom_02": {}, + "espduino": {"LED": 16}, + "espectro": {"LED": 15, "BUTTON": 2}, + "espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0}, + "espinotee": {"LED": 16}, + "espmxdevkit": {}, + "espresso_lite_v1": {"LED": 16}, + "espresso_lite_v2": {"LED": 2}, + "gen4iod": {}, + "heltec_wifi_kit_8": "d1_mini", + "huzzah": { + "LED": 0, + "LED_RED": 0, + "LED_BLUE": 2, + "D4": 4, + "D5": 5, + "D12": 12, + "D13": 13, + "D14": 14, + "D15": 15, + "D16": 16, + }, + "inventone": {}, + "modwifi": {}, + "nodemcu": { + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 3, + "D10": 1, + "LED": 16, + }, + "nodemcuv2": "nodemcu", + "oak": { + "P0": 2, + "P1": 5, + "P2": 0, + "P3": 3, + "P4": 1, + "P5": 4, + "P6": 15, + "P7": 13, + "P8": 12, + "P9": 14, + "P10": 16, + "P11": 17, + "LED": 5, + }, + "phoenix_v1": {"LED": 16}, + "phoenix_v2": {"LED": 2}, + "sonoff_basic": {}, + "sonoff_s20": {}, + "sonoff_sv": {}, + "sonoff_th": {}, + "sparkfunBlynk": "thing", + "thing": {"LED": 5, "SDA": 2, "SCL": 14}, + "thingdev": "thing", + "wifi_slot": {"LED": 2}, + "wifiduino": { + "D0": 3, + "D1": 1, + "D2": 2, + "D3": 0, + "D4": 4, + "D5": 5, + "D6": 16, + "D7": 14, + "D8": 12, + "D9": 13, + "D10": 15, + "D11": 13, + "D12": 12, + "D13": 14, + }, + "wifinfo": { + "LED": 12, + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 3, + "D10": 1, + }, + "wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0}, + "wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0}, + "xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13}, +} + +FLASH_SIZE_1_MB = 2 ** 20 +FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2 +FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB +FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB +FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB + +ESP8266_FLASH_SIZES = { + "d1": FLASH_SIZE_4_MB, + "d1_mini": FLASH_SIZE_4_MB, + "d1_mini_lite": FLASH_SIZE_1_MB, + "d1_mini_pro": FLASH_SIZE_16_MB, + "esp01": FLASH_SIZE_512_KB, + "esp01_1m": FLASH_SIZE_1_MB, + "esp07": FLASH_SIZE_4_MB, + "esp12e": FLASH_SIZE_4_MB, + "esp210": FLASH_SIZE_4_MB, + "esp8285": FLASH_SIZE_1_MB, + "esp_wroom_02": FLASH_SIZE_2_MB, + "espduino": FLASH_SIZE_4_MB, + "espectro": FLASH_SIZE_4_MB, + "espino": FLASH_SIZE_4_MB, + "espinotee": FLASH_SIZE_4_MB, + "espmxdevkit": FLASH_SIZE_1_MB, + "espresso_lite_v1": FLASH_SIZE_4_MB, + "espresso_lite_v2": FLASH_SIZE_4_MB, + "gen4iod": FLASH_SIZE_512_KB, + "heltec_wifi_kit_8": FLASH_SIZE_4_MB, + "huzzah": FLASH_SIZE_4_MB, + "inventone": FLASH_SIZE_4_MB, + "modwifi": FLASH_SIZE_2_MB, + "nodemcu": FLASH_SIZE_4_MB, + "nodemcuv2": FLASH_SIZE_4_MB, + "oak": FLASH_SIZE_4_MB, + "phoenix_v1": FLASH_SIZE_4_MB, + "phoenix_v2": FLASH_SIZE_4_MB, + "sonoff_basic": FLASH_SIZE_1_MB, + "sonoff_s20": FLASH_SIZE_1_MB, + "sonoff_sv": FLASH_SIZE_1_MB, + "sonoff_th": FLASH_SIZE_1_MB, + "sparkfunBlynk": FLASH_SIZE_4_MB, + "thing": FLASH_SIZE_512_KB, + "thingdev": FLASH_SIZE_512_KB, + "wifi_slot": FLASH_SIZE_1_MB, + "wifiduino": FLASH_SIZE_4_MB, + "wifinfo": FLASH_SIZE_1_MB, + "wio_link": FLASH_SIZE_4_MB, + "wio_node": FLASH_SIZE_4_MB, + "xinabox_cw01": FLASH_SIZE_4_MB, +} + +ESP8266_LD_SCRIPTS = { + FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), + FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), + FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"), + FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"), + FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"), +} diff --git a/esphome/components/esp8266/const.py b/esphome/components/esp8266/const.py new file mode 100644 index 0000000000..16a050360c --- /dev/null +++ b/esphome/components/esp8266/const.py @@ -0,0 +1,8 @@ +import esphome.codegen as cg + +KEY_ESP8266 = "esp8266" +KEY_BOARD = "board" +CONF_RESTORE_FROM_FLASH = "restore_from_flash" + +# esp8266 namespace is already defined by arduino, manually prefix esphome +esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266") diff --git a/esphome/components/esp8266/core.cpp b/esphome/components/esp8266/core.cpp new file mode 100644 index 0000000000..453f772e09 --- /dev/null +++ b/esphome/components/esp8266/core.cpp @@ -0,0 +1,37 @@ +#ifdef USE_ESP8266 + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "preferences.h" +#include +#include + +namespace esphome { + +void IRAM_ATTR HOT yield() { ::yield(); } +uint32_t IRAM_ATTR HOT millis() { return ::millis(); } +void IRAM_ATTR HOT delay(uint32_t ms) { ::delay(ms); } +uint32_t IRAM_ATTR HOT micros() { return ::micros(); } +void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { ::delayMicroseconds(us); } +void arch_restart() { + ESP.restart(); // NOLINT(readability-static-accessed-through-instance) + // restart() doesn't always end execution + while (true) { // NOLINT(clang-diagnostic-unreachable-code) + yield(); + } +} +void IRAM_ATTR HOT arch_feed_wdt() { + ESP.wdtFeed(); // NOLINT(readability-static-accessed-through-instance) +} + +uint8_t progmem_read_byte(const uint8_t *addr) { + return pgm_read_byte(addr); // NOLINT +} +uint32_t arch_get_cpu_cycle_count() { + return ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) +} +uint32_t arch_get_cpu_freq_hz() { return F_CPU; } + +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/gpio.cpp b/esphome/components/esp8266/gpio.cpp new file mode 100644 index 0000000000..4dbffa7f6c --- /dev/null +++ b/esphome/components/esp8266/gpio.cpp @@ -0,0 +1,96 @@ +#ifdef USE_ESP8266 + +#include "gpio.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace esp8266 { + +static const char *const TAG = "esp8266"; + +struct ISRPinArg { + uint8_t pin; + bool inverted; +}; + +ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const { + auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) + arg->pin = pin_; + arg->inverted = inverted_; + return ISRInternalGPIOPin((void *) arg); +} + +void ESP8266GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const { + uint8_t arduino_mode = 0; + switch (type) { + case gpio::INTERRUPT_RISING_EDGE: + arduino_mode = inverted_ ? FALLING : RISING; + break; + case gpio::INTERRUPT_FALLING_EDGE: + arduino_mode = inverted_ ? RISING : FALLING; + break; + case gpio::INTERRUPT_ANY_EDGE: + arduino_mode = CHANGE; + break; + case gpio::INTERRUPT_LOW_LEVEL: + arduino_mode = inverted_ ? ONHIGH : ONLOW; + break; + case gpio::INTERRUPT_HIGH_LEVEL: + arduino_mode = inverted_ ? ONLOW : ONHIGH; + break; + } + + attachInterruptArg(pin_, func, arg, arduino_mode); +} +void ESP8266GPIOPin::pin_mode(gpio::Flags flags) { + uint8_t mode; + if (flags == gpio::FLAG_INPUT) { + mode = INPUT; + } else if (flags == gpio::FLAG_OUTPUT) { + mode = OUTPUT; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + mode = INPUT_PULLUP; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) { + mode = INPUT_PULLDOWN_16; + } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + mode = OUTPUT_OPEN_DRAIN; + } else { + return; + } + pinMode(pin_, mode); // NOLINT +} + +std::string ESP8266GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "GPIO%u", pin_); + return buffer; +} + +bool ESP8266GPIOPin::digital_read() { + return bool(digitalRead(pin_)) != inverted_; // NOLINT +} +void ESP8266GPIOPin::digital_write(bool value) { + digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT +} +void ESP8266GPIOPin::detach_interrupt() const { detachInterrupt(pin_); } + +} // namespace esp8266 + +using namespace esp8266; + +bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { + auto *arg = reinterpret_cast(arg_); + return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { + auto *arg = reinterpret_cast(arg_); + digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { + auto *arg = reinterpret_cast(arg_); + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1UL << arg->pin); +} + +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/gpio.h b/esphome/components/esp8266/gpio.h new file mode 100644 index 0000000000..465d1099ae --- /dev/null +++ b/esphome/components/esp8266/gpio.h @@ -0,0 +1,38 @@ +#pragma once + +#ifdef USE_ESP8266 + +#include "esphome/core/hal.h" +#include + +namespace esphome { +namespace esp8266 { + +class ESP8266GPIOPin : public InternalGPIOPin { + public: + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + + void setup() override { pin_mode(flags_); } + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; + std::string dump_summary() const override; + void detach_interrupt() const override; + ISRInternalGPIOPin to_isr() const override; + uint8_t get_pin() const override { return pin_; } + bool is_inverted() const override { return inverted_; } + + protected: + void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override; + + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; +}; + +} // namespace esp8266 +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py new file mode 100644 index 0000000000..0ebfbd6f69 --- /dev/null +++ b/esphome/components/esp8266/gpio.py @@ -0,0 +1,170 @@ +import logging + +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_INVERTED, + CONF_MODE, + CONF_NUMBER, + CONF_OPEN_DRAIN, + CONF_OUTPUT, + CONF_PULLDOWN, + CONF_PULLUP, +) +from esphome import pins +from esphome.core import CORE +import esphome.config_validation as cv +import esphome.codegen as cg + +from . import boards +from .const import KEY_BOARD, KEY_ESP8266, esp8266_ns + + +_LOGGER = logging.getLogger(__name__) + + +ESP8266GPIOPin = esp8266_ns.class_("ESP8266GPIOPin", cg.InternalGPIOPin) + + +def _lookup_pin(value): + board = CORE.data[KEY_ESP8266][KEY_BOARD] + board_pins = boards.ESP8266_BOARD_PINS.get(board, {}) + + # Resolved aliased board pins (shorthand when two boards have the same pin configuration) + while isinstance(board_pins, str): + board_pins = boards.ESP8266_BOARD_PINS[board_pins] + + if value in board_pins: + return board_pins[value] + if value in boards.ESP8266_BASE_PINS: + return boards.ESP8266_BASE_PINS[value] + raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.") + + +def _translate_pin(value): + if isinstance(value, dict) or value is None: + raise cv.Invalid( + "This variable only supports pin numbers, not full pin schemas " + "(with inverted and mode)." + ) + if isinstance(value, int): + return value + try: + return int(value) + except ValueError: + pass + if value.startswith("GPIO"): + return cv.int_(value[len("GPIO") :].strip()) + return _lookup_pin(value) + + +_ESP_SDIO_PINS = { + 6: "Flash Clock", + 7: "Flash Data 0", + 8: "Flash Data 1", + 11: "Flash Command", +} + + +def validate_gpio_pin(value): + value = _translate_pin(value) + if value < 0 or value > 17: + raise cv.Invalid(f"ESP8266: Invalid pin number: {value}") + if value in _ESP_SDIO_PINS: + raise cv.Invalid( + f"This pin cannot be used on ESP8266s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" + ) + if 9 <= value <= 10: + _LOGGER.warning( + "ESP8266: Pin %s (9-10) might already be used by the " + "flash interface in QUAD IO flash mode.", + value, + ) + return value + + +def validate_supports(value): + num = value[CONF_NUMBER] + mode = value[CONF_MODE] + is_input = mode[CONF_INPUT] + is_output = mode[CONF_OUTPUT] + is_open_drain = mode[CONF_OPEN_DRAIN] + is_pullup = mode[CONF_PULLUP] + is_pulldown = mode[CONF_PULLDOWN] + is_analog = mode[CONF_ANALOG] + + if (not is_analog) and num == 17: + raise cv.Invalid( + "GPIO17 (TOUT) is an analog-only pin on the ESP8266.", + [CONF_MODE], + ) + if is_analog and num != 17: + raise cv.Invalid( + "Only GPIO17 is analog-capable on ESP8266.", + [CONF_MODE, CONF_ANALOG], + ) + if is_open_drain and not is_output: + raise cv.Invalid( + "Open-drain only works with output mode", [CONF_MODE, CONF_OPEN_DRAIN] + ) + if is_pullup and num == 0: + raise cv.Invalid( + "GPIO Pin 0 does not support pullup pin mode. " + "Please choose another pin.", + [CONF_MODE, CONF_PULLUP], + ) + if is_pulldown and num != 16: + raise cv.Invalid("Only GPIO16 supports pulldown.", [CONF_MODE, CONF_PULLDOWN]) + + # (input, output, open_drain, pullup, pulldown) + supported_modes = { + # INPUT + (True, False, False, False, False), + # OUTPUT + (False, True, False, False, False), + # INPUT_PULLUP + (True, False, False, True, False), + # INPUT_PULLDOWN_16 + (True, False, False, False, True), + # OUTPUT_OPEN_DRAIN + (False, True, True, False, False), + } + key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown) + if key not in supported_modes: + raise cv.Invalid( + "This pin mode is not supported on ESP8266", + [CONF_MODE], + ) + + return value + + +CONF_ANALOG = "analog" +ESP8266_PIN_SCHEMA = cv.All( + { + cv.GenerateID(): cv.declare_id(ESP8266GPIOPin), + cv.Required(CONF_NUMBER): validate_gpio_pin, + cv.Optional(CONF_MODE, default={}): cv.Schema( + { + cv.Optional(CONF_ANALOG, default=False): cv.boolean, + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_PULLDOWN, default=False): cv.boolean, + } + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + }, + validate_supports, +) + + +@pins.PIN_SCHEMA_REGISTRY.register("esp8266", ESP8266_PIN_SCHEMA) +async def esp8266_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp new file mode 100644 index 0000000000..7a8fdb8289 --- /dev/null +++ b/esphome/components/esp8266/preferences.cpp @@ -0,0 +1,263 @@ +#ifdef USE_ESP8266 + +#include +extern "C" { +#include "spi_flash.h" +} + +#include "preferences.h" +#include +#include "esphome/core/preferences.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "esphome/core/defines.h" + +namespace esphome { +namespace esp8266 { + +static const char *const TAG = "esp8266.preferences"; + +static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static uint32_t *s_flash_storage = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +static const uint32_t ESP_RTC_USER_MEM_START = 0x60001200; +#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START) +static const uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128; +static const uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4; + +#ifdef USE_ESP8266_PREFERENCES_FLASH +static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 128; +#else +static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 64; +#endif + +static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) { + if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { + return false; + } + *dest = ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) + return true; +} + +static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { + if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { + return false; + } + if (index < 32 && s_prevent_write) { + return false; + } + + auto *ptr = &ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) + *ptr = value; + return true; +} + +extern "C" uint32_t _SPIFFS_end; // NOLINT + +static const uint32_t get_esp8266_flash_sector() { + union { + uint32_t *ptr; + uint32_t uint; + } data{}; + data.ptr = &_SPIFFS_end; + return (data.uint - 0x40200000) / SPI_FLASH_SEC_SIZE; +} +static const uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; } + +template uint32_t calculate_crc(It first, It last, uint32_t type) { + uint32_t crc = type; + while (first != last) { + crc ^= (*first++ * 2654435769UL) >> 1; + } + return crc; +} + +static bool safe_flash() { + if (!s_flash_dirty) + return true; + + ESP_LOGVV(TAG, "Saving preferences to flash..."); + SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; + { + InterruptLock lock; + erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); + if (erase_res == SPI_FLASH_RESULT_OK) { + write_res = spi_flash_write(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4); + } + } + if (erase_res != SPI_FLASH_RESULT_OK) { + ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); + return false; + } + if (write_res != SPI_FLASH_RESULT_OK) { + ESP_LOGV(TAG, "Write ESP8266 flash failed!"); + return false; + } + + s_flash_dirty = false; + return true; +} + +static bool safe_to_flash(size_t offset, const uint32_t *data, size_t len) { + for (uint32_t i = 0; i < len; i++) { + uint32_t j = offset + i; + if (j >= ESP8266_FLASH_STORAGE_SIZE) + return false; + uint32_t v = data[i]; + uint32_t *ptr = &s_flash_storage[j]; + if (*ptr != v) + s_flash_dirty = true; + *ptr = v; + } + return safe_flash(); +} + +static bool load_from_flash(size_t offset, uint32_t *data, size_t len) { + for (size_t i = 0; i < len; i++) { + uint32_t j = offset + i; + if (j >= ESP8266_FLASH_STORAGE_SIZE) + return false; + data[i] = s_flash_storage[j]; + } + return true; +} + +static bool safe_to_rtc(size_t offset, const uint32_t *data, size_t len) { + for (uint32_t i = 0; i < len; i++) + if (!esp_rtc_user_mem_write(offset + i, data[i])) + return false; + return true; +} + +static bool load_from_rtc(size_t offset, uint32_t *data, size_t len) { + for (uint32_t i = 0; i < len; i++) + if (!esp_rtc_user_mem_read(offset + i, &data[i])) + return false; + return true; +} + +class ESP8266PreferenceBackend : public ESPPreferenceBackend { + public: + size_t offset = 0; + uint32_t type = 0; + bool in_flash = false; + size_t length_words = 0; + + bool save(const uint8_t *data, size_t len) override { + if ((len + 3) / 4 != length_words) { + return false; + } + std::vector buffer; + buffer.resize(length_words + 1); + memcpy(buffer.data(), data, len); + buffer[buffer.size() - 1] = calculate_crc(buffer.begin(), buffer.end() - 1, type); + + if (in_flash) { + return safe_to_flash(offset, buffer.data(), buffer.size()); + } else { + return safe_to_rtc(offset, buffer.data(), buffer.size()); + } + } + bool load(uint8_t *data, size_t len) override { + if ((len + 3) / 4 != length_words) { + return false; + } + std::vector buffer; + buffer.resize(length_words + 1); + bool ret; + if (in_flash) { + ret = load_from_flash(offset, buffer.data(), buffer.size()); + } else { + ret = load_from_rtc(offset, buffer.data(), buffer.size()); + } + if (!ret) + return false; + + uint32_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type); + return buffer[buffer.size() - 1] == crc; + } +}; + +class ESP8266Preferences : public ESPPreferences { + public: + uint32_t current_offset = 0; + uint32_t current_flash_offset = 0; // in words + + void setup() { + s_flash_storage = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT + ESP_LOGVV(TAG, "Loading preferences from flash..."); + + { + InterruptLock lock; + spi_flash_read(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4); + } + } + + ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { + uint32_t length_words = (length + 3) / 4; + if (in_flash) { + uint32_t start = current_flash_offset; + uint32_t end = start + length_words + 1; + if (end > ESP8266_FLASH_STORAGE_SIZE) + return {}; + auto *pref = new ESP8266PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) + pref->offset = start; + pref->type = type; + pref->length_words = length_words; + pref->in_flash = true; + current_flash_offset = end; + return {pref}; + } + + uint32_t start = current_offset; + uint32_t end = start + length_words + 1; + bool in_normal = start < 96; + // Normal: offset 0-95 maps to RTC offset 32 - 127, + // Eboot: offset 96-127 maps to RTC offset 0 - 31 words + if (in_normal && end > 96) { + // start is in normal but end is not -> switch to Eboot + current_offset = start = 96; + end = start + length_words + 1; + in_normal = false; + } + + if (end > 128) { + // Doesn't fit in data, return uninitialized preference obj. + return {}; + } + + uint32_t rtc_offset = in_normal ? start + 32 : start - 96; + + auto *pref = new ESP8266PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) + pref->offset = rtc_offset; + pref->type = type; + pref->length_words = length_words; + pref->in_flash = false; + current_offset += length_words + 1; + return pref; + } + + ESPPreferenceObject make_preference(size_t length, uint32_t type) override { +#ifdef USE_ESP8266_PREFERENCES_FLASH + return make_preference(length, type, true); +#else + return make_preference(length, type, false); +#endif + } +}; + +void setup_preferences() { + auto *pref = new ESP8266Preferences(); // NOLINT(cppcoreguidelines-owning-memory) + pref->setup(); + global_preferences = pref; +} +void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; } + +} // namespace esp8266 + +ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/preferences.h b/esphome/components/esp8266/preferences.h new file mode 100644 index 0000000000..edec915794 --- /dev/null +++ b/esphome/components/esp8266/preferences.h @@ -0,0 +1,14 @@ +#pragma once + +#ifdef USE_ESP8266 + +namespace esphome { +namespace esp8266 { + +void setup_preferences(); +void preferences_prevent_write(bool prevent); + +} // namespace esp8266 +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index 186653acd1..e472edf2a7 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -1,11 +1,11 @@ -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "esp8266_pwm.h" #include "esphome/core/macros.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) +#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) #error ESP8266 PWM requires at least arduino_version 2.4.0 #endif diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.h b/esphome/components/esp8266_pwm/esp8266_pwm.h index 1bf875dc43..79530aacd4 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.h +++ b/esphome/components/esp8266_pwm/esp8266_pwm.h @@ -1,9 +1,9 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/output/float_output.h" @@ -12,7 +12,7 @@ namespace esp8266_pwm { class ESP8266PWM : public output::FloatOutput, public Component { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_frequency(float frequency) { this->frequency_ = frequency; } /// Dynamically update frequency void update_frequency(float frequency) override { @@ -29,7 +29,7 @@ class ESP8266PWM : public output::FloatOutput, public Component { protected: void write_state(float state) override; - GPIOPin *pin_; + InternalGPIOPin *pin_; float frequency_{1000.0}; /// Cache last output level for dynamic frequency updating float last_output_{0.0}; diff --git a/esphome/components/esp8266_pwm/output.py b/esphome/components/esp8266_pwm/output.py index 770d400013..3d52e5af16 100644 --- a/esphome/components/esp8266_pwm/output.py +++ b/esphome/components/esp8266_pwm/output.py @@ -7,10 +7,9 @@ from esphome.const import ( CONF_ID, CONF_NUMBER, CONF_PIN, - ESP_PLATFORM_ESP8266, ) -ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] +DEPENDENCIES = ["esp8266"] def valid_pwm_pin(value): diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 95b7e40151..384fb3dbfb 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,7 +1,6 @@ from esphome import pins import esphome.config_validation as cv import esphome.codegen as cg -from esphome.components.network import add_mdns_library from esphome.const import ( CONF_DOMAIN, CONF_ID, @@ -9,17 +8,16 @@ from esphome.const import ( CONF_STATIC_IP, CONF_TYPE, CONF_USE_ADDRESS, - ESP_PLATFORM_ESP32, - CONF_ENABLE_MDNS, CONF_GATEWAY, CONF_SUBNET, CONF_DNS1, CONF_DNS2, ) from esphome.core import CORE, coroutine_with_priority +from esphome.components.network import IPAddress CONFLICTS_WITH = ["wifi"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] AUTO_LOAD = ["network"] ethernet_ns = cg.esphome_ns.namespace("ethernet") @@ -55,7 +53,6 @@ MANUAL_IP_SCHEMA = cv.Schema( ) EthernetComponent = ethernet_ns.class_("EthernetComponent", cg.Component) -IPAddress = cg.global_ns.class_("IPAddress") ManualIP = ethernet_ns.struct("ManualIP") @@ -74,20 +71,24 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(EthernetComponent), cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True), - cv.Required(CONF_MDC_PIN): pins.output_pin, - cv.Required(CONF_MDIO_PIN): pins.input_output_pin, + cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum( CLK_MODES, upper=True, space="_" ), cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, - cv.Optional(CONF_ENABLE_MDNS, default=True): cv.boolean, cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + cv.Optional("enable_mdns"): cv.invalid( + "This option has been removed. Please use the [disabled] option under the " + "new mdns component instead." + ), } ).extend(cv.COMPONENT_SCHEMA), _validate, + cv.only_with_arduino, ) @@ -122,6 +123,3 @@ async def to_code(config): cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) cg.add_define("USE_ETHERNET") - - if config[CONF_ENABLE_MDNS]: - add_mdns_library() diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index cad76ae476..d03211deaf 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -3,7 +3,7 @@ #include "esphome/core/util.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #include @@ -75,10 +75,6 @@ void EthernetComponent::setup() { ESPHL_ERROR_CHECK(err, "ETH init error"); err = esp_eth_enable(); ESPHL_ERROR_CHECK(err, "ETH enable error"); - -#ifdef USE_MDNS - network_setup_mdns(); -#endif } void EthernetComponent::loop() { const uint32_t now = millis(); @@ -118,8 +114,6 @@ void EthernetComponent::loop() { } break; } - - network_tick_mdns(); } void EthernetComponent::dump_config() { ESP_LOGCONFIG(TAG, "Ethernet:"); @@ -131,10 +125,10 @@ void EthernetComponent::dump_config() { } float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } bool EthernetComponent::can_proceed() { return this->is_connected(); } -IPAddress EthernetComponent::get_ip_address() { +network::IPAddress EthernetComponent::get_ip_address() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); - return IPAddress(ip.ip.addr); + return {ip.ip.addr}; } void EthernetComponent::on_wifi_event_(system_event_id_t event, system_event_info_t info) { @@ -229,10 +223,10 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen void EthernetComponent::dump_connect_params_() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); - ESP_LOGCONFIG(TAG, " IP Address: %s", IPAddress(ip.ip.addr).toString().c_str()); + ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(ip.ip.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - ESP_LOGCONFIG(TAG, " Subnet: %s", IPAddress(ip.netmask.addr).toString().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", IPAddress(ip.gw.addr).toString().c_str()); + ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(ip.netmask.addr).str().c_str()); + ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(ip.gw.addr).str().c_str()); #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 4) const ip_addr_t *dns_ip1 = dns_getserver(0); @@ -243,8 +237,8 @@ void EthernetComponent::dump_connect_params_() { ip_addr_t tmp_ip2 = dns_getserver(1); const ip_addr_t *dns_ip2 = &tmp_ip2; #endif - ESP_LOGCONFIG(TAG, " DNS1: %s", IPAddress(dns_ip1->u_addr.ip4.addr).toString().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", IPAddress(dns_ip2->u_addr.ip4.addr).toString().c_str()); + ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str()); + ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str()); uint8_t mac[6]; esp_eth_get_mac(mac); ESP_LOGCONFIG(TAG, " MAC Address: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -270,4 +264,4 @@ void EthernetComponent::set_use_address(const std::string &use_address) { this-> } // namespace ethernet } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index dc8816ef86..3e0c798a0c 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -1,9 +1,10 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#ifdef USE_ESP32_FRAMEWORK_ARDUINO -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/network/ip_address.h" #include "esp_eth.h" #include @@ -19,11 +20,11 @@ enum EthernetType { }; struct ManualIP { - IPAddress static_ip; - IPAddress gateway; - IPAddress subnet; - IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. - IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. + network::IPAddress static_ip; + network::IPAddress gateway; + network::IPAddress subnet; + network::IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. + network::IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. }; enum class EthernetComponentState { @@ -50,7 +51,7 @@ class EthernetComponent : public Component { void set_clk_mode(eth_clock_mode_t clk_mode); void set_manual_ip(const ManualIP &manual_ip); - IPAddress get_ip_address(); + network::IPAddress get_ip_address(); std::string get_use_address() const; void set_use_address(const std::string &use_address); @@ -84,4 +85,4 @@ extern EthernetComponent *global_eth_component; } // namespace ethernet } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/exposure_notifications/exposure_notifications.cpp b/esphome/components/exposure_notifications/exposure_notifications.cpp index 9db181fbee..3083cf429c 100644 --- a/esphome/components/exposure_notifications/exposure_notifications.cpp +++ b/esphome/components/exposure_notifications/exposure_notifications.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace exposure_notifications { diff --git a/esphome/components/exposure_notifications/exposure_notifications.h b/esphome/components/exposure_notifications/exposure_notifications.h index 6b9f61b2a0..f7383c28d9 100644 --- a/esphome/components/exposure_notifications/exposure_notifications.h +++ b/esphome/components/exposure_notifications/exposure_notifications.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace exposure_notifications { diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 90b1e4ace9..81597f3466 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -1,5 +1,6 @@ #include "ezo.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ezo { @@ -24,7 +25,7 @@ void EZOSensor::update() { return; } uint8_t c = 'R'; - this->write_bytes_raw(&c, 1); + this->write(&c, 1); this->state_ |= EZO_STATE_WAIT; this->start_time_ = millis(); this->wait_time_ = 900; @@ -35,7 +36,7 @@ void EZOSensor::loop() { if (!(this->state_ & EZO_STATE_WAIT)) { if (this->state_ & EZO_STATE_SEND_TEMP) { int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_); - this->write_bytes_raw(buf, len); + this->write(buf, len); this->state_ = EZO_STATE_WAIT | EZO_STATE_WAIT_TEMP; this->start_time_ = millis(); this->wait_time_ = 300; diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index a57115beb4..921b8d57e7 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -27,7 +27,7 @@ struct FanStateRTCState { }; void FanState::setup() { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); FanStateRTCState recovered{}; if (!this->rtc_.load(&recovered)) return; diff --git a/esphome/components/fastled_base/fastled_light.cpp b/esphome/components/fastled_base/fastled_light.cpp index 15a5c8984c..486364d0c0 100644 --- a/esphome/components/fastled_base/fastled_light.cpp +++ b/esphome/components/fastled_base/fastled_light.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "fastled_light.h" #include "esphome/core/log.h" @@ -37,3 +39,5 @@ void FastLEDLightOutput::write_state(light::LightState *state) { } // namespace fastled_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/fastled_base/fastled_light.h b/esphome/components/fastled_base/fastled_light.h index d1c470599d..80840c3003 100644 --- a/esphome/components/fastled_base/fastled_light.h +++ b/esphome/components/fastled_base/fastled_light.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/components/light/addressable_light.h" @@ -237,3 +239,5 @@ class FastLEDLightOutput : public light::AddressableLight { } // namespace fastled_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/fastled_clockless/light.py b/esphome/components/fastled_clockless/light.py index d437d01dcf..acf9488ae3 100644 --- a/esphome/components/fastled_clockless/light.py +++ b/esphome/components/fastled_clockless/light.py @@ -45,10 +45,11 @@ CONFIG_SCHEMA = cv.All( fastled_base.BASE_SCHEMA.extend( { cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), - cv.Required(CONF_PIN): pins.output_pin, + cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number, } ), _validate, + cv.only_with_arduino, ) diff --git a/esphome/components/fastled_spi/light.py b/esphome/components/fastled_spi/light.py index d6ba0e8358..a729fc015a 100644 --- a/esphome/components/fastled_spi/light.py +++ b/esphome/components/fastled_spi/light.py @@ -24,13 +24,16 @@ CHIPSETS = [ "DOTSTAR", ] -CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend( - { - cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), - cv.Required(CONF_DATA_PIN): pins.output_pin, - cv.Required(CONF_CLOCK_PIN): pins.output_pin, - cv.Optional(CONF_DATA_RATE): cv.frequency, - } +CONFIG_SCHEMA = cv.All( + fastled_base.BASE_SCHEMA.extend( + { + cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), + cv.Required(CONF_DATA_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_CLOCK_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_DATA_RATE): cv.frequency, + } + ), + cv.only_with_arduino, ) diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index b0c0be59af..be17e29de3 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -15,7 +15,7 @@ void FingerprintGrowComponent::update() { } if (this->sensing_pin_ != nullptr) { - if (this->sensing_pin_->digital_read() == HIGH) { + if (this->sensing_pin_->digital_read()) { ESP_LOGV(TAG, "No touch sensing"); this->waiting_removal_ = false; return; diff --git a/esphome/components/globals/__init__.py b/esphome/components/globals/__init__.py index 9039d0d62e..97a7ba3d54 100644 --- a/esphome/components/globals/__init__.py +++ b/esphome/components/globals/__init__.py @@ -14,6 +14,7 @@ from esphome.core import coroutine_with_priority CODEOWNERS = ["@esphome/core"] globals_ns = cg.esphome_ns.namespace("globals") GlobalsComponent = globals_ns.class_("GlobalsComponent", cg.Component) +RestoringGlobalsComponent = globals_ns.class_("RestoringGlobalsComponent", cg.Component) GlobalVarSetAction = globals_ns.class_("GlobalVarSetAction", automation.Action) MULTI_CONF = True @@ -32,22 +33,25 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): type_ = cg.RawExpression(config[CONF_TYPE]) template_args = cg.TemplateArguments(type_) - res_type = GlobalsComponent.template(template_args) + restore = config[CONF_RESTORE_VALUE] + + type = RestoringGlobalsComponent if restore else GlobalsComponent + res_type = type.template(template_args) initial_value = None if CONF_INITIAL_VALUE in config: initial_value = cg.RawExpression(config[CONF_INITIAL_VALUE]) - rhs = GlobalsComponent.new(template_args, initial_value) + rhs = type.new(template_args, initial_value) glob = cg.Pvariable(config[CONF_ID], rhs, res_type) await cg.register_component(glob, config) - if config[CONF_RESTORE_VALUE]: + if restore: value = config[CONF_ID].id if isinstance(value, str): value = value.encode() hash_ = int(hashlib.md5(value).hexdigest()[:8], 16) - cg.add(glob.set_restore_value(hash_)) + cg.add(glob.set_name_hash(hash_)) @automation.register_action( diff --git a/esphome/components/globals/globals_component.h b/esphome/components/globals/globals_component.h index 397c55f6c4..b39c5f404b 100644 --- a/esphome/components/globals/globals_component.h +++ b/esphome/components/globals/globals_component.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/core/helpers.h" +#include namespace esphome { namespace globals { @@ -16,37 +17,46 @@ template class GlobalsComponent : public Component { memcpy(this->value_, initial_value.data(), sizeof(T)); } + T &value() { return this->value_; } + void setup() override {} + + protected: + T value_{}; +}; + +template class RestoringGlobalsComponent : public Component { + public: + using value_type = T; + explicit RestoringGlobalsComponent() = default; + explicit RestoringGlobalsComponent(T initial_value) : value_(initial_value) {} + explicit RestoringGlobalsComponent( + std::array::type, std::extent::value> initial_value) { + memcpy(this->value_, initial_value.data(), sizeof(T)); + } + T &value() { return this->value_; } void setup() override { - if (this->restore_value_) { - this->rtc_ = global_preferences.make_preference(1944399030U ^ this->name_hash_); - this->rtc_.load(&this->value_); - } + this->rtc_ = global_preferences->make_preference(1944399030U ^ this->name_hash_); + this->rtc_.load(&this->value_); memcpy(&this->prev_value_, &this->value_, sizeof(T)); } float get_setup_priority() const override { return setup_priority::HARDWARE; } void loop() override { - if (this->restore_value_) { - int diff = memcmp(&this->value_, &this->prev_value_, sizeof(T)); - if (diff != 0) { - this->rtc_.save(&this->value_); - memcpy(&this->prev_value_, &this->value_, sizeof(T)); - } + int diff = memcmp(&this->value_, &this->prev_value_, sizeof(T)); + if (diff != 0) { + this->rtc_.save(&this->value_); + memcpy(&this->prev_value_, &this->value_, sizeof(T)); } } - void set_restore_value(uint32_t name_hash) { - this->restore_value_ = true; - this->name_hash_ = name_hash; - } + void set_name_hash(uint32_t name_hash) { this->name_hash_ = name_hash; } protected: T value_{}; T prev_value_{}; - bool restore_value_{false}; uint32_t name_hash_{}; ESPPreferenceObject rtc_; }; diff --git a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h index f655207243..33a173fe2e 100644 --- a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +++ b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/gpio/output/gpio_binary_output.h b/esphome/components/gpio/output/gpio_binary_output.h index 0a7dfb46e2..6b72c61c0f 100644 --- a/esphome/components/gpio/output/gpio_binary_output.h +++ b/esphome/components/gpio/output/gpio_binary_output.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/binary_output.h" namespace esphome { diff --git a/esphome/components/gpio/switch/gpio_switch.h b/esphome/components/gpio/switch/gpio_switch.h index b39e070c85..99f8060efa 100644 --- a/esphome/components/gpio/switch/gpio_switch.h +++ b/esphome/components/gpio/switch/gpio_switch.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/switch/switch.h" namespace esphome { diff --git a/esphome/components/gps/__init__.py b/esphome/components/gps/__init__.py index 9d323e6e4d..e485373175 100644 --- a/esphome/components/gps/__init__.py +++ b/esphome/components/gps/__init__.py @@ -28,7 +28,7 @@ GPSListener = gps_ns.class_("GPSListener") CONF_GPS_ID = "gps_id" MULTI_CONF = True -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(GPS), @@ -64,7 +64,8 @@ CONFIG_SCHEMA = ( } ) .extend(cv.polling_component_schema("20s")) - .extend(uart.UART_DEVICE_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA), + cv.only_with_arduino, ) FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema("gps", require_rx=True) diff --git a/esphome/components/gps/gps.cpp b/esphome/components/gps/gps.cpp index 1e8ca94e9e..8c924d629c 100644 --- a/esphome/components/gps/gps.cpp +++ b/esphome/components/gps/gps.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "gps.h" #include "esphome/core/log.h" @@ -69,3 +71,5 @@ void GPS::loop() { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/gps/gps.h b/esphome/components/gps/gps.h index 50dd476ae3..40cda145ca 100644 --- a/esphome/components/gps/gps.h +++ b/esphome/components/gps/gps.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" #include "esphome/components/sensor/sensor.h" @@ -63,3 +65,5 @@ class GPS : public PollingComponent, public uart::UARTDevice { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/gps/time/gps_time.cpp b/esphome/components/gps/time/gps_time.cpp index 5352c7e059..e46f24ba8e 100644 --- a/esphome/components/gps/time/gps_time.cpp +++ b/esphome/components/gps/time/gps_time.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "gps_time.h" #include "esphome/core/log.h" @@ -32,3 +34,5 @@ void GPSTime::from_tiny_gps_(TinyGPSPlus &tiny_gps) { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/gps/time/gps_time.h b/esphome/components/gps/time/gps_time.h index a1f69a7130..d0d1db83b5 100644 --- a/esphome/components/gps/time/gps_time.h +++ b/esphome/components/gps/time/gps_time.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/time/real_time_clock.h" #include "esphome/components/gps/gps.h" @@ -22,3 +24,5 @@ class GPSTime : public time::RealTimeClock, public GPSListener { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index 6ede553fdb..ff736b7cb7 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -2,7 +2,7 @@ #include "esphome/components/display/display_buffer.h" #include "esphome/core/color.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include #include #include // std::cout, std::fixed @@ -34,12 +34,12 @@ void HistoryData::take_sample(float data) { this->count_ = (this->count_ + 1) % this->length_; ESP_LOGV(TAG, "Updating trace with value: %f", data); } - if (!isnan(data)) { + if (!std::isnan(data)) { // Recalc recent max/min this->recent_min_ = data; this->recent_max_ = data; for (int i = 0; i < this->length_; i++) { - if (!isnan(this->samples_[i])) { + if (!std::isnan(this->samples_[i])) { if (this->recent_max_ < this->samples_[i]) this->recent_max_ = this->samples_[i]; if (this->recent_min_ > this->samples_[i]) @@ -70,15 +70,15 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo for (auto *trace : traces_) { float mx = trace->get_tracedata()->get_recent_max(); float mn = trace->get_tracedata()->get_recent_min(); - if (isnan(ymax) || (ymax < mx)) + if (std::isnan(ymax) || (ymax < mx)) ymax = mx; - if (isnan(ymin) || (ymin > mn)) + if (std::isnan(ymin) || (ymin > mn)) ymin = mn; } // Adjust if manually overridden - if (!isnan(this->min_value_)) + if (!std::isnan(this->min_value_)) ymin = this->min_value_; - if (!isnan(this->max_value_)) + if (!std::isnan(this->max_value_)) ymax = this->max_value_; float yrange = ymax - ymin; @@ -89,20 +89,20 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo for (int16_t i = 0; i < this->width_; i++) { for (auto *trace : traces_) { float v = trace->get_tracedata()->get_value(i); - if (!isnan(v)) { + if (!std::isnan(v)) { if ((v - mn) > this->max_range_) break; if ((mx - v) > this->max_range_) break; - if (isnan(mx) || (v > mx)) + if (std::isnan(mx) || (v > mx)) mx = v; - if (isnan(mn) || (v < mn)) + if (std::isnan(mn) || (v < mn)) mn = v; } } } yrange = this->max_range_; - if (!isnan(mn)) { + if (!std::isnan(mn)) { ymin = mn; ymax = ymin + this->max_range_; } @@ -110,7 +110,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo } float y_per_div = this->min_range_; - if (!isnan(this->gridspacing_y_)) { + if (!std::isnan(this->gridspacing_y_)) { y_per_div = this->gridspacing_y_; } // Restrict drawing too many gridlines @@ -129,7 +129,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo yrange = ymax - ymin; /// Draw grid - if (!isnan(this->gridspacing_y_)) { + if (!std::isnan(this->gridspacing_y_)) { for (int y = yn; y <= ym; y++) { int16_t py = (int16_t) roundf((this->height_ - 1) * (1.0 - (float) (y - yn) / (ym - yn))); for (int x = 0; x < this->width_; x += 2) { @@ -137,7 +137,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo } } } - if (!isnan(this->gridspacing_x_) && (this->gridspacing_x_ > 0)) { + if (!std::isnan(this->gridspacing_x_) && (this->gridspacing_x_ > 0)) { int n = this->duration_ / this->gridspacing_x_; // Restrict drawing too many gridlines if (n > 20) { @@ -160,7 +160,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo uint16_t thick = trace->get_line_thickness(); for (int16_t i = 0; i < this->width_; i++) { float v = (trace->get_tracedata()->get_value(i) - ymin) / yrange; - if (!isnan(v) && (thick > 0)) { + if (!std::isnan(v) && (thick > 0)) { int16_t x = this->width_ - 1 - i; uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick; if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) { diff --git a/esphome/components/hdc1080/hdc1080.cpp b/esphome/components/hdc1080/hdc1080.cpp index 507ac77a28..60e8943e67 100644 --- a/esphome/components/hdc1080/hdc1080.cpp +++ b/esphome/components/hdc1080/hdc1080.cpp @@ -1,5 +1,6 @@ #include "hdc1080.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace hdc1080 { @@ -36,18 +37,30 @@ void HDC1080Component::dump_config() { } void HDC1080Component::update() { uint16_t raw_temp; - if (!this->read_byte_16(HDC1080_CMD_TEMPERATURE, &raw_temp, 20)) { + if (this->write(&HDC1080_CMD_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(20); + if (this->read(reinterpret_cast(&raw_temp), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_temp = i2c::i2ctohs(raw_temp); float temp = raw_temp * 0.0025177f - 40.0f; // raw * 2^-16 * 165 - 40 this->temperature_->publish_state(temp); uint16_t raw_humidity; - if (!this->read_byte_16(HDC1080_CMD_HUMIDITY, &raw_humidity, 20)) { + if (this->write(&HDC1080_CMD_HUMIDITY, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(20); + if (this->read(reinterpret_cast(&raw_humidity), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_humidity = i2c::i2ctohs(raw_humidity); float humidity = raw_humidity * 0.001525879f; // raw * 2^-16 * 100 this->humidity_->publish_state(humidity); diff --git a/esphome/components/hlw8012/hlw8012.h b/esphome/components/hlw8012/hlw8012.h index 52fb03c020..5060957cf1 100644 --- a/esphome/components/hlw8012/hlw8012.h +++ b/esphome/components/hlw8012/hlw8012.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/pulse_counter/pulse_counter_sensor.h" @@ -31,8 +31,8 @@ class HLW8012Component : public PollingComponent { void set_current_resistor(float current_resistor) { current_resistor_ = current_resistor; } void set_voltage_divider(float voltage_divider) { voltage_divider_ = voltage_divider; } void set_sel_pin(GPIOPin *sel_pin) { sel_pin_ = sel_pin; } - void set_cf_pin(GPIOPin *cf_pin) { cf_pin_ = cf_pin; } - void set_cf1_pin(GPIOPin *cf1_pin) { cf1_pin_ = cf1_pin; } + void set_cf_pin(InternalGPIOPin *cf_pin) { cf_pin_ = cf_pin; } + void set_cf1_pin(InternalGPIOPin *cf1_pin) { cf1_pin_ = cf1_pin; } void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } @@ -48,9 +48,9 @@ class HLW8012Component : public PollingComponent { HLW8012SensorModels sensor_model_{HLW8012_SENSOR_MODEL_HLW8012}; uint64_t cf_total_pulses_{0}; GPIOPin *sel_pin_; - GPIOPin *cf_pin_; + InternalGPIOPin *cf_pin_; pulse_counter::PulseCounterStorage cf_store_; - GPIOPin *cf1_pin_; + InternalGPIOPin *cf1_pin_; pulse_counter::PulseCounterStorage cf1_store_; sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index 11e9c8e4d4..033cccc3d4 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -50,12 +50,8 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(HLW8012Component), cv.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CF_PIN): cv.All( - pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt - ), - cv.Required(CONF_CF1_PIN): cv.All( - pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_CF_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema), + cv.Required(CONF_CF1_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema), cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, diff --git a/esphome/components/hm3301/abstract_aqi_calculator.h b/esphome/components/hm3301/abstract_aqi_calculator.h index f2573ff108..fb41b921d9 100644 --- a/esphome/components/hm3301/abstract_aqi_calculator.h +++ b/esphome/components/hm3301/abstract_aqi_calculator.h @@ -1,5 +1,8 @@ #pragma once +#ifdef USE_ARDUINO +#include + namespace esphome { namespace hm3301 { @@ -10,3 +13,5 @@ class AbstractAQICalculator { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/aqi_calculator.h b/esphome/components/hm3301/aqi_calculator.h index 627ee686fc..1410eac72b 100644 --- a/esphome/components/hm3301/aqi_calculator.h +++ b/esphome/components/hm3301/aqi_calculator.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "abstract_aqi_calculator.h" namespace esphome { @@ -46,3 +48,5 @@ class AQICalculator : public AbstractAQICalculator { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/aqi_calculator_factory.h b/esphome/components/hm3301/aqi_calculator_factory.h index 55608b6e51..3c6f9709b6 100644 --- a/esphome/components/hm3301/aqi_calculator_factory.h +++ b/esphome/components/hm3301/aqi_calculator_factory.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "caqi_calculator.h" #include "aqi_calculator.h" @@ -27,3 +29,5 @@ class AQICalculatorFactory { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/caqi_calculator.h b/esphome/components/hm3301/caqi_calculator.h index 403bac2713..51158454d0 100644 --- a/esphome/components/hm3301/caqi_calculator.h +++ b/esphome/components/hm3301/caqi_calculator.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "abstract_aqi_calculator.h" @@ -52,3 +54,5 @@ class CAQICalculator : public AbstractAQICalculator { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/hm3301.cpp b/esphome/components/hm3301/hm3301.cpp index 96c1ec0ee9..759157f330 100644 --- a/esphome/components/hm3301/hm3301.cpp +++ b/esphome/components/hm3301/hm3301.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "hm3301.h" @@ -102,3 +104,5 @@ uint16_t HM3301Component::get_sensor_value_(const uint8_t *data, uint8_t i) { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/hm3301.h b/esphome/components/hm3301/hm3301.h index b024f93719..61bbf7e4ab 100644 --- a/esphome/components/hm3301/hm3301.h +++ b/esphome/components/hm3301/hm3301.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" @@ -48,3 +50,5 @@ class HM3301Component : public PollingComponent, public i2c::I2CDevice { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/sensor.py b/esphome/components/hm3301/sensor.py index 2c3c2f7221..976a0488e1 100644 --- a/esphome/components/hm3301/sensor.py +++ b/esphome/components/hm3301/sensor.py @@ -84,6 +84,7 @@ CONFIG_SCHEMA = cv.All( .extend(cv.polling_component_schema("60s")) .extend(i2c.i2c_device_schema(0x40)), _validate, + cv.only_with_arduino, ) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index eea2f0d407..a48b3c0acb 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -2,7 +2,6 @@ import urllib.parse as urlparse import esphome.codegen as cg import esphome.config_validation as cv -import esphome.final_validate as fv from esphome import automation from esphome.const import ( CONF_ID, @@ -12,7 +11,7 @@ from esphome.const import ( CONF_URL, CONF_ESP8266_DISABLE_SSL_SUPPORT, ) -from esphome.core import CORE, Lambda +from esphome.core import Lambda, CORE DEPENDENCIES = ["network"] AUTO_LOAD = ["json"] @@ -67,35 +66,24 @@ def validate_secure_url(config): return config -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(HttpRequestComponent), - cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string, - cv.Optional(CONF_TIMEOUT, default="5s"): cv.positive_time_period_milliseconds, - cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All( - cv.only_on_esp8266, cv.boolean - ), - } -).extend(cv.COMPONENT_SCHEMA) - - -def validate_framework(value): - if not CORE.is_esp8266: - # only for ESP8266 - return - - framework_version = fv.get_arduino_framework_version() - if framework_version is None or framework_version == "dev": - return - - if framework_version < "2.5.1": - raise cv.Invalid( - "This component is not supported on arduino framework version below 2.5.1, ", - "please check esphome->arduino_version", - ) - - -FINAL_VALIDATE_SCHEMA = cv.Schema(validate_framework) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HttpRequestComponent), + cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string, + cv.Optional( + CONF_TIMEOUT, default="5s" + ): cv.positive_time_period_milliseconds, + cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All( + cv.only_on_esp8266, cv.boolean + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.require_framework_version( + esp8266_arduino=cv.Version(2, 5, 1), + esp32_arduino=cv.Version(0, 0, 0), + ), +) async def to_code(config): diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index 3f5bf6da87..f88ee19e5c 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "http_request.h" #include "esphome/core/macros.h" #include "esphome/core/log.h" @@ -28,10 +30,10 @@ void HttpRequestComponent::set_url(std::string url) { void HttpRequestComponent::send(const std::vector &response_triggers) { bool begin_status = false; const String url = this->url_.c_str(); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 begin_status = this->client_.begin(url); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) this->client_.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); #elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) @@ -79,7 +81,7 @@ void HttpRequestComponent::send(const std::vector ESP_LOGD(TAG, "HTTP Request completed; URL: %s; Code: %d", this->url_.c_str(), http_code); } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::shared_ptr HttpRequestComponent::get_wifi_client_() { #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS if (this->secure_) { @@ -111,3 +113,5 @@ const char *HttpRequestComponent::get_string() { } // namespace http_request } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index fa29f8aa3a..511096e7fa 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/components/json/json_util.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" @@ -9,10 +11,10 @@ #include #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS #include @@ -54,7 +56,7 @@ class HttpRequestComponent : public Component { uint16_t timeout_{5000}; std::string body_; std::list
headers_; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::shared_ptr wifi_client_; #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS std::shared_ptr wifi_client_secure_; @@ -137,3 +139,5 @@ class HttpRequestResponseTrigger : public Trigger { } // namespace http_request } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index a954b2ad59..b53284ae3f 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -1,5 +1,6 @@ #include "htu21d.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace htu21d { @@ -35,18 +36,30 @@ void HTU21DComponent::dump_config() { } void HTU21DComponent::update() { uint16_t raw_temperature; - if (!this->read_byte_16(HTU21D_REGISTER_TEMPERATURE, &raw_temperature, 50)) { + if (this->write(&HTU21D_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(50); // NOLINT + if (this->read(reinterpret_cast(&raw_temperature), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_temperature = i2c::i2ctohs(raw_temperature); float temperature = (float(raw_temperature & 0xFFFC)) * 175.72f / 65536.0f - 46.85f; uint16_t raw_humidity; - if (!this->read_byte_16(HTU21D_REGISTER_HUMIDITY, &raw_humidity, 50)) { + if (this->write(&HTU21D_REGISTER_HUMIDITY, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(50); // NOLINT + if (this->read(reinterpret_cast(&raw_humidity), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_humidity = i2c::i2ctohs(raw_humidity); float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f; ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity); diff --git a/esphome/components/hx711/hx711.h b/esphome/components/hx711/hx711.h index 91c8317ee5..9fef649b03 100644 --- a/esphome/components/hx711/hx711.h +++ b/esphome/components/hx711/hx711.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index f43729066d..46f0abacc6 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -2,30 +2,56 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.const import ( - CONF_CHANNEL, CONF_FREQUENCY, CONF_ID, + CONF_INPUT, + CONF_OUTPUT, CONF_SCAN, CONF_SCL, CONF_SDA, CONF_ADDRESS, CONF_I2C_ID, - CONF_MULTIPLEXER, ) -from esphome.core import coroutine_with_priority +from esphome.core import coroutine_with_priority, CORE CODEOWNERS = ["@esphome/core"] i2c_ns = cg.esphome_ns.namespace("i2c") -I2CComponent = i2c_ns.class_("I2CComponent", cg.Component) +I2CBus = i2c_ns.class_("I2CBus") +ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", I2CBus, cg.Component) +IDFI2CBus = i2c_ns.class_("IDFI2CBus", I2CBus, cg.Component) I2CDevice = i2c_ns.class_("I2CDevice") -I2CMultiplexer = i2c_ns.class_("I2CMultiplexer", I2CDevice) + +CONF_SDA_PULLUP_ENABLED = "sda_pullup_enabled" +CONF_SCL_PULLUP_ENABLED = "scl_pullup_enabled" MULTI_CONF = True + + +def _bus_declare_type(value): + if CORE.using_arduino: + return cv.declare_id(ArduinoI2CBus)(value) + if CORE.using_esp_idf: + return cv.declare_id(IDFI2CBus)(value) + raise NotImplementedError + + +pin_with_input_and_output_support = cv.All( + pins.internal_gpio_pin_number({CONF_INPUT: True}), + pins.internal_gpio_pin_number({CONF_OUTPUT: True}), +) + + CONFIG_SCHEMA = cv.Schema( { - cv.GenerateID(): cv.declare_id(I2CComponent), - cv.Optional(CONF_SDA, default="SDA"): pins.input_pin, - cv.Optional(CONF_SCL, default="SCL"): pins.input_pin, + cv.GenerateID(): _bus_declare_type, + cv.Optional(CONF_SDA, default="SDA"): pin_with_input_and_output_support, + cv.SplitDefault(CONF_SDA_PULLUP_ENABLED, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), + cv.Optional(CONF_SCL, default="SCL"): pin_with_input_and_output_support, + cv.SplitDefault(CONF_SCL_PULLUP_ENABLED, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All( cv.frequency, cv.Range(min=0, min_included=False) ), @@ -33,13 +59,6 @@ CONFIG_SCHEMA = cv.Schema( } ).extend(cv.COMPONENT_SCHEMA) -I2CMULTIPLEXER_SCHEMA = cv.Schema( - { - cv.Required(CONF_ID): cv.use_id(I2CMultiplexer), - cv.Required(CONF_CHANNEL): cv.uint8_t, - } -) - @coroutine_with_priority(1.0) async def to_code(config): @@ -48,10 +67,16 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_sda_pin(config[CONF_SDA])) + if CONF_SDA_PULLUP_ENABLED in config: + cg.add(var.set_sda_pullup_enabled(config[CONF_SDA_PULLUP_ENABLED])) cg.add(var.set_scl_pin(config[CONF_SCL])) + if CONF_SCL_PULLUP_ENABLED in config: + cg.add(var.set_scl_pullup_enabled(config[CONF_SCL_PULLUP_ENABLED])) + cg.add(var.set_frequency(int(config[CONF_FREQUENCY]))) cg.add(var.set_scan(config[CONF_SCAN])) - cg.add_library("Wire", None) + if CORE.using_arduino: + cg.add_library("Wire", None) def i2c_device_schema(default_address): @@ -62,8 +87,11 @@ def i2c_device_schema(default_address): :return: The i2c device schema, `extend` this in your config schema. """ schema = { - cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CComponent), - cv.Optional(CONF_MULTIPLEXER): I2CMULTIPLEXER_SCHEMA, + cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CBus), + cv.Optional("multiplexer"): cv.invalid( + "This option has been removed, please see " + "the tca9584a docs for the updated way to use multiplexers" + ), } if default_address is None: schema[cv.Required(CONF_ADDRESS)] = cv.i2c_address @@ -80,10 +108,5 @@ async def register_i2c_device(var, config): This is a coroutine, you need to await it with a 'yield' expression! """ parent = await cg.get_variable(config[CONF_I2C_ID]) - cg.add(var.set_i2c_parent(parent)) + cg.add(var.set_i2c_bus(parent)) cg.add(var.set_i2c_address(config[CONF_ADDRESS])) - if CONF_MULTIPLEXER in config: - multiplexer = await cg.get_variable(config[CONF_MULTIPLEXER][CONF_ID]) - cg.add( - var.set_i2c_multiplexer(multiplexer, config[CONF_MULTIPLEXER][CONF_CHANNEL]) - ) diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index a3a3f7f82a..035c483344 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -1,303 +1,40 @@ #include "i2c.h" #include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/application.h" +#include namespace esphome { namespace i2c { static const char *const TAG = "i2c"; -I2CComponent::I2CComponent() { -#ifdef ARDUINO_ARCH_ESP32 - static uint8_t next_i2c_bus_num = 0; - if (next_i2c_bus_num == 0) - this->wire_ = &Wire; - else - this->wire_ = new TwoWire(next_i2c_bus_num); // NOLINT(cppcoreguidelines-owning-memory) - next_i2c_bus_num++; -#else - this->wire_ = &Wire; // NOLINT(cppcoreguidelines-prefer-member-initializer) -#endif +bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) { + // we have to copy in order to be able to change byte order + std::unique_ptr temp{new uint16_t[len]}; + for (size_t i = 0; i < len; i++) + temp[i] = htoi2cs(data[i]); + return write_register(a_register, reinterpret_cast(data), len * 2) == ERROR_OK; } -void I2CComponent::setup() { - this->wire_->begin(this->sda_pin_, this->scl_pin_); - this->wire_->setClock(this->frequency_); -} -void I2CComponent::dump_config() { - ESP_LOGCONFIG(TAG, "I2C Bus:"); - ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); - ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); - ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); - if (this->scan_) { - ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); - uint8_t found = 0; - for (uint8_t address = 1; address < 120; address++) { - this->wire_->beginTransmission(address); - uint8_t error = this->wire_->endTransmission(); - - if (error == 0) { - ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); - found++; - } else if (error == 4) { - ESP_LOGI(TAG, "Unknown error at address 0x%02X", address); - } - - delay(1); - } - if (found == 0) { - ESP_LOGI(TAG, "Found no i2c devices!"); - } - } -} -float I2CComponent::get_setup_priority() const { return setup_priority::BUS; } - -void I2CComponent::raw_begin_transmission(uint8_t address) { - ESP_LOGVV(TAG, "Beginning Transmission to 0x%02X:", address); - this->wire_->beginTransmission(address); -} -bool I2CComponent::raw_end_transmission(uint8_t address, bool send_stop) { - uint8_t status = this->wire_->endTransmission(send_stop); - ESP_LOGVV(TAG, " Transmission ended. Status code: 0x%02X", status); - - switch (status) { - case 0: - break; - case 1: - ESP_LOGW(TAG, "Too much data to fit in transmitter buffer for address 0x%02X", address); - break; - case 2: - ESP_LOGW(TAG, "Received NACK on transmit of address 0x%02X", address); - break; - case 3: - ESP_LOGW(TAG, "Received NACK on transmit of data for address 0x%02X", address); - break; - default: - ESP_LOGW(TAG, "Unknown transmit error %u for address 0x%02X", status, address); - break; - } - - return status == 0; -} -bool I2CComponent::raw_request_from(uint8_t address, uint8_t len) { - ESP_LOGVV(TAG, "Requesting %u bytes from 0x%02X:", len, address); - uint8_t ret = this->wire_->requestFrom(address, len); - if (ret != len) { - ESP_LOGW(TAG, "Requesting %u bytes from 0x%02X failed!", len, address); - return false; - } - return true; -} -void HOT I2CComponent::raw_write(uint8_t address, const uint8_t *data, uint8_t len) { - for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Writing 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); - this->wire_->write(data[i]); - App.feed_wdt(); - } -} -void HOT I2CComponent::raw_write_16(uint8_t address, const uint16_t *data, uint8_t len) { - for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Writing 0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN " (0x%04X)", - BYTE_TO_BINARY(data[i] >> 8), BYTE_TO_BINARY(data[i]), data[i]); - this->wire_->write(data[i] >> 8); - this->wire_->write(data[i]); - App.feed_wdt(); - } -} - -bool I2CComponent::raw_receive(uint8_t address, uint8_t *data, uint8_t len) { - if (!this->raw_request_from(address, len)) - return false; - for (uint8_t i = 0; i < len; i++) { - data[i] = this->wire_->read(); - ESP_LOGVV(TAG, " Received 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); - App.feed_wdt(); - } - return true; -} -bool I2CComponent::raw_receive_16(uint8_t address, uint16_t *data, uint8_t len) { - if (!this->raw_request_from(address, len * 2)) - return false; - auto *data_8 = reinterpret_cast(data); - for (uint8_t i = 0; i < len; i++) { - data_8[i * 2 + 1] = this->wire_->read(); - data_8[i * 2] = this->wire_->read(); - ESP_LOGVV(TAG, " Received 0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN " (0x%04X)", - BYTE_TO_BINARY(data_8[i * 2 + 1]), BYTE_TO_BINARY(data_8[i * 2]), data[i]); - } - return true; -} -bool I2CComponent::read_bytes(uint8_t address, uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) { - if (!this->write_bytes(address, a_register, nullptr, 0)) - return false; - - if (conversion > 0) - delay(conversion); - return this->raw_receive(address, data, len); -} -bool I2CComponent::read_bytes_raw(uint8_t address, uint8_t *data, uint8_t len) { - return this->raw_receive(address, data, len); -} -bool I2CComponent::read_bytes_16(uint8_t address, uint8_t a_register, uint16_t *data, uint8_t len, - uint32_t conversion) { - if (!this->write_bytes(address, a_register, nullptr, 0)) - return false; - - if (conversion > 0) - delay(conversion); - return this->raw_receive_16(address, data, len); -} -bool I2CComponent::read_byte(uint8_t address, uint8_t a_register, uint8_t *data, uint32_t conversion) { - return this->read_bytes(address, a_register, data, 1, conversion); -} -bool I2CComponent::read_byte_16(uint8_t address, uint8_t a_register, uint16_t *data, uint32_t conversion) { - return this->read_bytes_16(address, a_register, data, 1, conversion); -} -bool I2CComponent::write_bytes(uint8_t address, uint8_t a_register, const uint8_t *data, uint8_t len) { - this->raw_begin_transmission(address); - this->raw_write(address, &a_register, 1); - this->raw_write(address, data, len); - return this->raw_end_transmission(address); -} -bool I2CComponent::write_bytes_raw(uint8_t address, const uint8_t *data, uint8_t len) { - this->raw_begin_transmission(address); - this->raw_write(address, data, len); - return this->raw_end_transmission(address); -} -bool I2CComponent::write_bytes_16(uint8_t address, uint8_t a_register, const uint16_t *data, uint8_t len) { - this->raw_begin_transmission(address); - this->raw_write(address, &a_register, 1); - this->raw_write_16(address, data, len); - return this->raw_end_transmission(address); -} -bool I2CComponent::write_byte(uint8_t address, uint8_t a_register, uint8_t data) { - return this->write_bytes(address, a_register, &data, 1); -} -bool I2CComponent::write_byte_16(uint8_t address, uint8_t a_register, uint16_t data) { - return this->write_bytes_16(address, a_register, &data, 1); -} - -void I2CDevice::set_i2c_address(uint8_t address) { this->address_ = address; } -#ifdef USE_I2C_MULTIPLEXER -void I2CDevice::set_i2c_multiplexer(I2CMultiplexer *multiplexer, uint8_t channel) { - ESP_LOGVV(TAG, " Setting Multiplexer %p for channel %d", multiplexer, channel); - this->multiplexer_ = multiplexer; - this->channel_ = channel; -} - -void I2CDevice::check_multiplexer_() { - if (this->multiplexer_ != nullptr) { - ESP_LOGVV(TAG, "Multiplexer setting channel to %d", this->channel_); - this->multiplexer_->set_channel(this->channel_); - } -} -#endif - -void I2CDevice::raw_begin_transmission() { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - this->parent_->raw_begin_transmission(this->address_); -} -bool I2CDevice::raw_end_transmission(bool send_stop) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->raw_end_transmission(this->address_, send_stop); -} -void I2CDevice::raw_write(const uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - this->parent_->raw_write(this->address_, data, len); -} -bool I2CDevice::read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_bytes(this->address_, a_register, data, len, conversion); -} -bool I2CDevice::read_bytes_raw(uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_bytes_raw(this->address_, data, len); -} -bool I2CDevice::read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_byte(this->address_, a_register, data, conversion); -} -bool I2CDevice::write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_bytes(this->address_, a_register, data, len); -} -bool I2CDevice::write_bytes_raw(const uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_bytes_raw(this->address_, data, len); -} -bool I2CDevice::write_byte(uint8_t a_register, uint8_t data) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_byte(this->address_, a_register, data); -} -bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_bytes_16(this->address_, a_register, data, len, conversion); -} -bool I2CDevice::read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_byte_16(this->address_, a_register, data, conversion); -} -bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_bytes_16(this->address_, a_register, data, len); -} -bool I2CDevice::write_byte_16(uint8_t a_register, uint16_t data) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_byte_16(this->address_, a_register, data); -} -void I2CDevice::set_i2c_parent(I2CComponent *parent) { this->parent_ = parent; } - I2CRegister &I2CRegister::operator=(uint8_t value) { - this->parent_->write_byte(this->register_, value); + this->parent_->write_register(this->register_, &value, 1); return *this; } - I2CRegister &I2CRegister::operator&=(uint8_t value) { - this->parent_->write_byte(this->register_, this->get() & value); + value &= get(); + this->parent_->write_register(this->register_, &value, 1); return *this; } - I2CRegister &I2CRegister::operator|=(uint8_t value) { - this->parent_->write_byte(this->register_, this->get() | value); + value |= get(); + this->parent_->write_register(this->register_, &value, 1); return *this; } -uint8_t I2CRegister::get() { +uint8_t I2CRegister::get() const { uint8_t value = 0x00; - this->parent_->read_byte(this->register_, &value); + this->parent_->read_register(this->register_, &value, 1); return value; } -I2CRegister &I2CRegister::operator=(const std::vector &value) { - this->parent_->write_bytes(this->register_, value); - return *this; -} } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index 6f93d4c0e4..71ab650e97 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -1,198 +1,79 @@ #pragma once -#include -#include "esphome/core/defines.h" -#include "esphome/core/component.h" -#include "esphome/core/helpers.h" +#include "i2c_bus.h" +#include "esphome/core/optional.h" +#include +#include namespace esphome { namespace i2c { #define LOG_I2C_DEVICE(this) ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); -/** The I2CComponent is the base of ESPHome's i2c communication. - * - * It handles setting up the bus (with pins, clock frequency) and provides nice helper functions to - * make reading from the i2c bus easier (see read_bytes, write_bytes) and safe (with read timeouts). - * - * For the user, it has a few setters (see set_sda_pin, set_scl_pin, set_frequency) - * to setup some parameters for the bus. Additionally, the i2c component has a scan feature that will - * scan the entire 7-bit i2c address range for devices that respond to transmissions to make finding - * the address of an i2c device easier. - * - * On the ESP32, you can even have multiple I2C bus for communication, simply create multiple - * I2CComponents, each with different SDA and SCL pins and use `set_parent` on all I2CDevices that use - * the non-first I2C bus. - */ -class I2CComponent : public Component { - public: - I2CComponent(); - void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } - void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } - void set_frequency(uint32_t frequency) { frequency_ = frequency; } - void set_scan(bool scan) { scan_ = scan; } - - /** Read len amount of bytes from a register into data. Optionally with a conversion time after - * writing the register value to the bus. - * - * @param address The address to send the request to. - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 8-bit bytes into. - * @param len The amount of bytes to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes(uint8_t address, uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); - bool read_bytes_raw(uint8_t address, uint8_t *data, uint8_t len); - - /** Read len amount of 16-bit words (MSB first) from a register into data. - * - * @param address The address to send the request to. - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 16-bit words into. - * @param len The amount of 16-bit words to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes_16(uint8_t address, uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion = 0); - - /// Read a single byte from a register into the data variable. Return true if successful. - bool read_byte(uint8_t address, uint8_t a_register, uint8_t *data, uint32_t conversion = 0); - - /// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful. - bool read_byte_16(uint8_t address, uint8_t a_register, uint16_t *data, uint32_t conversion = 0); - - /** Write len amount of 8-bit bytes to the specified register for address. - * - * @param address The address to use for the transmission. - * @param a_register The register to write the values to. - * @param data An array from which len bytes of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ - bool write_bytes(uint8_t address, uint8_t a_register, const uint8_t *data, uint8_t len); - bool write_bytes_raw(uint8_t address, const uint8_t *data, uint8_t len); - - /** Write len amount of 16-bit words (MSB first) to the specified register for address. - * - * @param address The address to use for the transmission. - * @param a_register The register to write the values to. - * @param data An array from which len 16-bit words of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ - bool write_bytes_16(uint8_t address, uint8_t a_register, const uint16_t *data, uint8_t len); - - /// Write a single byte of data into the specified register of address. Return true if successful. - bool write_byte(uint8_t address, uint8_t a_register, uint8_t data); - - /// Write a single 16-bit word of data into the specified register of address. Return true if successful. - bool write_byte_16(uint8_t address, uint8_t a_register, uint16_t data); - - // ========== INTERNAL METHODS ========== - // (In most use cases you won't need these) - /// Begin a write transmission to an address. - void raw_begin_transmission(uint8_t address); - - /// End a write transmission to an address, return true if successful. - bool raw_end_transmission(uint8_t address, bool send_stop = true); - - /** Request data from an address with a number of (8-bit) bytes. - * - * @param address The address to request the bytes from. - * @param len The number of bytes to receive, must not be 0. - * @return True if all requested bytes were read, false otherwise. - */ - bool raw_request_from(uint8_t address, uint8_t len); - - /// Write len amount of bytes from data to address. begin_transmission_ must be called before this. - void raw_write(uint8_t address, const uint8_t *data, uint8_t len); - - /// Write len amount of 16-bit words from data to address. begin_transmission_ must be called before this. - void raw_write_16(uint8_t address, const uint16_t *data, uint8_t len); - - /// Request len amount of bytes from address and write the result it into data. Returns true iff was successful. - bool raw_receive(uint8_t address, uint8_t *data, uint8_t len); - - /// Request len amount of 16-bit words from address and write the result into data. Returns true iff was successful. - bool raw_receive_16(uint8_t address, uint16_t *data, uint8_t len); - - /// Setup the i2c. bus - void setup() override; - void dump_config() override; - /// Set a very high setup priority to make sure it's loaded before all other hardware. - float get_setup_priority() const override; - - protected: - TwoWire *wire_; - uint8_t sda_pin_; - uint8_t scl_pin_; - uint32_t frequency_; - bool scan_; -}; - class I2CDevice; -class I2CMultiplexer; class I2CRegister { public: I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} I2CRegister &operator=(uint8_t value); - I2CRegister &operator=(const std::vector &value); I2CRegister &operator&=(uint8_t value); I2CRegister &operator|=(uint8_t value); - uint8_t get(); + explicit operator uint8_t() const { return get(); } + + uint8_t get() const; protected: I2CDevice *parent_; uint8_t register_; }; -/** All components doing communication on the I2C bus should subclass I2CDevice. - * - * This class stores 1. the address of the i2c device and has a helper function to allow - * users to manually set the address and 2. stores a reference to the "parent" I2CComponent. - * - * - * All this class basically does is to expose all helper functions from I2CComponent. - */ +// like ntohs/htons but without including networking headers. +// ("i2c" byte order is big-endian) +inline uint16_t i2ctohs(uint16_t i2cshort) { + union { + uint16_t x; + uint8_t y[2]; + } conv; + conv.x = i2cshort; + return ((uint16_t) conv.y[0] << 8) | ((uint16_t) conv.y[1] << 0); +} + +inline uint16_t htoi2cs(uint16_t hostshort) { return i2ctohs(hostshort); } + class I2CDevice { public: I2CDevice() = default; - I2CDevice(I2CComponent *parent, uint8_t address) : address_(address), parent_(parent) {} - /// Manually set the i2c address of this device. - void set_i2c_address(uint8_t address); -#ifdef USE_I2C_MULTIPLEXER - /// Manually set the i2c multiplexer of this device. - void set_i2c_multiplexer(I2CMultiplexer *multiplexer, uint8_t channel); -#endif - /// Manually set the parent i2c bus for this device. - void set_i2c_parent(I2CComponent *parent); + void set_i2c_address(uint8_t address) { address_ = address; } + void set_i2c_bus(I2CBus *bus) { bus_ = bus; } I2CRegister reg(uint8_t a_register) { return {this, a_register}; } - /// Begin a write transmission. - void raw_begin_transmission(); + ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); } + ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len) { + ErrorCode err = this->write(&a_register, 1); + if (err != ERROR_OK) + return err; + return this->read(data, len); + } - /// End a write transmission, return true if successful. - bool raw_end_transmission(bool send_stop = true); + ErrorCode write(const uint8_t *data, uint8_t len) { return bus_->write(address_, data, len); } + ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) { + WriteBuffer buffers[2]; + buffers[0].data = &a_register; + buffers[0].len = 1; + buffers[1].data = data; + buffers[1].len = len; + return bus_->writev(address_, buffers, 2); + } - /// Write len amount of bytes from data. begin_transmission_ must be called before this. - void raw_write(const uint8_t *data, uint8_t len); + // Compat APIs - /** Read len amount of bytes from a register into data. Optionally with a conversion time after - * writing the register value to the bus. - * - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 8-bit bytes into. - * @param len The amount of bytes to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); - bool read_bytes_raw(uint8_t *data, uint8_t len); + bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) { + return read_register(a_register, data, len) == ERROR_OK; + } + bool read_bytes_raw(uint8_t *data, uint8_t len) { return read(data, len) == ERROR_OK; } template optional> read_bytes(uint8_t a_register) { std::array res; @@ -209,18 +90,15 @@ class I2CDevice { return res; } - /** Read len amount of 16-bit words (MSB first) from a register into data. - * - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 16-bit words into. - * @param len The amount of 16-bit words to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion = 0); + bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) { + if (read_register(a_register, reinterpret_cast(data), len * 2) != ERROR_OK) + return false; + for (size_t i = 0; i < len; i++) + data[i] = i2ctohs(data[i]); + return true; + } - /// Read a single byte from a register into the data variable. Return true if successful. - bool read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion = 0); + bool read_byte(uint8_t a_register, uint8_t *data) { return read_register(a_register, data, 1) == ERROR_OK; } optional read_byte(uint8_t a_register) { uint8_t data; @@ -229,66 +107,30 @@ class I2CDevice { return data; } - /// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful. - bool read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion = 0); + bool read_byte_16(uint8_t a_register, uint16_t *data) { return read_bytes_16(a_register, data, 1); } - /** Write len amount of 8-bit bytes to the specified register. - * - * @param a_register The register to write the values to. - * @param data An array from which len bytes of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ - bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len); - bool write_bytes_raw(const uint8_t *data, uint8_t len); - - /** Write a vector of data to a register. - * - * @param a_register The register to write to. - * @param data The data to write. - * @return If the operation was successful. - */ - bool write_bytes(uint8_t a_register, const std::vector &data) { - return this->write_bytes(a_register, data.data(), data.size()); + bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) { + return write_register(a_register, data, len) == ERROR_OK; + } + + bool write_bytes(uint8_t a_register, const std::vector &data) { + return write_bytes(a_register, data.data(), data.size()); } - bool write_bytes_raw(const std::vector &data) { return this->write_bytes_raw(data.data(), data.size()); } template bool write_bytes(uint8_t a_register, const std::array &data) { - return this->write_bytes(a_register, data.data(), data.size()); - } - template bool write_bytes_raw(const std::array &data) { - return this->write_bytes_raw(data.data(), data.size()); + return write_bytes(a_register, data.data(), data.size()); } - /** Write len amount of 16-bit words (MSB first) to the specified register. - * - * @param a_register The register to write the values to. - * @param data An array from which len 16-bit words of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len); - /// Write a single byte of data into the specified register. Return true if successful. - bool write_byte(uint8_t a_register, uint8_t data); + bool write_byte(uint8_t a_register, uint8_t data) { return write_bytes(a_register, &data, 1); } - /// Write a single 16-bit word of data into the specified register. Return true if successful. - bool write_byte_16(uint8_t a_register, uint16_t data); + bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); } protected: - // Checks for multiplexer set and set channel - void check_multiplexer_(); uint8_t address_{0x00}; - I2CComponent *parent_{nullptr}; -#ifdef USE_I2C_MULTIPLEXER - I2CMultiplexer *multiplexer_{nullptr}; - uint8_t channel_; -#endif -}; -class I2CMultiplexer : public I2CDevice { - public: - I2CMultiplexer() = default; - virtual void set_channel(uint8_t channelno); + I2CBus *bus_{nullptr}; }; + } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c_bus.h b/esphome/components/i2c/i2c_bus.h new file mode 100644 index 0000000000..cb00260f43 --- /dev/null +++ b/esphome/components/i2c/i2c_bus.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +namespace esphome { +namespace i2c { + +enum ErrorCode { + ERROR_OK = 0, + ERROR_INVALID_ARGUMENT = 1, + ERROR_NOT_ACKNOWLEDGED = 2, + ERROR_TIMEOUT = 3, + ERROR_NOT_INITIALIZED = 4, + ERROR_TOO_LARGE = 5, + ERROR_UNKNOWN = 6, +}; + +struct ReadBuffer { + uint8_t *data; + size_t len; +}; +struct WriteBuffer { + const uint8_t *data; + size_t len; +}; + +class I2CBus { + public: + virtual ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) { + ReadBuffer buf; + buf.data = buffer; + buf.len = len; + return readv(address, &buf, 1); + } + virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) = 0; + virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) { + WriteBuffer buf; + buf.data = buffer; + buf.len = len; + return writev(address, &buf, 1); + } + virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) = 0; +}; + +} // namespace i2c +} // namespace esphome diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp new file mode 100644 index 0000000000..aba412c3f7 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -0,0 +1,97 @@ +#ifdef USE_ARDUINO + +#include "i2c_bus_arduino.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace i2c { + +static const char *const TAG = "i2c.arduino"; + +void ArduinoI2CBus::setup() { +#ifdef USE_ESP32 + static uint8_t next_bus_num = 0; + if (next_bus_num == 0) + wire_ = &Wire; + else + wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory) + next_bus_num++; +#else + wire_ = &Wire; // NOLINT(cppcoreguidelines-prefer-member-initializer) +#endif + + wire_->begin(sda_pin_, scl_pin_); + wire_->setClock(frequency_); + initialized_ = true; +} +void ArduinoI2CBus::dump_config() { + ESP_LOGCONFIG(TAG, "I2C Bus:"); + ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); + ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); + ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + if (this->scan_) { + ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); + uint8_t found = 0; + for (uint8_t address = 1; address < 120; address++) { + auto err = readv(address, nullptr, 0); + + if (err == ERROR_OK) { + ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); + found++; + } else if (err == ERROR_UNKNOWN) { + ESP_LOGI(TAG, "Unknown error at address 0x%02X", address); + } + } + if (found == 0) { + ESP_LOGI(TAG, "Found no i2c devices!"); + } + } +} +ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + size_t to_request = 0; + for (size_t i = 0; i < cnt; i++) + to_request += buffers[i].len; + size_t ret = wire_->requestFrom((int) address, (int) to_request, 1); + if (ret != to_request) { + return ERROR_TIMEOUT; + } + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + for (size_t j = 0; j < buf.len; j++) + buf.data[j] = wire_->read(); + } + return ERROR_OK; +} +ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + + wire_->beginTransmission(address); + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + if (buf.len == 0) + continue; + size_t ret = wire_->write(buf.data, buf.len); + if (ret != buf.len) { + return ERROR_UNKNOWN; + } + } + uint8_t status = wire_->endTransmission(true); + if (status == 0) { + return ERROR_OK; + } else if (status == 1) { + // transmit buffer not large enough + return ERROR_UNKNOWN; + } else if (status == 2 || status == 3) { + return ERROR_NOT_ACKNOWLEDGED; + } + return ERROR_UNKNOWN; +} + +} // namespace i2c +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h new file mode 100644 index 0000000000..220027b3d4 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -0,0 +1,37 @@ +#pragma once + +#ifdef USE_ARDUINO + +#include "i2c_bus.h" +#include "esphome/core/component.h" +#include + +namespace esphome { +namespace i2c { + +class ArduinoI2CBus : public I2CBus, public Component { + public: + void setup() override; + void dump_config() override; + ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; + ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void set_scan(bool scan) { scan_ = scan; } + void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } + void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } + void set_frequency(uint32_t frequency) { frequency_ = frequency; } + + protected: + TwoWire *wire_; + bool scan_; + uint8_t sda_pin_; + uint8_t scl_pin_; + uint32_t frequency_; + bool initialized_ = false; +}; + +} // namespace i2c +} // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp new file mode 100644 index 0000000000..4b93b41877 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -0,0 +1,147 @@ +#ifdef USE_ESP_IDF + +#include "i2c_bus_esp_idf.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace i2c { + +static const char *const TAG = "i2c.idf"; + +void IDFI2CBus::setup() { + static i2c_port_t next_port = 0; + port_ = next_port++; + + i2c_config_t conf{}; + memset(&conf, 0, sizeof(conf)); + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = sda_pin_; + conf.sda_pullup_en = sda_pullup_enabled_; + conf.scl_io_num = scl_pin_; + conf.scl_pullup_en = scl_pullup_enabled_; + conf.master.clk_speed = frequency_; + esp_err_t err = i2c_param_config(port_, &conf); + if (err != ESP_OK) { + ESP_LOGW(TAG, "i2c_param_config failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM); + if (err != ESP_OK) { + ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + initialized_ = true; +} +void IDFI2CBus::dump_config() { + ESP_LOGCONFIG(TAG, "I2C Bus:"); + ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); + ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); + ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + if (this->scan_) { + ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); + uint8_t found = 0; + for (uint8_t address = 1; address < 120; address++) { + auto err = readv(address, nullptr, 0); + + if (err == ERROR_OK) { + ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); + found++; + } else if (err == ERROR_UNKNOWN) { + ESP_LOGI(TAG, "Unknown error at address 0x%02X", address); + } + } + if (found == 0) { + ESP_LOGI(TAG, "Found no i2c devices!"); + } + } +} +ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + esp_err_t err = i2c_master_start(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + if (buf.len == 0) + continue; + err = i2c_master_read(cmd, buf.data, buf.len, i == cnt - 1 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + } + err = i2c_master_stop(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + if (err == ESP_FAIL) { + // transfer not acked + return ERROR_NOT_ACKNOWLEDGED; + } else if (err == ESP_ERR_TIMEOUT) { + return ERROR_TIMEOUT; + } else if (err != ESP_OK) { + return ERROR_UNKNOWN; + } + return ERROR_OK; +} +ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + esp_err_t err = i2c_master_start(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + if (buf.len == 0) + continue; + err = i2c_master_write(cmd, buf.data, buf.len, true); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + } + err = i2c_master_stop(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + if (err == ESP_FAIL) { + // transfer not acked + return ERROR_NOT_ACKNOWLEDGED; + } else if (err == ESP_ERR_TIMEOUT) { + return ERROR_TIMEOUT; + } else if (err != ESP_OK) { + return ERROR_UNKNOWN; + } + return ERROR_OK; +} + +} // namespace i2c +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h new file mode 100644 index 0000000000..c7e67145a3 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -0,0 +1,41 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include "i2c_bus.h" +#include "esphome/core/component.h" +#include + +namespace esphome { +namespace i2c { + +class IDFI2CBus : public I2CBus, public Component { + public: + void setup() override; + void dump_config() override; + ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; + ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void set_scan(bool scan) { scan_ = scan; } + void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } + void set_sda_pullup_enabled(bool sda_pullup_enabled) { sda_pullup_enabled_ = sda_pullup_enabled; } + void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } + void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; } + void set_frequency(uint32_t frequency) { frequency_ = frequency; } + + protected: + i2c_port_t port_; + bool scan_; + uint8_t sda_pin_; + bool sda_pullup_enabled_; + uint8_t scl_pin_; + bool scl_pullup_enabled_; + uint32_t frequency_; + bool initialized_ = false; +}; + +} // namespace i2c +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp index c06d487e7d..b36d05c864 100644 --- a/esphome/components/ili9341/ili9341_display.cpp +++ b/esphome/components/ili9341/ili9341_display.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace ili9341 { @@ -185,8 +186,8 @@ void ILI9341Display::end_data_() { this->disable(); } void ILI9341Display::init_lcd_(const uint8_t *init_cmd) { uint8_t cmd, x, num_args; const uint8_t *addr = init_cmd; - while ((cmd = pgm_read_byte(addr++)) > 0) { - x = pgm_read_byte(addr++); + while ((cmd = progmem_read_byte(addr++)) > 0) { + x = progmem_read_byte(addr++); num_args = x & 0x7F; send_command(cmd, addr, num_args); addr += num_args; diff --git a/esphome/components/improv/improv.cpp b/esphome/components/improv/improv.cpp index d1fee72866..4f6ed7702d 100644 --- a/esphome/components/improv/improv.cpp +++ b/esphome/components/improv/improv.cpp @@ -65,6 +65,7 @@ std::vector build_rpc_response(Command command, const std::vector build_rpc_response(Command command, const std::vector &datum) { std::vector out; uint32_t length = 0; @@ -85,5 +86,6 @@ std::vector build_rpc_response(Command command, const std::vector #include #include @@ -50,6 +53,8 @@ ImprovCommand parse_improv_data(const std::vector &data); ImprovCommand parse_improv_data(const uint8_t *data, size_t length); std::vector build_rpc_response(Command command, const std::vector &datum); +#ifdef USE_ARDUINO std::vector build_rpc_response(Command command, const std::vector &datum); +#endif // USE_ARDUINO } // namespace improv diff --git a/esphome/components/ina219/ina219.cpp b/esphome/components/ina219/ina219.cpp index 506b7e06ed..609f3d0f08 100644 --- a/esphome/components/ina219/ina219.cpp +++ b/esphome/components/ina219/ina219.cpp @@ -1,5 +1,6 @@ #include "ina219.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ina219 { @@ -149,7 +150,7 @@ float INA219Component::get_setup_priority() const { return setup_priority::DATA; void INA219Component::update() { if (this->bus_voltage_sensor_ != nullptr) { uint16_t raw_bus_voltage; - if (!this->read_byte_16(INA219_REGISTER_BUS_VOLTAGE, &raw_bus_voltage, 1)) { + if (!this->read_byte_16(INA219_REGISTER_BUS_VOLTAGE, &raw_bus_voltage)) { this->status_set_warning(); return; } @@ -160,8 +161,9 @@ void INA219Component::update() { if (this->shunt_voltage_sensor_ != nullptr) { uint16_t raw_shunt_voltage; - if (!this->read_byte_16(INA219_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage, 1)) { + if (!this->read_byte_16(INA219_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage)) { this->status_set_warning(); + return; } float shunt_voltage_mv = int16_t(raw_shunt_voltage) * 0.01f; this->shunt_voltage_sensor_->publish_state(shunt_voltage_mv / 1000.0f); @@ -169,7 +171,7 @@ void INA219Component::update() { if (this->current_sensor_ != nullptr) { uint16_t raw_current; - if (!this->read_byte_16(INA219_REGISTER_CURRENT, &raw_current, 1)) { + if (!this->read_byte_16(INA219_REGISTER_CURRENT, &raw_current)) { this->status_set_warning(); return; } @@ -179,7 +181,7 @@ void INA219Component::update() { if (this->power_sensor_ != nullptr) { uint16_t raw_power; - if (!this->read_byte_16(INA219_REGISTER_POWER, &raw_power, 1)) { + if (!this->read_byte_16(INA219_REGISTER_POWER, &raw_power)) { this->status_set_warning(); return; } diff --git a/esphome/components/ina226/ina226.cpp b/esphome/components/ina226/ina226.cpp index 701a833041..2e30a5ac01 100644 --- a/esphome/components/ina226/ina226.cpp +++ b/esphome/components/ina226/ina226.cpp @@ -1,5 +1,6 @@ #include "ina226.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ina226 { @@ -96,7 +97,7 @@ float INA226Component::get_setup_priority() const { return setup_priority::DATA; void INA226Component::update() { if (this->bus_voltage_sensor_ != nullptr) { uint16_t raw_bus_voltage; - if (!this->read_byte_16(INA226_REGISTER_BUS_VOLTAGE, &raw_bus_voltage, 1)) { + if (!this->read_byte_16(INA226_REGISTER_BUS_VOLTAGE, &raw_bus_voltage)) { this->status_set_warning(); return; } @@ -106,8 +107,9 @@ void INA226Component::update() { if (this->shunt_voltage_sensor_ != nullptr) { uint16_t raw_shunt_voltage; - if (!this->read_byte_16(INA226_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage, 1)) { + if (!this->read_byte_16(INA226_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage)) { this->status_set_warning(); + return; } float shunt_voltage_v = int16_t(raw_shunt_voltage) * 0.0000025f; this->shunt_voltage_sensor_->publish_state(shunt_voltage_v); @@ -115,7 +117,7 @@ void INA226Component::update() { if (this->current_sensor_ != nullptr) { uint16_t raw_current; - if (!this->read_byte_16(INA226_REGISTER_CURRENT, &raw_current, 1)) { + if (!this->read_byte_16(INA226_REGISTER_CURRENT, &raw_current)) { this->status_set_warning(); return; } @@ -125,7 +127,7 @@ void INA226Component::update() { if (this->power_sensor_ != nullptr) { uint16_t raw_power; - if (!this->read_byte_16(INA226_REGISTER_POWER, &raw_power, 1)) { + if (!this->read_byte_16(INA226_REGISTER_POWER, &raw_power)) { this->status_set_warning(); return; } diff --git a/esphome/components/ina3221/ina3221.cpp b/esphome/components/ina3221/ina3221.cpp index f2fcdb21eb..3f8e2d06df 100644 --- a/esphome/components/ina3221/ina3221.cpp +++ b/esphome/components/ina3221/ina3221.cpp @@ -1,5 +1,6 @@ #include "ina3221.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ina3221 { @@ -87,7 +88,7 @@ void INA3221Component::update() { float bus_voltage_v = NAN, current_a = NAN; uint16_t raw; if (channel.should_measure_bus_voltage()) { - if (!this->read_byte_16(ina3221_bus_voltage_register(i), &raw, 1)) { + if (!this->read_byte_16(ina3221_bus_voltage_register(i), &raw)) { this->status_set_warning(); return; } @@ -96,7 +97,7 @@ void INA3221Component::update() { channel.bus_voltage_sensor_->publish_state(bus_voltage_v); } if (channel.should_measure_shunt_voltage()) { - if (!this->read_byte_16(ina3221_shunt_voltage_register(i), &raw, 1)) { + if (!this->read_byte_16(ina3221_shunt_voltage_register(i), &raw)) { this->status_set_warning(); return; } diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp index 81693c6d96..177bc76072 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -1,7 +1,7 @@ #include "inkbird_ibsth1_mini.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace inkbird_ibsth1_mini { @@ -88,10 +88,10 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi auto humidity = ((mnf_data.data[1] << 8) + mnf_data.data[0]) / 100.0f; // Send temperature only if the value is set - if (!isnan(temperature) && this->temperature_ != nullptr) { + if (!std::isnan(temperature) && this->temperature_ != nullptr) { this->temperature_->publish_state(temperature); } - if (!isnan(external_temperature) && this->external_temperature_ != nullptr) { + if (!std::isnan(external_temperature) && this->external_temperature_ != nullptr) { this->external_temperature_->publish_state(external_temperature); } if (this->humidity_ != nullptr) { diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h index c3a9f7062d..1b6be7afe0 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace inkbird_ibsth1_mini { diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py index 8e00a69751..e4c71ea717 100644 --- a/esphome/components/inkplate6/display.py +++ b/esphome/components/inkplate6/display.py @@ -8,11 +8,9 @@ from esphome.const import ( CONF_LAMBDA, CONF_PAGES, CONF_WAKEUP_PIN, - ESP_PLATFORM_ESP32, ) -DEPENDENCIES = ["i2c"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["i2c", "esp32"] CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin" CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin" @@ -91,6 +89,7 @@ CONFIG_SCHEMA = cv.All( .extend(cv.polling_component_schema("5s")) .extend(i2c.i2c_device_schema(0x48)), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), + cv.only_with_arduino, ) diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index b5cec6cf9e..8a05836db9 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -3,7 +3,9 @@ #include "esphome/core/application.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include namespace esphome { namespace inkplate6 { @@ -185,7 +187,7 @@ void Inkplate6::eink_on_() { delay(2); - this->read_byte(0x00, &temperature_, 0); + this->read_register(0x00, nullptr, 0); this->le_pin_->digital_write(false); this->oe_pin_->digital_write(false); @@ -208,7 +210,7 @@ void Inkplate6::fill(Color color) { memset(this->partial_buffer_, fill, this->get_buffer_length_()); } - ESP_LOGV(TAG, "Fill finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Fill finished (%ums)", millis() - start_time); } void Inkplate6::display() { ESP_LOGV(TAG, "Display called"); @@ -218,12 +220,12 @@ void Inkplate6::display() { this->display3b_(); } else { if (this->partial_updating_ && this->partial_update_()) { - ESP_LOGV(TAG, "Display finished (partial) (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display finished (partial) (%ums)", millis() - start_time); return; } this->display1b_(); } - ESP_LOGV(TAG, "Display finished (full) (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display finished (full) (%ums)", millis() - start_time); } void Inkplate6::display1b_() { ESP_LOGV(TAG, "Display1b called"); @@ -246,32 +248,32 @@ void Inkplate6::display1b_() { clean_fast_(0, 11); uint32_t clock = (1 << this->cl_pin_->get_pin()); - ESP_LOGV(TAG, "Display1b start loops (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b start loops (%ums)", millis() - start_time); for (int k = 0; k < 3; k++) { buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1]; vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { buffer_value = *(buffer_ptr--); data = LUTB[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); data = LUTB[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) { buffer_value = *(buffer_ptr--); data = LUTB[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; data = LUTB[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -281,31 +283,31 @@ void Inkplate6::display1b_() { } delayMicroseconds(230); } - ESP_LOGV(TAG, "Display1b first loop x %d (%lums)", 3, millis() - start_time); + ESP_LOGV(TAG, "Display1b first loop x %d (%ums)", 3, millis() - start_time); buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1]; vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { buffer_value = *(buffer_ptr--); data = LUT2[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); data = LUT2[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) { buffer_value = *(buffer_ptr--); data = LUT2[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; data = LUT2[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -314,13 +316,13 @@ void Inkplate6::display1b_() { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Display1b second loop (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b second loop (%ums)", millis() - start_time); vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { data = 0b00000000; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); send |= clock; GPIO.out_w1ts = send; @@ -336,13 +338,13 @@ void Inkplate6::display1b_() { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Display1b third loop (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b third loop (%ums)", millis() - start_time); vscan_start_(); eink_off_(); this->block_partial_ = false; this->partial_updates_ = 0; - ESP_LOGV(TAG, "Display1b finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b finished (%ums)", millis() - start_time); } void Inkplate6::display3b_() { ESP_LOGV(TAG, "Display3b called"); @@ -380,11 +382,11 @@ void Inkplate6::display3b_() { pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0); - send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | - (((pixel & B11100000) >> 5) << 25); + send = ((pixel & 0b00000011) << 4) | (((pixel & 0b00001100) >> 2) << 18) | (((pixel & 0b00010000) >> 4) << 23) | + (((pixel & 0b11100000) >> 5) << 25); hscan_start_(send); - send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | - (((pixel2 & B11100000) >> 5) << 25) | clock; + send = ((pixel2 & 0b00000011) << 4) | (((pixel2 & 0b00001100) >> 2) << 18) | + (((pixel2 & 0b00010000) >> 4) << 23) | (((pixel2 & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; @@ -398,13 +400,13 @@ void Inkplate6::display3b_() { pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0); - send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | - (((pixel & B11100000) >> 5) << 25) | clock; + send = ((pixel & 0b00000011) << 4) | (((pixel & 0b00001100) >> 2) << 18) | (((pixel & 0b00010000) >> 4) << 23) | + (((pixel & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; - send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | - (((pixel2 & B11100000) >> 5) << 25) | clock; + send = ((pixel2 & 0b00000011) << 4) | (((pixel2 & 0b00001100) >> 2) << 18) | + (((pixel2 & 0b00010000) >> 4) << 23) | (((pixel2 & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -418,7 +420,7 @@ void Inkplate6::display3b_() { clean_fast_(3, 1); vscan_start_(); eink_off_(); - ESP_LOGV(TAG, "Display3b finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display3b finished (%ums)", millis() - start_time); } bool Inkplate6::partial_update_() { ESP_LOGV(TAG, "Partial update called"); @@ -445,7 +447,7 @@ bool Inkplate6::partial_update_() { this->partial_buffer_2_[n--] = LUTW[diffw & 0x0F] & LUTB[diffb & 0x0F]; } } - ESP_LOGV(TAG, "Partial update buffer built after (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Partial update buffer built after (%ums)", millis() - start_time); eink_on_(); uint32_t clock = (1 << this->cl_pin_->get_pin()); @@ -454,13 +456,13 @@ bool Inkplate6::partial_update_() { const uint8_t *data_ptr = &this->partial_buffer_2_[(this->get_buffer_length_() * 2) - 1]; for (int i = 0; i < this->get_height_internal(); i++) { data = *(data_ptr--); - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); for (int j = 0, jm = (this->get_width_internal() / 4) - 1; j < jm; j++) { data = *(data_ptr--); - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -469,7 +471,7 @@ bool Inkplate6::partial_update_() { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Partial update loop k=%d (%lums)", k, millis() - start_time); + ESP_LOGV(TAG, "Partial update loop k=%d (%ums)", k, millis() - start_time); } clean_fast_(2, 2); clean_fast_(3, 1); @@ -477,7 +479,7 @@ bool Inkplate6::partial_update_() { eink_off_(); memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_()); - ESP_LOGV(TAG, "Partial update finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Partial update finished (%ums)", millis() - start_time); return true; } void Inkplate6::vscan_start_() { @@ -538,7 +540,7 @@ void Inkplate6::clean() { clean_fast_(0, 8); // Black to Black clean_fast_(2, 1); // Black to White clean_fast_(1, 10); // White to White - ESP_LOGV(TAG, "Clean finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Clean finished (%ums)", millis() - start_time); } void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep); @@ -547,16 +549,16 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { eink_on_(); uint8_t data = 0; if (c == 0) // White - data = B10101010; + data = 0b10101010; else if (c == 1) // Black - data = B01010101; + data = 0b01010101; else if (c == 2) // Discharge - data = B00000000; + data = 0b00000000; else if (c == 3) // Skip - data = B11111111; + data = 0b11111111; - uint32_t send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + uint32_t send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); uint32_t clock = (1 << this->cl_pin_->get_pin()); for (int k = 0; k < rep; k++) { @@ -576,46 +578,46 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Clean fast rep loop %d finished (%lums)", k, millis() - start_time); + ESP_LOGV(TAG, "Clean fast rep loop %d finished (%ums)", k, millis() - start_time); } - ESP_LOGV(TAG, "Clean fast finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Clean fast finished (%ums)", millis() - start_time); } void Inkplate6::pins_z_state_() { - this->ckv_pin_->pin_mode(INPUT); - this->sph_pin_->pin_mode(INPUT); + this->ckv_pin_->pin_mode(gpio::FLAG_INPUT); + this->sph_pin_->pin_mode(gpio::FLAG_INPUT); - this->oe_pin_->pin_mode(INPUT); - this->gmod_pin_->pin_mode(INPUT); - this->spv_pin_->pin_mode(INPUT); + this->oe_pin_->pin_mode(gpio::FLAG_INPUT); + this->gmod_pin_->pin_mode(gpio::FLAG_INPUT); + this->spv_pin_->pin_mode(gpio::FLAG_INPUT); - this->display_data_0_pin_->pin_mode(INPUT); - this->display_data_1_pin_->pin_mode(INPUT); - this->display_data_2_pin_->pin_mode(INPUT); - this->display_data_3_pin_->pin_mode(INPUT); - this->display_data_4_pin_->pin_mode(INPUT); - this->display_data_5_pin_->pin_mode(INPUT); - this->display_data_6_pin_->pin_mode(INPUT); - this->display_data_7_pin_->pin_mode(INPUT); + this->display_data_0_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_1_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_2_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_3_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_4_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_5_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_6_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_7_pin_->pin_mode(gpio::FLAG_INPUT); } void Inkplate6::pins_as_outputs_() { - this->ckv_pin_->pin_mode(OUTPUT); - this->sph_pin_->pin_mode(OUTPUT); + this->ckv_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->sph_pin_->pin_mode(gpio::FLAG_OUTPUT); - this->oe_pin_->pin_mode(OUTPUT); - this->gmod_pin_->pin_mode(OUTPUT); - this->spv_pin_->pin_mode(OUTPUT); + this->oe_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->gmod_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->spv_pin_->pin_mode(gpio::FLAG_OUTPUT); - this->display_data_0_pin_->pin_mode(OUTPUT); - this->display_data_1_pin_->pin_mode(OUTPUT); - this->display_data_2_pin_->pin_mode(OUTPUT); - this->display_data_3_pin_->pin_mode(OUTPUT); - this->display_data_4_pin_->pin_mode(OUTPUT); - this->display_data_5_pin_->pin_mode(OUTPUT); - this->display_data_6_pin_->pin_mode(OUTPUT); - this->display_data_7_pin_->pin_mode(OUTPUT); + this->display_data_0_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_1_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_2_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_3_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_4_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_5_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_6_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_7_pin_->pin_mode(gpio::FLAG_OUTPUT); } } // namespace inkplate6 } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h index f2821b22a9..56e95e95bb 100644 --- a/esphome/components/inkplate6/inkplate.h +++ b/esphome/components/inkplate6/inkplate.h @@ -1,26 +1,29 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" #include "esphome/components/display/display_buffer.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO namespace esphome { namespace inkplate6 { class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public i2c::I2CDevice { public: - const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101, - B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101}; - const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010, - B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010}; - const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101, - B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101}; - const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000, - B00010000, B00100000, B01000000, B10000000}; - const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000}; + const uint8_t LUT2[16] = {0b10101010, 0b10101001, 0b10100110, 0b10100101, 0b10011010, 0b10011001, + 0b10010110, 0b10010101, 0b01101010, 0b01101001, 0b01100110, 0b01100101, + 0b01011010, 0b01011001, 0b01010110, 0b01010101}; + const uint8_t LUTW[16] = {0b11111111, 0b11111110, 0b11111011, 0b11111010, 0b11101111, 0b11101110, + 0b11101011, 0b11101010, 0b10111111, 0b10111110, 0b10111011, 0b10111010, + 0b10101111, 0b10101110, 0b10101011, 0b10101010}; + const uint8_t LUTB[16] = {0b11111111, 0b11111101, 0b11110111, 0b11110101, 0b11011111, 0b11011101, + 0b11010111, 0b11010101, 0b01111111, 0b01111101, 0b01110111, 0b01110101, + 0b01011111, 0b01011101, 0b01010111, 0b01010101}; + const uint8_t pixelMaskLUT[8] = {0b00000001, 0b00000010, 0b00000100, 0b00001000, + 0b00010000, 0b00100000, 0b01000000, 0b10000000}; + const uint8_t pixelMaskGLUT[2] = {0b00001111, 0b11110000}; const uint8_t waveform3Bit[8][8] = {{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0}, {0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0}, {1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0}}; @@ -40,20 +43,20 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; } void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } - void set_display_data_0_pin(GPIOPin *data) { this->display_data_0_pin_ = data; } - void set_display_data_1_pin(GPIOPin *data) { this->display_data_1_pin_ = data; } - void set_display_data_2_pin(GPIOPin *data) { this->display_data_2_pin_ = data; } - void set_display_data_3_pin(GPIOPin *data) { this->display_data_3_pin_ = data; } - void set_display_data_4_pin(GPIOPin *data) { this->display_data_4_pin_ = data; } - void set_display_data_5_pin(GPIOPin *data) { this->display_data_5_pin_ = data; } - void set_display_data_6_pin(GPIOPin *data) { this->display_data_6_pin_ = data; } - void set_display_data_7_pin(GPIOPin *data) { this->display_data_7_pin_ = data; } + void set_display_data_0_pin(InternalGPIOPin *data) { this->display_data_0_pin_ = data; } + void set_display_data_1_pin(InternalGPIOPin *data) { this->display_data_1_pin_ = data; } + void set_display_data_2_pin(InternalGPIOPin *data) { this->display_data_2_pin_ = data; } + void set_display_data_3_pin(InternalGPIOPin *data) { this->display_data_3_pin_ = data; } + void set_display_data_4_pin(InternalGPIOPin *data) { this->display_data_4_pin_ = data; } + void set_display_data_5_pin(InternalGPIOPin *data) { this->display_data_5_pin_ = data; } + void set_display_data_6_pin(InternalGPIOPin *data) { this->display_data_6_pin_ = data; } + void set_display_data_7_pin(InternalGPIOPin *data) { this->display_data_7_pin_ = data; } void set_ckv_pin(GPIOPin *ckv) { this->ckv_pin_ = ckv; } - void set_cl_pin(GPIOPin *cl) { this->cl_pin_ = cl; } + void set_cl_pin(InternalGPIOPin *cl) { this->cl_pin_ = cl; } void set_gpio0_enable_pin(GPIOPin *gpio0_enable) { this->gpio0_enable_pin_ = gpio0_enable; } void set_gmod_pin(GPIOPin *gmod) { this->gmod_pin_ = gmod; } - void set_le_pin(GPIOPin *le) { this->le_pin_ = le; } + void set_le_pin(InternalGPIOPin *le) { this->le_pin_ = le; } void set_oe_pin(GPIOPin *oe) { this->oe_pin_ = oe; } void set_powerup_pin(GPIOPin *powerup) { this->powerup_pin_ = powerup; } void set_sph_pin(GPIOPin *sph) { this->sph_pin_ = sph; } @@ -130,20 +133,20 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public bool greyscale_; bool partial_updating_; - GPIOPin *display_data_0_pin_; - GPIOPin *display_data_1_pin_; - GPIOPin *display_data_2_pin_; - GPIOPin *display_data_3_pin_; - GPIOPin *display_data_4_pin_; - GPIOPin *display_data_5_pin_; - GPIOPin *display_data_6_pin_; - GPIOPin *display_data_7_pin_; + InternalGPIOPin *display_data_0_pin_; + InternalGPIOPin *display_data_1_pin_; + InternalGPIOPin *display_data_2_pin_; + InternalGPIOPin *display_data_3_pin_; + InternalGPIOPin *display_data_4_pin_; + InternalGPIOPin *display_data_5_pin_; + InternalGPIOPin *display_data_6_pin_; + InternalGPIOPin *display_data_7_pin_; GPIOPin *ckv_pin_; - GPIOPin *cl_pin_; + InternalGPIOPin *cl_pin_; GPIOPin *gpio0_enable_pin_; GPIOPin *gmod_pin_; - GPIOPin *le_pin_; + InternalGPIOPin *le_pin_; GPIOPin *oe_pin_; GPIOPin *powerup_pin_; GPIOPin *sph_pin_; @@ -155,4 +158,4 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public } // namespace inkplate6 } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/integration/integration_sensor.cpp b/esphome/components/integration/integration_sensor.cpp index 9a0f9fc58b..2a398e5240 100644 --- a/esphome/components/integration/integration_sensor.cpp +++ b/esphome/components/integration/integration_sensor.cpp @@ -1,6 +1,7 @@ #include "integration_sensor.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace integration { @@ -9,7 +10,7 @@ static const char *const TAG = "integration"; void IntegrationSensor::setup() { if (this->restore_) { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); float preference_value = 0; this->rtc_.load(&preference_value); this->result_ = preference_value; diff --git a/esphome/components/integration/integration_sensor.h b/esphome/components/integration/integration_sensor.h index c575de094c..a3bdfbbb2b 100644 --- a/esphome/components/integration/integration_sensor.h +++ b/esphome/components/integration/integration_sensor.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/automation.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { diff --git a/esphome/components/json/__init__.py b/esphome/components/json/__init__.py index 9879754d9e..fda0a552f1 100644 --- a/esphome/components/json/__init__.py +++ b/esphome/components/json/__init__.py @@ -1,9 +1,15 @@ import esphome.codegen as cg +import esphome.config_validation as cv from esphome.core import coroutine_with_priority CODEOWNERS = ["@OttoWinter"] json_ns = cg.esphome_ns.namespace("json") +CONFIG_SCHEMA = cv.All( + cv.Schema({}), + cv.only_with_arduino, +) + @coroutine_with_priority(1.0) async def to_code(config): diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index aab5b1ce9b..12c5beb73f 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "json_util.h" #include "esphome/core/log.h" @@ -113,3 +115,5 @@ VectorJsonBuffer global_json_buffer; // NOLINT(cppcoreguidelines-avoid-non-cons } // namespace json } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/json/json_util.h b/esphome/components/json/json_util.h index 65b2496b85..577510e63a 100644 --- a/esphome/components/json/json_util.h +++ b/esphome/components/json/json_util.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include #include "esphome/core/helpers.h" @@ -62,3 +64,5 @@ extern VectorJsonBuffer global_json_buffer; // NOLINT(cppcoreguidelines-avoid-n } // namespace json } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index 4c0d0ab3ea..ddd7d6a6b3 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -1,6 +1,7 @@ #include "lcd_display.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace lcd_base { diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.cpp b/esphome/components/lcd_gpio/gpio_lcd_display.cpp index 5c1656ec3e..b0344d313c 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.cpp +++ b/esphome/components/lcd_gpio/gpio_lcd_display.cpp @@ -30,8 +30,15 @@ void GPIOLCDDisplay::dump_config() { LOG_PIN(" RW Pin: ", this->rw_pin_); LOG_PIN(" Enable Pin: ", this->enable_pin_); - for (uint8_t i = 0; i < (this->is_four_bit_mode() ? 4 : 8); i++) { - ESP_LOGCONFIG(TAG, " Data Pin %u" LOG_PIN_PATTERN, i, LOG_PIN_ARGS(this->data_pins_[i])); + LOG_PIN(" Data Pin 0: ", data_pins_[0]); + LOG_PIN(" Data Pin 1: ", data_pins_[1]); + LOG_PIN(" Data Pin 2: ", data_pins_[2]); + LOG_PIN(" Data Pin 3: ", data_pins_[3]); + if (!is_four_bit_mode()) { + LOG_PIN(" Data Pin 4: ", data_pins_[4]); + LOG_PIN(" Data Pin 5: ", data_pins_[5]); + LOG_PIN(" Data Pin 6: ", data_pins_[6]); + LOG_PIN(" Data Pin 7: ", data_pins_[7]); } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.h b/esphome/components/lcd_gpio/gpio_lcd_display.h index 01f6f95d9a..aba254a90a 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.h +++ b/esphome/components/lcd_gpio/gpio_lcd_display.h @@ -1,6 +1,6 @@ #pragma once -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/lcd_base/lcd_display.h" namespace esphome { diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.cpp b/esphome/components/lcd_pcf8574/pcf8574_display.cpp index 4830b6f223..5b00b08aff 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.cpp +++ b/esphome/components/lcd_pcf8574/pcf8574_display.cpp @@ -1,5 +1,6 @@ #include "pcf8574_display.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace lcd_pcf8574 { diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 45bee5b871..77610a476f 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -1,40 +1,20 @@ #include "ledc_output.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 +#ifdef USE_ARDUINO #include +#endif +#ifdef USE_ESP_IDF +#include +#endif namespace esphome { namespace ledc { static const char *const TAG = "ledc.output"; -void LEDCOutput::write_state(float state) { - if (this->pin_->is_inverted()) - state = 1.0f - state; - - this->duty_ = state; - const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1; - const float duty_rounded = roundf(state * max_duty); - auto duty = static_cast(duty_rounded); - ledcWrite(this->channel_, duty); -} - -void LEDCOutput::setup() { - this->update_frequency(this->frequency_); - this->turn_off(); - // Attach pin after setting default value - ledcAttachPin(this->pin_->get_pin(), this->channel_); -} - -void LEDCOutput::dump_config() { - ESP_LOGCONFIG(TAG, "LEDC Output:"); - LOG_PIN(" Pin ", this->pin_); - ESP_LOGCONFIG(TAG, " LEDC Channel: %u", this->channel_); - ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_); -} - float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return 80e6f / float(1 << bit_depth); } float ledc_min_frequency_for_bit_depth(uint8_t bit_depth) { const float max_div_num = ((1 << 20) - 1) / 256.0f; @@ -50,6 +30,67 @@ optional ledc_bit_depth_for_frequency(float frequency) { return {}; } +void LEDCOutput::write_state(float state) { + if (this->pin_->is_inverted()) + state = 1.0f - state; + + this->duty_ = state; + const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1; + const float duty_rounded = roundf(state * max_duty); + auto duty = static_cast(duty_rounded); + +#ifdef USE_ARDUINO + ledcWrite(this->channel_, duty); +#endif +#ifdef USE_ESP_IDF + auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; + auto chan_num = static_cast(channel_ % 8); + ledc_set_duty(speed_mode, chan_num, duty); + ledc_update_duty(speed_mode, chan_num); +#endif +} + +void LEDCOutput::setup() { +#ifdef USE_ARDUINO + this->update_frequency(this->frequency_); + this->turn_off(); + // Attach pin after setting default value + ledcAttachPin(this->pin_->get_pin(), this->channel_); +#endif +#ifdef USE_ESP_IDF + auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; + auto timer_num = static_cast((channel_ % 8) / 2); + auto chan_num = static_cast(channel_ % 8); + + bit_depth_ = *ledc_bit_depth_for_frequency(frequency_); + + ledc_timer_config_t timer_conf{}; + timer_conf.speed_mode = speed_mode; + timer_conf.duty_resolution = static_cast(bit_depth_); + timer_conf.timer_num = timer_num; + timer_conf.freq_hz = (uint32_t) frequency_; + timer_conf.clk_cfg = LEDC_AUTO_CLK; + ledc_timer_config(&timer_conf); + + ledc_channel_config_t chan_conf{}; + chan_conf.gpio_num = pin_->get_pin(); + chan_conf.speed_mode = speed_mode; + chan_conf.channel = chan_num; + chan_conf.intr_type = LEDC_INTR_DISABLE; + chan_conf.timer_sel = timer_num; + chan_conf.duty = inverted_ == pin_->is_inverted() ? 0 : (1U << bit_depth_); + chan_conf.hpoint = 0; + ledc_channel_config(&chan_conf); +#endif +} + +void LEDCOutput::dump_config() { + ESP_LOGCONFIG(TAG, "LEDC Output:"); + LOG_PIN(" Pin ", this->pin_); + ESP_LOGCONFIG(TAG, " LEDC Channel: %u", this->channel_); + ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_); +} + void LEDCOutput::update_frequency(float frequency) { auto bit_depth_opt = ledc_bit_depth_for_frequency(frequency); if (!bit_depth_opt.has_value()) { @@ -58,7 +99,21 @@ void LEDCOutput::update_frequency(float frequency) { } this->bit_depth_ = bit_depth_opt.value_or(8); this->frequency_ = frequency; +#ifdef USE_ARDUINO ledcSetup(this->channel_, frequency, this->bit_depth_); +#endif // USE_ARDUINO +#ifdef USE_ESP_IDF + auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; + auto timer_num = static_cast((channel_ % 8) / 2); + + ledc_timer_config_t timer_conf{}; + timer_conf.speed_mode = speed_mode; + timer_conf.duty_resolution = static_cast(bit_depth_); + timer_conf.timer_num = timer_num; + timer_conf.freq_hz = (uint32_t) frequency_; + timer_conf.clk_cfg = LEDC_AUTO_CLK; + ledc_timer_config(&timer_conf); +#endif // re-apply duty this->write_state(this->duty_); } diff --git a/esphome/components/ledc/ledc_output.h b/esphome/components/ledc/ledc_output.h index b3b14fe855..f810ce1e35 100644 --- a/esphome/components/ledc/ledc_output.h +++ b/esphome/components/ledc/ledc_output.h @@ -1,11 +1,11 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/output/float_output.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ledc { @@ -14,7 +14,7 @@ extern uint8_t next_ledc_channel; class LEDCOutput : public output::FloatOutput, public Component { public: - explicit LEDCOutput(GPIOPin *pin) : pin_(pin) { this->channel_ = next_ledc_channel++; } + explicit LEDCOutput(InternalGPIOPin *pin) : pin_(pin) { this->channel_ = next_ledc_channel++; } void set_channel(uint8_t channel) { this->channel_ = channel; } void set_frequency(float frequency) { this->frequency_ = frequency; } @@ -31,7 +31,7 @@ class LEDCOutput : public output::FloatOutput, public Component { void write_state(float state) override; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; uint8_t channel_{}; uint8_t bit_depth_{}; float frequency_{}; diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index 0f5f186ff5..895dcc998b 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -7,10 +7,9 @@ from esphome.const import ( CONF_FREQUENCY, CONF_ID, CONF_PIN, - ESP_PLATFORM_ESP32, ) -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] def calc_max_frequency(bit_depth): diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 398546a09f..f888006b17 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -53,7 +53,7 @@ void LightState::setup() { case LIGHT_RESTORE_DEFAULT_ON: case LIGHT_RESTORE_INVERTED_DEFAULT_OFF: case LIGHT_RESTORE_INVERTED_DEFAULT_ON: - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); // Attempt to load from preferences, else fall back to default values if (!this->rtc_.load(&recovered)) { recovered.state = false; diff --git a/esphome/components/light/light_transformer.h b/esphome/components/light/light_transformer.h index c5181abd4f..dd904d0eed 100644 --- a/esphome/components/light/light_transformer.h +++ b/esphome/components/light/light_transformer.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" #include "light_color_values.h" namespace esphome { @@ -9,6 +10,8 @@ namespace light { /// Base class for all light color transformers, such as transitions or flashes. class LightTransformer { public: + virtual ~LightTransformer() = default; + void setup(const LightColorValues &start_values, const LightColorValues &target_values, uint32_t length) { this->start_time_ = millis(); this->length_ = length; diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index b821e8c5d8..bc1bc6bb41 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -214,7 +214,7 @@ def validate_printf(value): (?:\.(?:\d+|\*))? # precision (?:h|l|ll|w|I|I32|I64)? # size [cCdiouxXeEfgGaAnpsSZ] # type - ) + ) """ # noqa matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) if len(matches) != len(value[CONF_ARGS]): diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 59dfa93429..045f7059a9 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -1,9 +1,15 @@ #include "logger.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP_IDF +#include "freertos/FreeRTOS.h" +#include +#endif + +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) #include #endif -#include +#include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace logger { @@ -63,11 +69,11 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr recursion_guard_ = true; this->reset_buffer_(); // copy format string - const char *format_pgm_p = (PGM_P) format; + auto *format_pgm_p = reinterpret_cast(format); size_t len = 0; char ch = '.'; while (!this->is_buffer_full_() && ch != '\0') { - this->tx_buffer_[this->tx_buffer_at_++] = ch = pgm_read_byte(format_pgm_p++); + this->tx_buffer_[this->tx_buffer_at_++] = ch = (char) progmem_read_byte(format_pgm_p++); } // Buffer full form copying format if (this->is_buffer_full_()) @@ -105,19 +111,26 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { this->set_null_terminator_(); const char *msg = this->tx_buffer_ + offset; +#ifdef USE_ARDUINO if (this->baud_rate_ > 0) this->hw_serial_->println(msg); -#ifdef ARDUINO_ARCH_ESP32 +#endif // USE_ARDUINO +#ifdef USE_ESP_IDF + uart_write_bytes(uart_num_, msg, strlen(msg)); + uart_write_bytes(uart_num_, "\n", 1); +#endif + +#ifdef USE_ESP32 // Suppress network-logging if memory constrained, but still log to serial // ports. In some configurations (eg BLE enabled) there may be some transient // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping // here usually allows the stack to recover instead. // See issue #1234 for analysis. - if (xPortGetFreeHeapSize() > 2048) - this->log_callback_.call(level, tag, msg); -#else - this->log_callback_.call(level, tag, msg); + if (xPortGetFreeHeapSize() < 2048) + return; #endif + + this->log_callback_.call(level, tag, msg); } Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart) @@ -128,9 +141,10 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart) void Logger::pre_setup() { if (this->baud_rate_ > 0) { +#ifdef USE_ARDUINO switch (this->uart_) { case UART_SELECTION_UART0: -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 case UART_SELECTION_UART0_SWAP: #endif this->hw_serial_ = &Serial; @@ -138,7 +152,7 @@ void Logger::pre_setup() { case UART_SELECTION_UART1: this->hw_serial_ = &Serial1; break; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 case UART_SELECTION_UART2: #if !CONFIG_IDF_TARGET_ESP32S2 && !CONFIG_IDF_TARGET_ESP32C3 // FIXME: Validate in config that UART2 can't be set for ESP32-S2 (only has @@ -148,23 +162,51 @@ void Logger::pre_setup() { break; #endif } +#endif // USE_ARDUINO +#ifdef USE_ESP_IDF + uart_num_ = UART_NUM_0; + switch (uart_) { + case UART_SELECTION_UART0: + uart_num_ = UART_NUM_0; + break; + case UART_SELECTION_UART1: + uart_num_ = UART_NUM_1; + break; + case UART_SELECTION_UART2: + uart_num_ = UART_NUM_2; + break; + } + uart_config_t uart_config = { + .baud_rate = (int) baud_rate_, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + }; + uart_param_config(uart_num_, &uart_config); + const int uart_buffer_size = tx_buffer_size_; + // Install UART driver using an event queue here + uart_driver_install(uart_num_, uart_buffer_size, uart_buffer_size, 10, nullptr, 0); +#endif +#ifdef USE_ARDUINO this->hw_serial_->begin(this->baud_rate_); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 if (this->uart_ == UART_SELECTION_UART0_SWAP) { this->hw_serial_->swap(); } this->hw_serial_->setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE); #endif +#endif // USE_ARDUINO } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 else { uart_set_debug(UART_NO); } #endif global_logger = this; -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP_IDF) || defined(USE_ESP32_FRAMEWORK_ARDUINO) esp_log_set_vprintf(esp_idf_log_vprintf_); if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE) { esp_log_level_set("*", ESP_LOG_VERBOSE); @@ -183,10 +225,10 @@ void Logger::add_on_log_callback(std::function + +#ifdef USE_ARDUINO +#include +#endif +#ifdef USE_ESP_IDF +#include +#endif namespace esphome { @@ -17,11 +24,11 @@ namespace logger { enum UARTSelection { UART_SELECTION_UART0 = 0, UART_SELECTION_UART1, -#ifdef ARDUINO_ARCH_ESP32 - UART_SELECTION_UART2 +#ifdef USE_ESP32 + UART_SELECTION_UART2, #endif -#ifdef ARDUINO_ARCH_ESP8266 - UART_SELECTION_UART0_SWAP +#ifdef USE_ESP8266 + UART_SELECTION_UART0_SWAP, #endif }; @@ -32,7 +39,12 @@ class Logger : public Component { /// Manually set the baud rate for serial, set to 0 to disable. void set_baud_rate(uint32_t baud_rate); uint32_t get_baud_rate() const { return baud_rate_; } +#ifdef USE_ARDUINO HardwareSerial *get_hw_serial() const { return hw_serial_; } +#endif +#ifdef USE_ESP_IDF + uart_port_t get_uart_num() const { return uart_num_; } +#endif /// Get the UART used by the logger. UARTSelection get_uart() const; @@ -106,7 +118,12 @@ class Logger : public Component { int tx_buffer_at_{0}; int tx_buffer_size_{0}; UARTSelection uart_{UART_SELECTION_UART0}; +#ifdef USE_ARDUINO HardwareSerial *hw_serial_{nullptr}; +#endif +#ifdef USE_ESP_IDF + uart_port_t uart_num_; +#endif struct LogLevelOverride { std::string tag; int level; diff --git a/esphome/components/max31856/max31856.cpp b/esphome/components/max31856/max31856.cpp index 9b627e21a2..9300916fdc 100644 --- a/esphome/components/max31856/max31856.cpp +++ b/esphome/components/max31856/max31856.cpp @@ -163,7 +163,7 @@ void MAX31856Sensor::write_register_(uint8_t reg, uint8_t value) { ESP_LOGV(TAG, "write_register_ 0x%02X: 0x%02X", reg, value); } -const uint8_t MAX31856Sensor::read_register_(uint8_t reg) { +uint8_t MAX31856Sensor::read_register_(uint8_t reg) { ESP_LOGVV(TAG, "read_register_ 0x%02X", reg); this->enable(); ESP_LOGVV(TAG, "write_byte reg=0x%02X", reg); @@ -175,7 +175,7 @@ const uint8_t MAX31856Sensor::read_register_(uint8_t reg) { return value; } -const uint32_t MAX31856Sensor::read_register24_(uint8_t reg) { +uint32_t MAX31856Sensor::read_register24_(uint8_t reg) { ESP_LOGVV(TAG, "read_register_24_ 0x%02X", reg); this->enable(); ESP_LOGVV(TAG, "write_byte reg=0x%02X", reg); diff --git a/esphome/components/max31856/max31856.h b/esphome/components/max31856/max31856.h index 779eb52c8e..157aad433c 100644 --- a/esphome/components/max31856/max31856.h +++ b/esphome/components/max31856/max31856.h @@ -82,8 +82,8 @@ class MAX31856Sensor : public sensor::Sensor, protected: MAX31856ConfigFilter filter_; - const uint8_t read_register_(uint8_t reg); - const uint32_t read_register24_(uint8_t reg); + uint8_t read_register_(uint8_t reg); + uint32_t read_register24_(uint8_t reg); void write_register_(uint8_t reg, uint8_t value); void one_shot_temperature_(); diff --git a/esphome/components/max31865/max31865.cpp b/esphome/components/max31865/max31865.cpp index daadc26cdc..91946cde2c 100644 --- a/esphome/components/max31865/max31865.cpp +++ b/esphome/components/max31865/max31865.cpp @@ -156,7 +156,7 @@ void MAX31865Sensor::write_register_(uint8_t reg, uint8_t value) { ESP_LOGVV(TAG, "write_register_ 0x%02X: 0x%02X", reg, value); } -const uint8_t MAX31865Sensor::read_register_(uint8_t reg) { +uint8_t MAX31865Sensor::read_register_(uint8_t reg) { this->enable(); this->write_byte(reg); const uint8_t value(this->read_byte()); @@ -165,7 +165,7 @@ const uint8_t MAX31865Sensor::read_register_(uint8_t reg) { return value; } -const uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) { +uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) { this->enable(); this->write_byte(reg); const uint8_t msb(this->read_byte()); @@ -176,7 +176,7 @@ const uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) { return value; } -float MAX31865Sensor::calc_temperature_(const float &rtd_ratio) { +float MAX31865Sensor::calc_temperature_(float rtd_ratio) { // Based loosely on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31865 // Mainly based on formulas provided by Analog: // http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf diff --git a/esphome/components/max31865/max31865.h b/esphome/components/max31865/max31865.h index 393dd4b434..b83753a678 100644 --- a/esphome/components/max31865/max31865.h +++ b/esphome/components/max31865/max31865.h @@ -49,9 +49,9 @@ class MAX31865Sensor : public sensor::Sensor, void read_data_(); void write_config_(uint8_t mask, uint8_t bits, uint8_t start_position = 0); void write_register_(uint8_t reg, uint8_t value); - const uint8_t read_register_(uint8_t reg); - const uint16_t read_register_16_(uint8_t reg); - float calc_temperature_(const float &rtd_ratio); + uint8_t read_register_(uint8_t reg); + uint16_t read_register_16_(uint8_t reg); + float calc_temperature_(float rtd_ratio); }; } // namespace max31865 diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index 97886e53fa..960ac58071 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -1,6 +1,7 @@ #include "max7219.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace max7219 { @@ -172,7 +173,7 @@ uint8_t MAX7219Component::print(uint8_t start_pos, const char *str) { for (; *str != '\0'; str++) { uint8_t data = MAX7219_UNKNOWN_CHAR; if (*str >= ' ' && *str <= '~') - data = pgm_read_byte(&MAX7219_ASCII_TO_RAW[*str - ' ']); + data = progmem_read_byte(&MAX7219_ASCII_TO_RAW[*str - ' ']); if (data == MAX7219_UNKNOWN_CHAR) { ESP_LOGW(TAG, "Encountered character '%c' with no MAX7219 representation while translating string!", *str); diff --git a/esphome/components/max7219digit/max7219digit.cpp b/esphome/components/max7219digit/max7219digit.cpp index 520694af9d..4fedd3d312 100644 --- a/esphome/components/max7219digit/max7219digit.cpp +++ b/esphome/components/max7219digit/max7219digit.cpp @@ -1,6 +1,7 @@ #include "max7219digit.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" #include "max7219font.h" namespace esphome { @@ -212,7 +213,7 @@ void MAX7219Component::scroll_left() { void MAX7219Component::send_char(uint8_t chip, uint8_t data) { // get this character from PROGMEM for (uint8_t i = 0; i < 8; i++) - this->max_displaybuffer_[chip * 8 + i] = pgm_read_byte(&MAX7219_DOT_MATRIX_FONT[data][i]); + this->max_displaybuffer_[chip * 8 + i] = progmem_read_byte(&MAX7219_DOT_MATRIX_FONT[data][i]); } // end of send_char // send one character (data) to position (chip) diff --git a/esphome/components/max7219digit/max7219digit.h b/esphome/components/max7219digit/max7219digit.h index 83f45e3a00..02fe8b6f42 100644 --- a/esphome/components/max7219digit/max7219digit.h +++ b/esphome/components/max7219digit/max7219digit.h @@ -54,8 +54,8 @@ class MAX7219Component : public PollingComponent, void set_scroll_mode(uint8_t mode) { this->scroll_mode_ = mode; }; void set_reverse(bool on_off) { this->reverse_ = on_off; }; - void send_char(byte chip, byte data); - void send64pixels(byte chip, const byte pixels[8]); + void send_char(uint8_t chip, uint8_t data); + void send64pixels(uint8_t chip, const uint8_t pixels[8]); void scroll_left(); void scroll(bool on_off, uint8_t mode, uint16_t speed, uint16_t delay, uint16_t dwell); diff --git a/esphome/components/max7219digit/max7219font.h b/esphome/components/max7219digit/max7219font.h index 3d42d1cc2d..22d64d1ecd 100644 --- a/esphome/components/max7219digit/max7219font.h +++ b/esphome/components/max7219digit/max7219font.h @@ -1,11 +1,13 @@ #pragma once +#include "esphome/core/hal.h" + namespace esphome { namespace max7219digit { // bit patterns for the CP437 font -const byte MAX7219_DOT_MATRIX_FONT[256][8] PROGMEM = { +const uint8_t MAX7219_DOT_MATRIX_FONT[256][8] PROGMEM = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x00 {0x7E, 0x81, 0x95, 0xB1, 0xB1, 0x95, 0x81, 0x7E}, // 0x01 {0x7E, 0xFF, 0xEB, 0xCF, 0xCF, 0xEB, 0xFF, 0x7E}, // 0x02 diff --git a/esphome/components/mcp23008/mcp23008.h b/esphome/components/mcp23008/mcp23008.h index 42c8e497fa..406ce0b419 100644 --- a/esphome/components/mcp23008/mcp23008.h +++ b/esphome/components/mcp23008/mcp23008.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x08_base/mcp23x08_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { diff --git a/esphome/components/mcp23016/__init__.py b/esphome/components/mcp23016/__init__.py index 4d9657e794..c1209a9627 100644 --- a/esphome/components/mcp23016/__init__.py +++ b/esphome/components/mcp23016/__init__.py @@ -2,17 +2,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_OUTPUT, +) DEPENDENCIES = ["i2c"] MULTI_CONF = True mcp23016_ns = cg.esphome_ns.namespace("mcp23016") -MCP23016GPIOMode = mcp23016_ns.enum("MCP23016GPIOMode") -MCP23016_GPIO_MODES = { - "INPUT": MCP23016GPIOMode.MCP23016_INPUT, - "OUTPUT": MCP23016GPIOMode.MCP23016_OUTPUT, -} MCP23016 = mcp23016_ns.class_("MCP23016", cg.Component, i2c.I2CDevice) MCP23016GPIOPin = mcp23016_ns.class_("MCP23016GPIOPin", cg.GPIOPin) @@ -34,34 +36,41 @@ async def to_code(config): await i2c.register_i2c_device(var, config) +def validate_mode(value): + if not (value[CONF_INPUT] or value[CONF_OUTPUT]): + raise cv.Invalid("Mode must be either input or output") + if value[CONF_INPUT] and value[CONF_OUTPUT]: + raise cv.Invalid("Mode must be either input or output") + return value + + CONF_MCP23016 = "mcp23016" -MCP23016_OUTPUT_PIN_SCHEMA = cv.Schema( +MCP23016_PIN_SCHEMA = cv.All( { + cv.GenerateID(): cv.declare_id(MCP23016GPIOPin), cv.Required(CONF_MCP23016): cv.use_id(MCP23016), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23016_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -MCP23016_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23016): cv.use_id(MCP23016), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23016_GPIO_MODES, upper=True + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + }, + validate_mode, ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23016, (MCP23016_OUTPUT_PIN_SCHEMA, MCP23016_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23016, MCP23016_PIN_SCHEMA) async def mcp23016_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_MCP23016]) - return MCP23016GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) + + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/mcp23016/mcp23016.cpp b/esphome/components/mcp23016/mcp23016.cpp index f2b55fe2e2..a8df4e1745 100644 --- a/esphome/components/mcp23016/mcp23016.cpp +++ b/esphome/components/mcp23016/mcp23016.cpp @@ -1,5 +1,6 @@ #include "mcp23016.h" #include "esphome/core/log.h" +#include namespace esphome { namespace mcp23016 { @@ -29,17 +30,12 @@ void MCP23016::digital_write(uint8_t pin, bool value) { uint8_t reg_addr = pin < 8 ? MCP23016_OLAT0 : MCP23016_OLAT1; this->update_reg_(pin, value, reg_addr); } -void MCP23016::pin_mode(uint8_t pin, uint8_t mode) { +void MCP23016::pin_mode(uint8_t pin, gpio::Flags flags) { uint8_t iodir = pin < 8 ? MCP23016_IODIR0 : MCP23016_IODIR1; - switch (mode) { - case MCP23016_INPUT: - this->update_reg_(pin, true, iodir); - break; - case MCP23016_OUTPUT: - this->update_reg_(pin, false, iodir); - break; - default: - break; + if (flags == gpio::FLAG_INPUT) { + this->update_reg_(pin, true, iodir); + } else if (flags == gpio::FLAG_OUTPUT) { + this->update_reg_(pin, false, iodir); } } float MCP23016::get_setup_priority() const { return setup_priority::HARDWARE; } @@ -80,12 +76,15 @@ void MCP23016::update_reg_(uint8_t pin, bool pin_value, uint8_t reg_addr) { } } -MCP23016GPIOPin::MCP23016GPIOPin(MCP23016 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23016GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23016GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } +void MCP23016GPIOPin::setup() { pin_mode(flags_); } +void MCP23016GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool MCP23016GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void MCP23016GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string MCP23016GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via MCP23016", pin_); + return buffer; +} } // namespace mcp23016 } // namespace esphome diff --git a/esphome/components/mcp23016/mcp23016.h b/esphome/components/mcp23016/mcp23016.h index 53502f80eb..a4890b4120 100644 --- a/esphome/components/mcp23016/mcp23016.h +++ b/esphome/components/mcp23016/mcp23016.h @@ -1,18 +1,12 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace mcp23016 { -/// Modes for MCP23016 pins -enum MCP23016GPIOMode : uint8_t { - MCP23016_INPUT = INPUT, // 0x00 - MCP23016_OUTPUT = OUTPUT // 0x01 -}; - enum MCP23016GPIORegisters { // 0 side MCP23016_GP0 = 0x00, @@ -38,7 +32,7 @@ class MCP23016 : public Component, public i2c::I2CDevice { bool digital_read(uint8_t pin); void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); + void pin_mode(uint8_t pin, gpio::Flags flags); float get_setup_priority() const override; @@ -56,15 +50,22 @@ class MCP23016 : public Component, public i2c::I2CDevice { class MCP23016GPIOPin : public GPIOPin { public: - MCP23016GPIOPin(MCP23016 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(MCP23016 *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } protected: MCP23016 *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; }; } // namespace mcp23016 diff --git a/esphome/components/mcp23017/mcp23017.h b/esphome/components/mcp23017/mcp23017.h index fd9086a492..8959e06a41 100644 --- a/esphome/components/mcp23017/mcp23017.h +++ b/esphome/components/mcp23017/mcp23017.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x17_base/mcp23x17_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { diff --git a/esphome/components/mcp23s08/mcp23s08.h b/esphome/components/mcp23s08/mcp23s08.h index 4ca02c54fc..a2a6be880a 100644 --- a/esphome/components/mcp23s08/mcp23s08.h +++ b/esphome/components/mcp23s08/mcp23s08.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x08_base/mcp23x08_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/spi/spi.h" namespace esphome { diff --git a/esphome/components/mcp23s17/mcp23s17.h b/esphome/components/mcp23s17/mcp23s17.h index 1ced144c23..cb5d6cfcd8 100644 --- a/esphome/components/mcp23s17/mcp23s17.h +++ b/esphome/components/mcp23s17/mcp23s17.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x17_base/mcp23x17_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/spi/spi.h" namespace esphome { diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.cpp b/esphome/components/mcp23x08_base/mcp23x08_base.cpp index 2b047fa288..2137b36921 100644 --- a/esphome/components/mcp23x08_base/mcp23x08_base.cpp +++ b/esphome/components/mcp23x08_base/mcp23x08_base.cpp @@ -19,22 +19,16 @@ void MCP23X08Base::digital_write(uint8_t pin, bool value) { this->update_reg(pin, value, reg_addr); } -void MCP23X08Base::pin_mode(uint8_t pin, uint8_t mode) { +void MCP23X08Base::pin_mode(uint8_t pin, gpio::Flags flags) { uint8_t iodir = mcp23x08_base::MCP23X08_IODIR; uint8_t gppu = mcp23x08_base::MCP23X08_GPPU; - switch (mode) { - case mcp23xxx_base::MCP23XXX_INPUT: - this->update_reg(pin, true, iodir); - break; - case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case mcp23xxx_base::MCP23XXX_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; + if (flags == gpio::FLAG_INPUT) { + this->update_reg(pin, true, iodir); + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + } else if (flags == gpio::FLAG_OUTPUT) { + this->update_reg(pin, false, iodir); } } diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.h b/esphome/components/mcp23x08_base/mcp23x08_base.h index 5e2c1a047f..910519119b 100644 --- a/esphome/components/mcp23x08_base/mcp23x08_base.h +++ b/esphome/components/mcp23x08_base/mcp23x08_base.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace mcp23x08_base { @@ -26,7 +26,7 @@ class MCP23X08Base : public mcp23xxx_base::MCP23XXXBase { public: bool digital_read(uint8_t pin) override; void digital_write(uint8_t pin, bool value) override; - void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_mode(uint8_t pin, gpio::Flags flags) override; void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; protected: diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.cpp b/esphome/components/mcp23x17_base/mcp23x17_base.cpp index 72dec2d457..e975670faa 100644 --- a/esphome/components/mcp23x17_base/mcp23x17_base.cpp +++ b/esphome/components/mcp23x17_base/mcp23x17_base.cpp @@ -19,22 +19,16 @@ void MCP23X17Base::digital_write(uint8_t pin, bool value) { this->update_reg(pin, value, reg_addr); } -void MCP23X17Base::pin_mode(uint8_t pin, uint8_t mode) { +void MCP23X17Base::pin_mode(uint8_t pin, gpio::Flags flags) { uint8_t iodir = pin < 8 ? mcp23x17_base::MCP23X17_IODIRA : mcp23x17_base::MCP23X17_IODIRB; uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB; - switch (mode) { - case mcp23xxx_base::MCP23XXX_INPUT: - this->update_reg(pin, true, iodir); - break; - case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case mcp23xxx_base::MCP23XXX_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; + if (flags == gpio::FLAG_INPUT) { + this->update_reg(pin, true, iodir); + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + } else if (flags == gpio::FLAG_OUTPUT) { + this->update_reg(pin, false, iodir); } } diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.h b/esphome/components/mcp23x17_base/mcp23x17_base.h index 1bbcb97041..3d50ee8c03 100644 --- a/esphome/components/mcp23x17_base/mcp23x17_base.h +++ b/esphome/components/mcp23x17_base/mcp23x17_base.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace mcp23x17_base { @@ -38,7 +38,7 @@ class MCP23X17Base : public mcp23xxx_base::MCP23XXXBase { public: bool digital_read(uint8_t pin) override; void digital_write(uint8_t pin, bool value) override; - void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_mode(uint8_t pin, gpio::Flags flags) override; void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; protected: diff --git a/esphome/components/mcp23xxx_base/__init__.py b/esphome/components/mcp23xxx_base/__init__.py index c22d377b3c..f2c2706416 100644 --- a/esphome/components/mcp23xxx_base/__init__.py +++ b/esphome/components/mcp23xxx_base/__init__.py @@ -3,11 +3,14 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import ( CONF_ID, + CONF_INPUT, CONF_NUMBER, CONF_MODE, CONF_INVERTED, CONF_INTERRUPT, CONF_OPEN_DRAIN_INTERRUPT, + CONF_OUTPUT, + CONF_PULLUP, ) from esphome.core import coroutine @@ -47,26 +50,29 @@ async def register_mcp23xxx(config): return var +def validate_mode(value): + if not (value[CONF_INPUT] or value[CONF_OUTPUT]): + raise cv.Invalid("Mode must be either input or output") + if value[CONF_INPUT] and value[CONF_OUTPUT]: + raise cv.Invalid("Mode must be either input or output") + if value[CONF_PULLUP] and not value[CONF_INPUT]: + raise cv.Invalid("Pullup only available with input") + return value + + CONF_MCP23XXX = "mcp23xxx" -MCP23XXX_OUTPUT_PIN_SCHEMA = cv.Schema( +MCP23XXX_PIN_SCHEMA = cv.All( { + cv.GenerateID(): cv.declare_id(MCP23XXXGPIOPin), cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23XXX_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( - MCP23XXX_INTERRUPT_MODES, upper=True - ), - } -) -MCP23XXX_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23XXX_GPIO_MODES, upper=True + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + }, + validate_mode, ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( @@ -76,42 +82,31 @@ MCP23XXX_INPUT_PIN_SCHEMA = cv.Schema( ) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23XXX, (MCP23XXX_OUTPUT_PIN_SCHEMA, MCP23XXX_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23XXX, MCP23XXX_PIN_SCHEMA) async def mcp23xxx_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_MCP23XXX]) - return MCP23XXXGPIOPin.new( - parent, - config[CONF_NUMBER], - config[CONF_MODE], - config[CONF_INVERTED], - config[CONF_INTERRUPT], - ) + + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + cg.add(var.set_interrupt_mode(config[CONF_INTERRUPT])) + return var # BEGIN Removed pin schemas below to show error in configuration # TODO remove in 2022.5.0 for id in ["mcp23008", "mcp23s08", "mcp23017", "mcp23s17"]: - PIN_SCHEMA = cv.Schema( - { - cv.Required(id): cv.invalid( - f"'{id}:' has been removed from the pin schema in 1.17.0, please use 'mcp23xxx:'" - ), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23XXX_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( - MCP23XXX_INTERRUPT_MODES, upper=True - ), - } + invalid_schema = cv.invalid( + f"'{id}:' has been removed from the pin schema in 1.17.0, please use 'mcp23xxx:'" ) # pylint: disable=cell-var-from-loop - @pins.PIN_SCHEMA_REGISTRY.register(id, (PIN_SCHEMA, PIN_SCHEMA)) + @pins.PIN_SCHEMA_REGISTRY.register(id, invalid_schema) def pin_to_code(config): pass diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp index 37c55fceaf..14a703fb9f 100644 --- a/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp @@ -6,16 +6,15 @@ namespace mcp23xxx_base { float MCP23XXXBase::get_setup_priority() const { return setup_priority::IO; } -MCP23XXXGPIOPin::MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted, - MCP23XXXInterruptMode interrupt_mode) - : GPIOPin(pin, mode, inverted), parent_(parent), interrupt_mode_(interrupt_mode) {} -void MCP23XXXGPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23XXXGPIOPin::pin_mode(uint8_t mode) { - this->parent_->pin_mode(this->pin_, mode); - this->parent_->pin_interrupt_mode(this->pin_, this->interrupt_mode_); -} +void MCP23XXXGPIOPin::setup() { pin_mode(flags_); } +void MCP23XXXGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool MCP23XXXGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void MCP23XXXGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string MCP23XXXGPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via MCP23XXX", pin_); + return buffer; +} } // namespace mcp23xxx_base } // namespace esphome diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.h b/esphome/components/mcp23xxx_base/mcp23xxx_base.h index bf01320264..a522ea28c5 100644 --- a/esphome/components/mcp23xxx_base/mcp23xxx_base.h +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.h @@ -1,25 +1,18 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace mcp23xxx_base { enum MCP23XXXInterruptMode : uint8_t { MCP23XXX_NO_INTERRUPT = 0, MCP23XXX_CHANGE, MCP23XXX_RISING, MCP23XXX_FALLING }; -/// Modes for MCP23XXX pins -enum MCP23XXXGPIOMode : uint8_t { - MCP23XXX_INPUT = INPUT, // 0x00 - MCP23XXX_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23XXX_OUTPUT = OUTPUT // 0x01 -}; - class MCP23XXXBase : public Component { public: virtual bool digital_read(uint8_t pin); virtual void digital_write(uint8_t pin, bool value); - virtual void pin_mode(uint8_t pin, uint8_t mode); + virtual void pin_mode(uint8_t pin, gpio::Flags flags); virtual void pin_interrupt_mode(uint8_t pin, MCP23XXXInterruptMode interrupt_mode); void set_open_drain_ints(const bool value) { this->open_drain_ints_ = value; } @@ -38,16 +31,23 @@ class MCP23XXXBase : public Component { class MCP23XXXGPIOPin : public GPIOPin { public: - MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted = false, - MCP23XXXInterruptMode interrupt_mode = MCP23XXX_NO_INTERRUPT); - void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(MCP23XXXBase *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + void set_interrupt_mode(MCP23XXXInterruptMode interrupt_mode) { interrupt_mode_ = interrupt_mode; } protected: MCP23XXXBase *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; MCP23XXXInterruptMode interrupt_mode_; }; diff --git a/esphome/components/mcp3008/mcp3008.h b/esphome/components/mcp3008/mcp3008.h index 16f1c14fcb..6f3dc576ea 100644 --- a/esphome/components/mcp3008/mcp3008.h +++ b/esphome/components/mcp3008/mcp3008.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/spi/spi.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" diff --git a/esphome/components/mcp4725/mcp4725.cpp b/esphome/components/mcp4725/mcp4725.cpp index a8b130208e..2cb19282b6 100644 --- a/esphome/components/mcp4725/mcp4725.cpp +++ b/esphome/components/mcp4725/mcp4725.cpp @@ -8,13 +8,10 @@ static const char *const TAG = "mcp4725"; void MCP4725::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP4725 (0x%02X)...", this->address_); - - this->raw_begin_transmission(); - - if (!this->raw_end_transmission()) { + auto err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); - return; } } diff --git a/esphome/components/mcp9808/mcp9808.cpp b/esphome/components/mcp9808/mcp9808.cpp index 8f60df1d88..fca1331fc3 100644 --- a/esphome/components/mcp9808/mcp9808.cpp +++ b/esphome/components/mcp9808/mcp9808.cpp @@ -20,14 +20,14 @@ static const char *const TAG = "mcp9808"; void MCP9808Sensor::setup() { ESP_LOGCONFIG(TAG, "Setting up %s...", this->name_.c_str()); - uint16_t manu; - if (!this->read_byte_16(MCP9808_REG_MANUF_ID, &manu, 0) || manu != MCP9808_MANUF_ID) { + uint16_t manu = 0; + if (!this->read_byte_16(MCP9808_REG_MANUF_ID, &manu) || manu != MCP9808_MANUF_ID) { this->mark_failed(); ESP_LOGE(TAG, "%s manufacuturer id failed, device returned %X", this->name_.c_str(), manu); return; } - uint16_t dev_id; - if (!this->read_byte_16(MCP9808_REG_DEVICE_ID, &dev_id, 0) || dev_id != MCP9808_DEV_ID) { + uint16_t dev_id = 0; + if (!this->read_byte_16(MCP9808_REG_DEVICE_ID, &dev_id) || dev_id != MCP9808_DEV_ID) { this->mark_failed(); ESP_LOGE(TAG, "%s device id failed, device returned %X", this->name_.c_str(), dev_id); return; @@ -66,7 +66,7 @@ void MCP9808Sensor::update() { temp = (uint16_t)(msb) *16 + lsb / 16.0f; } - if (isnan(temp)) { + if (std::isnan(temp)) { this->status_set_warning(); return; } diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py new file mode 100644 index 0000000000..ec568ae2d5 --- /dev/null +++ b/esphome/components/mdns/__init__.py @@ -0,0 +1,25 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.core import CORE + +CODEOWNERS = ["@esphome/core"] +DEPENDENCIES = ["network"] + +CONF_DISABLED = "disabled" +CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_DISABLED, default=False): cv.boolean, + } +) + + +async def to_code(config): + if config[CONF_DISABLED]: + return + + cg.add_define("USE_MDNS") + if CORE.using_arduino: + if CORE.is_esp32: + cg.add_library("ESPmDNS", None) + elif CORE.is_esp8266: + cg.add_library("ESP8266mDNS", None) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp new file mode 100644 index 0000000000..c742fe3948 --- /dev/null +++ b/esphome/components/mdns/mdns_component.cpp @@ -0,0 +1,74 @@ +#include "mdns_component.h" +#include "esphome/core/defines.h" +#include "esphome/core/version.h" +#include "esphome/core/application.h" + +#ifdef USE_API +#include "esphome/components/api/api_server.h" +#endif + +namespace esphome { +namespace mdns { + +#ifndef WEBSERVER_PORT +#define WEBSERVER_PORT 80 // NOLINT +#endif + +std::vector MDNSComponent::compile_services_() { + std::vector res; + +#ifdef USE_API + if (api::global_api_server != nullptr) { + MDNSService service{}; + service.service_type = "esphomelib"; + service.proto = "_tcp"; + service.port = api::global_api_server->get_port(); + service.txt_records.push_back({"version", ESPHOME_VERSION}); + service.txt_records.push_back({"mac", get_mac_address()}); + const char *platform = nullptr; +#ifdef USE_ESP8266 + platform = "ESP8266"; +#endif +#ifdef USE_ESP32 + platform = "ESP32"; +#endif + if (platform != nullptr) { + service.txt_records.push_back({"platform", platform}); + } + + service.txt_records.push_back({"board", ESPHOME_BOARD}); + +#ifdef ESPHOME_PROJECT_NAME + service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME}); + service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION}); +#endif // ESPHOME_PROJECT_NAME + res.push_back(service); + } +#endif // USE_API + +#ifdef USE_PROMETHEUS + { + MDNSService service{}; + service.service_type = "prometheus-http"; + service.proto = "_tcp"; + service.port = WEBSERVER_PORT; + res.push_back(service); + } +#endif + + if (res.empty()) { + // Publish "http" service if not using native API + // This is just to have *some* mDNS service so that .local resolution works + MDNSService service{}; + service.service_type = "http"; + service.proto = "_tcp"; + service.port = WEBSERVER_PORT; + service.txt_records.push_back({"version", ESPHOME_VERSION}); + res.push_back(service); + } + return res; +} +std::string MDNSComponent::compile_hostname_() { return App.get_name(); } + +} // namespace mdns +} // namespace esphome diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h new file mode 100644 index 0000000000..985947d99c --- /dev/null +++ b/esphome/components/mdns/mdns_component.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include "esphome/core/component.h" + +namespace esphome { +namespace mdns { + +struct MDNSTXTRecord { + std::string key; + std::string value; +}; + +struct MDNSService { + std::string service_type; + std::string proto; + uint16_t port; + std::vector txt_records; +}; + +class MDNSComponent : public Component { + public: + void setup() override; + +#if defined(USE_ESP8266) && defined(USE_ARDUINO) + void loop() override; +#endif + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + protected: + std::vector compile_services_(); + std::string compile_hostname_(); +}; + +} // namespace mdns +} // namespace esphome diff --git a/esphome/components/mdns/mdns_esp32_arduino.cpp b/esphome/components/mdns/mdns_esp32_arduino.cpp new file mode 100644 index 0000000000..4d13b7321a --- /dev/null +++ b/esphome/components/mdns/mdns_esp32_arduino.cpp @@ -0,0 +1,27 @@ +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include "mdns_component.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace mdns { + +static const char *const TAG = "mdns"; + +void MDNSComponent::setup() { + MDNS.begin(compile_hostname_().c_str()); + + auto services = compile_services_(); + for (const auto &service : services) { + MDNS.addService(service.service_type.c_str(), service.proto.c_str(), service.port); + for (const auto &record : service.txt_records) { + MDNS.addServiceTxt(service.service_type.c_str(), service.proto.c_str(), record.key.c_str(), record.value.c_str()); + } + } +} + +} // namespace mdns +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/mdns/mdns_esp8266.cpp b/esphome/components/mdns/mdns_esp8266.cpp new file mode 100644 index 0000000000..48f31f1bbf --- /dev/null +++ b/esphome/components/mdns/mdns_esp8266.cpp @@ -0,0 +1,32 @@ +#if defined(USE_ESP8266) && defined(USE_ARDUINO) + +#include "mdns_component.h" +#include "esphome/core/log.h" +#include "esphome/components/network/ip_address.h" +#include "esphome/components/network/util.h" +#include + +namespace esphome { +namespace mdns { + +static const char *const TAG = "mdns"; + +void MDNSComponent::setup() { + network::IPAddress addr = network::get_ip_address(); + MDNS.begin(compile_hostname_().c_str(), (uint32_t) addr); + + auto services = compile_services_(); + for (const auto &service : services) { + MDNS.addService(service.service_type.c_str(), service.proto.c_str(), service.port); + for (const auto &record : service.txt_records) { + MDNS.addServiceTxt(service.service_type.c_str(), service.proto.c_str(), record.key.c_str(), record.value.c_str()); + } + } +} + +void MDNSComponent::loop() { MDNS.update(); } + +} // namespace mdns +} // namespace esphome + +#endif diff --git a/esphome/components/mdns/mdns_esp_idf.cpp b/esphome/components/mdns/mdns_esp_idf.cpp new file mode 100644 index 0000000000..17874f1ffe --- /dev/null +++ b/esphome/components/mdns/mdns_esp_idf.cpp @@ -0,0 +1,52 @@ +#ifdef USE_ESP_IDF + +#include "mdns_component.h" +#include "esphome/core/log.h" +#include +#include + +namespace esphome { +namespace mdns { + +static const char *const TAG = "mdns"; + +void MDNSComponent::setup() { + esp_err_t err = mdns_init(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "MDNS init failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + mdns_hostname_set(compile_hostname_().c_str()); + mdns_instance_name_set(compile_hostname_().c_str()); + + auto services = compile_services_(); + for (const auto &service : services) { + std::vector txt_records; + for (const auto &record : service.txt_records) { + mdns_txt_item_t it{}; + // dup strings to ensure the pointer is valid even after the record loop + it.key = strdup(record.key.c_str()); + it.value = strdup(record.value.c_str()); + txt_records.push_back(it); + } + err = mdns_service_add(nullptr, service.service_type.c_str(), service.proto.c_str(), service.port, + txt_records.data(), txt_records.size()); + + // free records + for (const auto &it : txt_records) { + delete it.key; // NOLINT(cppcoreguidelines-owning-memory) + delete it.value; // NOLINT(cppcoreguidelines-owning-memory) + } + + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to register mDNS service %s: %s", service.service_type.c_str(), esp_err_to_name(err)); + } + } +} + +} // namespace mdns +} // namespace esphome + +#endif diff --git a/esphome/components/midea/adapter.cpp b/esphome/components/midea/adapter.cpp index bd5b289095..a3f19dbda8 100644 --- a/esphome/components/midea/adapter.cpp +++ b/esphome/components/midea/adapter.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "adapter.h" @@ -171,3 +173,5 @@ void Converters::to_climate_traits(ClimateTraits &traits, const dudanov::midea:: } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/adapter.h b/esphome/components/midea/adapter.h index 8d8d57e8f9..2497cbbe5b 100644 --- a/esphome/components/midea/adapter.h +++ b/esphome/components/midea/adapter.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include #include "esphome/components/climate/climate_traits.h" #include "appliance_base.h" @@ -40,3 +43,5 @@ class Converters { } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index a71f1dbdfb..103b852936 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "air_conditioner.h" #include "adapter.h" @@ -150,3 +152,5 @@ void AirConditioner::do_display_toggle() { } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index 895b6412f3..8dfb9dcb3d 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include #include "appliance_base.h" #include "esphome/components/sensor/sensor.h" @@ -39,3 +42,5 @@ class AirConditioner : public ApplianceBase } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/appliance_base.h b/esphome/components/midea/appliance_base.h index aa616ced36..43dd2ffb32 100644 --- a/esphome/components/midea/appliance_base.h +++ b/esphome/components/midea/appliance_base.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/core/log.h" #include "esphome/components/uart/uart.h" @@ -19,7 +22,8 @@ using climate::ClimateMode; using climate::ClimateSwingMode; using climate::ClimateFanMode; -template class ApplianceBase : public Component, public uart::UARTDevice, public climate::Climate { +template +class ApplianceBase : public Component, public uart::UARTDevice, public climate::Climate, public Stream { static_assert(std::is_base_of::value, "T must derive from dudanov::midea::ApplianceBase class"); @@ -60,6 +64,12 @@ template class ApplianceBase : public Component, public uart::UARTDe } #endif + int available() override { return uart::UARTDevice::available(); } + int read() override { return uart::UARTDevice::read(); } + int peek() override { return uart::UARTDevice::peek(); } + void flush() override { uart::UARTDevice::flush(); } + size_t write(uint8_t data) override { return uart::UARTDevice::write(data); } + protected: T base_; std::set supported_modes_{}; @@ -74,3 +84,5 @@ template class ApplianceBase : public Component, public uart::UARTDe } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/automations.h b/esphome/components/midea/automations.h index 1f026c0c15..5b638286ac 100644 --- a/esphome/components/midea/automations.h +++ b/esphome/components/midea/automations.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include "esphome/core/automation.h" #include "air_conditioner.h" @@ -54,3 +57,5 @@ template class PowerOffAction : public MideaActionBase { } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index 137fcdd607..0d0bdce471 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -151,7 +151,8 @@ CONFIG_SCHEMA = cv.All( } ) .extend(uart.UART_DEVICE_SCHEMA) - .extend(cv.COMPONENT_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + cv.only_with_arduino, ) # Actions diff --git a/esphome/components/midea/midea_ir.h b/esphome/components/midea/midea_ir.h index 2459d844a1..abd4324bcc 100644 --- a/esphome/components/midea/midea_ir.h +++ b/esphome/components/midea/midea_ir.h @@ -1,4 +1,6 @@ #pragma once + +#ifdef USE_ARDUINO #ifdef USE_REMOTE_TRANSMITTER #include "esphome/components/remote_base/midea_protocol.h" @@ -40,3 +42,4 @@ class IrSpecialData : public IrData { } // namespace esphome #endif +#endif // USE_ARDUINO diff --git a/esphome/components/mpr121/mpr121.cpp b/esphome/components/mpr121/mpr121.cpp index 274ed6dfec..7ba3da7b4d 100644 --- a/esphome/components/mpr121/mpr121.cpp +++ b/esphome/components/mpr121/mpr121.cpp @@ -1,5 +1,6 @@ #include "mpr121.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace mpr121 { diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 73ee50ee65..8f02f8d437 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -192,6 +192,7 @@ CONFIG_SCHEMA = cv.All( } ), validate_config, + cv.only_with_arduino, ) diff --git a/esphome/components/mqtt/custom_mqtt_device.cpp b/esphome/components/mqtt/custom_mqtt_device.cpp index 9dec9498ad..787cc1153f 100644 --- a/esphome/components/mqtt/custom_mqtt_device.cpp +++ b/esphome/components/mqtt/custom_mqtt_device.cpp @@ -1,4 +1,7 @@ #include "custom_mqtt_device.h" + +#ifdef USE_MQTT + #include "esphome/core/log.h" namespace esphome { @@ -28,3 +31,5 @@ bool CustomMQTTDevice::is_connected() { return global_mqtt_client != nullptr && } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/custom_mqtt_device.h b/esphome/components/mqtt/custom_mqtt_device.h index 1c8b2e916e..9795d69304 100644 --- a/esphome/components/mqtt/custom_mqtt_device.h +++ b/esphome/components/mqtt/custom_mqtt_device.h @@ -1,5 +1,8 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_MQTT + #include "esphome/core/component.h" #include "mqtt_client.h" @@ -215,3 +218,5 @@ void CustomMQTTDevice::subscribe_json(const std::string &topic, void (T::*callba } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 53a49c6844..d7322298bb 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -1,6 +1,7 @@ #include "mqtt_binary_sensor.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_BINARY_SENSOR namespace esphome { @@ -55,3 +56,4 @@ bool MQTTBinarySensorComponent::publish_state(bool state) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_binary_sensor.h b/esphome/components/mqtt/mqtt_binary_sensor.h index 1ca82a947e..c459bea1f7 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.h +++ b/esphome/components/mqtt/mqtt_binary_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/defines.h" - +#ifdef USE_MQTT #ifdef USE_BINARY_SENSOR #include "mqtt_component.h" @@ -41,3 +41,4 @@ class MQTTBinarySensorComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 237a8146e6..040b0001fe 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -1,9 +1,11 @@ #include "mqtt_client.h" +#ifdef USE_MQTT + #include "esphome/core/application.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -60,7 +62,7 @@ void MQTTClientComponent::setup() { void MQTTClientComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT:"); ESP_LOGCONFIG(TAG, " Server Address: %s:%u (%s)", this->credentials_.address.c_str(), this->credentials_.port, - this->ip_.toString().c_str()); + this->ip_.str().c_str()); ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str()); ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str()); if (!this->discovery_info_.prefix.empty()) { @@ -87,11 +89,11 @@ void MQTTClientComponent::start_dnslookup_() { this->dns_resolve_error_ = false; this->dns_resolved_ = false; ip_addr_t addr; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 err_t err = dns_gethostbyname(this->credentials_.address.c_str(), &addr, esphome::mqtt::MQTTClientComponent::dns_found_callback, this); #endif @@ -99,11 +101,11 @@ void MQTTClientComponent::start_dnslookup_() { case ERR_OK: { // Got IP immediately this->dns_resolved_ = true; -#ifdef ARDUINO_ARCH_ESP32 - this->ip_ = IPAddress(addr.u_addr.ip4.addr); +#ifdef USE_ESP32 + this->ip_ = addr.u_addr.ip4.addr; #endif -#ifdef ARDUINO_ARCH_ESP8266 - this->ip_ = IPAddress(addr.addr); +#ifdef USE_ESP8266 + this->ip_ = addr.addr; #endif this->start_connect_(); return; @@ -116,7 +118,7 @@ void MQTTClientComponent::start_dnslookup_() { default: case ERR_ARG: { // error -#if defined(ARDUINO_ARCH_ESP8266) +#if defined(USE_ESP8266) ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %ld", err); #else ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %d", err); @@ -143,10 +145,10 @@ void MQTTClientComponent::check_dnslookup_() { return; } - ESP_LOGD(TAG, "Resolved broker IP address to %s", this->ip_.toString().c_str()); + ESP_LOGD(TAG, "Resolved broker IP address to %s", this->ip_.str().c_str()); this->start_connect_(); } -#if defined(ARDUINO_ARCH_ESP8266) && LWIP_VERSION_MAJOR == 1 +#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1 void MQTTClientComponent::dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg) { #else void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) { @@ -155,18 +157,18 @@ void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t * if (ipaddr == nullptr) { a_this->dns_resolve_error_ = true; } else { -#ifdef ARDUINO_ARCH_ESP32 - a_this->ip_ = IPAddress(ipaddr->u_addr.ip4.addr); +#ifdef USE_ESP32 + a_this->ip_ = ipaddr->u_addr.ip4.addr; #endif -#ifdef ARDUINO_ARCH_ESP8266 - a_this->ip_ = IPAddress(ipaddr->addr); +#ifdef USE_ESP8266 + a_this->ip_ = ipaddr->addr; #endif a_this->dns_resolved_ = true; } } void MQTTClientComponent::start_connect_() { - if (!network_is_connected()) + if (!network::is_connected()) return; ESP_LOGI(TAG, "Connecting to MQTT..."); @@ -183,7 +185,7 @@ void MQTTClientComponent::start_connect_() { this->mqtt_client_.setCredentials(username, password); - this->mqtt_client_.setServer(this->ip_, this->credentials_.port); + this->mqtt_client_.setServer((uint32_t) this->ip_, this->credentials_.port); if (!this->last_will_.topic.empty()) { this->mqtt_client_.setWill(this->last_will_.topic.c_str(), this->last_will_.qos, this->last_will_.retain, this->last_will_.payload.c_str(), this->last_will_.payload.length()); @@ -251,7 +253,7 @@ void MQTTClientComponent::loop() { reason_s = LOG_STR("Unknown"); break; } - if (!network_is_connected()) { + if (!network::is_connected()) { reason_s = LOG_STR("WiFi disconnected"); } ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s)); @@ -477,7 +479,7 @@ static bool topic_match(const char *message, const char *subscription) { } void MQTTClientComponent::on_message(const std::string &topic, const std::string &payload) { -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 // on ESP8266, this is called in LWiP thread; some components do not like running // in an ISR. this->defer([this, topic, payload]() { @@ -485,7 +487,7 @@ void MQTTClientComponent::on_message(const std::string &topic, const std::string for (auto &subscription : this->subscriptions_) if (topic_match(topic.c_str(), subscription.topic.c_str())) subscription.callback(topic, payload); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 }); #endif } @@ -587,3 +589,5 @@ float MQTTMessageTrigger::get_setup_priority() const { return setup_priority::AF } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 119e61db2b..fa689eaa04 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -1,10 +1,14 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/defines.h" + +#ifdef USE_MQTT + +#include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/core/log.h" #include "esphome/components/json/json_util.h" +#include "esphome/components/network/ip_address.h" #include #include "lwip/ip_addr.h" @@ -226,7 +230,7 @@ class MQTTClientComponent : public Component { void start_connect_(); void start_dnslookup_(); void check_dnslookup_(); -#if defined(ARDUINO_ARCH_ESP8266) && LWIP_VERSION_MAJOR == 1 +#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1 static void dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg); #else static void dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg); @@ -265,7 +269,7 @@ class MQTTClientComponent : public Component { std::vector subscriptions_; AsyncMqttClient mqtt_client_; MQTTClientState state_{MQTT_CLIENT_DISCONNECTED}; - IPAddress ip_; + network::IPAddress ip_; bool dns_resolved_{false}; bool dns_resolve_error_{false}; std::vector children_; @@ -352,3 +356,5 @@ template class MQTTConnectedCondition : public Condition } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index be9dbb0a08..8519e296b0 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -1,6 +1,7 @@ #include "mqtt_climate.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_CLIMATE namespace esphome { @@ -245,7 +246,7 @@ bool MQTTClimateComponent::publish_state_() { if (!this->publish(this->get_mode_state_topic(), mode_s)) success = false; int8_t accuracy = traits.get_temperature_accuracy_decimals(); - if (traits.get_supports_current_temperature() && !isnan(this->device_->current_temperature)) { + if (traits.get_supports_current_temperature() && !std::isnan(this->device_->current_temperature)) { std::string payload = value_accuracy_to_string(this->device_->current_temperature, accuracy); if (!this->publish(this->get_current_temperature_state_topic(), payload)) success = false; @@ -359,3 +360,4 @@ bool MQTTClimateComponent::publish_state_() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_climate.h b/esphome/components/mqtt/mqtt_climate.h index 8aea4feb26..8b8a8e866e 100644 --- a/esphome/components/mqtt/mqtt_climate.h +++ b/esphome/components/mqtt/mqtt_climate.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_CLIMATE #include "esphome/components/climate/climate.h" @@ -48,3 +49,4 @@ class MQTTClimateComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 3ed9aafb42..96cda57914 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -1,4 +1,7 @@ #include "mqtt_component.h" + +#ifdef USE_MQTT + #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" @@ -198,3 +201,5 @@ bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connec } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 668162da5a..f07e752e02 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -1,5 +1,9 @@ #pragma once +#include "esphome/core/defines.h" + +#ifdef USE_MQTT + #include #include "esphome/core/component.h" @@ -181,3 +185,5 @@ class MQTTComponent : public Component { } // namespace mqtt } // namespace esphome + +#endif // USE_MQTt diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index b11ae1fb93..6a0d2d1bc5 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -1,6 +1,7 @@ #include "mqtt_cover.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_COVER namespace esphome { @@ -115,3 +116,4 @@ bool MQTTCoverComponent::publish_state() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_cover.h b/esphome/components/mqtt/mqtt_cover.h index 5c2ce93987..b8c9f2617c 100644 --- a/esphome/components/mqtt/mqtt_cover.h +++ b/esphome/components/mqtt/mqtt_cover.h @@ -3,6 +3,7 @@ #include "esphome/core/defines.h" #include "mqtt_component.h" +#ifdef USE_MQTT #ifdef USE_COVER #include "esphome/components/cover/cover.h" @@ -40,3 +41,4 @@ class MQTTCoverComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index b8eecf0ff3..c8db5ecece 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -1,6 +1,7 @@ #include "mqtt_fan.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_FAN #include "esphome/components/fan/fan_helpers.h" @@ -127,3 +128,4 @@ bool MQTTFanComponent::publish_state() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_fan.h b/esphome/components/mqtt/mqtt_fan.h index 5495780a27..99d9c055cf 100644 --- a/esphome/components/mqtt/mqtt_fan.h +++ b/esphome/components/mqtt/mqtt_fan.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_FAN #include "esphome/components/fan/fan_state.h" @@ -45,3 +46,4 @@ class MQTTFanComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index f53be9c010..d028b1b037 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -1,6 +1,7 @@ #include "mqtt_light.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_LIGHT #include "esphome/components/light/light_json_schema.h" @@ -79,3 +80,4 @@ void MQTTJSONLightComponent::dump_config() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_light.h b/esphome/components/mqtt/mqtt_light.h index 9417e71ddd..b0f3145900 100644 --- a/esphome/components/mqtt/mqtt_light.h +++ b/esphome/components/mqtt/mqtt_light.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_LIGHT #include "mqtt_component.h" @@ -39,3 +40,4 @@ class MQTTJSONLightComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index f209f4fe20..faa38056a9 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -1,6 +1,7 @@ #include "mqtt_number.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_NUMBER namespace esphome { @@ -63,3 +64,4 @@ bool MQTTNumberComponent::publish_state(float value) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_number.h b/esphome/components/mqtt/mqtt_number.h index f44de91435..46dc221e9e 100644 --- a/esphome/components/mqtt/mqtt_number.h +++ b/esphome/components/mqtt/mqtt_number.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_NUMBER #include "esphome/components/number/number.h" @@ -44,3 +45,4 @@ class MQTTNumberComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index c0ac472d46..467a0cd84c 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -1,6 +1,7 @@ #include "mqtt_select.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_SELECT namespace esphome { @@ -56,3 +57,4 @@ bool MQTTSelectComponent::publish_state(const std::string &value) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_select.h b/esphome/components/mqtt/mqtt_select.h index 013e905ead..0115c0a41e 100644 --- a/esphome/components/mqtt/mqtt_select.h +++ b/esphome/components/mqtt/mqtt_select.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_SELECT #include "esphome/components/select/select.h" @@ -44,3 +45,4 @@ class MQTTSelectComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index e921056167..72ec7c54ee 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -1,6 +1,7 @@ #include "mqtt_sensor.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_SENSOR #ifdef USE_DEEP_SLEEP @@ -77,3 +78,4 @@ std::string MQTTSensorComponent::unique_id() { return this->sensor_->unique_id() } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_sensor.h b/esphome/components/mqtt/mqtt_sensor.h index 8d8fa83531..2385529f4f 100644 --- a/esphome/components/mqtt/mqtt_sensor.h +++ b/esphome/components/mqtt/mqtt_sensor.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" @@ -58,3 +59,4 @@ class MQTTSensorComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index b73e1ab8dc..b13ddd5d9d 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -1,6 +1,7 @@ #include "mqtt_switch.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_SWITCH namespace esphome { @@ -58,3 +59,4 @@ bool MQTTSwitchComponent::publish_state(bool state) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_switch.h b/esphome/components/mqtt/mqtt_switch.h index 33b829c856..9959d21872 100644 --- a/esphome/components/mqtt/mqtt_switch.h +++ b/esphome/components/mqtt/mqtt_switch.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_SWITCH #include "esphome/components/switch/switch.h" @@ -39,3 +40,4 @@ class MQTTSwitchComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index 8bc11d954c..fd96cd0902 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -1,6 +1,7 @@ #include "mqtt_text_sensor.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_TEXT_SENSOR namespace esphome { @@ -43,3 +44,4 @@ std::string MQTTTextSensor::unique_id() { return this->sensor_->unique_id(); } } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text_sensor.h b/esphome/components/mqtt/mqtt_text_sensor.h index a5ce0658c7..1d3f95b894 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.h +++ b/esphome/components/mqtt/mqtt_text_sensor.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_TEXT_SENSOR #include "esphome/components/text_sensor/text_sensor.h" @@ -40,3 +41,4 @@ class MQTTTextSensor : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp index 06251bd7c8..e1accf3c70 100644 --- a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp +++ b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp @@ -1,4 +1,7 @@ #include "mqtt_subscribe_sensor.h" + +#ifdef USE_MQTT + #include "esphome/core/log.h" namespace esphome { @@ -31,3 +34,5 @@ void MQTTSubscribeSensor::dump_config() { } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h index a303ccad89..0619326ac9 100644 --- a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h +++ b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h @@ -1,5 +1,9 @@ #pragma once +#include "esphome/core/defines.h" + +#ifdef USE_MQTT + #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/mqtt/mqtt_client.h" @@ -25,3 +29,5 @@ class MQTTSubscribeSensor : public sensor::Sensor, public Component { } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp index 2b0908979c..8aa094a2d4 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp +++ b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp @@ -1,5 +1,7 @@ #include "mqtt_subscribe_text_sensor.h" +#ifdef USE_MQTT + #include "esphome/core/log.h" #include @@ -22,3 +24,5 @@ void MQTTSubscribeTextSensor::dump_config() { } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h index 69409f6348..9f8e5c63cc 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h +++ b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h @@ -1,5 +1,9 @@ #pragma once +#include "esphome/core/defines.h" + +#ifdef USE_MQTT + #include "esphome/core/component.h" #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/mqtt/mqtt_client.h" @@ -24,3 +28,5 @@ class MQTTSubscribeTextSensor : public text_sensor::TextSensor, public Component } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/ms5611/ms5611.cpp b/esphome/components/ms5611/ms5611.cpp index 51dc569240..1d7516dbe8 100644 --- a/esphome/components/ms5611/ms5611.cpp +++ b/esphome/components/ms5611/ms5611.cpp @@ -1,5 +1,6 @@ #include "ms5611.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ms5611 { diff --git a/esphome/components/my9231/my9231.h b/esphome/components/my9231/my9231.h index ee15f9743c..a777dcc960 100644 --- a/esphome/components/my9231/my9231.h +++ b/esphome/components/my9231/my9231.h @@ -1,8 +1,9 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace my9231 { diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index a86b4e4588..0117f1b063 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -168,14 +168,15 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_VARIANT, default="800KBPS"): validate_variant, cv.Optional(CONF_METHOD, default=None): validate_method, cv.Optional(CONF_INVERT, default="no"): cv.boolean, - cv.Optional(CONF_PIN): pins.output_pin, - cv.Optional(CONF_CLOCK_PIN): pins.output_pin, - cv.Optional(CONF_DATA_PIN): pins.output_pin, + cv.Optional(CONF_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_CLOCK_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_DATA_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, } ).extend(cv.COMPONENT_SCHEMA), _validate, validate_method_pin, + cv.only_with_arduino, ) diff --git a/esphome/components/neopixelbus/neopixelbus_light.h b/esphome/components/neopixelbus/neopixelbus_light.h index 5359bac61b..34e10f2cfe 100644 --- a/esphome/components/neopixelbus/neopixelbus_light.h +++ b/esphome/components/neopixelbus/neopixelbus_light.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/macros.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" @@ -7,7 +9,7 @@ #include "esphome/components/light/light_output.h" #include "esphome/components/light/addressable_light.h" -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) +#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) #error The NeoPixelBus library requires at least arduino_version 2.4.x #endif @@ -144,3 +146,5 @@ class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase +#include +#include +#include + +namespace esphome { +namespace network { + +struct IPAddress { + public: + IPAddress() : addr_({0, 0, 0, 0}) {} + IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) : addr_({first, second, third, fourth}) {} + IPAddress(uint32_t raw) { + addr_[0] = (uint8_t)(raw >> 0); + addr_[1] = (uint8_t)(raw >> 8); + addr_[2] = (uint8_t)(raw >> 16); + addr_[3] = (uint8_t)(raw >> 24); + } + operator uint32_t() const { + uint32_t res = 0; + res |= ((uint32_t) addr_[0]) << 0; + res |= ((uint32_t) addr_[1]) << 8; + res |= ((uint32_t) addr_[2]) << 16; + res |= ((uint32_t) addr_[3]) << 24; + return res; + } + std::string str() const { + char buffer[24]; + snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", addr_[0], addr_[1], addr_[2], addr_[3]); + return buffer; + } + bool operator==(const IPAddress &other) const { + return addr_[0] == other.addr_[0] && addr_[1] == other.addr_[1] && addr_[2] == other.addr_[2] && + addr_[3] == other.addr_[3]; + } + uint8_t operator[](int index) const { return addr_[index]; } + uint8_t &operator[](int index) { return addr_[index]; } + + protected: + std::array addr_; +}; + +} // namespace network +} // namespace esphome diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp new file mode 100644 index 0000000000..f7ac6b543e --- /dev/null +++ b/esphome/components/network/util.cpp @@ -0,0 +1,54 @@ +#include "util.h" +#include "esphome/core/defines.h" + +#ifdef USE_WIFI +#include "esphome/components/wifi/wifi_component.h" +#endif + +#ifdef USE_ETHERNET +#include "esphome/components/ethernet/ethernet_component.h" +#endif + +namespace esphome { +namespace network { + +bool is_connected() { +#ifdef USE_ETHERNET + if (ethernet::global_eth_component != nullptr && ethernet::global_eth_component->is_connected()) + return true; +#endif + +#ifdef USE_WIFI + if (wifi::global_wifi_component != nullptr) + return wifi::global_wifi_component->is_connected(); +#endif + + return false; +} + +network::IPAddress get_ip_address() { +#ifdef USE_ETHERNET + if (ethernet::global_eth_component != nullptr) + return ethernet::global_eth_component->get_ip_address(); +#endif +#ifdef USE_WIFI + if (wifi::global_wifi_component != nullptr) + return wifi::global_wifi_component->get_ip_address(); +#endif + return {}; +} + +std::string get_use_address() { +#ifdef USE_ETHERNET + if (ethernet::global_eth_component != nullptr) + return ethernet::global_eth_component->get_use_address(); +#endif +#ifdef USE_WIFI + if (wifi::global_wifi_component != nullptr) + return wifi::global_wifi_component->get_use_address(); +#endif + return ""; +} + +} // namespace network +} // namespace esphome diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h new file mode 100644 index 0000000000..f248d5cbf4 --- /dev/null +++ b/esphome/components/network/util.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "ip_address.h" + +namespace esphome { +namespace network { + +/// Return whether the node is connected to the network (through wifi, eth, ...) +bool is_connected(); +/// Get the active network hostname +std::string get_use_address(); +IPAddress get_ip_address(); + +} // namespace network +} // namespace esphome diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index e693b2f1ec..f4b35fd56f 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -33,7 +33,7 @@ CONFIG_SCHEMA = ( display.BASIC_DISPLAY_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(Nextion), - cv.Optional(CONF_TFT_URL): cv.string, + cv.Optional(CONF_TFT_URL): cv.All(cv.string, cv.only_with_arduino), cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, cv.Optional(CONF_ON_SETUP): automation.validate_automation( { @@ -74,7 +74,7 @@ async def to_code(config): cg.add(var.set_writer(lambda_)) if CONF_TFT_URL in config: - cg.add_define("USE_TFT_UPLOAD") + cg.add_define("USE_NEXTION_TFT_UPLOAD") cg.add(var.set_tft_url(config[CONF_TFT_URL])) if CONF_TOUCH_SLEEP_TIMEOUT in config: diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 45c4a629c4..1bee41f6cf 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -7,11 +7,11 @@ #include "nextion_component.h" #include "esphome/components/display/display_color_utils.h" -#if defined(USE_ETHERNET) || defined(USE_WIFI) -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_NEXTION_TFT_UPLOAD +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #include #endif @@ -652,7 +652,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ bool send_command_printf(const char *format, ...) __attribute__((format(printf, 2, 3))); -#ifdef USE_TFT_UPLOAD +#ifdef USE_NEXTION_TFT_UPLOAD /** * Set the tft file URL. https seems problamtic with arduino.. */ @@ -770,9 +770,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe const std::string &variable_name_to_send, const std::string &state_value, bool is_sleep_safe = false); -#ifdef USE_TFT_UPLOAD -#if defined(USE_ETHERNET) || defined(USE_WIFI) -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_NEXTION_TFT_UPLOAD +#ifdef USE_ESP8266 WiFiClient *wifi_client_{nullptr}; BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr}; WiFiClient *get_wifi_client_(); @@ -801,9 +800,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size); void upload_end_(); -#endif - -#endif +#endif // USE_NEXTION_TFT_UPLOAD bool get_is_connected_() { return this->is_connected_; } @@ -828,7 +825,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void remove_front_no_sensors_(); -#ifdef USE_TFT_UPLOAD +#ifdef USE_NEXTION_TFT_UPLOAD std::string tft_url_; uint8_t *transfer_buffer_{nullptr}; size_t transfer_buffer_size_; diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp index f864a397cc..9a748277d8 100644 --- a/esphome/components/nextion/nextion_upload.cpp +++ b/esphome/components/nextion/nextion_upload.cpp @@ -1,16 +1,16 @@ +#ifdef USE_NEXTION_TFT_UPLOAD #include "nextion.h" #include "esphome/core/application.h" #include "esphome/core/macros.h" #include "esphome/core/util.h" #include "esphome/core/log.h" +#include "esphome/components/network/util.h" namespace esphome { namespace nextion { static const char *const TAG = "nextion_upload"; -#if defined(USE_TFT_UPLOAD) && (defined(USE_ETHERNET) || defined(USE_WIFI)) - // Followed guide // https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2 @@ -26,7 +26,7 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { if (range_end > this->tft_size_) range_end = this->tft_size_; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); #elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) @@ -46,10 +46,10 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { int code = 0; bool begin_status = false; while (tries <= 5) { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 begin_status = http->begin(this->tft_url_.c_str()); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str()); #endif @@ -129,7 +129,7 @@ void Nextion::upload_tft() { return; } - if (!network_is_connected()) { + if (!network::is_connected()) { ESP_LOGD(TAG, "network is not connected"); return; } @@ -139,10 +139,10 @@ void Nextion::upload_tft() { HTTPClient http; http.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along bool begin_status = false; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 begin_status = http.begin(this->tft_url_.c_str()); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); #elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) @@ -157,7 +157,7 @@ void Nextion::upload_tft() { if (!begin_status) { this->is_updating_ = false; ESP_LOGD(TAG, "connection failed"); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (psramFound()) free(this->transfer_buffer_); // NOLINT else @@ -249,7 +249,7 @@ void Nextion::upload_tft() { } // Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096 -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 uint32_t chunk_size = 8192; if (psramFound()) { chunk_size = this->content_length_; @@ -268,7 +268,7 @@ void Nextion::upload_tft() { #endif if (this->transfer_buffer_ == nullptr) { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (psramFound()) { ESP_LOGD(TAG, "Allocating PSRAM buffer size %d, Free PSRAM size is %u", chunk_size, ESP.getFreePsram()); this->transfer_buffer_ = (uint8_t *) ps_malloc(chunk_size); @@ -289,7 +289,7 @@ void Nextion::upload_tft() { if (!this->transfer_buffer_) this->upload_end_(); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 } #endif } @@ -325,7 +325,7 @@ void Nextion::upload_end_() { ESP.restart(); // NOLINT(readability-static-accessed-through-instance) } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 WiFiClient *Nextion::get_wifi_client_() { if (this->tft_url_.compare(0, 6, "https:") == 0) { if (this->wifi_client_secure_ == nullptr) { @@ -342,9 +342,7 @@ WiFiClient *Nextion::get_wifi_client_() { return this->wifi_client_; } #endif - -#else -void Nextion::upload_tft() { ESP_LOGW(TAG, "tft_url, WIFI or Ethernet components are needed. Cannot upload."); } -#endif } // namespace nextion } // namespace esphome + +#endif // USE_NEXTION_TFT_UPLOAD diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 1e79164546..e983ebcc6f 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -48,7 +48,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { if (!this->nextion_->is_setup()) return; - if (isnan(state)) + if (std::isnan(state)) return; if (this->wave_chan_id_ == UINT8_MAX) { diff --git a/esphome/components/ntc/ntc.cpp b/esphome/components/ntc/ntc.cpp index 80a11384b9..333dbc5a75 100644 --- a/esphome/components/ntc/ntc.cpp +++ b/esphome/components/ntc/ntc.cpp @@ -14,7 +14,7 @@ void NTC::setup() { void NTC::dump_config() { LOG_SENSOR("", "NTC Sensor", this) } float NTC::get_setup_priority() const { return setup_priority::DATA; } void NTC::process_(float value) { - if (isnan(value)) { + if (std::isnan(value)) { this->publish_state(NAN); return; } diff --git a/esphome/components/number/automation.cpp b/esphome/components/number/automation.cpp index a0b169427f..c75d272660 100644 --- a/esphome/components/number/automation.cpp +++ b/esphome/components/number/automation.cpp @@ -7,7 +7,7 @@ namespace number { static const char *const TAG = "number.automation"; void ValueRangeTrigger::setup() { - this->rtc_ = global_preferences.make_preference(this->parent_->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->parent_->get_object_id_hash()); bool initial_state; if (this->rtc_.load(&initial_state)) { this->previous_in_range_ = initial_state; @@ -18,18 +18,18 @@ void ValueRangeTrigger::setup() { float ValueRangeTrigger::get_setup_priority() const { return setup_priority::HARDWARE; } void ValueRangeTrigger::on_state_(float state) { - if (isnan(state)) + if (std::isnan(state)) return; float local_min = this->min_.value(state); float local_max = this->max_.value(state); bool in_range; - if (isnan(local_min) && isnan(local_max)) { + if (std::isnan(local_min) && std::isnan(local_max)) { in_range = this->previous_in_range_; - } else if (isnan(local_min)) { + } else if (std::isnan(local_min)) { in_range = state <= local_max; - } else if (isnan(local_max)) { + } else if (std::isnan(local_max)) { in_range = state >= local_min; } else { in_range = local_min <= state && state <= local_max; diff --git a/esphome/components/number/automation.h b/esphome/components/number/automation.h index 9e812f8c49..98554a346a 100644 --- a/esphome/components/number/automation.h +++ b/esphome/components/number/automation.h @@ -57,9 +57,9 @@ template class NumberInRangeCondition : public Condition void set_max(float max) { this->max_ = max; } bool check(Ts... x) override { const float state = this->parent_->state; - if (isnan(this->min_)) { + if (std::isnan(this->min_)) { return state <= this->max_; - } else if (isnan(this->max_)) { + } else if (std::isnan(this->max_)) { return state >= this->min_; } else { return this->min_ <= state && state <= this->max_; diff --git a/esphome/components/number/number.cpp b/esphome/components/number/number.cpp index dbc1c88a5d..57a5c7c4bd 100644 --- a/esphome/components/number/number.cpp +++ b/esphome/components/number/number.cpp @@ -8,7 +8,7 @@ static const char *const TAG = "number"; void NumberCall::perform() { ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); - if (!this->value_.has_value() || isnan(*this->value_)) { + if (!this->value_.has_value() || std::isnan(*this->value_)) { ESP_LOGW(TAG, "No value set for NumberCall"); return; } diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 75641ad399..59ab22056b 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -15,6 +15,7 @@ from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] +AUTO_LOAD = ["socket"] CONF_ON_STATE_CHANGE = "on_state_change" CONF_ON_BEGIN = "on_begin" @@ -33,12 +34,21 @@ OTAProgressTrigger = ota_ns.class_("OTAProgressTrigger", automation.Trigger.temp OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) + +def validate_password_support(value): + if CORE.using_arduino: + return value + if CORE.using_esp_idf: + raise cv.Invalid("Password support is not implemented yet for ESP-IDF") + raise NotImplementedError + + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(OTAComponent), cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232): cv.port, - cv.Optional(CONF_PASSWORD, default=""): cv.string, + cv.Optional(CONF_PASSWORD): cv.All(cv.string, validate_password_support), cv.Optional( CONF_REBOOT_TIMEOUT, default="5min" ): cv.positive_time_period_milliseconds, @@ -76,7 +86,9 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_port(config[CONF_PORT])) - cg.add(var.set_auth_password(config[CONF_PASSWORD])) + if CONF_PASSWORD in config: + cg.add(var.set_auth_password(config[CONF_PASSWORD])) + cg.add_define("USE_OTA_PASSWORD") await cg.register_component(var, config) @@ -88,7 +100,7 @@ async def to_code(config): if CORE.is_esp8266: cg.add_library("Update", None) - elif CORE.is_esp32: + elif CORE.is_esp32 and CORE.using_arduino: cg.add_library("Hash", None) use_state_callback = False diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index e8ac1b9bcd..7ee3ed2866 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -2,14 +2,31 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/hal.h" #include "esphome/core/util.h" +#include "esphome/components/network/util.h" +#include #include + +#ifdef USE_ARDUINO +#ifdef USE_OTA_PASSWORD #include -#ifdef ARDUINO_ARCH_ESP32 +#endif // USE_OTA_PASSWORD + +#ifdef USE_ESP32 #include +#endif // USE_ESP32 +#endif // USE_ARDUINO + +#ifdef USE_ESP8266 +#include +#include "esphome/components/esp8266/preferences.h" +#endif // USE_ESP8266 + +#ifdef USE_ESP_IDF +#include #endif -#include namespace esphome { namespace ota { @@ -18,19 +35,177 @@ static const char *const TAG = "ota"; static const uint8_t OTA_VERSION_1_0 = 1; +class OTABackend { + public: + virtual ~OTABackend() = default; + virtual OTAResponseTypes begin(size_t image_size) = 0; + virtual void set_update_md5(const char *md5) = 0; + virtual OTAResponseTypes write(uint8_t *data, size_t len) = 0; + virtual OTAResponseTypes end() = 0; + virtual void abort() = 0; +}; + +#ifdef USE_ARDUINO +class ArduinoOTABackend : public OTABackend { + public: + OTAResponseTypes begin(size_t image_size) override { + bool ret = Update.begin(image_size, U_FLASH); + if (ret) { +#ifdef USE_ESP8266 + esp8266::preferences_prevent_write(true); +#endif + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); +#ifdef USE_ESP8266 + if (error == UPDATE_ERROR_BOOTSTRAP) + return OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; + if (error == UPDATE_ERROR_NEW_FLASH_CONFIG) + return OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; + if (error == UPDATE_ERROR_FLASH_CONFIG) + return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; + if (error == UPDATE_ERROR_SPACE) + return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; +#endif +#ifdef USE_ESP32 + if (error == UPDATE_ERROR_SIZE) + return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; +#endif + return OTA_RESPONSE_ERROR_UNKNOWN; + } + void set_update_md5(const char *md5) override { Update.setMD5(md5); } + OTAResponseTypes write(uint8_t *data, size_t len) override { + size_t written = Update.write(data, len); + if (written != len) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_OK; + } + OTAResponseTypes end() override { + if (!Update.end()) + return OTA_RESPONSE_ERROR_UPDATE_END; + return OTA_RESPONSE_OK; + } + void abort() override { +#ifdef USE_ESP32 + Update.abort(); +#endif + +#ifdef USE_ESP8266 + Update.end(); + esp8266::preferences_prevent_write(false); +#endif + } +}; +std::unique_ptr make_ota_backend() { return make_unique(); } +#endif // USE_ARDUINO + +#ifdef USE_ESP_IDF +class IDFOTABackend : public OTABackend { + public: + esp_ota_handle_t update_handle = 0; + + OTAResponseTypes begin(size_t image_size) override { + const esp_partition_t *update_partition = esp_ota_get_next_update_partition(nullptr); + if (update_partition == nullptr) { + return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; + } + esp_err_t err = esp_ota_begin(update_partition, image_size, &update_handle); + if (err != ESP_OK) { + esp_ota_abort(update_handle); + update_handle = 0; + if (err == ESP_ERR_INVALID_SIZE) { + return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; + } + void set_update_md5(const char *md5) override { + // pass + } + OTAResponseTypes write(uint8_t *data, size_t len) override { + esp_err_t err = esp_ota_write(update_handle, data, len); + if (err != ESP_OK) { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + return OTA_RESPONSE_ERROR_MAGIC; + } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; + } + OTAResponseTypes end() override { + esp_err_t err = esp_ota_end(update_handle); + update_handle = 0; + if (err != ESP_OK) { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + return OTA_RESPONSE_ERROR_UPDATE_END; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; + } + void abort() override { esp_ota_abort(update_handle); } +}; +std::unique_ptr make_ota_backend() { return make_unique(); } +#endif // USE_ESP_IDF + void OTAComponent::setup() { - this->server_ = make_unique(this->port_); - this->server_->begin(); + server_ = socket::socket(AF_INET, SOCK_STREAM, 0); + if (server_ == nullptr) { + ESP_LOGW(TAG, "Could not create socket."); + this->mark_failed(); + return; + } + int enable = 1; + int err = server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err); + // we can still continue + } + err = server_->setblocking(false); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err); + this->mark_failed(); + return; + } + + struct sockaddr_in server; + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = ESPHOME_INADDR_ANY; + server.sin_port = htons(this->port_); + + err = server_->bind((struct sockaddr *) &server, sizeof(server)); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); + this->mark_failed(); + return; + } + + err = server_->listen(4); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno); + this->mark_failed(); + return; + } this->dump_config(); } void OTAComponent::dump_config() { ESP_LOGCONFIG(TAG, "Over-The-Air Updates:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); +#ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { ESP_LOGCONFIG(TAG, " Using Password."); } +#endif if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1) { ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %d restarts", this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_); @@ -57,25 +232,31 @@ void OTAComponent::handle_() { char *sbuf = reinterpret_cast(buf); uint32_t ota_size; uint8_t ota_features; + std::unique_ptr backend; (void) ota_features; - if (!this->client_.connected()) { - this->client_ = this->server_->available(); + if (client_ == nullptr) { + struct sockaddr_storage source_addr; + socklen_t addr_len = sizeof(source_addr); + client_ = server_->accept((struct sockaddr *) &source_addr, &addr_len); + } + if (client_ == nullptr) + return; - if (!this->client_.connected()) - return; + int enable = 1; + int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); + if (err != 0) { + ESP_LOGW(TAG, "Socket could not enable tcp nodelay, errno: %d", errno); + return; } - // enable nodelay for outgoing data - this->client_.setNoDelay(true); - - ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_.remoteIP().toString().c_str()); + ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_->getpeername().c_str()); this->status_set_warning(); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(OTA_STARTED, 0.0f, 0); #endif - if (!this->wait_receive_(buf, 5)) { + if (!this->readall_(buf, 5)) { ESP_LOGW(TAG, "Reading magic bytes failed!"); goto error; } @@ -88,11 +269,12 @@ void OTAComponent::handle_() { } // Send OK and version - 2 bytes - this->client_.write(OTA_RESPONSE_OK); - this->client_.write(OTA_VERSION_1_0); + buf[0] = OTA_RESPONSE_OK; + buf[1] = OTA_VERSION_1_0; + this->writeall_(buf, 2); // Read features - 1 byte - if (!this->wait_receive_(buf, 1)) { + if (!this->readall_(buf, 1)) { ESP_LOGW(TAG, "Reading features failed!"); goto error; } @@ -100,10 +282,13 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "OTA features is 0x%02X", ota_features); // Acknowledge header - 1 byte - this->client_.write(OTA_RESPONSE_HEADER_OK); + buf[0] = OTA_RESPONSE_HEADER_OK; + this->writeall_(buf, 1); +#ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { - this->client_.write(OTA_RESPONSE_REQUEST_AUTH); + buf[0] = OTA_RESPONSE_REQUEST_AUTH; + this->writeall_(buf, 1); MD5Builder md5_builder{}; md5_builder.begin(); sprintf(sbuf, "%08X", random_uint32()); @@ -113,7 +298,7 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "Auth: Nonce is %s", sbuf); // Send nonce, 32 bytes hex MD5 - if (this->client_.write(reinterpret_cast(sbuf), 32) != 32) { + if (!this->writeall_(reinterpret_cast(sbuf), 32)) { ESP_LOGW(TAG, "Auth: Writing nonce failed!"); goto error; } @@ -125,7 +310,7 @@ void OTAComponent::handle_() { md5_builder.add(sbuf); // Receive cnonce, 32 bytes hex MD5 - if (!this->wait_receive_(buf, 32)) { + if (!this->readall_(buf, 32)) { ESP_LOGW(TAG, "Auth: Reading cnonce failed!"); goto error; } @@ -140,7 +325,7 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "Auth: Result is %s", sbuf); // Receive result, 32 bytes hex MD5 - if (!this->wait_receive_(buf + 64, 32)) { + if (!this->writeall_(buf + 64, 32)) { ESP_LOGW(TAG, "Auth: Reading response failed!"); goto error; } @@ -157,12 +342,14 @@ void OTAComponent::handle_() { goto error; } } +#endif // USE_OTA_PASSWORD // Acknowledge auth OK - 1 byte - this->client_.write(OTA_RESPONSE_AUTH_OK); + buf[0] = OTA_RESPONSE_AUTH_OK; + this->writeall_(buf, 1); // Read size, 4 bytes MSB first - if (!this->wait_receive_(buf, 4)) { + if (!this->readall_(buf, 4)) { ESP_LOGW(TAG, "Reading size failed!"); goto error; } @@ -173,72 +360,46 @@ void OTAComponent::handle_() { } ESP_LOGV(TAG, "OTA size is %u bytes", ota_size); -#ifdef ARDUINO_ARCH_ESP8266 - global_preferences.prevent_write(true); -#endif - - if (!Update.begin(ota_size, U_FLASH)) { - uint8_t error = Update.getError(); - StreamString ss; - Update.printError(ss); -#ifdef ARDUINO_ARCH_ESP8266 - if (error == UPDATE_ERROR_BOOTSTRAP) { - error_code = OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; - goto error; - } - if (error == UPDATE_ERROR_NEW_FLASH_CONFIG) { - error_code = OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; - goto error; - } - if (error == UPDATE_ERROR_FLASH_CONFIG) { - error_code = OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; - goto error; - } - if (error == UPDATE_ERROR_SPACE) { - error_code = OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; - goto error; - } -#endif -#ifdef ARDUINO_ARCH_ESP32 - if (error == UPDATE_ERROR_SIZE) { - error_code = OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; - goto error; - } -#endif - ESP_LOGW(TAG, "Preparing OTA partition failed! '%s'", ss.c_str()); - error_code = OTA_RESPONSE_ERROR_UPDATE_PREPARE; + backend = make_ota_backend(); + error_code = backend->begin(ota_size); + if (error_code != OTA_RESPONSE_OK) goto error; - } update_started = true; // Acknowledge prepare OK - 1 byte - this->client_.write(OTA_RESPONSE_UPDATE_PREPARE_OK); + buf[0] = OTA_RESPONSE_UPDATE_PREPARE_OK; + this->writeall_(buf, 1); // Read binary MD5, 32 bytes - if (!this->wait_receive_(buf, 32)) { + if (!this->readall_(buf, 32)) { ESP_LOGW(TAG, "Reading binary MD5 checksum failed!"); goto error; } sbuf[32] = '\0'; ESP_LOGV(TAG, "Update: Binary MD5 is %s", sbuf); - Update.setMD5(sbuf); + backend->set_update_md5(sbuf); // Acknowledge MD5 OK - 1 byte - this->client_.write(OTA_RESPONSE_BIN_MD5_OK); + buf[0] = OTA_RESPONSE_BIN_MD5_OK; + this->writeall_(buf, 1); - while (!Update.isFinished()) { - size_t available = this->wait_receive_(buf, 0); - if (!available) { + while (total < ota_size) { + // TODO: timeout check + size_t requested = std::min(sizeof(buf), ota_size - total); + ssize_t read = this->client_->read(buf, requested); + if (read == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + ESP_LOGW(TAG, "Error receiving data for update, errno: %d", errno); goto error; } - uint32_t written = Update.write(buf, available); - if (written != available) { - ESP_LOGW(TAG, "Error writing binary data to flash: %u != %u!", written, available); // NOLINT - error_code = OTA_RESPONSE_ERROR_WRITING_FLASH; + error_code = backend->write(buf, read); + if (error_code != OTA_RESPONSE_OK) { + ESP_LOGW(TAG, "Error writing binary data to flash!"); goto error; } - total += written; + total += read; uint32_t now = millis(); if (now - last_progress > 1000) { @@ -254,24 +415,27 @@ void OTAComponent::handle_() { } // Acknowledge receive OK - 1 byte - this->client_.write(OTA_RESPONSE_RECEIVE_OK); + buf[0] = OTA_RESPONSE_RECEIVE_OK; + this->writeall_(buf, 1); - if (!Update.end()) { - error_code = OTA_RESPONSE_ERROR_UPDATE_END; + error_code = backend->end(); + if (error_code != OTA_RESPONSE_OK) { + ESP_LOGW(TAG, "Error ending OTA!"); goto error; } // Acknowledge Update end OK - 1 byte - this->client_.write(OTA_RESPONSE_UPDATE_END_OK); + buf[0] = OTA_RESPONSE_UPDATE_END_OK; + this->writeall_(buf, 1); // Read ACK - if (!this->wait_receive_(buf, 1, false) || buf[0] != OTA_RESPONSE_OK) { + if (!this->readall_(buf, 1) || buf[0] != OTA_RESPONSE_OK) { ESP_LOGW(TAG, "Reading back acknowledgement failed!"); // do not go to error, this is not fatal } - this->client_.flush(); - this->client_.stop(); + this->client_->close(); + this->client_ = nullptr; delay(10); ESP_LOGI(TAG, "OTA update finished!"); this->status_clear_warning(); @@ -282,88 +446,72 @@ void OTAComponent::handle_() { App.safe_reboot(); error: - if (update_started) { - StreamString ss; - Update.printError(ss); - ESP_LOGW(TAG, "Update end failed! Error: %s", ss.c_str()); - } - if (this->client_.connected()) { - this->client_.write(static_cast(error_code)); - this->client_.flush(); - } - this->client_.stop(); + buf[0] = static_cast(error_code); + this->writeall_(buf, 1); + this->client_->close(); + this->client_ = nullptr; -#ifdef ARDUINO_ARCH_ESP32 - if (update_started) { - Update.abort(); + if (backend != nullptr && update_started) { + backend->abort(); } -#endif - -#ifdef ARDUINO_ARCH_ESP8266 - if (update_started) { - Update.end(); - } -#endif this->status_momentary_error("onerror", 5000); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(OTA_ERROR, 0.0f, static_cast(error_code)); #endif - -#ifdef ARDUINO_ARCH_ESP8266 - global_preferences.prevent_write(false); -#endif } -size_t OTAComponent::wait_receive_(uint8_t *buf, size_t bytes, bool check_disconnected) { - size_t available = 0; +bool OTAComponent::readall_(uint8_t *buf, size_t len) { uint32_t start = millis(); - do { - App.feed_wdt(); - if (check_disconnected && !this->client_.connected()) { - ESP_LOGW(TAG, "Error client disconnected while receiving data!"); - return 0; - } - int availi = this->client_.available(); - if (availi < 0) { - ESP_LOGW(TAG, "Error reading data!"); - return 0; - } + uint32_t at = 0; + while (len - at > 0) { uint32_t now = millis(); - if (availi == 0 && now - start > 10000) { - ESP_LOGW(TAG, "Timeout waiting for data!"); - return 0; + if (now - start > 1000) { + ESP_LOGW(TAG, "Timed out reading %d bytes of data", len); + return false; } - available = size_t(availi); - yield(); - } while (bytes == 0 ? available == 0 : available < bytes); - if (bytes == 0) - bytes = std::min(available, size_t(1024)); - - bool success = false; - for (uint32_t i = 0; !success && i < 100; i++) { - int res = this->client_.read(buf, bytes); - - if (res != int(bytes)) { - // ESP32 implementation has an issue where calling read can fail with EAGAIN (race condition) - // so just re-try it until it works (with generous timeout of 1s) - // because we check with available() first this should not cause us any trouble in all other cases - delay(10); + ssize_t read = this->client_->read(buf + at, len - at); + if (read == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + delay(1); + continue; + } + ESP_LOGW(TAG, "Failed to read %d bytes of data, errno: %d", len, errno); + return false; } else { - success = true; + at += read; } + delay(1); } - if (!success) { - ESP_LOGW(TAG, "Reading %u bytes of binary data failed!", bytes); // NOLINT - return 0; - } - - return bytes; + return true; } +bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { + uint32_t start = millis(); + uint32_t at = 0; + while (len - at > 0) { + uint32_t now = millis(); + if (now - start > 1000) { + ESP_LOGW(TAG, "Timed out writing %d bytes of data", len); + return false; + } -void OTAComponent::set_auth_password(const std::string &password) { this->password_ = password; } + ssize_t written = this->client_->write(buf + at, len - at); + if (written == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + delay(1); + continue; + } + ESP_LOGW(TAG, "Failed to write %d bytes of data, errno: %d", len, errno); + return false; + } else { + at += written; + } + delay(1); + } + return true; +} float OTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } uint16_t OTAComponent::get_port() const { return this->port_; } @@ -373,7 +521,7 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ this->safe_mode_start_time_ = millis(); this->safe_mode_enable_time_ = enable_time; this->safe_mode_num_attempts_ = num_attempts; - this->rtc_ = global_preferences.make_preference(233825507UL, false); + this->rtc_ = global_preferences->make_preference(233825507UL, false); this->safe_mode_rtc_value_ = this->read_rtc_(); ESP_LOGCONFIG(TAG, "There have been %u suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_); diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index 04cea56ec2..f76295735e 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -1,12 +1,10 @@ #pragma once -#include - +#include "esphome/components/socket/socket.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/helpers.h" -#include -#include +#include "esphome/core/defines.h" namespace esphome { namespace ota { @@ -32,6 +30,7 @@ enum OTAResponseTypes { OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 135, OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 136, OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 137, + OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 138, OTA_RESPONSE_ERROR_UNKNOWN = 255, }; @@ -40,14 +39,9 @@ enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; /// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. class OTAComponent : public Component { public: - /** Set a plaintext password that OTA will use for authentication. - * - * Warning: This password will be stored in plaintext in the ROM and can be read - * by intruders. - * - * @param password The plaintext password. - */ - void set_auth_password(const std::string &password); +#ifdef USE_OTA_PASSWORD + void set_auth_password(const std::string &password) { password_ = password; } +#endif // USE_OTA_PASSWORD /// Manually set the port OTA should listen on. void set_port(uint16_t port); @@ -76,14 +70,17 @@ class OTAComponent : public Component { uint32_t read_rtc_(); void handle_(); - size_t wait_receive_(uint8_t *buf, size_t bytes, bool check_disconnected = true); + bool readall_(uint8_t *buf, size_t len); + bool writeall_(const uint8_t *buf, size_t len); +#ifdef USE_OTA_PASSWORD std::string password_; +#endif // USE_OTA_PASSWORD uint16_t port_; - std::unique_ptr server_{nullptr}; - WiFiClient client_{}; + std::unique_ptr server_; + std::unique_ptr client_; bool has_safe_mode_{false}; ///< stores whether safe mode can be enabled. uint32_t safe_mode_start_time_; ///< stores when safe mode was enabled. diff --git a/esphome/components/pca9685/pca9685_output.h b/esphome/components/pca9685/pca9685_output.h index bd3f7f15f9..5dd52b5510 100644 --- a/esphome/components/pca9685/pca9685_output.h +++ b/esphome/components/pca9685/pca9685_output.h @@ -20,9 +20,10 @@ extern const uint8_t PCA9685_MODE_OUTNE_LOW; class PCA9685Output; -class PCA9685Channel : public output::FloatOutput, public Parented { +class PCA9685Channel : public output::FloatOutput { public: void set_channel(uint8_t channel) { channel_ = channel; } + void set_parent(PCA9685Output *parent) { parent_ = parent; } protected: friend class PCA9685Output; @@ -30,6 +31,7 @@ class PCA9685Channel : public output::FloatOutput, public Parentedwrite_gpio_(); } -void PCF8574Component::pin_mode(uint8_t pin, uint8_t mode) { - switch (mode) { - case PCF8574_INPUT: - // Clear mode mask bit - this->mode_mask_ &= ~(1 << pin); - // Write GPIO to enable input mode - this->write_gpio_(); - break; - case PCF8574_OUTPUT: - // Set mode mask bit - this->mode_mask_ |= 1 << pin; - break; - default: - break; +void PCF8574Component::pin_mode(uint8_t pin, gpio::Flags flags) { + if (flags == gpio::FLAG_INPUT) { + // Clear mode mask bit + this->mode_mask_ &= ~(1 << pin); + // Write GPIO to enable input mode + this->write_gpio_(); + } else if (flags == gpio::FLAG_OUTPUT) { + // Set mode mask bit + this->mode_mask_ |= 1 << pin; } } bool PCF8574Component::read_gpio_() { @@ -87,7 +82,7 @@ bool PCF8574Component::write_gpio_() { uint8_t data[2]; data[0] = value; data[1] = value >> 8; - if (!this->write_bytes_raw(data, this->pcf8575_ ? 2 : 1)) { + if (this->write(data, this->pcf8575_ ? 2 : 1) != i2c::ERROR_OK) { this->status_set_warning(); return false; } @@ -97,12 +92,15 @@ bool PCF8574Component::write_gpio_() { } float PCF8574Component::get_setup_priority() const { return setup_priority::IO; } -void PCF8574GPIOPin::setup() { this->pin_mode(this->mode_); } +void PCF8574GPIOPin::setup() { pin_mode(flags_); } +void PCF8574GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool PCF8574GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void PCF8574GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } -void PCF8574GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -PCF8574GPIOPin::PCF8574GPIOPin(PCF8574Component *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} +std::string PCF8574GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via PCF8574", pin_); + return buffer; +} } // namespace pcf8574 } // namespace esphome diff --git a/esphome/components/pcf8574/pcf8574.h b/esphome/components/pcf8574/pcf8574.h index 925fa30899..c201e0615f 100644 --- a/esphome/components/pcf8574/pcf8574.h +++ b/esphome/components/pcf8574/pcf8574.h @@ -1,18 +1,12 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace pcf8574 { -/// Modes for PCF8574 pins -enum PCF8574GPIOMode : uint8_t { - PCF8574_INPUT = INPUT, - PCF8574_OUTPUT = OUTPUT, -}; - class PCF8574Component : public Component, public i2c::I2CDevice { public: PCF8574Component() = default; @@ -26,7 +20,7 @@ class PCF8574Component : public Component, public i2c::I2CDevice { /// Helper function to write the value of a pin. void digital_write(uint8_t pin, bool value); /// Helper function to set the pin mode of a pin. - void pin_mode(uint8_t pin, uint8_t mode); + void pin_mode(uint8_t pin, gpio::Flags flags); float get_setup_priority() const override; @@ -49,15 +43,22 @@ class PCF8574Component : public Component, public i2c::I2CDevice { /// Helper class to expose a PCF8574 pin as an internal input GPIO pin. class PCF8574GPIOPin : public GPIOPin { public: - PCF8574GPIOPin(PCF8574Component *parent, uint8_t pin, uint8_t mode, bool inverted = false); - void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(PCF8574Component *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } protected: PCF8574Component *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; }; } // namespace pcf8574 diff --git a/esphome/components/pid/pid_autotuner.cpp b/esphome/components/pid/pid_autotuner.cpp index 83e4b6be32..15c1c5f076 100644 --- a/esphome/components/pid/pid_autotuner.cpp +++ b/esphome/components/pid/pid_autotuner.cpp @@ -72,7 +72,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce return res; } - if (!isnan(this->setpoint_) && this->setpoint_ != setpoint) { + if (!std::isnan(this->setpoint_) && this->setpoint_ != setpoint) { ESP_LOGW(TAG, "Setpoint changed during autotune! The result will not be accurate!"); } this->setpoint_ = setpoint; diff --git a/esphome/components/pid/pid_climate.cpp b/esphome/components/pid/pid_climate.cpp index 4c7d92e26d..f5c7792782 100644 --- a/esphome/components/pid/pid_climate.cpp +++ b/esphome/components/pid/pid_climate.cpp @@ -100,7 +100,7 @@ void PIDClimate::write_output_(float value) { } void PIDClimate::update_pid_() { float value; - if (isnan(this->current_temperature) || isnan(this->target_temperature)) { + if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature)) { // if any control parameters are nan, turn off all outputs value = 0.0; } else { diff --git a/esphome/components/pid/pid_controller.h b/esphome/components/pid/pid_controller.h index 4caad8dd8b..35e3eb9fc0 100644 --- a/esphome/components/pid/pid_controller.h +++ b/esphome/components/pid/pid_controller.h @@ -1,6 +1,6 @@ #pragma once -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace pid { @@ -23,9 +23,9 @@ struct PIDController { // i(t) := K_i * \int_{0}^{t} e(t) dt accumulated_integral_ += error * dt * ki; // constrain accumulated integral value - if (!isnan(min_integral) && accumulated_integral_ < min_integral) + if (!std::isnan(min_integral) && accumulated_integral_ < min_integral) accumulated_integral_ = min_integral; - if (!isnan(max_integral) && accumulated_integral_ > max_integral) + if (!std::isnan(max_integral) && accumulated_integral_ > max_integral) accumulated_integral_ = max_integral; integral_term = accumulated_integral_; diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 8603adfbf4..7dbbd798ad 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -840,7 +840,7 @@ void Pipsolar::send_next_poll_() { this->used_polling_commands_[this->last_polling_command_].length); } -void Pipsolar::queue_command_(const char *command, byte length) { +void Pipsolar::queue_command_(const char *command, uint8_t length) { uint8_t next_position = command_queue_position_; for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) { uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH; diff --git a/esphome/components/pipsolar/pipsolar.h b/esphome/components/pipsolar/pipsolar.h index 508036c7de..fe2a80d1d5 100644 --- a/esphome/components/pipsolar/pipsolar.h +++ b/esphome/components/pipsolar/pipsolar.h @@ -197,7 +197,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { uint16_t crc_xmodem_update_(uint16_t crc, uint8_t data); uint8_t send_next_command_(); void send_next_poll_(); - void queue_command_(const char *command, byte length); + void queue_command_(const char *command, uint8_t length); std::string command_queue_[COMMAND_QUEUE_LENGTH]; uint8_t command_queue_position_ = 0; uint8_t read_buffer_[PIPSOLAR_READ_BUFFER_LENGTH]; diff --git a/esphome/components/pmsa003i/pmsa003i.cpp b/esphome/components/pmsa003i/pmsa003i.cpp index 1396c9f3d4..ca3d28367a 100644 --- a/esphome/components/pmsa003i/pmsa003i.cpp +++ b/esphome/components/pmsa003i/pmsa003i.cpp @@ -1,5 +1,6 @@ #include "pmsa003i.h" #include "esphome/core/log.h" +#include namespace esphome { namespace pmsa003i { diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index 1c4160539a..ed2a2c1e35 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -2,6 +2,7 @@ #include #include "esphome/core/log.h" +#include "esphome/core/hal.h" // Based on: // - https://cdn-shop.adafruit.com/datasheets/PN532C106_Application+Note_v1.2.pdf diff --git a/esphome/components/pn532_i2c/pn532_i2c.cpp b/esphome/components/pn532_i2c/pn532_i2c.cpp index 25f24758bf..ef7480ec25 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.cpp +++ b/esphome/components/pn532_i2c/pn532_i2c.cpp @@ -1,5 +1,6 @@ #include "pn532_i2c.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" // Based on: // - https://cdn-shop.adafruit.com/datasheets/PN532C106_Application+Note_v1.2.pdf @@ -11,7 +12,7 @@ namespace pn532_i2c { static const char *const TAG = "pn532_i2c"; -bool PN532I2C::write_data(const std::vector &data) { return this->write_bytes_raw(data.data(), data.size()); } +bool PN532I2C::write_data(const std::vector &data) { return this->write(data.data(), data.size()); } bool PN532I2C::read_data(std::vector &data, uint8_t len) { delay(1); diff --git a/esphome/components/power_supply/power_supply.h b/esphome/components/power_supply/power_supply.h index 12d2a9984f..66e4a7565a 100644 --- a/esphome/components/power_supply/power_supply.h +++ b/esphome/components/power_supply/power_supply.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace power_supply { diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 06a0e39e2c..fa7b4fe132 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "prometheus_handler.h" #include "esphome/core/application.h" @@ -55,7 +57,7 @@ void PrometheusHandler::sensor_type_(AsyncResponseStream *stream) { void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj) { if (obj->is_internal()) return; - if (!isnan(obj->state)) { + if (!std::isnan(obj->state)) { // We have a valid value, output this value stream->print(F("esphome_sensor_failed{id=\"")); stream->print(obj->get_object_id().c_str()); @@ -249,7 +251,7 @@ void PrometheusHandler::cover_type_(AsyncResponseStream *stream) { void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *obj) { if (obj->is_internal()) return; - if (!isnan(obj->position)) { + if (!std::isnan(obj->position)) { // We have a valid value, output this value stream->print(F("esphome_cover_failed{id=\"")); stream->print(obj->get_object_id().c_str()); @@ -310,3 +312,5 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch } // namespace prometheus } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 6abd406556..5076883ba6 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/components/web_server_base/web_server_base.h" #include "esphome/core/controller.h" #include "esphome/core/component.h" @@ -79,3 +81,5 @@ class PrometheusHandler : public AsyncWebHandler, public Component { } // namespace prometheus } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 602c0bdd67..f538a4c905 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -8,15 +8,15 @@ static const char *const TAG = "pulse_counter"; const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"}; -#ifdef ARDUINO_ARCH_ESP8266 -void ICACHE_RAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { +#ifdef USE_ESP8266 +void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { const uint32_t now = micros(); const bool discard = now - arg->last_pulse < arg->filter_us; arg->last_pulse = now; if (discard) return; - PulseCounterCountMode mode = arg->isr_pin->digital_read() ? arg->rising_edge_mode : arg->falling_edge_mode; + PulseCounterCountMode mode = arg->isr_pin.digital_read() ? arg->rising_edge_mode : arg->falling_edge_mode; switch (mode) { case PULSE_COUNTER_DISABLE: break; @@ -28,11 +28,11 @@ void ICACHE_RAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { break; } } -bool PulseCounterStorage::pulse_counter_setup(GPIOPin *pin) { +bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { this->pin = pin; this->pin->setup(); this->isr_pin = this->pin->to_isr(); - this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, CHANGE); + this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); return true; } pulse_counter_t PulseCounterStorage::read_raw_value() { @@ -43,8 +43,8 @@ pulse_counter_t PulseCounterStorage::read_raw_value() { } #endif -#ifdef ARDUINO_ARCH_ESP32 -bool PulseCounterStorage::pulse_counter_setup(GPIOPin *pin) { +#ifdef USE_ESP32 +bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; this->pin = pin; this->pin->setup(); diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index 3203ab81fd..94e37bc232 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -1,10 +1,10 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif @@ -17,30 +17,30 @@ enum PulseCounterCountMode { PULSE_COUNTER_DECREMENT, }; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 using pulse_counter_t = int16_t; #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 using pulse_counter_t = int32_t; #endif struct PulseCounterStorage { - bool pulse_counter_setup(GPIOPin *pin); + bool pulse_counter_setup(InternalGPIOPin *pin); pulse_counter_t read_raw_value(); static void gpio_intr(PulseCounterStorage *arg); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 volatile pulse_counter_t counter{0}; volatile uint32_t last_pulse{0}; #endif - GPIOPin *pin; -#ifdef ARDUINO_ARCH_ESP32 + InternalGPIOPin *pin; +#ifdef USE_ESP32 pcnt_unit_t pcnt_unit; #endif -#ifdef ARDUINO_ARCH_ESP8266 - ISRInternalGPIOPin *isr_pin; +#ifdef USE_ESP8266 + ISRInternalGPIOPin isr_pin; #endif PulseCounterCountMode rising_edge_mode{PULSE_COUNTER_INCREMENT}; PulseCounterCountMode falling_edge_mode{PULSE_COUNTER_DISABLE}; @@ -50,7 +50,7 @@ struct PulseCounterStorage { class PulseCounterSensor : public sensor::Sensor, public PollingComponent { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_rising_edge_mode(PulseCounterCountMode mode) { storage_.rising_edge_mode = mode; } void set_falling_edge_mode(PulseCounterCountMode mode) { storage_.falling_edge_mode = mode; } void set_filter_us(uint32_t filter) { storage_.filter_us = filter; } @@ -63,7 +63,7 @@ class PulseCounterSensor : public sensor::Sensor, public PollingComponent { void dump_config() override; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; PulseCounterStorage storage_; uint32_t current_total_ = 0; sensor::Sensor *total_sensor_; diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.cpp b/esphome/components/pulse_meter/pulse_meter_sensor.cpp index 1a35deba2f..fd1403b4fd 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.cpp +++ b/esphome/components/pulse_meter/pulse_meter_sensor.cpp @@ -9,7 +9,7 @@ static const char *const TAG = "pulse_meter"; void PulseMeterSensor::setup() { this->pin_->setup(); this->isr_pin_ = pin_->to_isr(); - this->pin_->attach_interrupt(PulseMeterSensor::gpio_intr, this, CHANGE); + this->pin_->attach_interrupt(PulseMeterSensor::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); this->last_detected_edge_us_ = 0; this->last_valid_edge_us_ = 0; @@ -56,14 +56,14 @@ void PulseMeterSensor::dump_config() { ESP_LOGCONFIG(TAG, " Assuming 0 pulses/min after not receiving a pulse for %us", this->timeout_us_ / 1000000); } -void ICACHE_RAM_ATTR PulseMeterSensor::gpio_intr(PulseMeterSensor *sensor) { +void IRAM_ATTR PulseMeterSensor::gpio_intr(PulseMeterSensor *sensor) { // This is an interrupt handler - we can't call any virtual method from this method // Get the current time before we do anything else so the measurements are consistent const uint32_t now = micros(); // We only look at rising edges - if (!sensor->isr_pin_->digital_read()) { + if (!sensor->isr_pin_.digital_read()) { return; } diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.h b/esphome/components/pulse_meter/pulse_meter_sensor.h index d02cff6123..1cebc1748e 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.h +++ b/esphome/components/pulse_meter/pulse_meter_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/core/helpers.h" @@ -10,7 +10,7 @@ namespace pulse_meter { class PulseMeterSensor : public sensor::Sensor, public Component { public: - void set_pin(GPIOPin *pin) { this->pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } void set_filter_us(uint32_t filter) { this->filter_us_ = filter; } void set_timeout_us(uint32_t timeout) { this->timeout_us_ = timeout; } void set_total_sensor(sensor::Sensor *sensor) { this->total_sensor_ = sensor; } @@ -25,8 +25,8 @@ class PulseMeterSensor : public sensor::Sensor, public Component { protected: static void gpio_intr(PulseMeterSensor *sensor); - GPIOPin *pin_ = nullptr; - ISRInternalGPIOPin *isr_pin_; + InternalGPIOPin *pin_ = nullptr; + ISRInternalGPIOPin isr_pin_; uint32_t filter_us_ = 0; uint32_t timeout_us_ = 1000000UL * 60UL * 5UL; sensor::Sensor *total_sensor_ = nullptr; diff --git a/esphome/components/pulse_width/pulse_width.cpp b/esphome/components/pulse_width/pulse_width.cpp index fb998ef4e1..8d66861049 100644 --- a/esphome/components/pulse_width/pulse_width.cpp +++ b/esphome/components/pulse_width/pulse_width.cpp @@ -6,8 +6,8 @@ namespace pulse_width { static const char *const TAG = "pulse_width"; -void ICACHE_RAM_ATTR PulseWidthSensorStore::gpio_intr(PulseWidthSensorStore *arg) { - const bool new_level = arg->pin_->digital_read(); +void IRAM_ATTR PulseWidthSensorStore::gpio_intr(PulseWidthSensorStore *arg) { + const bool new_level = arg->pin_.digital_read(); const uint32_t now = micros(); if (new_level) { arg->last_rise_ = now; diff --git a/esphome/components/pulse_width/pulse_width.h b/esphome/components/pulse_width/pulse_width.h index 9d32ce99b1..822688ec88 100644 --- a/esphome/components/pulse_width/pulse_width.h +++ b/esphome/components/pulse_width/pulse_width.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -10,11 +10,11 @@ namespace pulse_width { /// Store data in a class that doesn't use multiple-inheritance (vtables in flash) class PulseWidthSensorStore { public: - void setup(GPIOPin *pin) { + void setup(InternalGPIOPin *pin) { pin->setup(); this->pin_ = pin->to_isr(); this->last_rise_ = micros(); - pin->attach_interrupt(&PulseWidthSensorStore::gpio_intr, this, CHANGE); + pin->attach_interrupt(&PulseWidthSensorStore::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); } static void gpio_intr(PulseWidthSensorStore *arg); uint32_t get_pulse_width_us() const { return this->last_width_; } @@ -22,14 +22,14 @@ class PulseWidthSensorStore { uint32_t get_last_rise() const { return last_rise_; } protected: - ISRInternalGPIOPin *pin_; + ISRInternalGPIOPin pin_; volatile uint32_t last_width_{0}; volatile uint32_t last_rise_{0}; }; class PulseWidthSensor : public sensor::Sensor, public PollingComponent { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void setup() override { this->store_.setup(this->pin_); } void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } @@ -37,7 +37,7 @@ class PulseWidthSensor : public sensor::Sensor, public PollingComponent { protected: PulseWidthSensorStore store_; - GPIOPin *pin_; + InternalGPIOPin *pin_; }; } // namespace pulse_width diff --git a/esphome/components/pulse_width/sensor.py b/esphome/components/pulse_width/sensor.py index 6c91104036..b090647627 100644 --- a/esphome/components/pulse_width/sensor.py +++ b/esphome/components/pulse_width/sensor.py @@ -26,9 +26,7 @@ CONFIG_SCHEMA = ( .extend( { cv.GenerateID(): cv.declare_id(PulseWidthSensor), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), } ) .extend(cv.polling_component_schema("60s")) diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp index 9edcd9166c..ff9723ab2f 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp @@ -1,7 +1,7 @@ #include "pvvx_mithermometer.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace pvvx_mithermometer { diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h index 4132954983..bb67769d4f 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace pvvx_mithermometer { diff --git a/esphome/components/qmc5883l/qmc5883l.cpp b/esphome/components/qmc5883l/qmc5883l.cpp index 9e80cdbd88..f03b6af191 100644 --- a/esphome/components/qmc5883l/qmc5883l.cpp +++ b/esphome/components/qmc5883l/qmc5883l.cpp @@ -1,5 +1,7 @@ #include "qmc5883l.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include namespace esphome { namespace qmc5883l { @@ -115,9 +117,10 @@ void QMC5883LComponent::update() { } bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) { - bool success = this->read_byte_16(a_register, data); + if (!this->read_byte_16(a_register, data)) + return false; *data = (*data & 0x00FF) << 8 | (*data & 0xFF00) >> 8; // Flip Byte order, LSB first; - return success; + return true; } } // namespace qmc5883l diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp index 6261f63132..385641fea0 100644 --- a/esphome/components/rc522/rc522.cpp +++ b/esphome/components/rc522/rc522.cpp @@ -43,14 +43,14 @@ void RC522::setup() { // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode. if (reset_pin_ != nullptr) { - reset_pin_->pin_mode(INPUT); + reset_pin_->pin_mode(gpio::FLAG_INPUT); - if (reset_pin_->digital_read() == LOW) { // The MFRC522 chip is in power down mode. + if (!reset_pin_->digital_read()) { // The MFRC522 chip is in power down mode. ESP_LOGV(TAG, "Power down mode detected. Hard resetting..."); - reset_pin_->pin_mode(OUTPUT); // Now set the resetPowerDownPin as digital output. - reset_pin_->digital_write(LOW); // Make sure we have a clean LOW state. + reset_pin_->pin_mode(gpio::FLAG_OUTPUT); // Now set the resetPowerDownPin as digital output. + reset_pin_->digital_write(false); // Make sure we have a clean LOW state. delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl - reset_pin_->digital_write(HIGH); // Exit power down mode. This triggers a hard reset. + reset_pin_->digital_write(true); // Exit power down mode. This triggers a hard reset. // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. // Let us be generous: 50ms. reset_timeout_ = millis(); diff --git a/esphome/components/rc522/rc522.h b/esphome/components/rc522/rc522.h index e4e3387ff6..d853d2f5ff 100644 --- a/esphome/components/rc522/rc522.h +++ b/esphome/components/rc522/rc522.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" diff --git a/esphome/components/rc522_i2c/rc522_i2c.cpp b/esphome/components/rc522_i2c/rc522_i2c.cpp index 896e27214a..6a3d8d2486 100644 --- a/esphome/components/rc522_i2c/rc522_i2c.cpp +++ b/esphome/components/rc522_i2c/rc522_i2c.cpp @@ -18,8 +18,9 @@ void RC522I2C::dump_config() { uint8_t RC522I2C::pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. ) { uint8_t value; - read_byte(reg >> 1, &value); - ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value); + if (!read_byte(reg >> 1, &value)) + return 0; + ESP_LOGVV(TAG, "read_register_(%x) -> %u", reg, value); return value; } diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 537ae2283c..d2b848600d 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1092,15 +1092,13 @@ MIDEA_SCHEMA = cv.Schema( [cv.Any(cv.hex_uint8_t, cv.uint8_t)], cv.Length(min=5, max=5), ), - cv.GenerateID(CONF_CODE_STORAGE_ID): cv.declare_id(cg.uint8), } ) @register_binary_sensor("midea", MideaBinarySensor, MIDEA_SCHEMA) def midea_binary_sensor(var, config): - arr_ = cg.progmem_array(config[CONF_CODE_STORAGE_ID], config[CONF_CODE]) - cg.add(var.set_code(arr_)) + cg.add(var.set_code(config[CONF_CODE])) @register_trigger("midea", MideaTrigger, MideaData) @@ -1119,5 +1117,4 @@ def midea_dumper(var, config): MIDEA_SCHEMA, ) async def midea_action(var, config, args): - arr_ = cg.progmem_array(config[CONF_CODE_STORAGE_ID], config[CONF_CODE]) - cg.add(var.set_code(arr_)) + cg.add(var.set_code(config[CONF_CODE])) diff --git a/esphome/components/remote_base/midea_protocol.h b/esphome/components/remote_base/midea_protocol.h index 9b0d156617..12916bd44d 100644 --- a/esphome/components/remote_base/midea_protocol.h +++ b/esphome/components/remote_base/midea_protocol.h @@ -17,8 +17,6 @@ class MideaData { MideaData(const std::vector &data) { memcpy(this->data_, data.data(), std::min(data.size(), sizeof(this->data_))); } - // Make 40-bit copy from PROGMEM array - MideaData(const uint8_t *data) { memcpy_P(this->data_, data, OFFSET_CS); } // Default copy constructor MideaData(const MideaData &) = default; @@ -83,7 +81,7 @@ class MideaBinarySensor : public RemoteReceiverBinarySensorBase { auto data = MideaProtocol().decode(src); return data.has_value() && data.value() == this->data_; } - void set_code(const uint8_t *code) { this->data_ = code; } + void set_code(const std::vector &code) { this->data_ = code; } protected: MideaData data_; @@ -93,7 +91,8 @@ using MideaTrigger = RemoteReceiverTrigger; using MideaDumper = RemoteReceiverDumper; template class MideaAction : public RemoteTransmitterActionBase { - TEMPLATABLE_VALUE(const uint8_t *, code) + TEMPLATABLE_VALUE(std::vector, code) + void set_code(std::vector code) { code_ = code; } void encode(RemoteTransmitData *dst, Ts... x) override { MideaData data = this->code_.value(x...); data.finalize(); diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index d36c0d7ebe..a43f743ab6 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -6,7 +6,7 @@ namespace remote_base { static const char *const TAG = "remote_base"; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_block_num) { static rmt_channel_t next_rmt_channel = RMT_CHANNEL_0; this->channel_ = next_rmt_channel; diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 16c732df83..68cf67d175 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -3,11 +3,11 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif @@ -146,13 +146,13 @@ template class RemoteProtocol { class RemoteComponentBase { public: - explicit RemoteComponentBase(GPIOPin *pin) : pin_(pin){}; + explicit RemoteComponentBase(InternalGPIOPin *pin) : pin_(pin){}; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; }; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 class RemoteRMTChannel { public: explicit RemoteRMTChannel(uint8_t mem_block_num = 1); @@ -178,7 +178,7 @@ class RemoteRMTChannel { class RemoteTransmitterBase : public RemoteComponentBase { public: - RemoteTransmitterBase(GPIOPin *pin) : RemoteComponentBase(pin) {} + RemoteTransmitterBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {} class TransmitCall { public: explicit TransmitCall(RemoteTransmitterBase *parent) : parent_(parent) {} @@ -221,7 +221,7 @@ class RemoteReceiverDumperBase { class RemoteReceiverBase : public RemoteComponentBase { public: - RemoteReceiverBase(GPIOPin *pin) : RemoteComponentBase(pin) {} + RemoteReceiverBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {} void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); } void register_dumper(RemoteReceiverDumperBase *dumper) { if (dumper->is_secondary()) { diff --git a/esphome/components/remote_base/samsung_protocol.cpp b/esphome/components/remote_base/samsung_protocol.cpp index 0f2605d865..4571f332b3 100644 --- a/esphome/components/remote_base/samsung_protocol.cpp +++ b/esphome/components/remote_base/samsung_protocol.cpp @@ -1,5 +1,6 @@ #include "samsung_protocol.h" #include "esphome/core/log.h" +#include namespace esphome { namespace remote_base { diff --git a/esphome/components/remote_base/toshiba_ac_protocol.cpp b/esphome/components/remote_base/toshiba_ac_protocol.cpp index 27d042ace0..bd1d2a8f5b 100644 --- a/esphome/components/remote_base/toshiba_ac_protocol.cpp +++ b/esphome/components/remote_base/toshiba_ac_protocol.cpp @@ -1,5 +1,6 @@ #include "toshiba_ac_protocol.h" #include "esphome/core/log.h" +#include namespace esphome { namespace remote_base { diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 7158368ed8..253204bd1a 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -25,9 +25,7 @@ CONFIG_SCHEMA = remote_base.validate_triggers( cv.Schema( { cv.GenerateID(): cv.declare_id(RemoteReceiverComponent), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers, cv.Optional(CONF_TOLERANCE, default=25): cv.All( cv.percentage_int, cv.Range(min=0) diff --git a/esphome/components/remote_receiver/remote_receiver.h b/esphome/components/remote_receiver/remote_receiver.h index 25262e3366..50153c105d 100644 --- a/esphome/components/remote_receiver/remote_receiver.h +++ b/esphome/components/remote_receiver/remote_receiver.h @@ -6,7 +6,7 @@ namespace esphome { namespace remote_receiver { -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 struct RemoteReceiverComponentStore { static void gpio_intr(RemoteReceiverComponentStore *arg); @@ -21,23 +21,23 @@ struct RemoteReceiverComponentStore { bool overflow{false}; uint32_t buffer_size{1000}; uint8_t filter_us{10}; - ISRInternalGPIOPin *pin; + ISRInternalGPIOPin pin; }; #endif class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, public Component -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 , public remote_base::RemoteRMTChannel #endif { public: -#ifdef ARDUINO_ARCH_ESP32 - RemoteReceiverComponent(GPIOPin *pin, uint8_t mem_block_num = 1) +#ifdef USE_ESP32 + RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} #else - RemoteReceiverComponent(GPIOPin *pin) : RemoteReceiverBase(pin) {} + RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {} #endif void setup() override; void dump_config() override; @@ -49,13 +49,13 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, void set_idle_us(uint32_t idle_us) { this->idle_us_ = idle_us; } protected: -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void decode_rmt_(rmt_item32_t *item, size_t len); RingbufHandle_t ringbuf_; esp_err_t error_code_{ESP_OK}; #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 RemoteReceiverComponentStore store_; HighFrequencyLoopRequester high_freq_; #endif diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index b2ddc69b1c..bec2af6718 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -1,7 +1,7 @@ #include "remote_receiver.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include namespace esphome { diff --git a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp index d509eea925..cf2c15402e 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp @@ -1,20 +1,20 @@ #include "remote_receiver.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 namespace esphome { namespace remote_receiver { static const char *const TAG = "remote_receiver.esp8266"; -void ICACHE_RAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverComponentStore *arg) { +void IRAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverComponentStore *arg) { const uint32_t now = micros(); // If the lhs is 1 (rising edge) we should write to an uneven index and vice versa const uint32_t next = (arg->buffer_write_at + 1) % arg->buffer_size; - const bool level = arg->pin->digital_read(); + const bool level = arg->pin.digital_read(); if (level != next % 2) return; @@ -54,7 +54,7 @@ void RemoteReceiverComponent::setup() { } else { s.buffer_write_at = s.buffer_read_at = 0; } - this->pin_->attach_interrupt(RemoteReceiverComponentStore::gpio_intr, &this->store_, CHANGE); + this->pin_->attach_interrupt(RemoteReceiverComponentStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void RemoteReceiverComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Receiver:"); diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 853b5b6289..d05942de3b 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -8,13 +8,13 @@ namespace remote_transmitter { class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, public Component -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 , public remote_base::RemoteRMTChannel #endif { public: - explicit RemoteTransmitterComponent(GPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} + explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} void setup() override; @@ -26,7 +26,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, protected: void send_internal(uint32_t send_times, uint32_t send_wait) override; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 void calculate_on_off_time_(uint32_t carrier_frequency, uint32_t *on_time_period, uint32_t *off_time_period); void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec); @@ -34,7 +34,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, void space_(uint32_t usec); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void configure_rmt(); uint32_t current_carrier_frequency_{UINT32_MAX}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index 90166d2741..ff53d0be84 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace remote_transmitter { diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index f8735fe763..33c01985d7 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 namespace esphome { namespace remote_transmitter { diff --git a/esphome/components/resistance/resistance_sensor.cpp b/esphome/components/resistance/resistance_sensor.cpp index 1380354a5f..4d3dfa5928 100644 --- a/esphome/components/resistance/resistance_sensor.cpp +++ b/esphome/components/resistance/resistance_sensor.cpp @@ -13,7 +13,7 @@ void ResistanceSensor::dump_config() { ESP_LOGCONFIG(TAG, " Reference Voltage: %.1fV", this->reference_voltage_); } void ResistanceSensor::process_(float value) { - if (isnan(value)) { + if (std::isnan(value)) { this->publish_state(NAN); return; } diff --git a/esphome/components/restart/restart_switch.cpp b/esphome/components/restart/restart_switch.cpp index ea46c5f910..3076fde99e 100644 --- a/esphome/components/restart/restart_switch.cpp +++ b/esphome/components/restart/restart_switch.cpp @@ -1,4 +1,5 @@ #include "restart_switch.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/application.h" diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index 8ef6f932c5..7c95fac98e 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -82,12 +82,12 @@ static const uint16_t DRAM_ATTR STATE_LOOKUP_TABLE[32] = { STATE_CW | STATE_S3 // 0x1F: stay here }; -void ICACHE_RAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore *arg) { +void IRAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore *arg) { // Forget upper bits and add pin states uint8_t input_state = arg->state & STATE_LUT_MASK; - if (arg->pin_a->digital_read()) + if (arg->pin_a.digital_read()) input_state |= STATE_PIN_A_HIGH; - if (arg->pin_b->digital_read()) + if (arg->pin_b.digital_read()) input_state |= STATE_PIN_B_HIGH; int8_t rotation_dir = 0; @@ -134,8 +134,8 @@ void RotaryEncoderSensor::setup() { this->pin_i_->setup(); } - this->pin_a_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, CHANGE); - this->pin_b_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, CHANGE); + this->pin_a_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); + this->pin_b_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void RotaryEncoderSensor::dump_config() { LOG_SENSOR("", "Rotary Encoder", this); @@ -157,13 +157,14 @@ void RotaryEncoderSensor::dump_config() { void RotaryEncoderSensor::loop() { std::array rotation_events; bool rotation_events_overflow; - ets_intr_lock(); - rotation_events = this->store_.rotation_events; - rotation_events_overflow = this->store_.rotation_events_overflow; + { + InterruptLock lock; + rotation_events = this->store_.rotation_events; + rotation_events_overflow = this->store_.rotation_events_overflow; - this->store_.rotation_events.fill(0); - this->store_.rotation_events_overflow = false; - ets_intr_unlock(); + this->store_.rotation_events.fill(0); + this->store_.rotation_events_overflow = false; + } if (rotation_events_overflow) { ESP_LOGW(TAG, "Captured more rotation events than expected"); diff --git a/esphome/components/rotary_encoder/rotary_encoder.h b/esphome/components/rotary_encoder/rotary_encoder.h index 000350d66c..4825e472a1 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.h +++ b/esphome/components/rotary_encoder/rotary_encoder.h @@ -3,7 +3,7 @@ #include #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/sensor/sensor.h" @@ -19,8 +19,8 @@ enum RotaryEncoderResolution { }; struct RotaryEncoderSensorStore { - ISRInternalGPIOPin *pin_a; - ISRInternalGPIOPin *pin_b; + ISRInternalGPIOPin pin_a; + ISRInternalGPIOPin pin_b; volatile int32_t counter{0}; RotaryEncoderResolution resolution{ROTARY_ENCODER_1_PULSE_PER_CYCLE}; @@ -37,8 +37,8 @@ struct RotaryEncoderSensorStore { class RotaryEncoderSensor : public sensor::Sensor, public Component { public: - void set_pin_a(GPIOPin *pin_a) { pin_a_ = pin_a; } - void set_pin_b(GPIOPin *pin_b) { pin_b_ = pin_b; } + void set_pin_a(InternalGPIOPin *pin_a) { pin_a_ = pin_a; } + void set_pin_b(InternalGPIOPin *pin_b) { pin_b_ = pin_b; } /** Set the resolution of the rotary encoder. * @@ -76,8 +76,8 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component { } protected: - GPIOPin *pin_a_; - GPIOPin *pin_b_; + InternalGPIOPin *pin_a_; + InternalGPIOPin *pin_b_; GPIOPin *pin_i_{nullptr}; /// Index pin, if this is not nullptr, the counter will reset to 0 once this pin is HIGH. RotaryEncoderSensorStore store_{}; diff --git a/esphome/components/rotary_encoder/sensor.py b/esphome/components/rotary_encoder/sensor.py index e82b9d5f13..ef1110c6d8 100644 --- a/esphome/components/rotary_encoder/sensor.py +++ b/esphome/components/rotary_encoder/sensor.py @@ -64,12 +64,8 @@ CONFIG_SCHEMA = cv.All( .extend( { cv.GenerateID(): cv.declare_id(RotaryEncoderSensor), - cv.Required(CONF_PIN_A): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), - cv.Required(CONF_PIN_B): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN_A): cv.All(pins.internal_gpio_input_pin_schema), + cv.Required(CONF_PIN_B): cv.All(pins.internal_gpio_input_pin_schema), cv.Optional(CONF_PIN_RESET): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_RESOLUTION, default=1): cv.enum(RESOLUTIONS, int=True), cv.Optional(CONF_MIN_VALUE): cv.int_, diff --git a/esphome/components/ruuvi_ble/ruuvi_ble.cpp b/esphome/components/ruuvi_ble/ruuvi_ble.cpp index e3e9f42305..bdd012cf5c 100644 --- a/esphome/components/ruuvi_ble/ruuvi_ble.cpp +++ b/esphome/components/ruuvi_ble/ruuvi_ble.cpp @@ -1,7 +1,7 @@ #include "ruuvi_ble.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvi_ble { diff --git a/esphome/components/ruuvi_ble/ruuvi_ble.h b/esphome/components/ruuvi_ble/ruuvi_ble.h index 848004f3d7..add431ce42 100644 --- a/esphome/components/ruuvi_ble/ruuvi_ble.h +++ b/esphome/components/ruuvi_ble/ruuvi_ble.h @@ -3,7 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvi_ble { diff --git a/esphome/components/ruuvitag/ruuvitag.cpp b/esphome/components/ruuvitag/ruuvitag.cpp index f4e4a72270..9b462b4794 100644 --- a/esphome/components/ruuvitag/ruuvitag.cpp +++ b/esphome/components/ruuvitag/ruuvitag.cpp @@ -1,7 +1,7 @@ #include "ruuvitag.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvitag { diff --git a/esphome/components/ruuvitag/ruuvitag.h b/esphome/components/ruuvitag/ruuvitag.h index 863c5775c2..63029ebb4d 100644 --- a/esphome/components/ruuvitag/ruuvitag.h +++ b/esphome/components/ruuvitag/ruuvitag.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/ruuvi_ble/ruuvi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvitag { diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index 8dfd992abe..30775fdea4 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -1,5 +1,10 @@ #include "scd30.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" + +#ifdef USE_ESP8266 +#include +#endif namespace esphome { namespace scd30 { @@ -23,7 +28,7 @@ static const uint16_t SCD30_CMD_SOFT_RESET = 0xD304; void SCD30Component::setup() { ESP_LOGCONFIG(TAG, "Setting up scd30..."); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 Wire.setClockStretchLimit(150000); #endif @@ -51,7 +56,7 @@ void SCD30Component::setup() { return; } } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 // According ESP32 clock stretching is typically 30ms and up to 150ms "due to // internal calibration processes". The I2C peripheral only supports 13ms (at // least when running at 80MHz). @@ -73,7 +78,7 @@ void SCD30Component::setup() { return; } } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 delay(30); #endif @@ -83,7 +88,7 @@ void SCD30Component::setup() { this->mark_failed(); return; } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 delay(30); #endif @@ -193,7 +198,7 @@ bool SCD30Component::write_command_(uint16_t command, uint16_t data) { raw[2] = data >> 8; raw[3] = data & 0xFF; raw[4] = sht_crc_(raw[2], raw[3]); - return this->write_bytes_raw(raw, 5); + return this->write(raw, 5); } uint8_t SCD30Component::sht_crc_(uint8_t data1, uint8_t data2) { @@ -223,7 +228,7 @@ bool SCD30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sdp3x/sdp3x.cpp b/esphome/components/sdp3x/sdp3x.cpp index 5e6c5887ac..ba7a028f8e 100644 --- a/esphome/components/sdp3x/sdp3x.cpp +++ b/esphome/components/sdp3x/sdp3x.cpp @@ -17,29 +17,29 @@ void SDP3XComponent::update() { this->read_pressure_(); } void SDP3XComponent::setup() { ESP_LOGD(TAG, "Setting up SDP3X..."); - if (!this->write_bytes_raw(SDP3X_STOP_MEAS, 2)) { + if (this->write(SDP3X_STOP_MEAS, 2) != i2c::ERROR_OK) { ESP_LOGW(TAG, "Stop SDP3X failed!"); // This sometimes fails for no good reason } - if (!this->write_bytes_raw(SDP3X_SOFT_RESET, 2)) { + if (this->write(SDP3X_SOFT_RESET, 2) != i2c::ERROR_OK) { ESP_LOGW(TAG, "Soft Reset SDP3X failed!"); // This sometimes fails for no good reason } delay_microseconds_accurate(20000); - if (!this->write_bytes_raw(SDP3X_READ_ID1, 2)) { + if (this->write(SDP3X_READ_ID1, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Read ID1 SDP3X failed!"); this->mark_failed(); return; } - if (!this->write_bytes_raw(SDP3X_READ_ID2, 2)) { + if (this->write(SDP3X_READ_ID2, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Read ID2 SDP3X failed!"); this->mark_failed(); return; } uint8_t data[18]; - if (!this->read_bytes_raw(data, 18)) { + if (this->read(data, 18) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Read ID SDP3X failed!"); this->mark_failed(); return; @@ -59,7 +59,7 @@ void SDP3XComponent::setup() { pressure_scale_factor_ = 240.0f * 100.0f; } - if (!this->write_bytes_raw(SDP3X_START_DP_AVG, 2)) { + if (this->write(SDP3X_START_DP_AVG, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Start Measurements SDP3X failed!"); this->mark_failed(); return; @@ -77,7 +77,7 @@ void SDP3XComponent::dump_config() { void SDP3XComponent::read_pressure_() { uint8_t data[9]; - if (!this->read_bytes_raw(data, 9)) { + if (this->read(data, 9) != i2c::ERROR_OK) { ESP_LOGW(TAG, "Couldn't read SDP3X data!"); this->status_set_warning(); return; diff --git a/esphome/components/sensor/automation.h b/esphome/components/sensor/automation.h index c70fb93963..8cd0adbeb2 100644 --- a/esphome/components/sensor/automation.h +++ b/esphome/components/sensor/automation.h @@ -40,7 +40,7 @@ class ValueRangeTrigger : public Trigger, public Component { template void set_max(V max) { this->max_ = max; } void setup() override { - this->rtc_ = global_preferences.make_preference(this->parent_->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->parent_->get_object_id_hash()); bool initial_state; if (this->rtc_.load(&initial_state)) { this->previous_in_range_ = initial_state; @@ -52,18 +52,18 @@ class ValueRangeTrigger : public Trigger, public Component { protected: void on_state_(float state) { - if (isnan(state)) + if (std::isnan(state)) return; float local_min = this->min_.value(state); float local_max = this->max_.value(state); bool in_range; - if (isnan(local_min) && isnan(local_max)) { + if (std::isnan(local_min) && std::isnan(local_max)) { in_range = this->previous_in_range_; - } else if (isnan(local_min)) { + } else if (std::isnan(local_min)) { in_range = state <= local_max; - } else if (isnan(local_max)) { + } else if (std::isnan(local_max)) { in_range = state >= local_min; } else { in_range = local_min <= state && state <= local_max; @@ -92,9 +92,9 @@ template class SensorInRangeCondition : public Condition void set_max(float max) { this->max_ = max; } bool check(Ts... x) override { const float state = this->parent_->state; - if (isnan(this->min_)) { + if (std::isnan(this->min_)) { return state <= this->max_; - } else if (isnan(this->max_)) { + } else if (std::isnan(this->max_)) { return state >= this->min_; } else { return this->min_ <= state && state <= this->max_; diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index f048189959..63801e7996 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -1,6 +1,7 @@ #include "filter.h" #include "sensor.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace sensor { @@ -35,7 +36,7 @@ MedianFilter::MedianFilter(size_t window_size, size_t send_every, size_t send_fi void MedianFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void MedianFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional MedianFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { while (this->queue_.size() >= this->window_size_) { this->queue_.pop_front(); } @@ -71,7 +72,7 @@ MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at void MinFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void MinFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional MinFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { while (this->queue_.size() >= this->window_size_) { this->queue_.pop_front(); } @@ -100,7 +101,7 @@ MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at void MaxFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void MaxFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional MaxFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { while (this->queue_.size() >= this->window_size_) { this->queue_.pop_front(); } @@ -130,7 +131,7 @@ SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window void SlidingWindowMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void SlidingWindowMovingAverageFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional SlidingWindowMovingAverageFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { if (this->queue_.size() == this->window_size_) { this->sum_ -= this->queue_[0]; this->queue_.pop_front(); @@ -165,7 +166,7 @@ optional SlidingWindowMovingAverageFilter::new_value(float value) { ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every) : send_every_(send_every), send_at_(send_every - 1), alpha_(alpha) {} optional ExponentialMovingAverageFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { if (this->first_value_) this->accumulator_ = value; else @@ -211,8 +212,8 @@ optional MultiplyFilter::new_value(float value) { return value * this->mu FilterOutValueFilter::FilterOutValueFilter(float value_to_filter_out) : value_to_filter_out_(value_to_filter_out) {} optional FilterOutValueFilter::new_value(float value) { - if (isnan(this->value_to_filter_out_)) { - if (isnan(value)) + if (std::isnan(this->value_to_filter_out_)) { + if (std::isnan(value)) return {}; else return value; @@ -243,9 +244,9 @@ optional ThrottleFilter::new_value(float value) { // DeltaFilter DeltaFilter::DeltaFilter(float min_delta) : min_delta_(min_delta), last_value_(NAN) {} optional DeltaFilter::new_value(float value) { - if (isnan(value)) + if (std::isnan(value)) return {}; - if (isnan(this->last_value_)) { + if (std::isnan(this->last_value_)) { return this->last_value_ = value; } if (fabsf(value - this->last_value_) >= this->min_delta_) { diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index 0b018ddb2e..2e1ba587a2 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -1,5 +1,6 @@ #include "servo.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace servo { diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index d95a524a8b..e2e3823158 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -24,7 +24,7 @@ class Servo : public Component { void setup() override { float v; if (this->restore_) { - this->rtc_ = global_preferences.make_preference(global_servo_id); + this->rtc_ = global_preferences->make_preference(global_servo_id); global_servo_id++; if (this->rtc_.load(&v)) { this->output_->set_level(v); diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 5a6f438429..1a64a12907 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -1,6 +1,7 @@ #include "sgp30.h" #include "esphome/core/log.h" #include "esphome/core/application.h" +#include namespace esphome { namespace sgp30 { @@ -84,7 +85,7 @@ void SGP30Component::setup() { // Hash with compilation time // This ensures the baseline storage is cleared after OTA uint32_t hash = fnv1_hash(App.get_compilation_time()); - this->pref_ = global_preferences.make_preference(hash, true); + this->pref_ = global_preferences->make_preference(hash, true); if (this->pref_.load(&this->baselines_storage_)) { ESP_LOGI(TAG, "Loaded eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, @@ -172,7 +173,7 @@ void SGP30Component::send_env_data_() { float humidity = NAN; if (this->humidity_sensor_ != nullptr) humidity = this->humidity_sensor_->state; - if (isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { + if (std::isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { ESP_LOGW(TAG, "Compensation not possible yet: bad humidity data."); return; } else { @@ -182,7 +183,7 @@ void SGP30Component::send_env_data_() { if (this->temperature_sensor_ != nullptr) { temperature = float(this->temperature_sensor_->state); } - if (isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { + if (std::isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { ESP_LOGW(TAG, "Compensation not possible yet: bad temperature value data."); return; } else { @@ -334,7 +335,7 @@ bool SGP30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sgp40/sgp40.cpp b/esphome/components/sgp40/sgp40.cpp index 1a1909eeb0..fddd0255b8 100644 --- a/esphome/components/sgp40/sgp40.cpp +++ b/esphome/components/sgp40/sgp40.cpp @@ -1,5 +1,6 @@ -#include "esphome/core/log.h" #include "sgp40.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" #include namespace esphome { @@ -55,7 +56,7 @@ void SGP40Component::setup() { // Hash with compilation time // This ensures the baseline storage is cleared after OTA uint32_t hash = fnv1_hash(App.get_compilation_time()); - this->pref_ = global_preferences.make_preference(hash, true); + this->pref_ = global_preferences->make_preference(hash, true); if (this->pref_.load(&this->baselines_storage_)) { this->state0_ = this->baselines_storage_.state0; @@ -165,7 +166,7 @@ uint16_t SGP40Component::measure_raw_() { if (this->humidity_sensor_ != nullptr) { humidity = this->humidity_sensor_->state; } - if (isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { + if (std::isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { humidity = 50; } @@ -173,7 +174,7 @@ uint16_t SGP40Component::measure_raw_() { if (this->temperature_sensor_ != nullptr) { temperature = float(this->temperature_sensor_->state); } - if (isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { + if (std::isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { temperature = 25; } @@ -191,9 +192,9 @@ uint16_t SGP40Component::measure_raw_() { command[6] = tempticks & 0xFF; command[7] = generate_crc_(command + 5, 2); - if (!this->write_bytes_raw(command, 8)) { + if (this->write(command, 8) != i2c::ERROR_OK) { this->status_set_warning(); - ESP_LOGD(TAG, "write_bytes_raw error"); + ESP_LOGD(TAG, "write error"); return UINT16_MAX; } delay(250); // NOLINT @@ -302,7 +303,7 @@ bool SGP40Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sht3xd/sht3xd.cpp b/esphome/components/sht3xd/sht3xd.cpp index be5b93c124..56a43d5161 100644 --- a/esphome/components/sht3xd/sht3xd.cpp +++ b/esphome/components/sht3xd/sht3xd.cpp @@ -103,7 +103,7 @@ bool SHT3XDComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sht4x/sht4x.cpp b/esphome/components/sht4x/sht4x.cpp index 6d7c917b57..248f32c4de 100644 --- a/esphome/components/sht4x/sht4x.cpp +++ b/esphome/components/sht4x/sht4x.cpp @@ -12,7 +12,7 @@ void SHT4XComponent::start_heater_() { uint8_t cmd[] = {MEASURECOMMANDS[this->heater_command_]}; ESP_LOGD(TAG, "Heater turning on"); - this->write_bytes_raw(cmd, 1); + this->write(cmd, 1); } void SHT4XComponent::setup() { @@ -53,7 +53,7 @@ void SHT4XComponent::update() { uint8_t cmd[] = {MEASURECOMMANDS[this->precision_]}; // Send command - this->write_bytes_raw(cmd, 1); + this->write(cmd, 1); this->set_timeout(10, [this]() { const uint8_t num_bytes = 6; diff --git a/esphome/components/shtcx/shtcx.cpp b/esphome/components/shtcx/shtcx.cpp index db3a3bf803..f2fb6bd5c3 100644 --- a/esphome/components/shtcx/shtcx.cpp +++ b/esphome/components/shtcx/shtcx.cpp @@ -1,5 +1,6 @@ #include "shtcx.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace shtcx { @@ -132,7 +133,7 @@ bool SHTCXComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/shutdown/shutdown_switch.cpp b/esphome/components/shutdown/shutdown_switch.cpp index 87b755cab6..0e5853cc46 100644 --- a/esphome/components/shutdown/shutdown_switch.cpp +++ b/esphome/components/shutdown/shutdown_switch.cpp @@ -2,6 +2,13 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" +#ifdef USE_ESP32 +#include +#endif +#ifdef USE_ESP8266 +#include +#endif + namespace esphome { namespace shutdown { @@ -17,10 +24,10 @@ void ShutdownSwitch::write_state(bool state) { delay(100); // NOLINT App.run_safe_shutdown_hooks(); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 ESP.deepSleep(0); // NOLINT(readability-static-accessed-through-instance) #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_deep_sleep_start(); #endif } diff --git a/esphome/components/slow_pwm/slow_pwm_output.h b/esphome/components/slow_pwm/slow_pwm_output.h index 4a2c1d0a14..f0524f36d8 100644 --- a/esphome/components/slow_pwm/slow_pwm_output.h +++ b/esphome/components/slow_pwm/slow_pwm_output.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" namespace esphome { diff --git a/esphome/components/sm16716/sm16716.h b/esphome/components/sm16716/sm16716.h index 85f78c8cf5..73414c0003 100644 --- a/esphome/components/sm16716/sm16716.h +++ b/esphome/components/sm16716/sm16716.h @@ -1,8 +1,9 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace sm16716 { diff --git a/esphome/components/sm2135/sm2135.h b/esphome/components/sm2135/sm2135.h index e39730579f..0277e9ba1c 100644 --- a/esphome/components/sm2135/sm2135.h +++ b/esphome/components/sm2135/sm2135.h @@ -1,8 +1,9 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace sm2135 { diff --git a/esphome/components/sn74hc595/__init__.py b/esphome/components/sn74hc595/__init__.py index 4437878970..0d1ff6ecba 100644 --- a/esphome/components/sn74hc595/__init__.py +++ b/esphome/components/sn74hc595/__init__.py @@ -3,10 +3,12 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import ( CONF_ID, + CONF_MODE, CONF_NUMBER, CONF_INVERTED, CONF_DATA_PIN, CONF_CLOCK_PIN, + CONF_OUTPUT, ) DEPENDENCIES = [] @@ -48,19 +50,36 @@ async def to_code(config): cg.add(var.set_sr_count(config[CONF_SR_COUNT])) -SN74HC595_OUTPUT_PIN_SCHEMA = cv.Schema( +def _validate_output_mode(value): + if value is not True: + raise cv.Invalid("Only output mode is supported") + return value + + +SN74HC595_PIN_SCHEMA = cv.All( { + cv.GenerateID(): cv.declare_id(SN74HC595GPIOPin), cv.Required(CONF_SN74HC595): cv.use_id(SN74HC595Component), - cv.Required(CONF_NUMBER): cv.int_, + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=7), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_OUTPUT, default=True): cv.All( + cv.boolean, _validate_output_mode + ), + }, + ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) -SN74HC595_INPUT_PIN_SCHEMA = cv.Schema({}) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_SN74HC595, (SN74HC595_OUTPUT_PIN_SCHEMA, SN74HC595_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_SN74HC595, SN74HC595_PIN_SCHEMA) async def sn74hc595_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_SN74HC595]) - return SN74HC595GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_INVERTED]) + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + return var diff --git a/esphome/components/sn74hc595/sn74hc595.cpp b/esphome/components/sn74hc595/sn74hc595.cpp index 596ebf755d..5ebf50e5cb 100644 --- a/esphome/components/sn74hc595/sn74hc595.cpp +++ b/esphome/components/sn74hc595/sn74hc595.cpp @@ -10,17 +10,17 @@ void SN74HC595Component::setup() { ESP_LOGCONFIG(TAG, "Setting up SN74HC595..."); if (this->have_oe_pin_) { // disable output - this->oe_pin_->pin_mode(OUTPUT); + this->oe_pin_->setup(); this->oe_pin_->digital_write(true); } // initialize output pins - this->clock_pin_->pin_mode(OUTPUT); - this->data_pin_->pin_mode(OUTPUT); - this->latch_pin_->pin_mode(OUTPUT); - this->clock_pin_->digital_write(LOW); - this->data_pin_->digital_write(LOW); - this->latch_pin_->digital_write(LOW); + this->clock_pin_->setup(); + this->data_pin_->setup(); + this->latch_pin_->setup(); + this->clock_pin_->digital_write(false); + this->data_pin_->digital_write(false); + this->latch_pin_->digital_write(false); // send state to shift register this->write_gpio_(); @@ -62,16 +62,14 @@ bool SN74HC595Component::write_gpio_() { float SN74HC595Component::get_setup_priority() const { return setup_priority::IO; } -void SN74HC595GPIOPin::setup() {} - -bool SN74HC595GPIOPin::digital_read() { return this->parent_->digital_read_(this->pin_) != this->inverted_; } - void SN74HC595GPIOPin::digital_write(bool value) { this->parent_->digital_write_(this->pin_, value != this->inverted_); } - -SN74HC595GPIOPin::SN74HC595GPIOPin(SN74HC595Component *parent, uint8_t pin, bool inverted) - : GPIOPin(pin, OUTPUT, inverted), parent_(parent) {} +std::string SN74HC595GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via SN74HC595", pin_); + return buffer; +} } // namespace sn74hc595 } // namespace esphome diff --git a/esphome/components/sn74hc595/sn74hc595.h b/esphome/components/sn74hc595/sn74hc595.h index d6f9a68bc8..784019c3a6 100644 --- a/esphome/components/sn74hc595/sn74hc595.h +++ b/esphome/components/sn74hc595/sn74hc595.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace sn74hc595 { @@ -41,14 +41,20 @@ class SN74HC595Component : public Component { /// Helper class to expose a SC74HC595 pin as an internal output GPIO pin. class SN74HC595GPIOPin : public GPIOPin { public: - SN74HC595GPIOPin(SN74HC595Component *parent, uint8_t pin, bool inverted = false); - - void setup() override; - bool digital_read() override; + void setup() override {} + void pin_mode(gpio::Flags flags) override {} + bool digital_read() override { return false; } void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(SN74HC595Component *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } protected: SN74HC595Component *parent_; + uint8_t pin_; + bool inverted_; }; } // namespace sn74hc595 diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 895c775b19..2b6cd10e80 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -1,10 +1,10 @@ #include "sntp_component.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "lwip/apps/sntp.h" #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "sntp.h" #endif @@ -20,13 +20,13 @@ static const char *const TAG = "sntp"; void SNTPComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up SNTP..."); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (sntp_enabled()) { sntp_stop(); } sntp_setoperatingmode(SNTP_OPMODE_POLL); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 sntp_stop(); #endif diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index aa1bcd3b3c..1db24973e7 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -6,8 +6,9 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include +#include #endif namespace esphome { @@ -81,7 +82,7 @@ class BSDSocketImpl : public Socket { int listen(int backlog) override { return ::listen(fd_, backlog); } ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); } ssize_t readv(const struct iovec *iov, int iovcnt) override { -#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 4 // esp-idf v3 doesn't have readv, emulate it ssize_t ret = 0; for (int i = 0; i < iovcnt; i++) { @@ -97,6 +98,9 @@ class BSDSocketImpl : public Socket { break; } return ret; +#elif defined(USE_ESP32) + // ESP-IDF v4 only has symbol lwip_readv + return ::lwip_readv(fd_, iov, iovcnt); #else return ::readv(fd_, iov, iovcnt); #endif @@ -104,7 +108,7 @@ class BSDSocketImpl : public Socket { ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); } ssize_t send(void *buf, size_t len, int flags) { return ::send(fd_, buf, len, flags); } ssize_t writev(const struct iovec *iov, int iovcnt) override { -#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 4 // esp-idf v3 doesn't have writev, emulate it ssize_t ret = 0; for (int i = 0; i < iovcnt; i++) { @@ -121,6 +125,9 @@ class BSDSocketImpl : public Socket { break; } return ret; +#elif defined(USE_ESP32) + // ESP-IDF v4 only has symbol lwip_writev + return ::lwip_writev(fd_, iov, iovcnt); #else return ::writev(fd_, iov, iovcnt); #endif diff --git a/esphome/components/socket/headers.h b/esphome/components/socket/headers.h index fbe8f929a0..f9697fd421 100644 --- a/esphome/components/socket/headers.h +++ b/esphome/components/socket/headers.h @@ -86,7 +86,7 @@ struct iovec { size_t iov_len; }; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 // arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define #ifdef INADDR_ANY #undef INADDR_ANY @@ -97,7 +97,7 @@ struct iovec { #define ESPHOME_INADDR_ANY ((uint32_t) 0x00000000UL) #define ESPHOME_INADDR_NONE ((uint32_t) 0xFFFFFFFFUL) -#else // !ARDUINO_ARCH_ESP8266 +#else // !USE_ESP8266 #define ESPHOME_INADDR_ANY INADDR_ANY #define ESPHOME_INADDR_NONE INADDR_NONE #endif @@ -114,7 +114,7 @@ struct iovec { #include #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ARDUINO // arduino-esp32 declares a global var called INADDR_NONE which is replaced // by the define #ifdef INADDR_NONE @@ -125,7 +125,7 @@ typedef uint32_t socklen_t; #define ESPHOME_INADDR_ANY ((uint32_t) 0x00000000UL) #define ESPHOME_INADDR_NONE ((uint32_t) 0xFFFFFFFFUL) -#else // !ARDUINO_ARCH_ESP32 +#else // !USE_ESP32 #define ESPHOME_INADDR_ANY INADDR_ANY #define ESPHOME_INADDR_NONE INADDR_NONE #endif diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 366f0972ef..7521d1339f 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -11,6 +11,7 @@ #include #include +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -492,7 +493,7 @@ class LWIPRawImpl : public Socket { // nothing to do here, we just don't push it to the queue return ERR_OK; } - auto sock = std::unique_ptr(new LWIPRawImpl(newpcb)); + auto sock = make_unique(newpcb); sock->init(); accepted_sockets_.push(std::move(sock)); return ERR_OK; diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 3180447711..d883142c81 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -8,12 +8,13 @@ namespace spi { static const char *const TAG = "spi"; -void ICACHE_RAM_ATTR HOT SPIComponent::disable() { +void IRAM_ATTR HOT SPIComponent::disable() { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->endTransaction(); } +#endif // USE_SPI_ARDUINO_BACKEND if (this->active_cs_) { - ESP_LOGVV(TAG, "Disabling SPI Chip on pin %u...", this->active_cs_->get_pin()); this->active_cs_->digital_write(true); this->active_cs_ = nullptr; } @@ -23,19 +24,37 @@ void SPIComponent::setup() { this->clk_->setup(); this->clk_->digital_write(true); +#ifdef USE_SPI_ARDUINO_BACKEND bool use_hw_spi = true; - if (this->clk_->is_inverted()) - use_hw_spi = false; const bool has_miso = this->miso_ != nullptr; const bool has_mosi = this->mosi_ != nullptr; - if (has_miso && this->miso_->is_inverted()) + int8_t clk_pin = -1, miso_pin = -1, mosi_pin = -1; + + if (!this->clk_->is_internal()) use_hw_spi = false; - if (has_mosi && this->mosi_->is_inverted()) + if (has_miso && !miso_->is_internal()) use_hw_spi = false; - int8_t clk_pin = this->clk_->get_pin(); - int8_t miso_pin = has_miso ? this->miso_->get_pin() : -1; - int8_t mosi_pin = has_mosi ? this->mosi_->get_pin() : -1; -#ifdef ARDUINO_ARCH_ESP8266 + if (has_mosi && !mosi_->is_internal()) + use_hw_spi = false; + if (use_hw_spi) { + auto *clk_internal = (InternalGPIOPin *) clk_; + auto *miso_internal = (InternalGPIOPin *) miso_; + auto *mosi_internal = (InternalGPIOPin *) mosi_; + + if (clk_internal->is_inverted()) + use_hw_spi = false; + if (has_miso && miso_internal->is_inverted()) + use_hw_spi = false; + if (has_mosi && mosi_internal->is_inverted()) + use_hw_spi = false; + + if (use_hw_spi) { + clk_pin = clk_internal->get_pin(); + miso_pin = has_miso ? miso_internal->get_pin() : -1; + mosi_pin = has_mosi ? mosi_internal->get_pin() : -1; + } + } +#ifdef USE_ESP8266 if (clk_pin == 6 && miso_pin == 7 && mosi_pin == 8) { // pass } else if (clk_pin == 14 && (!has_miso || miso_pin == 12) && (!has_mosi || mosi_pin == 13)) { @@ -50,8 +69,8 @@ void SPIComponent::setup() { this->hw_spi_->begin(); return; } -#endif -#ifdef ARDUINO_ARCH_ESP32 +#endif // USE_ESP8266 +#ifdef USE_ESP32 static uint8_t spi_bus_num = 0; if (spi_bus_num >= 2) { use_hw_spi = false; @@ -67,7 +86,8 @@ void SPIComponent::setup() { this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin); return; } -#endif +#endif // USE_ESP32 +#endif // USE_SPI_ARDUINO_BACKEND if (this->miso_ != nullptr) { this->miso_->setup(); @@ -82,32 +102,28 @@ void SPIComponent::dump_config() { LOG_PIN(" CLK Pin: ", this->clk_); LOG_PIN(" MISO Pin: ", this->miso_); LOG_PIN(" MOSI Pin: ", this->mosi_); +#ifdef USE_SPI_ARDUINO_BACKEND ESP_LOGCONFIG(TAG, " Using HW SPI: %s", YESNO(this->hw_spi_ != nullptr)); +#endif // USE_SPI_ARDUINO_BACKEND } float SPIComponent::get_setup_priority() const { return setup_priority::BUS; } -void SPIComponent::debug_tx(uint8_t value) { - ESP_LOGVV(TAG, " TX 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(value), value); -} -void SPIComponent::debug_rx(uint8_t value) { - ESP_LOGVV(TAG, " RX 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(value), value); -} -void SPIComponent::debug_enable(uint8_t pin) { ESP_LOGVV(TAG, "Enabling SPI Chip on pin %u...", pin); } - void SPIComponent::cycle_clock_(bool value) { - uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) - while (start - ESP.getCycleCount() < this->wait_cycle_) // NOLINT(readability-static-accessed-through-instance) + uint32_t start = arch_get_cpu_cycle_count(); + while (start - arch_get_cpu_cycle_count() < this->wait_cycle_) ; this->clk_->digital_write(value); start += this->wait_cycle_; - while (start - ESP.getCycleCount() < this->wait_cycle_) // NOLINT(readability-static-accessed-through-instance) + while (start - arch_get_cpu_cycle_count() < this->wait_cycle_) ; } // NOLINTNEXTLINE +#ifndef CLANG_TIDY #pragma GCC optimize("unroll-loops") // NOLINTNEXTLINE #pragma GCC optimize("O2") +#endif // CLANG_TIDY template uint8_t HOT SPIComponent::transfer_(uint8_t data) { @@ -153,15 +169,6 @@ uint8_t HOT SPIComponent::transfer_(uint8_t data) { } } -#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE - if (WRITE) { - SPIComponent::debug_tx(data); - } - if (READ) { - SPIComponent::debug_rx(out_data); - } -#endif - App.feed_wdt(); return out_data; diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index eb8f9ce7ce..601a5c5a7e 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -1,8 +1,16 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" +#include + +#ifdef USE_ARDUINO +#define USE_SPI_ARDUINO_BACKEND +#endif + +#ifdef USE_SPI_ARDUINO_BACKEND #include +#endif namespace esphome { namespace spi { @@ -72,18 +80,22 @@ class SPIComponent : public Component { void dump_config() override; template uint8_t read_byte() { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { return this->hw_spi_->transfer(0x00); } +#endif // USE_SPI_ARDUINO_BACKEND return this->transfer_(0x00); } template void read_array(uint8_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->transfer(data, length); return; } +#endif // USE_SPI_ARDUINO_BACKEND for (size_t i = 0; i < length; i++) { data[i] = this->read_byte(); } @@ -91,19 +103,23 @@ class SPIComponent : public Component { template void write_byte(uint8_t data) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->write(data); return; } +#endif // USE_SPI_ARDUINO_BACKEND this->transfer_(data); } template void write_byte16(const uint16_t data) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->write16(data); return; } +#endif // USE_SPI_ARDUINO_BACKEND this->write_byte(data >> 8); this->write_byte(data); @@ -111,12 +127,14 @@ class SPIComponent : public Component { template void write_array16(const uint16_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { for (size_t i = 0; i < length; i++) { this->hw_spi_->write16(data[i]); } return; } +#endif // USE_SPI_ARDUINO_BACKEND for (size_t i = 0; i < length; i++) { this->write_byte16(data[i]); } @@ -124,11 +142,13 @@ class SPIComponent : public Component { template void write_array(const uint8_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { auto *data_c = const_cast(data); this->hw_spi_->writeBytes(data_c, length); return; } +#endif // USE_SPI_ARDUINO_BACKEND for (size_t i = 0; i < length; i++) { this->write_byte(data[i]); } @@ -136,6 +156,7 @@ class SPIComponent : public Component { template uint8_t transfer_byte(uint8_t data) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->miso_ != nullptr) { if (this->hw_spi_ != nullptr) { return this->hw_spi_->transfer(data); @@ -143,12 +164,14 @@ class SPIComponent : public Component { return this->transfer_(data); } } +#endif // USE_SPI_ARDUINO_BACKEND this->write_byte(data); return 0; } template void transfer_array(uint8_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { if (this->miso_ != nullptr) { this->hw_spi_->transfer(data, length); @@ -157,6 +180,7 @@ class SPIComponent : public Component { } return; } +#endif // USE_SPI_ARDUINO_BACKEND if (this->miso_ != nullptr) { for (size_t i = 0; i < length; i++) { @@ -169,18 +193,19 @@ class SPIComponent : public Component { template void enable(GPIOPin *cs) { - if (cs != nullptr) { - SPIComponent::debug_enable(cs->get_pin()); - } - +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { uint8_t data_mode = (uint8_t(CLOCK_POLARITY) << 1) | uint8_t(CLOCK_PHASE); SPISettings settings(DATA_RATE, BIT_ORDER, data_mode); this->hw_spi_->beginTransaction(settings); } else { +#endif // USE_SPI_ARDUINO_BACKEND this->clk_->digital_write(CLOCK_POLARITY); - this->wait_cycle_ = uint32_t(F_CPU) / DATA_RATE / 2ULL; + uint32_t cpu_freq_hz = arch_get_cpu_freq_hz(); + this->wait_cycle_ = uint32_t(cpu_freq_hz) / DATA_RATE / 2ULL; +#ifdef USE_SPI_ARDUINO_BACKEND } +#endif // USE_SPI_ARDUINO_BACKEND if (cs != nullptr) { this->active_cs_ = cs; @@ -195,10 +220,6 @@ class SPIComponent : public Component { protected: inline void cycle_clock_(bool value); - static void debug_enable(uint8_t pin); - static void debug_tx(uint8_t value); - static void debug_rx(uint8_t value); - template uint8_t transfer_(uint8_t data); @@ -206,7 +227,9 @@ class SPIComponent : public Component { GPIOPin *miso_{nullptr}; GPIOPin *mosi_{nullptr}; GPIOPin *active_cs_{nullptr}; +#ifdef USE_SPI_ARDUINO_BACKEND SPIClass *hw_spi_{nullptr}; +#endif // USE_SPI_ARDUINO_BACKEND uint32_t wait_cycle_; }; diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 3a31f4d607..472b7606ed 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -244,7 +244,7 @@ bool SPS30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/ssd1306_base/ssd1306_base.h b/esphome/components/ssd1306_base/ssd1306_base.h index 0fe09709e7..2c54af7a67 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.h +++ b/esphome/components/ssd1306_base/ssd1306_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index fce9796008..45fda4870e 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -10,8 +10,8 @@ void I2CSSD1306::setup() { ESP_LOGCONFIG(TAG, "Setting up I2C SSD1306..."); this->init_reset_(); - this->raw_begin_transmission(); - if (!this->raw_end_transmission()) { + auto err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); return; diff --git a/esphome/components/ssd1322_base/ssd1322_base.h b/esphome/components/ssd1322_base/ssd1322_base.h index 125e374246..6a790c0199 100644 --- a/esphome/components/ssd1322_base/ssd1322_base.h +++ b/esphome/components/ssd1322_base/ssd1322_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1325_base/ssd1325_base.h b/esphome/components/ssd1325_base/ssd1325_base.h index a06ba69a59..cca9412c43 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.h +++ b/esphome/components/ssd1325_base/ssd1325_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1327_base/ssd1327_base.h b/esphome/components/ssd1327_base/ssd1327_base.h index 03f360b258..35b021c71b 100644 --- a/esphome/components/ssd1327_base/ssd1327_base.h +++ b/esphome/components/ssd1327_base/ssd1327_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp index 2967d2f9c4..e9e047bfb6 100644 --- a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +++ b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp @@ -10,8 +10,8 @@ void I2CSSD1327::setup() { ESP_LOGCONFIG(TAG, "Setting up I2C SSD1327..."); this->init_reset_(); - this->raw_begin_transmission(); - if (!this->raw_end_transmission()) { + auto err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); return; diff --git a/esphome/components/ssd1331_base/ssd1331_base.h b/esphome/components/ssd1331_base/ssd1331_base.h index 8d2bca5de0..b889a47fbe 100644 --- a/esphome/components/ssd1331_base/ssd1331_base.h +++ b/esphome/components/ssd1331_base/ssd1331_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1351_base/ssd1351_base.h b/esphome/components/ssd1351_base/ssd1351_base.h index 2730f798b5..422e601f8b 100644 --- a/esphome/components/ssd1351_base/ssd1351_base.h +++ b/esphome/components/ssd1351_base/ssd1351_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index 1f14136637..8490aa1fe4 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -1,6 +1,7 @@ #include "st7735.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace st7735 { @@ -353,17 +354,17 @@ void ST7735::display_init_(const uint8_t *addr) { uint8_t num_commands, cmd, num_args; uint16_t ms; - num_commands = pgm_read_byte(addr++); // Number of commands to follow - while (num_commands--) { // For each command... - cmd = pgm_read_byte(addr++); // Read command - num_args = pgm_read_byte(addr++); // Number of args to follow - ms = num_args & ST_CMD_DELAY; // If hibit set, delay follows args - num_args &= ~ST_CMD_DELAY; // Mask out delay bit + num_commands = progmem_read_byte(addr++); // Number of commands to follow + while (num_commands--) { // For each command... + cmd = progmem_read_byte(addr++); // Read command + num_args = progmem_read_byte(addr++); // Number of args to follow + ms = num_args & ST_CMD_DELAY; // If hibit set, delay follows args + num_args &= ~ST_CMD_DELAY; // Mask out delay bit this->sendcommand_(cmd, addr, num_args); addr += num_args; if (ms) { - ms = pgm_read_byte(addr++); // Read post-command delay time (ms) + ms = progmem_read_byte(addr++); // Read post-command delay time (ms) if (ms == 255) ms = 500; // If 255, delay for 500 ms delay(ms); @@ -410,7 +411,7 @@ void HOT ST7735::senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes) { this->cs_->digital_write(false); this->enable(); for (uint8_t i = 0; i < num_data_bytes; i++) { - this->write_byte(pgm_read_byte(data_bytes++)); // write byte - SPI library + this->write_byte(progmem_read_byte(data_bytes++)); // write byte - SPI library } this->cs_->digital_write(true); this->disable(); diff --git a/esphome/components/status/status_binary_sensor.cpp b/esphome/components/status/status_binary_sensor.cpp index 152e3aff9d..1795a9c41b 100644 --- a/esphome/components/status/status_binary_sensor.cpp +++ b/esphome/components/status/status_binary_sensor.cpp @@ -1,6 +1,6 @@ #include "status_binary_sensor.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include "esphome/core/defines.h" #ifdef USE_MQTT @@ -16,7 +16,7 @@ namespace status { static const char *const TAG = "status"; void StatusBinarySensor::loop() { - bool status = network_is_connected(); + bool status = network::is_connected(); #ifdef USE_MQTT if (mqtt::global_mqtt_client != nullptr) { status = status && mqtt::global_mqtt_client->is_connected(); diff --git a/esphome/components/status_led/light/status_led_light.h b/esphome/components/status_led/light/status_led_light.h index 8a7f4b4da8..e90d381e3c 100644 --- a/esphome/components/status_led/light/status_led_light.h +++ b/esphome/components/status_led/light/status_led_light.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/light/light_output.h" namespace esphome { diff --git a/esphome/components/status_led/status_led.h b/esphome/components/status_led/status_led.h index 79f9f524a3..490557f3e7 100644 --- a/esphome/components/status_led/status_led.h +++ b/esphome/components/status_led/status_led.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace status_led { diff --git a/esphome/components/stepper/stepper.cpp b/esphome/components/stepper/stepper.cpp index d7f6cc6dda..7926024204 100644 --- a/esphome/components/stepper/stepper.cpp +++ b/esphome/components/stepper/stepper.cpp @@ -1,5 +1,6 @@ #include "stepper.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace stepper { diff --git a/esphome/components/sts3x/sts3x.cpp b/esphome/components/sts3x/sts3x.cpp index 77383c6daa..b1ecbc98f8 100644 --- a/esphome/components/sts3x/sts3x.cpp +++ b/esphome/components/sts3x/sts3x.cpp @@ -99,7 +99,7 @@ bool STS3XComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sun/sun.h b/esphome/components/sun/sun.h index 6a8364a5f0..0a2e6bcf97 100644 --- a/esphome/components/sun/sun.h +++ b/esphome/components/sun/sun.h @@ -80,7 +80,7 @@ class SunTrigger : public Trigger<>, public PollingComponent, public Parentedparent_->elevation(); - if (isnan(current)) + if (std::isnan(current)) return; bool crossed; @@ -90,7 +90,7 @@ class SunTrigger : public Trigger<>, public PollingComponent, public Parentedlast_elevation_ >= this->elevation_ && this->elevation_ > current; } - if (crossed && !isnan(this->last_elevation_)) { + if (crossed && !std::isnan(this->last_elevation_)) { this->trigger(); } this->last_elevation_ = current; diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index c96f9a40d0..0e12f5af0f 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -30,7 +30,7 @@ void Switch::toggle() { this->write_state(this->inverted_ == this->state); } optional Switch::get_initial_state() { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); bool initial_state; if (!this->rtc_.load(&initial_state)) return {}; diff --git a/esphome/components/sx1509/__init__.py b/esphome/components/sx1509/__init__.py index 8e1239924a..f1b7d5f424 100644 --- a/esphome/components/sx1509/__init__.py +++ b/esphome/components/sx1509/__init__.py @@ -2,7 +2,15 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_OUTPUT, + CONF_PULLUP, +) CONF_KEYPAD = "keypad" CONF_KEY_ROWS = "key_rows" @@ -10,17 +18,12 @@ CONF_KEY_COLUMNS = "key_columns" CONF_SLEEP_TIME = "sleep_time" CONF_SCAN_TIME = "scan_time" CONF_DEBOUNCE_TIME = "debounce_time" +CONF_SX1509_ID = "sx1509_id" DEPENDENCIES = ["i2c"] MULTI_CONF = True sx1509_ns = cg.esphome_ns.namespace("sx1509") -SX1509GPIOMode = sx1509_ns.enum("SX1509GPIOMode") -SX1509_GPIO_MODES = { - "INPUT": SX1509GPIOMode.SX1509_INPUT, - "INPUT_PULLUP": SX1509GPIOMode.SX1509_INPUT_PULLUP, - "OUTPUT": SX1509GPIOMode.SX1509_OUTPUT, -} SX1509Component = sx1509_ns.class_("SX1509Component", cg.Component, i2c.I2CDevice) SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin) @@ -64,34 +67,43 @@ async def to_code(config): cg.add(var.set_debounce_time(keypad[CONF_DEBOUNCE_TIME])) -CONF_SX1509 = "sx1509" -CONF_SX1509_ID = "sx1509_id" +def validate_mode(value): + if not (value[CONF_INPUT] or value[CONF_OUTPUT]): + raise cv.Invalid("Mode must be either input or output") + if value[CONF_INPUT] and value[CONF_OUTPUT]: + raise cv.Invalid("Mode must be either input or output") + if value[CONF_PULLUP] and not value[CONF_INPUT]: + raise cv.Invalid("Pullup only available with input") + return value -SX1509_OUTPUT_PIN_SCHEMA = cv.Schema( + +CONF_SX1509 = "sx1509" +SX1509_PIN_SCHEMA = cv.All( { - cv.Required(CONF_SX1509): cv.use_id(SX1509Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - SX1509_GPIO_MODES, upper=True + cv.GenerateID(): cv.declare_id(SX1509Component), + cv.Required(CONF_SX1509): cv.use_id(SX1509GPIOPin), + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + }, + validate_mode, ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) -SX1509_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_SX1509): cv.use_id(SX1509Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(SX1509_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_SX1509, (SX1509_OUTPUT_PIN_SCHEMA, SX1509_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_SX1509, SX1509_PIN_SCHEMA) async def sx1509_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_SX1509]) - return SX1509GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/sx1509/output/sx1509_float_output.cpp b/esphome/components/sx1509/output/sx1509_float_output.cpp index c68f8f9ded..e9c401eeed 100644 --- a/esphome/components/sx1509/output/sx1509_float_output.cpp +++ b/esphome/components/sx1509/output/sx1509_float_output.cpp @@ -16,7 +16,8 @@ void SX1509FloatOutputChannel::write_state(float state) { void SX1509FloatOutputChannel::setup() { ESP_LOGD(TAG, "setup pin %d", this->pin_); - this->parent_->pin_mode(this->pin_, SX1509_ANALOG_OUTPUT); + this->parent_->pin_mode(this->pin_, gpio::FLAG_OUTPUT); + this->parent_->setup_led_driver(this->pin_); this->turn_off(); } diff --git a/esphome/components/sx1509/sx1509.cpp b/esphome/components/sx1509/sx1509.cpp index 14d9ad7a61..9095dfeffa 100644 --- a/esphome/components/sx1509/sx1509.cpp +++ b/esphome/components/sx1509/sx1509.cpp @@ -18,13 +18,15 @@ void SX1509Component::setup() { this->write_byte(REG_RESET, 0x34); uint16_t data; - this->read_byte_16(REG_INTERRUPT_MASK_A, &data); - if (data == 0xFF00) { - clock_(INTERNAL_CLOCK_2MHZ); - } else { + if (!this->read_byte_16(REG_INTERRUPT_MASK_A, &data)) { this->mark_failed(); return; } + if (data != 0xFF00) { + this->mark_failed(); + return; + } + clock_(INTERNAL_CLOCK_2MHZ); delayMicroseconds(500); if (this->has_keypad_) this->setup_keypad_(); @@ -49,7 +51,8 @@ void SX1509Component::loop() { bool SX1509Component::digital_read(uint8_t pin) { if (this->ddr_mask_ & (1 << pin)) { uint16_t temp_reg_data; - this->read_byte_16(REG_DATA_B, &temp_reg_data); + if (!this->read_byte_16(REG_DATA_B, &temp_reg_data)) + return false; if (temp_reg_data & (1 << pin)) return true; } @@ -68,9 +71,9 @@ void SX1509Component::digital_write(uint8_t pin, bool bit_value) { this->write_byte_16(REG_DATA_B, temp_reg_data); } else { // Otherwise the pin is an input, pull-up/down - uint16_t temp_pullup; + uint16_t temp_pullup = 0; this->read_byte_16(REG_PULL_UP_B, &temp_pullup); - uint16_t temp_pull_down; + uint16_t temp_pull_down = 0; this->read_byte_16(REG_PULL_DOWN_B, &temp_pull_down); if (bit_value) { @@ -89,25 +92,21 @@ void SX1509Component::digital_write(uint8_t pin, bool bit_value) { } } -void SX1509Component::pin_mode(uint8_t pin, uint8_t mode) { +void SX1509Component::pin_mode(uint8_t pin, gpio::Flags flags) { this->read_byte_16(REG_DIR_B, &this->ddr_mask_); - if ((mode == SX1509_OUTPUT) || (mode == SX1509_ANALOG_OUTPUT)) + if (flags == gpio::FLAG_OUTPUT) this->ddr_mask_ &= ~(1 << pin); else this->ddr_mask_ |= (1 << pin); this->write_byte_16(REG_DIR_B, this->ddr_mask_); - if (mode == INPUT_PULLUP) - digital_write(pin, HIGH); - - if (mode == SX1509_ANALOG_OUTPUT) { - setup_led_driver_(pin); - } + if (flags & gpio::FLAG_PULLUP) + digital_write(pin, true); } -void SX1509Component::setup_led_driver_(uint8_t pin) { - uint16_t temp_word; - uint8_t temp_byte; +void SX1509Component::setup_led_driver(uint8_t pin) { + uint16_t temp_word = 0; + uint8_t temp_byte = 0; this->read_byte_16(REG_INPUT_DISABLE_B, &temp_word); temp_word |= (1 << pin); @@ -140,18 +139,18 @@ void SX1509Component::setup_led_driver_(uint8_t pin) { this->write_byte_16(REG_DATA_B, temp_word); } -void SX1509Component::clock_(byte osc_source, byte osc_pin_function, byte osc_freq_out, byte osc_divider) { +void SX1509Component::clock_(uint8_t osc_source, uint8_t osc_pin_function, uint8_t osc_freq_out, uint8_t osc_divider) { osc_source = (osc_source & 0b11) << 5; // 2-bit value, bits 6:5 osc_pin_function = (osc_pin_function & 1) << 4; // 1-bit value bit 4 osc_freq_out = (osc_freq_out & 0b1111); // 4-bit value, bits 3:0 uint8_t reg_clock = osc_source | osc_pin_function | osc_freq_out; this->write_byte(REG_CLOCK, reg_clock); - osc_divider = constrain(osc_divider, 1, 7); + osc_divider = clamp(osc_divider, 1, 7u); this->clk_x_ = 2000000; osc_divider = (osc_divider & 0b111) << 4; // 3-bit value, bits 6:4 - uint8_t reg_misc; + uint8_t reg_misc = 0; this->read_byte(REG_MISC, ®_misc); reg_misc &= ~(0b111 << 4); reg_misc |= osc_divider; @@ -159,7 +158,7 @@ void SX1509Component::clock_(byte osc_source, byte osc_pin_function, byte osc_fr } void SX1509Component::setup_keypad_() { - uint8_t temp_byte; + uint8_t temp_byte = 0; // setup row/col pins for INPUT OUTPUT this->read_byte_16(REG_DIR_B, &this->ddr_mask_); @@ -199,14 +198,14 @@ void SX1509Component::setup_keypad_() { } uint16_t SX1509Component::read_key_data() { - uint16_t key_data; + uint16_t key_data = 0; this->read_byte_16(REG_KEY_DATA_1, &key_data); return (0xFFFF ^ key_data); } void SX1509Component::set_debounce_config_(uint8_t config_value) { // First make sure clock is configured - uint8_t temp_byte; + uint8_t temp_byte = 0; this->read_byte(REG_MISC, &temp_byte); temp_byte |= (1 << 4); // Just default to no divider if not set this->write_byte(REG_MISC, temp_byte); @@ -227,13 +226,13 @@ void SX1509Component::set_debounce_time_(uint8_t time) { break; } } - config_value = constrain(config_value, 0, 7); + config_value = clamp(config_value, 0, 7); set_debounce_config_(config_value); } void SX1509Component::set_debounce_enable_(uint8_t pin) { - uint16_t debounce_enable; + uint16_t debounce_enable = 0; this->read_byte_16(REG_DEBOUNCE_ENABLE_B, &debounce_enable); debounce_enable |= (1 << pin); this->write_byte_16(REG_DEBOUNCE_ENABLE_B, debounce_enable); diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index 1390059675..5f0697b534 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -2,7 +2,7 @@ #include "esphome/components/i2c/i2c.h" #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "sx1509_gpio_pin.h" #include "sx1509_registers.h" @@ -15,16 +15,6 @@ const uint8_t EXTERNAL_CLOCK = 1; const uint8_t SOFTWARE_RESET = 0; const uint8_t HARDWARE_RESET = 1; -const uint8_t ANALOG_OUTPUT = 0x03; // To set a pin mode for PWM output - -// PinModes for SX1509 pins -enum SX1509GPIOMode : uint8_t { - SX1509_INPUT = INPUT, // 0x00 - SX1509_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - SX1509_ANALOG_OUTPUT = ANALOG_OUTPUT, // 0x03 - SX1509_OUTPUT = OUTPUT, // 0x01 -}; - const uint8_t REG_I_ON[16] = {REG_I_ON_0, REG_I_ON_1, REG_I_ON_2, REG_I_ON_3, REG_I_ON_4, REG_I_ON_5, REG_I_ON_6, REG_I_ON_7, REG_I_ON_8, REG_I_ON_9, REG_I_ON_10, REG_I_ON_11, REG_I_ON_12, REG_I_ON_13, REG_I_ON_14, REG_I_ON_15}; @@ -47,9 +37,9 @@ class SX1509Component : public Component, public i2c::I2CDevice { bool digital_read(uint8_t pin); uint16_t read_key_data(); void set_pin_value(uint8_t pin, uint8_t i_on) { this->write_byte(REG_I_ON[pin], i_on); }; - void pin_mode(uint8_t pin, uint8_t mode); + void pin_mode(uint8_t pin, gpio::Flags flags); void digital_write(uint8_t pin, bool bit_value); - u_long get_clock() { return this->clk_x_; }; + uint32_t get_clock() { return this->clk_x_; }; void set_rows_cols(uint8_t rows, uint8_t cols) { this->rows_ = rows; this->cols_ = cols; @@ -60,10 +50,11 @@ class SX1509Component : public Component, public i2c::I2CDevice { void set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; }; void register_keypad_binary_sensor(SX1509Processor *binary_sensor) { this->keypad_binary_sensors_.push_back(binary_sensor); - }; + } + void setup_led_driver(uint8_t pin); protected: - u_long clk_x_ = 2000000; + uint32_t clk_x_ = 2000000; uint8_t frequency_ = 0; uint16_t ddr_mask_ = 0x00; uint16_t input_mask_ = 0x00; @@ -82,7 +73,6 @@ class SX1509Component : public Component, public i2c::I2CDevice { void set_debounce_pin_(uint8_t pin); void set_debounce_enable_(uint8_t pin); void set_debounce_keypad_(uint8_t time, uint8_t num_rows, uint8_t num_cols); - void setup_led_driver_(uint8_t pin); void clock_(uint8_t osc_source = 2, uint8_t osc_pin_function = 1, uint8_t osc_freq_out = 0, uint8_t osc_divider = 0); }; diff --git a/esphome/components/sx1509/sx1509_gpio_pin.cpp b/esphome/components/sx1509/sx1509_gpio_pin.cpp index ac55ac1ca5..2c6e0b0c32 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.cpp +++ b/esphome/components/sx1509/sx1509_gpio_pin.cpp @@ -7,14 +7,15 @@ namespace sx1509 { static const char *const TAG = "sx1509_gpio_pin"; -void SX1509GPIOPin::setup() { - ESP_LOGD(TAG, "setup pin %d", this->pin_); - this->parent_->pin_mode(this->pin_, this->mode_); -} - -void SX1509GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } +void SX1509GPIOPin::setup() { pin_mode(flags_); } +void SX1509GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool SX1509GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void SX1509GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string SX1509GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via MCP23016", pin_); + return buffer; +} } // namespace sx1509 } // namespace esphome diff --git a/esphome/components/sx1509/sx1509_gpio_pin.h b/esphome/components/sx1509/sx1509_gpio_pin.h index 39f841a2a4..4d8aa5ec83 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.h +++ b/esphome/components/sx1509/sx1509_gpio_pin.h @@ -9,15 +9,22 @@ class SX1509Component; class SX1509GPIOPin : public GPIOPin { public: - SX1509GPIOPin(SX1509Component *parent, uint8_t pin, uint8_t mode, bool inverted = false) - : GPIOPin(pin, mode, inverted), parent_(parent){}; void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(SX1509Component *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } protected: SX1509Component *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; }; } // namespace sx1509 diff --git a/esphome/components/tca9548a/__init__.py b/esphome/components/tca9548a/__init__.py index 62cbace56a..0f222b8fc7 100644 --- a/esphome/components/tca9548a/__init__.py +++ b/esphome/components/tca9548a/__init__.py @@ -1,30 +1,43 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c -from esphome.const import CONF_ID, CONF_SCAN +from esphome.const import CONF_CHANNEL, CONF_CHANNELS, CONF_ID, CONF_SCAN CODEOWNERS = ["@andreashergert1984"] DEPENDENCIES = ["i2c"] tca9548a_ns = cg.esphome_ns.namespace("tca9548a") -TCA9548AComponent = tca9548a_ns.class_( - "TCA9548AComponent", cg.PollingComponent, i2c.I2CMultiplexer -) +TCA9548AComponent = tca9548a_ns.class_("TCA9548AComponent", cg.Component, i2c.I2CDevice) +TCA9548AChannel = tca9548a_ns.class_("TCA9548AChannel", i2c.I2CBus) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(TCA9548AComponent), - cv.Optional(CONF_SCAN, default=True): cv.boolean, - } -).extend(i2c.i2c_device_schema(0x70)) +CONF_BUS_ID = "bus_id" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TCA9548AComponent), + cv.Optional(CONF_SCAN): cv.invalid("This option has been removed"), + cv.Optional(CONF_CHANNELS, default=[]): cv.ensure_list( + { + cv.Required(CONF_BUS_ID): cv.declare_id(TCA9548AChannel), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7), + } + ), + } + ) + .extend(i2c.i2c_device_schema(0x70)) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - cg.add_define("USE_I2C_MULTIPLEXER") await cg.register_component(var, config) await i2c.register_i2c_device(var, config) - cg.add(var.set_scan(config[CONF_SCAN])) + + for conf in config[CONF_CHANNELS]: + chan = cg.new_Pvariable(conf[CONF_BUS_ID]) + cg.add(chan.set_parent(var)) + cg.add(chan.set_channel(conf[CONF_CHANNEL])) diff --git a/esphome/components/tca9548a/tca9548a.cpp b/esphome/components/tca9548a/tca9548a.cpp index 472b8b6673..5117ad8969 100644 --- a/esphome/components/tca9548a/tca9548a.cpp +++ b/esphome/components/tca9548a/tca9548a.cpp @@ -6,35 +6,46 @@ namespace tca9548a { static const char *const TAG = "tca9548a"; +i2c::ErrorCode TCA9548AChannel::readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) { + auto err = parent_->switch_to_channel(channel_); + if (err != i2c::ERROR_OK) + return err; + return parent_->bus_->readv(address, buffers, cnt); +} +i2c::ErrorCode TCA9548AChannel::writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt) { + auto err = parent_->switch_to_channel(channel_); + if (err != i2c::ERROR_OK) + return err; + return parent_->bus_->writev(address, buffers, cnt); +} + void TCA9548AComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up TCA9548A..."); uint8_t status = 0; - if (!this->read_byte(0x00, &status)) { + if (!this->read_register(0x00, &status, 1)) { ESP_LOGI(TAG, "TCA9548A failed"); + this->mark_failed(); return; } - // out of range to make sure on first set_channel a new one will be set - this->current_channelno_ = 8; - ESP_LOGCONFIG(TAG, "Channels currently open: %d", status); + ESP_LOGD(TAG, "Channels currently open: %d", status); } void TCA9548AComponent::dump_config() { ESP_LOGCONFIG(TAG, "TCA9548A:"); LOG_I2C_DEVICE(this); - if (this->scan_) { - for (uint8_t i = 0; i < 8; i++) { - ESP_LOGCONFIG(TAG, "Activating channel: %d", i); - this->set_channel(i); - this->parent_->dump_config(); - } - } } -void TCA9548AComponent::set_channel(uint8_t channelno) { - if (this->current_channelno_ != channelno) { - this->current_channelno_ = channelno; - uint8_t channelbyte = 1 << channelno; - this->write_byte(0x70, channelbyte); +i2c::ErrorCode TCA9548AComponent::switch_to_channel(uint8_t channel) { + if (this->is_failed()) + return i2c::ERROR_NOT_INITIALIZED; + if (current_channel_ == channel) + return i2c::ERROR_OK; + + uint8_t channel_val = 1 << channel; + auto err = this->write_register(0x70, &channel_val, 1); + if (err == i2c::ERROR_OK) { + current_channel_ = channel; } + return err; } } // namespace tca9548a diff --git a/esphome/components/tca9548a/tca9548a.h b/esphome/components/tca9548a/tca9548a.h index 50b1eb8b56..314346d317 100644 --- a/esphome/components/tca9548a/tca9548a.h +++ b/esphome/components/tca9548a/tca9548a.h @@ -6,17 +6,31 @@ namespace esphome { namespace tca9548a { -class TCA9548AComponent : public Component, public i2c::I2CMultiplexer { +class TCA9548AComponent; +class TCA9548AChannel : public i2c::I2CBus { + public: + void set_channel(uint8_t channel) { channel_ = channel; } + void set_parent(TCA9548AComponent *parent) { parent_ = parent; } + + i2c::ErrorCode readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) override; + i2c::ErrorCode writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt) override; + + protected: + uint8_t channel_; + TCA9548AComponent *parent_; +}; + +class TCA9548AComponent : public Component, public i2c::I2CDevice { public: - void set_scan(bool scan) { scan_ = scan; } void setup() override; void dump_config() override; void update(); - void set_channel(uint8_t channelno) override; + + i2c::ErrorCode switch_to_channel(uint8_t channel); protected: - bool scan_; - uint8_t current_channelno_; + friend class TCA9548AChannel; + uint8_t current_channel_ = 255; }; } // namespace tca9548a } // namespace esphome diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 52548262c1..564d3dcda7 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -1,5 +1,6 @@ #include "tcs34725.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace tcs34725 { diff --git a/esphome/components/template/number/template_number.cpp b/esphome/components/template/number/template_number.cpp index eb9b17b976..a5b015c44d 100644 --- a/esphome/components/template/number/template_number.cpp +++ b/esphome/components/template/number/template_number.cpp @@ -14,9 +14,9 @@ void TemplateNumber::setup() { if (!this->restore_value_) { value = this->initial_value_; } else { - this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); if (!this->pref_.load(&value)) { - if (!isnan(this->initial_value_)) + if (!std::isnan(this->initial_value_)) value = this->initial_value_; else value = this->traits.get_min_value(); diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index 8695880856..219c341ec9 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -17,7 +17,7 @@ void TemplateSelect::setup() { ESP_LOGD(TAG, "State from initial: %s", value.c_str()); } else { size_t index; - this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); if (!this->pref_.load(&index)) { value = this->initial_option_; ESP_LOGD(TAG, "State from initial (could not load): %s", value.c_str()); diff --git a/esphome/components/template/sensor/template_sensor.cpp b/esphome/components/template/sensor/template_sensor.cpp index 63cbd70db0..b28eb3fed2 100644 --- a/esphome/components/template/sensor/template_sensor.cpp +++ b/esphome/components/template/sensor/template_sensor.cpp @@ -1,5 +1,6 @@ #include "template_sensor.h" #include "esphome/core/log.h" +#include namespace esphome { namespace template_ { @@ -12,7 +13,7 @@ void TemplateSensor::update() { if (val.has_value()) { this->publish_state(*val); } - } else if (!isnan(this->get_raw_state())) { + } else if (!std::isnan(this->get_raw_state())) { this->publish_state(this->get_raw_state()); } } diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index a75713cbb9..6193185321 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -88,17 +88,17 @@ climate::ClimateFanMode ThermostatClimate::locked_fan_mode() { return this->prev bool ThermostatClimate::hysteresis_valid() { if ((this->supports_cool_ || (this->supports_fan_only_ && this->supports_fan_only_cooling_)) && - (isnan(this->cooling_deadband_) || isnan(this->cooling_overrun_))) + (std::isnan(this->cooling_deadband_) || std::isnan(this->cooling_overrun_))) return false; - if (this->supports_heat_ && (isnan(this->heating_deadband_) || isnan(this->heating_overrun_))) + if (this->supports_heat_ && (std::isnan(this->heating_deadband_) || std::isnan(this->heating_overrun_))) return false; return true; } void ThermostatClimate::validate_target_temperature() { - if (isnan(this->target_temperature)) { + if (std::isnan(this->target_temperature)) { this->target_temperature = ((this->get_traits().get_visual_max_temperature() - this->get_traits().get_visual_min_temperature()) / 2) + this->get_traits().get_visual_min_temperature(); @@ -121,7 +121,7 @@ void ThermostatClimate::validate_target_temperatures() { } void ThermostatClimate::validate_target_temperature_low() { - if (isnan(this->target_temperature_low)) { + if (std::isnan(this->target_temperature_low)) { this->target_temperature_low = this->get_traits().get_visual_min_temperature(); } else { // target_temperature_low must not be lower than the visual minimum @@ -139,7 +139,7 @@ void ThermostatClimate::validate_target_temperature_low() { } void ThermostatClimate::validate_target_temperature_high() { - if (isnan(this->target_temperature_high)) { + if (std::isnan(this->target_temperature_high)) { this->target_temperature_high = this->get_traits().get_visual_max_temperature(); } else { // target_temperature_high must not be lower than the visual maximum @@ -245,7 +245,7 @@ climate::ClimateTraits ThermostatClimate::traits() { climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_timers) { auto target_action = climate::CLIMATE_ACTION_IDLE; // if any hysteresis values or current_temperature is not valid, we go to OFF; - if (isnan(this->current_temperature) || !this->hysteresis_valid()) { + if (std::isnan(this->current_temperature) || !this->hysteresis_valid()) { return climate::CLIMATE_ACTION_OFF; } // do not change the action if an "ON" timer is running @@ -307,7 +307,7 @@ climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_time climate::ClimateAction ThermostatClimate::compute_supplemental_action_() { auto target_action = climate::CLIMATE_ACTION_IDLE; // if any hysteresis values or current_temperature is not valid, we go to OFF; - if (isnan(this->current_temperature) || !this->hysteresis_valid()) { + if (std::isnan(this->current_temperature) || !this->hysteresis_valid()) { return climate::CLIMATE_ACTION_OFF; } diff --git a/esphome/components/time/automation.cpp b/esphome/components/time/automation.cpp index f133ab021c..7e16d7141f 100644 --- a/esphome/components/time/automation.cpp +++ b/esphome/components/time/automation.cpp @@ -1,5 +1,6 @@ #include "automation.h" #include "esphome/core/log.h" +#include namespace esphome { namespace time { diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 27a2d84da6..064e6f899c 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -1,7 +1,7 @@ #include "real_time_clock.h" #include "esphome/core/log.h" #include "lwip/opt.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "sys/time.h" #endif #include diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index 60a33aa82a..3fa07167ca 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -1,5 +1,6 @@ #include "time_based_cover.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace time_based { diff --git a/esphome/components/tlc59208f/tlc59208f_output.cpp b/esphome/components/tlc59208f/tlc59208f_output.cpp index e416a5c62b..59fb9f98ed 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.cpp +++ b/esphome/components/tlc59208f/tlc59208f_output.cpp @@ -75,7 +75,7 @@ void TLC59208FOutput::setup() { ESP_LOGV(TAG, " Resetting all devices on the bus..."); // Reset all devices on the bus - if (!this->parent_->write_byte(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ[0], TLC59208F_SWRST_SEQ[1])) { + if (this->bus_->write(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "RESET failed"); this->mark_failed(); return; diff --git a/esphome/components/tlc59208f/tlc59208f_output.h b/esphome/components/tlc59208f/tlc59208f_output.h index 79fccc97f3..68ca8061d7 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.h +++ b/esphome/components/tlc59208f/tlc59208f_output.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/output/float_output.h" #include "esphome/components/i2c/i2c.h" diff --git a/esphome/components/tlc5947/tlc5947.h b/esphome/components/tlc5947/tlc5947.h index b608b861e7..0eb7f10604 100644 --- a/esphome/components/tlc5947/tlc5947.h +++ b/esphome/components/tlc5947/tlc5947.h @@ -3,8 +3,9 @@ // https://www.ti.com/lit/ds/symlink/tlc5947.pdf #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace tlc5947 { diff --git a/esphome/components/tm1637/tm1637.cpp b/esphome/components/tm1637/tm1637.cpp index 1f1c0fd301..488f3b6727 100644 --- a/esphome/components/tm1637/tm1637.cpp +++ b/esphome/components/tm1637/tm1637.cpp @@ -1,6 +1,7 @@ #include "tm1637.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace tm1637 { @@ -146,16 +147,16 @@ void TM1637Display::update() { float TM1637Display::get_setup_priority() const { return setup_priority::PROCESSOR; } void TM1637Display::bit_delay_() { delayMicroseconds(100); } void TM1637Display::start_() { - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); } void TM1637Display::stop_() { - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); bit_delay_(); - this->clk_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); bit_delay_(); - this->dio_pin_->pin_mode(INPUT); + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); bit_delay_(); } @@ -189,39 +190,39 @@ bool TM1637Display::send_byte_(uint8_t b) { // 8 Data Bits for (uint8_t i = 0; i < 8; i++) { // CLK low - this->clk_pin_->pin_mode(OUTPUT); + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); // Set data bit if (data & 0x01) - this->dio_pin_->pin_mode(INPUT); + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); else - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); // CLK high - this->clk_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); data = data >> 1; } // Wait for acknowledge // CLK to zero - this->clk_pin_->pin_mode(OUTPUT); - this->dio_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); // CLK to high - this->clk_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); uint8_t ack = this->dio_pin_->digital_read(); if (ack == 0) { - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); } this->bit_delay_(); - this->clk_pin_->pin_mode(OUTPUT); + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); return ack; @@ -233,7 +234,7 @@ uint8_t TM1637Display::print(uint8_t start_pos, const char *str) { for (; *str != '\0'; str++) { uint8_t data = TM1637_UNKNOWN_CHAR; if (*str >= ' ' && *str <= '~') - data = pgm_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']); + data = progmem_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']); if (data == TM1637_UNKNOWN_CHAR) { ESP_LOGW(TAG, "Encountered character '%c' with no TM1637 representation while translating string!", *str); diff --git a/esphome/components/tm1637/tm1637.h b/esphome/components/tm1637/tm1637.h index 003344eae9..63b30ac13e 100644 --- a/esphome/components/tm1637/tm1637.h +++ b/esphome/components/tm1637/tm1637.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/tm1651/__init__.py b/esphome/components/tm1651/__init__.py index d06c1bedde..9d2b17afdc 100644 --- a/esphome/components/tm1651/__init__.py +++ b/esphome/components/tm1651/__init__.py @@ -27,12 +27,15 @@ TM1651_BRIGHTNESS_OPTIONS = { 3: TM1651Display.TM1651_BRIGHTNESS_HIGH, } -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(TM1651Display), - cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, - } +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TM1651Display), + cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, + } + ), + cv.only_with_arduino, ) validate_level_percent = cv.All(cv.int_range(min=0, max=100)) diff --git a/esphome/components/tm1651/tm1651.cpp b/esphome/components/tm1651/tm1651.cpp index 3689ef5894..c6bb1bc025 100644 --- a/esphome/components/tm1651/tm1651.cpp +++ b/esphome/components/tm1651/tm1651.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "tm1651.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" @@ -88,3 +90,5 @@ uint8_t TM1651Display::calculate_brightness_(uint8_t new_brightness) { } // namespace tm1651 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/tm1651/tm1651.h b/esphome/components/tm1651/tm1651.h index a18519bf41..72849bc8eb 100644 --- a/esphome/components/tm1651/tm1651.h +++ b/esphome/components/tm1651/tm1651.h @@ -1,9 +1,11 @@ #pragma once +#ifdef USE_ARDUINO + #include #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include @@ -13,8 +15,8 @@ namespace tm1651 { class TM1651Display : public Component { public: - void set_clk_pin(GPIOPin *pin) { clk_pin_ = pin; } - void set_dio_pin(GPIOPin *pin) { dio_pin_ = pin; } + void set_clk_pin(InternalGPIOPin *pin) { clk_pin_ = pin; } + void set_dio_pin(InternalGPIOPin *pin) { dio_pin_ = pin; } void setup() override; void dump_config() override; @@ -28,8 +30,8 @@ class TM1651Display : public Component { protected: std::unique_ptr battery_display_; - GPIOPin *clk_pin_; - GPIOPin *dio_pin_; + InternalGPIOPin *clk_pin_; + InternalGPIOPin *dio_pin_; bool is_on_ = true; uint8_t brightness_; @@ -83,3 +85,5 @@ template class TurnOffAction : public Action, public Pare } // namespace tm1651 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/tmp102/tmp102.cpp b/esphome/components/tmp102/tmp102.cpp index 7b3dcad4aa..f6bb9a05c0 100644 --- a/esphome/components/tmp102/tmp102.cpp +++ b/esphome/components/tmp102/tmp102.cpp @@ -1,5 +1,6 @@ #include "tmp102.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace tmp102 { @@ -28,10 +29,16 @@ void TMP102Component::dump_config() { void TMP102Component::update() { uint16_t raw_temperature; - if (!this->read_byte_16(TMP102_REGISTER_TEMPERATURE, &raw_temperature, 50)) { + if (this->write(&TMP102_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(50); // NOLINT + if (this->read(reinterpret_cast(&raw_temperature), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_temperature = i2c::i2ctohs(raw_temperature); raw_temperature = raw_temperature >> 4; float temperature = raw_temperature * TMP102_CONVERSION_FACTOR; diff --git a/esphome/components/tof10120/tof10120_sensor.cpp b/esphome/components/tof10120/tof10120_sensor.cpp index cabfdad41d..4ba591f9c4 100644 --- a/esphome/components/tof10120/tof10120_sensor.cpp +++ b/esphome/components/tof10120/tof10120_sensor.cpp @@ -1,5 +1,6 @@ #include "tof10120_sensor.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" // Very basic support for TOF10120 distance sensor @@ -31,7 +32,12 @@ void TOF10120Sensor::update() { } uint8_t data[2]; - if (!this->read_bytes(TOF10120_DISTANCE_REGISTER, data, 2, TOF10120_DEFAULT_DELAY)) { + if (this->write(&TOF10120_DISTANCE_REGISTER, 1) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + delay(TOF10120_DEFAULT_DELAY); + if (this->read(data, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with TOF10120 failed on read"); this->status_set_warning(); return; diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index 81ed5ddce4..25528abbe1 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -127,7 +127,7 @@ void ToshibaClimate::setup() { this->fan_modes_ = this->toshiba_fan_modes_(); this->swing_modes_ = this->toshiba_swing_modes_(); // Never send nan to HA - if (isnan(this->target_temperature)) + if (std::isnan(this->target_temperature)) this->target_temperature = 24; } diff --git a/esphome/components/total_daily_energy/total_daily_energy.cpp b/esphome/components/total_daily_energy/total_daily_energy.cpp index 83333acab7..178dc7cbe0 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.cpp +++ b/esphome/components/total_daily_energy/total_daily_energy.cpp @@ -7,7 +7,7 @@ namespace total_daily_energy { static const char *const TAG = "total_daily_energy"; void TotalDailyEnergy::setup() { - this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); float recovered; if (this->pref_.load(&recovered)) { @@ -52,7 +52,7 @@ void TotalDailyEnergy::publish_state_and_save(float state) { } void TotalDailyEnergy::process_new_state_(float state) { - if (isnan(state)) + if (std::isnan(state)) return; const uint32_t now = millis(); const float old_state = this->last_power_state_; diff --git a/esphome/components/total_daily_energy/total_daily_energy.h b/esphome/components/total_daily_energy/total_daily_energy.h index fd71b8decc..9d2396d6e3 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.h +++ b/esphome/components/total_daily_energy/total_daily_energy.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/tsl2591/tsl2591.cpp b/esphome/components/tsl2591/tsl2591.cpp index 1785fa46b4..7755437de2 100644 --- a/esphome/components/tsl2591/tsl2591.cpp +++ b/esphome/components/tsl2591/tsl2591.cpp @@ -1,5 +1,6 @@ #include "tsl2591.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace tsl2591 { @@ -362,7 +363,7 @@ float TSL2591Component::get_calculated_lux(uint16_t full_spectrum, uint16_t infr // For the curious "cpl" is counts per lux, a term used in AMS application notes. float cpl = (atime * again) / (this->device_factor_ * this->glass_attenuation_factor_); float lux = (((float) full_spectrum - (float) infrared)) * (1.0F - ((float) infrared / (float) full_spectrum)) / cpl; - return max(lux, 0.0F); + return std::max(lux, 0.0F); } } // namespace tsl2591 diff --git a/esphome/components/ttp229_bsf/ttp229_bsf.h b/esphome/components/ttp229_bsf/ttp229_bsf.h index d73e4fb185..59749a4fa7 100644 --- a/esphome/components/ttp229_bsf/ttp229_bsf.h +++ b/esphome/components/ttp229_bsf/ttp229_bsf.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/ttp229_lsf/ttp229_lsf.cpp b/esphome/components/ttp229_lsf/ttp229_lsf.cpp index 6e3e68ea7a..21c7b02740 100644 --- a/esphome/components/ttp229_lsf/ttp229_lsf.cpp +++ b/esphome/components/ttp229_lsf/ttp229_lsf.cpp @@ -8,7 +8,8 @@ static const char *const TAG = "ttp229_lsf"; void TTP229LSFComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up ttp229..."); - if (!this->parent_->raw_request_from(this->address_, 2)) { + uint8_t data[2]; + if (this->read(data, 2) != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); return; @@ -28,10 +29,11 @@ void TTP229LSFComponent::dump_config() { } void TTP229LSFComponent::loop() { uint16_t touched = 0; - if (!this->parent_->raw_receive_16(this->address_, &touched, 1)) { + if (this->read(reinterpret_cast(&touched), 2) != i2c::ERROR_OK) { this->status_set_warning(); return; } + touched = i2c::i2ctohs(touched); this->status_clear_warning(); touched = reverse_bits_16(touched); for (auto *channel : this->channels_) { diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index 293bfb4f88..39d4203684 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -169,7 +169,7 @@ void TuyaClimate::compute_target_temperature_() { } void TuyaClimate::compute_state_() { - if (isnan(this->current_temperature) || isnan(this->target_temperature)) { + if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature)) { // if any control parameters are nan, go to OFF action (not IDLE!) this->switch_to_action_(climate::CLIMATE_ACTION_OFF); return; diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index e42c74005e..bbbc9274c3 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -1,7 +1,8 @@ #include "tuya.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include "esphome/core/helpers.h" +#include "esphome/core/util.h" namespace esphome { namespace tuya { @@ -389,7 +390,7 @@ void Tuya::send_empty_command_(TuyaCommandType command) { void Tuya::send_wifi_status_() { uint8_t status = 0x02; - if (network_is_connected()) { + if (network::is_connected()) { status = 0x03; // Protocol version 3 also supports specifying when connected to "the cloud" diff --git a/esphome/components/tx20/sensor.py b/esphome/components/tx20/sensor.py index ceb9b88d8d..84df82b5e6 100644 --- a/esphome/components/tx20/sensor.py +++ b/esphome/components/tx20/sensor.py @@ -33,9 +33,7 @@ CONFIG_SCHEMA = cv.Schema( accuracy_decimals=1, state_class=STATE_CLASS_NONE, ), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/tx20/tx20.cpp b/esphome/components/tx20/tx20.cpp index f48e29521c..6e0b6343d1 100644 --- a/esphome/components/tx20/tx20.cpp +++ b/esphome/components/tx20/tx20.cpp @@ -20,7 +20,7 @@ void Tx20Component::setup() { this->store_.pin = this->pin_->to_isr(); this->store_.reset(); - this->pin_->attach_interrupt(Tx20ComponentStore::gpio_intr, &this->store_, CHANGE); + this->pin_->attach_interrupt(Tx20ComponentStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void Tx20Component::dump_config() { ESP_LOGCONFIG(TAG, "Tx20:"); @@ -140,8 +140,8 @@ void Tx20Component::decode_and_publish_() { } } -void ICACHE_RAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) { - arg->pin_state = arg->pin->digital_read(); +void IRAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) { + arg->pin_state = arg->pin.digital_read(); const uint32_t now = micros(); if (!arg->start_time) { // only detect a start if the bit is high @@ -183,7 +183,7 @@ void ICACHE_RAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) { arg->start_time = now; arg->buffer_index++; } -void ICACHE_RAM_ATTR Tx20ComponentStore::reset() { +void IRAM_ATTR Tx20ComponentStore::reset() { tx20_available = false; buffer_index = 0; spent_time = 0; diff --git a/esphome/components/tx20/tx20.h b/esphome/components/tx20/tx20.h index 56cb723fa1..1c617d0674 100644 --- a/esphome/components/tx20/tx20.h +++ b/esphome/components/tx20/tx20.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -15,7 +15,7 @@ struct Tx20ComponentStore { volatile uint32_t spent_time; volatile bool tx20_available; volatile bool pin_state; - ISRInternalGPIOPin *pin; + ISRInternalGPIOPin pin; void reset(); static void gpio_intr(Tx20ComponentStore *arg); @@ -27,7 +27,7 @@ class Tx20Component : public Component { /// Get the textual representation of the wind direction ('N', 'SSE', ..). std::string get_wind_cardinal_direction() const; - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_wind_speed_sensor(sensor::Sensor *wind_speed_sensor) { wind_speed_sensor_ = wind_speed_sensor; } void set_wind_direction_degrees_sensor(sensor::Sensor *wind_direction_degrees_sensor) { wind_direction_degrees_sensor_ = wind_direction_degrees_sensor; @@ -42,7 +42,7 @@ class Tx20Component : public Component { void decode_and_publish_(); std::string wind_cardinal_direction_; - GPIOPin *pin_; + InternalGPIOPin *pin_; sensor::Sensor *wind_speed_sensor_; sensor::Sensor *wind_direction_degrees_sensor_; Tx20ComponentStore store_; diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index d2fcac2cb6..35af3eedf7 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -7,6 +7,7 @@ from esphome import pins, automation from esphome.const import ( CONF_BAUD_RATE, CONF_ID, + CONF_NUMBER, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID, @@ -18,7 +19,16 @@ from esphome.core import CORE CODEOWNERS = ["@esphome/core"] uart_ns = cg.esphome_ns.namespace("uart") -UARTComponent = uart_ns.class_("UARTComponent", cg.Component) +UARTComponent = uart_ns.class_("UARTComponent") + +IDFUARTComponent = uart_ns.class_("IDFUARTComponent", UARTComponent, cg.Component) +ESP32ArduinoUARTComponent = uart_ns.class_( + "ESP32ArduinoUARTComponent", UARTComponent, cg.Component +) +ESP8266UartComponent = uart_ns.class_( + "ESP8266UartComponent", UARTComponent, cg.Component +) + UARTDevice = uart_ns.class_("UARTDevice") UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) MULTI_CONF = True @@ -37,12 +47,23 @@ def validate_raw_data(value): def validate_rx_pin(value): - value = pins.input_pin(value) - if CORE.is_esp8266 and value >= 16: + value = pins.internal_gpio_input_pin_schema(value) + if CORE.is_esp8266 and value[CONF_NUMBER] >= 16: raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as RX pins on ESP8266.") return value +def _uart_declare_type(value): + if CORE.is_esp8266: + return cv.declare_id(ESP8266UartComponent)(value) + if CORE.is_esp32: + if CORE.using_arduino: + return cv.declare_id(ESP32ArduinoUARTComponent)(value) + if CORE.using_esp_idf: + return cv.declare_id(IDFUARTComponent)(value) + raise NotImplementedError + + UARTParityOptions = uart_ns.enum("UARTParityOptions") UART_PARITY_OPTIONS = { "NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE, @@ -57,19 +78,19 @@ CONF_PARITY = "parity" CONFIG_SCHEMA = cv.All( cv.Schema( { - cv.GenerateID(): cv.declare_id(UARTComponent), + cv.GenerateID(): _uart_declare_type, cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), - cv.Optional(CONF_TX_PIN): pins.output_pin, + cv.Optional(CONF_TX_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_RX_PIN): validate_rx_pin, cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes, - cv.SplitDefault(CONF_INVERT, esp32=False): cv.All( - cv.only_on_esp32, cv.boolean - ), cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8), cv.Optional(CONF_PARITY, default="NONE"): cv.enum( UART_PARITY_OPTIONS, upper=True ), + cv.Optional(CONF_INVERT): cv.invalid( + "This option has been removed. Please instead use invert in the tx/rx pin schemas." + ), } ).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN), @@ -84,12 +105,12 @@ async def to_code(config): cg.add(var.set_baud_rate(config[CONF_BAUD_RATE])) if CONF_TX_PIN in config: - cg.add(var.set_tx_pin(config[CONF_TX_PIN])) + tx_pin = await cg.gpio_pin_expression(config[CONF_TX_PIN]) + cg.add(var.set_tx_pin(tx_pin)) if CONF_RX_PIN in config: - cg.add(var.set_rx_pin(config[CONF_RX_PIN])) + rx_pin = await cg.gpio_pin_expression(config[CONF_RX_PIN]) + cg.add(var.set_rx_pin(rx_pin)) cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE])) - if CONF_INVERT in config: - cg.add(var.set_invert(config[CONF_INVERT])) cg.add(var.set_stop_bits(config[CONF_STOP_BITS])) cg.add(var.set_data_bits(config[CONF_DATA_BITS])) cg.add(var.set_parity(config[CONF_PARITY])) diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 8cc8a47b14..22a22e2772 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -4,62 +4,28 @@ #include "esphome/core/application.h" #include "esphome/core/defines.h" -#ifdef USE_LOGGER -#include "esphome/components/logger/logger.h" -#endif - namespace esphome { namespace uart { static const char *const TAG = "uart"; -size_t UARTComponent::write(uint8_t data) { - this->write_byte(data); - return 1; -} -int UARTComponent::read() { - uint8_t data; - if (!this->read_byte(&data)) - return -1; - return data; -} -int UARTComponent::peek() { - uint8_t data; - if (!this->peek_byte(&data)) - return -1; - return data; -} - -void UARTComponent::check_logger_conflict_() { -#ifdef USE_LOGGER - if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) { - return; - } - - if (this->hw_serial_ == logger::global_logger->get_hw_serial()) { - ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please " - "disable logging over the serial port by setting logger->baud_rate to 0."); - } -#endif -} - void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity, uint8_t data_bits) { - if (this->parent_->baud_rate_ != baud_rate) { + if (this->parent_->get_baud_rate() != baud_rate) { ESP_LOGE(TAG, " Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate, - this->parent_->baud_rate_); + this->parent_->get_baud_rate()); } - if (this->parent_->stop_bits_ != stop_bits) { + if (this->parent_->get_stop_bits() != stop_bits) { ESP_LOGE(TAG, " Invalid stop bits: Integration requested stop_bits %u but you have %u!", stop_bits, - this->parent_->stop_bits_); + this->parent_->get_stop_bits()); } - if (this->parent_->data_bits_ != data_bits) { + if (this->parent_->get_data_bits() != data_bits) { ESP_LOGE(TAG, " Invalid number of data bits: Integration requested %u data bits but you have %u!", data_bits, - this->parent_->data_bits_); + this->parent_->get_data_bits()); } - if (this->parent_->parity_ != parity) { + if (this->parent_->get_parity() != parity) { ESP_LOGE(TAG, " Invalid parity: Integration requested parity %s but you have %s!", - LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->parity_))); + LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->get_parity()))); } } diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index a79b7b841e..c368f9ed6b 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -1,132 +1,15 @@ #pragma once #include -#include -#include "esphome/core/esphal.h" #include "esphome/core/component.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" +#include "uart_component.h" namespace esphome { namespace uart { -enum UARTParityOptions { - UART_CONFIG_PARITY_NONE, - UART_CONFIG_PARITY_EVEN, - UART_CONFIG_PARITY_ODD, -}; - -const LogString *parity_to_str(UARTParityOptions parity); - -#ifdef ARDUINO_ARCH_ESP8266 -class ESP8266SoftwareSerial { - public: - void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t data_bits, - UARTParityOptions parity, size_t rx_buffer_size); - - uint8_t read_byte(); - uint8_t peek_byte(); - - void flush(); - - void write_byte(uint8_t data); - - int available(); - - GPIOPin *gpio_tx_pin_{nullptr}; - GPIOPin *gpio_rx_pin_{nullptr}; - - protected: - static void gpio_intr(ESP8266SoftwareSerial *arg); - - void wait_(uint32_t *wait, const uint32_t &start); - bool read_bit_(uint32_t *wait, const uint32_t &start); - void write_bit_(bool bit, uint32_t *wait, const uint32_t &start); - - uint32_t bit_time_{0}; - uint8_t *rx_buffer_{nullptr}; - size_t rx_buffer_size_; - volatile size_t rx_in_pos_{0}; - size_t rx_out_pos_{0}; - uint8_t stop_bits_; - uint8_t data_bits_; - UARTParityOptions parity_; - ISRInternalGPIOPin *tx_pin_{nullptr}; - ISRInternalGPIOPin *rx_pin_{nullptr}; -}; -#endif - -class UARTComponent : public Component, public Stream { - public: - void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } - uint32_t get_baud_rate() const { return baud_rate_; } - - uint32_t get_config(); - - void setup() override; - - void dump_config() override; - - void write_byte(uint8_t data); - - void write_array(const uint8_t *data, size_t len); - void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); } - - void write_str(const char *str); - - bool peek_byte(uint8_t *data); - - bool read_byte(uint8_t *data); - - bool read_array(uint8_t *data, size_t len); - - int available() override; - - /// Block until all bytes have been written to the UART bus. - void flush() override; - - float get_setup_priority() const override { return setup_priority::BUS; } - - size_t write(uint8_t data) override; - int read() override; - int peek() override; - - void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; } - void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; } - void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; } -#ifdef ARDUINO_ARCH_ESP32 - void set_invert(bool invert) { this->invert_ = invert; } -#endif - void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } - void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; } - void set_parity(UARTParityOptions parity) { this->parity_ = parity; } - - protected: - void check_logger_conflict_(); - bool check_read_timeout_(size_t len = 1); - friend class UARTDevice; - - HardwareSerial *hw_serial_{nullptr}; -#ifdef ARDUINO_ARCH_ESP8266 - ESP8266SoftwareSerial *sw_serial_{nullptr}; -#endif - optional tx_pin_; - optional rx_pin_; - size_t rx_buffer_size_; -#ifdef ARDUINO_ARCH_ESP32 - bool invert_; -#endif - uint32_t baud_rate_; - uint8_t stop_bits_; - uint8_t data_bits_; - UARTParityOptions parity_; - - private: -#ifdef ARDUINO_ARCH_ESP8266 - static bool serial0InUse; -#endif -}; - -class UARTDevice : public Stream { +class UARTDevice { public: UARTDevice() = default; UARTDevice(UARTComponent *parent) : parent_(parent) {} @@ -155,13 +38,27 @@ class UARTDevice : public Stream { return res; } - int available() override { return this->parent_->available(); } + int available() { return this->parent_->available(); } - void flush() override { return this->parent_->flush(); } + void flush() { return this->parent_->flush(); } - size_t write(uint8_t data) override { return this->parent_->write(data); } - int read() override { return this->parent_->read(); } - int peek() override { return this->parent_->peek(); } + // Compat APIs + int read() { + uint8_t data; + if (!read_byte(&data)) + return -1; + return data; + } + size_t write(uint8_t data) { + write_byte(data); + return 1; + } + int peek() { + uint8_t data; + if (!peek_byte(&data)) + return -1; + return data; + } /// Check that the configuration of the UART bus matches the provided values and otherwise print a warning void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits = 1, diff --git a/esphome/components/uart/uart_component.cpp b/esphome/components/uart/uart_component.cpp new file mode 100644 index 0000000000..09b8c975ab --- /dev/null +++ b/esphome/components/uart/uart_component.cpp @@ -0,0 +1,24 @@ +#include "uart_component.h" + +namespace esphome { +namespace uart { + +static const char *const TAG = "uart"; + +bool UARTComponent::check_read_timeout_(size_t len) { + if (this->available() >= int(len)) + return true; + + uint32_t start_time = millis(); + while (this->available() < int(len)) { + if (millis() - start_time > 100) { + ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); + return false; + } + yield(); + } + return true; +} + +} // namespace uart +} // namespace esphome diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h new file mode 100644 index 0000000000..de85cd2ca3 --- /dev/null +++ b/esphome/components/uart/uart_component.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace uart { + +enum UARTParityOptions { + UART_CONFIG_PARITY_NONE, + UART_CONFIG_PARITY_EVEN, + UART_CONFIG_PARITY_ODD, +}; + +const LogString *parity_to_str(UARTParityOptions parity); + +class UARTComponent { + public: + void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); } + void write_byte(uint8_t data) { this->write_array(&data, 1); }; + void write_str(const char *str) { + const auto *data = reinterpret_cast(str); + this->write_array(data, strlen(str)); + }; + + virtual void write_array(const uint8_t *data, size_t len) = 0; + + bool read_byte(uint8_t *data) { return this->read_array(data, 1); }; + virtual bool peek_byte(uint8_t *data) = 0; + virtual bool read_array(uint8_t *data, size_t len) = 0; + + /// Return available number of bytes. + virtual int available() = 0; + /// Block until all bytes have been written to the UART bus. + virtual void flush() = 0; + + void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; } + void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; } + void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; } + + void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } + uint8_t get_stop_bits() const { return this->stop_bits_; } + void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; } + uint8_t get_data_bits() const { return this->data_bits_; } + void set_parity(UARTParityOptions parity) { this->parity_ = parity; } + UARTParityOptions get_parity() const { return this->parity_; } + void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } + uint32_t get_baud_rate() const { return baud_rate_; } + + protected: + virtual void check_logger_conflict() = 0; + bool check_read_timeout_(size_t len = 1); + + InternalGPIOPin *tx_pin_; + InternalGPIOPin *rx_pin_; + size_t rx_buffer_size_; + uint32_t baud_rate_; + uint8_t stop_bits_; + uint8_t data_bits_; + UARTParityOptions parity_; +}; + +} // namespace uart +} // namespace esphome diff --git a/esphome/components/uart/uart_esp32.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp similarity index 64% rename from esphome/components/uart/uart_esp32.cpp rename to esphome/components/uart/uart_component_esp32_arduino.cpp index db2757780e..1b1ce382f2 100644 --- a/esphome/components/uart/uart_esp32.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -1,13 +1,17 @@ -#ifdef ARDUINO_ARCH_ESP32 -#include "uart.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "uart_component_esp32_arduino.h" + +#ifdef USE_LOGGER +#include "esphome/components/logger/logger.h" +#endif namespace esphome { namespace uart { -static const char *const TAG = "uart_esp32"; +static const char *const TAG = "uart.arduino_esp32"; static const uint32_t UART_PARITY_EVEN = 0 << 0; static const uint32_t UART_PARITY_ODD = 1 << 0; @@ -20,7 +24,7 @@ static const uint32_t UART_NB_STOP_BIT_1 = 1 << 4; static const uint32_t UART_NB_STOP_BIT_2 = 3 << 4; static const uint32_t UART_TICK_APB_CLOCK = 1 << 27; -uint32_t UARTComponent::get_config() { +uint32_t ESP32ArduinoUARTComponent::get_config() { uint32_t config = 0; /* @@ -67,71 +71,63 @@ uint32_t UARTComponent::get_config() { return config; } -void UARTComponent::setup() { +void ESP32ArduinoUARTComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up UART..."); // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. + bool is_default_tx, is_default_rx; #ifdef CONFIG_IDF_TARGET_ESP32C3 - if (this->tx_pin_.value_or(21) == 21 && this->rx_pin_.value_or(20) == 20) { + is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 21; + is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 20; #else - if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) { + is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 1; + is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 3; #endif + if (is_default_tx && is_default_rx) { this->hw_serial_ = &Serial; } else { static uint8_t next_uart_num = 1; this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory) } - int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; - int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; - this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, this->invert_); + int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1; + int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1; + bool invert = false; + if (tx_pin_ != nullptr && tx_pin_->is_inverted()) + invert = true; + if (rx_pin_ != nullptr && rx_pin_->is_inverted()) + invert = true; + this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, invert); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); } -void UARTComponent::dump_config() { +void ESP32ArduinoUARTComponent::dump_config() { ESP_LOGCONFIG(TAG, "UART Bus:"); - if (this->tx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " TX Pin: GPIO%d", *this->tx_pin_); - } - if (this->rx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_); + LOG_PIN(" TX Pin: ", tx_pin_); + LOG_PIN(" RX Pin: ", rx_pin_); + if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); - this->check_logger_conflict_(); + this->check_logger_conflict(); } -void UARTComponent::write_byte(uint8_t data) { - this->hw_serial_->write(data); - ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); -} -void UARTComponent::write_array(const uint8_t *data, size_t len) { +void ESP32ArduinoUARTComponent::write_array(const uint8_t *data, size_t len) { this->hw_serial_->write(data, len); for (size_t i = 0; i < len; i++) { ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); } } -void UARTComponent::write_str(const char *str) { - this->hw_serial_->write(str); - ESP_LOGVV(TAG, " Wrote \"%s\"", str); -} -bool UARTComponent::read_byte(uint8_t *data) { - if (!this->check_read_timeout_()) - return false; - *data = this->hw_serial_->read(); - ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); - return true; -} -bool UARTComponent::peek_byte(uint8_t *data) { +bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) { if (!this->check_read_timeout_()) return false; *data = this->hw_serial_->peek(); return true; } -bool UARTComponent::read_array(uint8_t *data, size_t len) { +bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) { if (!this->check_read_timeout_(len)) return false; this->hw_serial_->readBytes(data, len); @@ -141,26 +137,25 @@ bool UARTComponent::read_array(uint8_t *data, size_t len) { return true; } -bool UARTComponent::check_read_timeout_(size_t len) { - if (this->available() >= len) - return true; - - uint32_t start_time = millis(); - while (this->available() < len) { - if (millis() - start_time > 1000) { - ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); - return false; - } - yield(); - } - return true; -} -int UARTComponent::available() { return this->hw_serial_->available(); } -void UARTComponent::flush() { +int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); } +void ESP32ArduinoUARTComponent::flush() { ESP_LOGVV(TAG, " Flushing..."); this->hw_serial_->flush(); } +void ESP32ArduinoUARTComponent::check_logger_conflict() { +#ifdef USE_LOGGER + if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) { + return; + } + + if (this->hw_serial_ == logger::global_logger->get_hw_serial()) { + ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please " + "disable logging over the serial port by setting logger->baud_rate to 0."); + } +#endif +} + } // namespace uart } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/uart/uart_component_esp32_arduino.h b/esphome/components/uart/uart_component_esp32_arduino.h new file mode 100644 index 0000000000..c6f445ff12 --- /dev/null +++ b/esphome/components/uart/uart_component_esp32_arduino.h @@ -0,0 +1,40 @@ +#pragma once + +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include +#include +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +class ESP32ArduinoUARTComponent : public UARTComponent, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void write_array(const uint8_t *data, size_t len) override; + + bool peek_byte(uint8_t *data) override; + bool read_array(uint8_t *data, size_t len) override; + + int available() override; + void flush() override; + + uint32_t get_config(); + + protected: + void check_logger_conflict() override; + + HardwareSerial *hw_serial_{nullptr}; +}; + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/uart/uart_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp similarity index 60% rename from esphome/components/uart/uart_esp8266.cpp rename to esphome/components/uart/uart_component_esp8266.cpp index a74f2601e4..2188a4a4bc 100644 --- a/esphome/components/uart/uart_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -1,9 +1,9 @@ -#ifdef ARDUINO_ARCH_ESP8266 -#include "uart.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" +#ifdef USE_ESP8266 +#include "uart_component_esp8266.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -12,10 +12,10 @@ namespace esphome { namespace uart { -static const char *const TAG = "uart_esp8266"; -bool UARTComponent::serial0InUse = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static const char *const TAG = "uart.arduino_esp8266"; +bool ESP8266UartComponent::serial0InUse = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -uint32_t UARTComponent::get_config() { +uint32_t ESP8266UartComponent::get_config() { uint32_t config = 0; if (this->parity_ == UART_CONFIG_PARITY_NONE) @@ -48,15 +48,15 @@ uint32_t UARTComponent::get_config() { return config; } -void UARTComponent::setup() { +void ESP8266UartComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up UART bus..."); // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. SerialConfig config = static_cast(get_config()); - if (!UARTComponent::serial0InUse && this->tx_pin_.value_or(1) == 1 && - this->rx_pin_.value_or(3) == 3 + if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) && + (rx_pin_ == nullptr || rx_pin_->get_pin() == 3) #ifdef USE_LOGGER // we will use UART0 if logger isn't using it in swapped mode && (logger::global_logger->get_hw_serial() == nullptr || @@ -66,9 +66,9 @@ void UARTComponent::setup() { this->hw_serial_ = &Serial; this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); - UARTComponent::serial0InUse = true; - } else if (!UARTComponent::serial0InUse && this->tx_pin_.value_or(15) == 15 && - this->rx_pin_.value_or(13) == 13 + ESP8266UartComponent::serial0InUse = true; + } else if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) && + (rx_pin_ == nullptr || rx_pin_->get_pin() == 13) #ifdef USE_LOGGER // we will use UART0 swapped if logger isn't using it in regular mode && (logger::global_logger->get_hw_serial() == nullptr || @@ -79,27 +79,23 @@ void UARTComponent::setup() { this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); this->hw_serial_->swap(); - UARTComponent::serial0InUse = true; - } else if (this->tx_pin_.value_or(2) == 2 && this->rx_pin_.value_or(8) == 8) { + ESP8266UartComponent::serial0InUse = true; + } else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) { this->hw_serial_ = &Serial1; this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); } else { this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT - int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; - int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; - this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_, + this->sw_serial_->setup(tx_pin_, rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_, this->rx_buffer_size_); } } -void UARTComponent::dump_config() { +void ESP8266UartComponent::dump_config() { ESP_LOGCONFIG(TAG, "UART Bus:"); - if (this->tx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " TX Pin: GPIO%d", *this->tx_pin_); - } - if (this->rx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_); + LOG_PIN(" TX Pin: ", tx_pin_); + LOG_PIN(" RX Pin: ", rx_pin_); + if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT } ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); @@ -111,18 +107,23 @@ void UARTComponent::dump_config() { } else { ESP_LOGCONFIG(TAG, " Using software serial"); } - this->check_logger_conflict_(); + this->check_logger_conflict(); } -void UARTComponent::write_byte(uint8_t data) { - if (this->hw_serial_ != nullptr) { - this->hw_serial_->write(data); - } else { - this->sw_serial_->write_byte(data); +void ESP8266UartComponent::check_logger_conflict() { +#ifdef USE_LOGGER + if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) { + return; } - ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); + + if (this->hw_serial_ == logger::global_logger->get_hw_serial()) { + ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please " + "disable logging over the serial port by setting logger->baud_rate to 0."); + } +#endif } -void UARTComponent::write_array(const uint8_t *data, size_t len) { + +void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) { if (this->hw_serial_ != nullptr) { this->hw_serial_->write(data, len); } else { @@ -133,28 +134,7 @@ void UARTComponent::write_array(const uint8_t *data, size_t len) { ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); } } -void UARTComponent::write_str(const char *str) { - if (this->hw_serial_ != nullptr) { - this->hw_serial_->write(str); - } else { - const auto *data = reinterpret_cast(str); - for (size_t i = 0; data[i] != 0; i++) - this->sw_serial_->write_byte(data[i]); - } - ESP_LOGVV(TAG, " Wrote \"%s\"", str); -} -bool UARTComponent::read_byte(uint8_t *data) { - if (!this->check_read_timeout_()) - return false; - if (this->hw_serial_ != nullptr) { - *data = this->hw_serial_->read(); - } else { - *data = this->sw_serial_->read_byte(); - } - ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); - return true; -} -bool UARTComponent::peek_byte(uint8_t *data) { +bool ESP8266UartComponent::peek_byte(uint8_t *data) { if (!this->check_read_timeout_()) return false; if (this->hw_serial_ != nullptr) { @@ -164,7 +144,7 @@ bool UARTComponent::peek_byte(uint8_t *data) { } return true; } -bool UARTComponent::read_array(uint8_t *data, size_t len) { +bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) { if (!this->check_read_timeout_(len)) return false; if (this->hw_serial_ != nullptr) { @@ -179,28 +159,14 @@ bool UARTComponent::read_array(uint8_t *data, size_t len) { return true; } -bool UARTComponent::check_read_timeout_(size_t len) { - if (this->available() >= int(len)) - return true; - - uint32_t start_time = millis(); - while (this->available() < int(len)) { - if (millis() - start_time > 100) { - ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); - return false; - } - yield(); - } - return true; -} -int UARTComponent::available() { +int ESP8266UartComponent::available() { if (this->hw_serial_ != nullptr) { return this->hw_serial_->available(); } else { return this->sw_serial_->available(); } } -void UARTComponent::flush() { +void ESP8266UartComponent::flush() { ESP_LOGVV(TAG, " Flushing..."); if (this->hw_serial_ != nullptr) { this->hw_serial_->flush(); @@ -208,32 +174,31 @@ void UARTComponent::flush() { this->sw_serial_->flush(); } } -void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, - uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size) { +void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, + uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity, + size_t rx_buffer_size) { this->bit_time_ = F_CPU / baud_rate; this->rx_buffer_size_ = rx_buffer_size; this->stop_bits_ = stop_bits; this->data_bits_ = data_bits; this->parity_ = parity; - if (tx_pin != -1) { - auto pin = GPIOPin(tx_pin, OUTPUT); - this->gpio_tx_pin_ = &pin; - pin.setup(); - this->tx_pin_ = pin.to_isr(); - this->tx_pin_->digital_write(true); + if (tx_pin != nullptr) { + gpio_tx_pin_ = tx_pin; + gpio_tx_pin_->setup(); + tx_pin_ = gpio_tx_pin_->to_isr(); + tx_pin_.digital_write(true); } - if (rx_pin != -1) { - auto pin = GPIOPin(rx_pin, INPUT); - pin.setup(); - this->gpio_rx_pin_ = &pin; - this->rx_pin_ = pin.to_isr(); - this->rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT - pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING); + if (rx_pin != nullptr) { + gpio_rx_pin_ = rx_pin; + gpio_rx_pin_->setup(); + rx_pin_ = gpio_rx_pin_->to_isr(); + rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT + gpio_rx_pin_->attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE); } } -void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) { +void IRAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) { uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500; - const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) + const uint32_t start = arch_get_cpu_cycle_count(); uint8_t rec = 0; // Manually unroll the loop for (int i = 0; i < arg->data_bits_; i++) @@ -254,10 +219,10 @@ void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg arg->rx_buffer_[arg->rx_in_pos_] = rec; arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_; // Clear RX pin so that the interrupt doesn't re-trigger right away again. - arg->rx_pin_->clear_interrupt(); + arg->rx_pin_.clear_interrupt(); } -void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { - if (this->tx_pin_ == nullptr) { +void IRAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { + if (this->gpio_tx_pin_ == nullptr) { ESP_LOGE(TAG, "UART doesn't have TX pins set!"); return; } @@ -273,7 +238,7 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { { InterruptLock lock; uint32_t wait = this->bit_time_; - const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) + const uint32_t start = arch_get_cpu_cycle_count(); // Start bit this->write_bit_(false, &wait, start); for (int i = 0; i < this->data_bits_; i++) { @@ -290,17 +255,17 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { this->wait_(&wait, start); } } -void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { - while (ESP.getCycleCount() - start < *wait) // NOLINT(readability-static-accessed-through-instance) +void IRAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { + while (arch_get_cpu_cycle_count() - start < *wait) ; *wait += this->bit_time_; } -bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) { +bool IRAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) { this->wait_(wait, start); - return this->rx_pin_->digital_read(); + return this->rx_pin_.digital_read(); } -void ICACHE_RAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { - this->tx_pin_->digital_write(bit); +void IRAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { + this->tx_pin_.digital_write(bit); this->wait_(wait, start); } uint8_t ESP8266SoftwareSerial::read_byte() { @@ -327,4 +292,4 @@ int ESP8266SoftwareSerial::available() { } // namespace uart } // namespace esphome -#endif // ARDUINO_ARCH_ESP8266 +#endif // USE_ESP8266 diff --git a/esphome/components/uart/uart_component_esp8266.h b/esphome/components/uart/uart_component_esp8266.h new file mode 100644 index 0000000000..921d77e4f3 --- /dev/null +++ b/esphome/components/uart/uart_component_esp8266.h @@ -0,0 +1,79 @@ +#pragma once + +#ifdef USE_ESP8266 + +#include +#include +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +class ESP8266SoftwareSerial { + public: + void setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, uint8_t stop_bits, + uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size); + + uint8_t read_byte(); + uint8_t peek_byte(); + + void flush(); + + void write_byte(uint8_t data); + + int available(); + + protected: + static void gpio_intr(ESP8266SoftwareSerial *arg); + + void wait_(uint32_t *wait, const uint32_t &start); + bool read_bit_(uint32_t *wait, const uint32_t &start); + void write_bit_(bool bit, uint32_t *wait, const uint32_t &start); + + uint32_t bit_time_{0}; + uint8_t *rx_buffer_{nullptr}; + size_t rx_buffer_size_; + volatile size_t rx_in_pos_{0}; + size_t rx_out_pos_{0}; + uint8_t stop_bits_; + uint8_t data_bits_; + UARTParityOptions parity_; + InternalGPIOPin *gpio_tx_pin_{nullptr}; + ISRInternalGPIOPin tx_pin_; + InternalGPIOPin *gpio_rx_pin_{nullptr}; + ISRInternalGPIOPin rx_pin_; +}; + +class ESP8266UartComponent : public UARTComponent, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void write_array(const uint8_t *data, size_t len) override; + + bool peek_byte(uint8_t *data) override; + bool read_array(uint8_t *data, size_t len) override; + + int available() override; + void flush() override; + + uint32_t get_config(); + + protected: + void check_logger_conflict() override; + + HardwareSerial *hw_serial_{nullptr}; + ESP8266SoftwareSerial *sw_serial_{nullptr}; + + private: + static bool serial0InUse; +}; + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp new file mode 100644 index 0000000000..3d4a634a72 --- /dev/null +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -0,0 +1,201 @@ +#ifdef USE_ESP_IDF + +#include "uart_component_esp_idf.h" +#include "esphome/core/application.h" +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_LOGGER +#include "esphome/components/logger/logger.h" +#endif + +namespace esphome { +namespace uart { +static const char *const TAG = "uart.idf"; + +uart_config_t IDFUARTComponent::get_config() { + uart_parity_t parity = UART_PARITY_DISABLE; + if (this->parity_ == UART_CONFIG_PARITY_EVEN) + parity = UART_PARITY_EVEN; + else if (this->parity_ == UART_CONFIG_PARITY_ODD) + parity = UART_PARITY_ODD; + + uart_word_length_t data_bits; + switch (this->data_bits_) { + case 5: + data_bits = UART_DATA_5_BITS; + break; + case 6: + data_bits = UART_DATA_6_BITS; + break; + case 7: + data_bits = UART_DATA_7_BITS; + break; + case 8: + data_bits = UART_DATA_8_BITS; + break; + default: + data_bits = UART_DATA_BITS_MAX; + break; + } + + uart_config_t uart_config; + uart_config.baud_rate = this->baud_rate_; + uart_config.data_bits = data_bits; + uart_config.parity = parity; + uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.source_clk = UART_SCLK_APB; + uart_config.rx_flow_ctrl_thresh = 122; + + return uart_config; +} + +void IDFUARTComponent::setup() { + static uint8_t next_uart_num = 0; +#ifdef USE_LOGGER + if (logger::global_logger->get_uart_num() == next_uart_num) + next_uart_num++; +#endif + if (next_uart_num >= UART_NUM_MAX) { + ESP_LOGW(TAG, "Maximum number of UART components created already."); + this->mark_failed(); + return; + } + this->uart_num_ = next_uart_num++; + ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_); + + this->lock_ = xSemaphoreCreateMutex(); + + xSemaphoreTake(this->lock_, portMAX_DELAY); + + uart_config_t uart_config = this->get_config(); + esp_err_t err = uart_param_config(this->uart_num_, &uart_config); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + err = uart_driver_install(this->uart_num_, this->rx_buffer_size_, 0, 0, nullptr, 0); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1; + int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1; + + err = uart_set_pin(this->uart_num_, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_set_pin failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + uint32_t invert = 0; + if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted()) + invert |= UART_SIGNAL_TXD_INV; + if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted()) + invert |= UART_SIGNAL_RXD_INV; + + err = uart_set_line_inverse(this->uart_num_, invert); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_set_line_inverse failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + xSemaphoreGive(this->lock_); +} + +void IDFUARTComponent::dump_config() { + ESP_LOGCONFIG(TAG, "UART Bus:"); + ESP_LOGCONFIG(TAG, " Number: %u", this->uart_num_); + LOG_PIN(" TX Pin: ", tx_pin_); + LOG_PIN(" RX Pin: ", rx_pin_); + if (this->rx_pin_ != nullptr) { + ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); + } + ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); + ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); + ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); + ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + this->check_logger_conflict(); +} + +void IDFUARTComponent::write_array(const uint8_t *data, size_t len) { + xSemaphoreTake(this->lock_, portMAX_DELAY); + uart_write_bytes(this->uart_num_, data, len); + xSemaphoreGive(this->lock_); + for (size_t i = 0; i < len; i++) { + ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + } +} +bool IDFUARTComponent::peek_byte(uint8_t *data) { + if (!this->check_read_timeout_()) + return false; + xSemaphoreTake(this->lock_, portMAX_DELAY); + if (this->has_peek_) + *data = this->peek_byte_; + else { + int len = uart_read_bytes(this->uart_num_, data, 1, 20 / portTICK_RATE_MS); + if (len == 0) { + *data = 0; + } else { + this->has_peek_ = true; + this->peek_byte_ = *data; + } + } + xSemaphoreGive(this->lock_); + return true; +} +bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { + size_t length_to_read = len; + if (!this->check_read_timeout_(len)) + return false; + xSemaphoreTake(this->lock_, portMAX_DELAY); + if (this->has_peek_) { + length_to_read--; + *data = this->peek_byte_; + data++; + this->has_peek_ = false; + } + if (length_to_read > 0) + uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS); + + xSemaphoreGive(this->lock_); + for (size_t i = 0; i < len; i++) { + ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + } + + return true; +} + +int IDFUARTComponent::available() { + size_t available; + + xSemaphoreTake(this->lock_, portMAX_DELAY); + uart_get_buffered_data_len(this->uart_num_, &available); + if (this->has_peek_) + available++; + xSemaphoreGive(this->lock_); + + return available; +} + +void IDFUARTComponent::flush() { + ESP_LOGVV(TAG, " Flushing..."); + xSemaphoreTake(this->lock_, portMAX_DELAY); + uart_wait_tx_done(this->uart_num_, portMAX_DELAY); + xSemaphoreGive(this->lock_); +} + +void IDFUARTComponent::check_logger_conflict() {} + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/uart/uart_component_esp_idf.h b/esphome/components/uart/uart_component_esp_idf.h new file mode 100644 index 0000000000..68cceafda2 --- /dev/null +++ b/esphome/components/uart/uart_component_esp_idf.h @@ -0,0 +1,39 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include +#include "esphome/core/component.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +class IDFUARTComponent : public UARTComponent, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void write_array(const uint8_t *data, size_t len) override; + + bool peek_byte(uint8_t *data) override; + bool read_array(uint8_t *data, size_t len) override; + + int available() override; + void flush() override; + + protected: + void check_logger_conflict() override; + uart_port_t uart_num_; + uart_config_t get_config(); + SemaphoreHandle_t lock_; + + bool has_peek_{false}; + uint8_t peek_byte_; +}; + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/uln2003/uln2003.h b/esphome/components/uln2003/uln2003.h index 4bcf1e88e3..4f559ed9a0 100644 --- a/esphome/components/uln2003/uln2003.h +++ b/esphome/components/uln2003/uln2003.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/stepper/stepper.h" namespace esphome { diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.cpp b/esphome/components/ultrasonic/ultrasonic_sensor.cpp index e53cd7cf7a..9f47f9f6b9 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.cpp +++ b/esphome/components/ultrasonic/ultrasonic_sensor.cpp @@ -1,5 +1,6 @@ #include "ultrasonic_sensor.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ultrasonic { @@ -11,22 +12,31 @@ void UltrasonicSensorComponent::setup() { this->trigger_pin_->setup(); this->trigger_pin_->digital_write(false); this->echo_pin_->setup(); + // isr is faster to access + echo_isr_ = echo_pin_->to_isr(); } void UltrasonicSensorComponent::update() { this->trigger_pin_->digital_write(true); delayMicroseconds(this->pulse_time_us_); this->trigger_pin_->digital_write(false); - uint32_t time = pulseIn( // NOLINT - this->echo_pin_->get_pin(), uint8_t(!this->echo_pin_->is_inverted()), this->timeout_us_); + const uint32_t start = micros(); + while (micros() - start < timeout_us_ && echo_isr_.digital_read()) + ; + while (micros() - start < timeout_us_ && !echo_isr_.digital_read()) + ; + const uint32_t pulse_start = micros(); + while (micros() - start < timeout_us_ && echo_isr_.digital_read()) + ; + const uint32_t pulse_end = micros(); - ESP_LOGV(TAG, "Echo took %uµs", time); + ESP_LOGV(TAG, "Echo took %uµs", pulse_end - pulse_start); - if (time == 0) { + if (pulse_end - start >= timeout_us_) { ESP_LOGD(TAG, "'%s' - Distance measurement timed out!", this->name_.c_str()); this->publish_state(NAN); } else { - float result = UltrasonicSensorComponent::us_to_m(time); + float result = UltrasonicSensorComponent::us_to_m(pulse_end - pulse_start); ESP_LOGD(TAG, "'%s' - Got distance: %.2f m", this->name_.c_str(), result); this->publish_state(result); } diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.h b/esphome/components/ultrasonic/ultrasonic_sensor.h index 633c1b17fb..e0d71b99ef 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.h +++ b/esphome/components/ultrasonic/ultrasonic_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/gpio.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -10,7 +10,7 @@ namespace ultrasonic { class UltrasonicSensorComponent : public sensor::Sensor, public PollingComponent { public: void set_trigger_pin(GPIOPin *trigger_pin) { trigger_pin_ = trigger_pin; } - void set_echo_pin(GPIOPin *echo_pin) { echo_pin_ = echo_pin; } + void set_echo_pin(InternalGPIOPin *echo_pin) { echo_pin_ = echo_pin; } /// Set the timeout for waiting for the echo in µs. void set_timeout_us(uint32_t timeout_us); @@ -34,7 +34,8 @@ class UltrasonicSensorComponent : public sensor::Sensor, public PollingComponent /// Helper function to convert the specified distance in meters to the echo duration in µs. GPIOPin *trigger_pin_; - GPIOPin *echo_pin_; + InternalGPIOPin *echo_pin_; + ISRInternalGPIOPin echo_isr_; uint32_t timeout_us_{}; /// 2 meters. uint32_t pulse_time_us_{}; }; diff --git a/esphome/components/uptime/uptime_sensor.cpp b/esphome/components/uptime/uptime_sensor.cpp index 755795ad53..40325d2a36 100644 --- a/esphome/components/uptime/uptime_sensor.cpp +++ b/esphome/components/uptime/uptime_sensor.cpp @@ -1,6 +1,7 @@ #include "uptime_sensor.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace uptime { diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.cpp b/esphome/components/vl53l0x/vl53l0x_sensor.cpp index 65a4ec72bb..d68d69b79c 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.cpp +++ b/esphome/components/vl53l0x/vl53l0x_sensor.cpp @@ -36,8 +36,6 @@ void VL53L0XSensor::setup() { if (!esphome::vl53l0x::VL53L0XSensor::enable_pin_setup_complete) { for (auto &vl53_sensor : vl53_sensors) { if (vl53_sensor->enable_pin_ != nullptr) { - // Disable the enable pin to force vl53 to HW Standby mode - ESP_LOGD(TAG, "i2c vl53l0x disable enable pins: GPIO%u", (vl53_sensor->enable_pin_)->get_pin()); // Set enable pin as OUTPUT and disable the enable pin to force vl53 to HW Standby mode vl53_sensor->enable_pin_->setup(); vl53_sensor->enable_pin_->digital_write(false); @@ -111,7 +109,7 @@ void VL53L0XSensor::setup() { reg(0xFF) = 0x00; reg(0x80) = 0x00; - uint8_t ref_spad_map[6]; + uint8_t ref_spad_map[6] = {}; this->read_bytes(0xB0, ref_spad_map, 6); reg(0xFF) = 0x01; @@ -294,7 +292,7 @@ void VL53L0XSensor::loop() { } if (this->waiting_for_interrupt_) { if (reg(0x13).get() & 0x07) { - uint16_t range_mm; + uint16_t range_mm = 0; this->read_byte_16(0x14 + 10, &range_mm); reg(0x0B) = 0x01; this->waiting_for_interrupt_ = false; diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.h b/esphome/components/vl53l0x/vl53l0x_sensor.h index 83a6322dcd..a2e24e7550 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.h +++ b/esphome/components/vl53l0x/vl53l0x_sensor.h @@ -3,7 +3,7 @@ #include #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index f82a8893eb..97777a8986 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1,8 +1,11 @@ +#ifdef USE_ARDUINO + #include "web_server.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/util.h" #include "esphome/components/json/json_util.h" +#include "esphome/components/network/util.h" #include "StreamString.h" @@ -154,7 +157,7 @@ void WebServer::setup() { } void WebServer::dump_config() { ESP_LOGCONFIG(TAG, "Web Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->base_->get_port()); + ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port()); if (this->using_auth()) { ESP_LOGCONFIG(TAG, " Basic authentication enabled"); } @@ -853,3 +856,5 @@ bool WebServer::isRequestHandlerTrivial() { return false; } } // namespace web_server } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 54d7356ac9..0eaa2e9a75 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/core/controller.h" #include "esphome/components/web_server_base/web_server_base.h" @@ -192,3 +194,5 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { } // namespace web_server } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 6351941aee..b0babcab46 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -1,12 +1,14 @@ +#ifdef USE_ARDUINO + #include "web_server_base.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #endif @@ -27,12 +29,12 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin if (index == 0) { ESP_LOGI(TAG, "OTA Update Start: %s", filename.c_str()); this->ota_read_length_ = 0; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 Update.runAsync(true); // NOLINTNEXTLINE(readability-static-accessed-through-instance) success = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (Update.isRunning()) Update.abort(); success = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH); @@ -97,3 +99,5 @@ float WebServerBase::get_setup_priority() const { } // namespace web_server_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 158006ae40..4ef67f959c 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include #include "esphome/core/component.h" @@ -74,3 +76,5 @@ class OTARequestHandler : public AsyncWebHandler { } // namespace web_server_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index c2943d0645..7d029bbea1 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -3,7 +3,6 @@ import esphome.config_validation as cv import esphome.final_validate as fv from esphome import automation from esphome.automation import Condition -from esphome.components.network import add_mdns_library from esphome.const import ( CONF_AP, CONF_BSSID, @@ -24,7 +23,6 @@ from esphome.const import ( CONF_STATIC_IP, CONF_SUBNET, CONF_USE_ADDRESS, - CONF_ENABLE_MDNS, CONF_PRIORITY, CONF_IDENTITY, CONF_CERTIFICATE_AUTHORITY, @@ -34,6 +32,7 @@ from esphome.const import ( CONF_EAP, ) from esphome.core import CORE, HexInt, coroutine_with_priority +from esphome.components.network import IPAddress from . import wpa2_eap @@ -41,7 +40,6 @@ AUTO_LOAD = ["network"] wifi_ns = cg.esphome_ns.namespace("wifi") EAPAuth = wifi_ns.struct("EAPAuth") -IPAddress = cg.global_ns.class_("IPAddress") ManualIP = wifi_ns.struct("ManualIP") WiFiComponent = wifi_ns.class_("WiFiComponent", cg.Component) WiFiAP = wifi_ns.struct("WiFiAP") @@ -155,18 +153,17 @@ def final_validate_power_esp32_ble(value): # WiFi should be in modem sleep (!=NONE) with BLE coexistence # https://docs.espressif.com/projects/esp-idf/en/v3.3.5/api-guides/wifi.html#station-sleep return - framework_version = fv.get_arduino_framework_version() - if framework_version not in (None, "dev") and framework_version < "1.0.5": - # Only frameworks 1.0.5+ impacted - return - full = fv.full_config.get() for conflicting in [ "esp32_ble", "esp32_ble_beacon", "esp32_ble_server", "esp32_ble_tracker", ]: - if conflicting in full: + try: + cv.require_framework_version(esp32_arduino=cv.Version(1, 0, 5))(None) + except cv.Invalid: + pass + else: raise cv.Invalid( f"power_save_mode NONE is incompatible with {conflicting}. " f"Please remove the power save mode. See also " @@ -236,7 +233,6 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, cv.Optional(CONF_AP): WIFI_NETWORK_AP, - cv.Optional(CONF_ENABLE_MDNS, default=True): cv.boolean, cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional( CONF_REBOOT_TIMEOUT, default="15min" @@ -249,6 +245,10 @@ CONFIG_SCHEMA = cv.All( cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( cv.decibel, cv.float_range(min=10.0, max=20.5) ), + cv.Optional("enable_mdns"): cv.invalid( + "This option has been removed. Please use the [disabled] option under the " + "new mdns component instead." + ), } ), _validate, @@ -345,9 +345,6 @@ async def to_code(config): cg.add_define("USE_WIFI") - if config[CONF_ENABLE_MDNS]: - add_mdns_library() - # Register at end for OTA safe mode await cg.register_component(var, config) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 0a1e347e8f..6e0ce8c044 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1,9 +1,9 @@ #include "wifi_component.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP32) || defined(USE_ESP_IDF) #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #endif @@ -14,7 +14,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/util.h" #include "esphome/core/application.h" @@ -39,7 +39,7 @@ void WiFiComponent::setup() { this->wifi_pre_setup_(); uint32_t hash = fnv1_hash(App.get_compilation_time()); - this->pref_ = global_preferences.make_preference(hash, true); + this->pref_ = global_preferences->make_preference(hash, true); SavedWifiSettings save{}; if (this->pref_.load(&save)) { @@ -83,12 +83,10 @@ void WiFiComponent::setup() { esp32_improv::global_improv_component->start(); #endif this->wifi_apply_hostname_(); -#if defined(ARDUINO_ARCH_ESP32) && defined(USE_MDNS) - network_setup_mdns(); -#endif } void WiFiComponent::loop() { + this->wifi_loop_(); const uint32_t now = millis(); if (this->has_sta()) { @@ -158,8 +156,6 @@ void WiFiComponent::loop() { } } } - - network_tick_mdns(); } WiFiComponent::WiFiComponent() { global_wifi_component = this; } @@ -167,9 +163,9 @@ WiFiComponent::WiFiComponent() { global_wifi_component = this; } bool WiFiComponent::has_ap() const { return this->has_ap_; } bool WiFiComponent::has_sta() const { return !this->sta_.empty(); } void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = fast_connect; } -IPAddress WiFiComponent::get_ip_address() { +network::IPAddress WiFiComponent::get_ip_address() { if (this->has_sta()) - return this->wifi_sta_ip_(); + return this->wifi_sta_ip(); if (this->has_ap()) return this->wifi_soft_ap_ip(); return {}; @@ -205,16 +201,13 @@ void WiFiComponent::setup_ap_config_() { ESP_LOGCONFIG(TAG, " AP Password: '%s'", this->ap_.get_password().c_str()); if (this->ap_.get_manual_ip().has_value()) { auto manual = *this->ap_.get_manual_ip(); - ESP_LOGCONFIG(TAG, " AP Static IP: '%s'", manual.static_ip.toString().c_str()); - ESP_LOGCONFIG(TAG, " AP Gateway: '%s'", manual.gateway.toString().c_str()); - ESP_LOGCONFIG(TAG, " AP Subnet: '%s'", manual.subnet.toString().c_str()); + ESP_LOGCONFIG(TAG, " AP Static IP: '%s'", manual.static_ip.str().c_str()); + ESP_LOGCONFIG(TAG, " AP Gateway: '%s'", manual.gateway.str().c_str()); + ESP_LOGCONFIG(TAG, " AP Subnet: '%s'", manual.subnet.str().c_str()); } this->ap_setup_ = this->wifi_start_ap_(this->ap_); - ESP_LOGCONFIG(TAG, " IP Address: %s", this->wifi_soft_ap_ip().toString().c_str()); -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) - network_setup_mdns(this->wifi_soft_ap_ip(), 1); -#endif + ESP_LOGCONFIG(TAG, " IP Address: %s", this->wifi_soft_ap_ip().str().c_str()); if (!this->has_sta()) { this->state_ = WIFI_COMPONENT_STATE_AP; @@ -286,9 +279,8 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { } if (ap.get_manual_ip().has_value()) { ManualIP m = *ap.get_manual_ip(); - ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.toString().c_str(), - m.gateway.toString().c_str(), m.subnet.toString().c_str(), m.dns1.toString().c_str(), - m.dns2.toString().c_str()); + ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.str().c_str(), + m.gateway.str().c_str(), m.subnet.str().c_str(), m.dns1.str().c_str(), m.dns2.str().c_str()); } else { ESP_LOGV(TAG, " Using DHCP IP"); } @@ -353,26 +345,23 @@ const LogString *get_signal_bars(int8_t rssi) { } void WiFiComponent::print_connect_params_() { - uint8_t bssid[6] = {}; - uint8_t *raw_bssid = WiFi.BSSID(); - if (raw_bssid != nullptr) - memcpy(bssid, raw_bssid, sizeof(bssid)); + bssid_t bssid = wifi_bssid(); - ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), WiFi.SSID().c_str()); - ESP_LOGCONFIG(TAG, " IP Address: %s", WiFi.localIP().toString().c_str()); + ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str()); + ESP_LOGCONFIG(TAG, " IP Address: %s", wifi_sta_ip().str().c_str()); ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - int8_t rssi = WiFi.RSSI(); + int8_t rssi = wifi_rssi(); ESP_LOGCONFIG(TAG, " Signal strength: %d dB %s", rssi, LOG_STR_ARG(get_signal_bars(rssi))); if (this->selected_ap_.get_bssid().has_value()) { ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid())); } - ESP_LOGCONFIG(TAG, " Channel: %d", WiFi.channel()); - ESP_LOGCONFIG(TAG, " Subnet: %s", WiFi.subnetMask().toString().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", WiFi.gatewayIP().toString().c_str()); - ESP_LOGCONFIG(TAG, " DNS1: %s", WiFi.dnsIP(0).toString().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", WiFi.dnsIP(1).toString().c_str()); + ESP_LOGCONFIG(TAG, " Channel: %d", wifi_channel_()); + ESP_LOGCONFIG(TAG, " Subnet: %s", wifi_subnet_mask_().str().c_str()); + ESP_LOGCONFIG(TAG, " Gateway: %s", wifi_gateway_ip_().str().c_str()); + ESP_LOGCONFIG(TAG, " DNS1: %s", wifi_dns_ip_(0).str().c_str()); + ESP_LOGCONFIG(TAG, " DNS2: %s", wifi_dns_ip_(1).str().c_str()); } void WiFiComponent::start_scanning() { @@ -500,10 +489,10 @@ void WiFiComponent::dump_config() { } void WiFiComponent::check_connecting_finished() { - wl_status_t status = this->wifi_sta_status_(); + auto status = this->wifi_sta_connect_status_(); - if (status == WL_CONNECTED) { - if (WiFi.SSID().equals("")) { + if (status == WiFiSTAConnectStatus::CONNECTED) { + if (wifi_ssid().empty()) { ESP_LOGW(TAG, "Incomplete connection."); this->retry_connect(); return; @@ -527,9 +516,6 @@ void WiFiComponent::check_connecting_finished() { } #endif -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) - network_setup_mdns(this->wifi_sta_ip_(), 0); -#endif this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED; this->num_retried_ = 0; return; @@ -548,26 +534,23 @@ void WiFiComponent::check_connecting_finished() { return; } - if (status == WL_IDLE_STATUS || status == WL_DISCONNECTED || status == WL_CONNECTION_LOST) { - // WL_DISCONNECTED is set while not connected yet. - // WL_IDLE_STATUS is set while we're waiting for the IP address. - // WL_CONNECTION_LOST happens on the ESP32 + if (status == WiFiSTAConnectStatus::CONNECTING) { return; } - if (status == WL_NO_SSID_AVAIL) { + if (status == WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND) { ESP_LOGW(TAG, "WiFi network can not be found anymore."); this->retry_connect(); return; } - if (status == WL_CONNECT_FAILED) { + if (status == WiFiSTAConnectStatus::ERROR_CONNECT_FAILED) { ESP_LOGW(TAG, "Connecting to WiFi network failed. Are the credentials wrong?"); this->retry_connect(); return; } - ESP_LOGW(TAG, "WiFi Unknown connection status %d", status); + ESP_LOGW(TAG, "WiFi Unknown connection status %d", (int) status); } void WiFiComponent::retry_connect() { @@ -608,8 +591,8 @@ bool WiFiComponent::can_proceed() { } void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } bool WiFiComponent::is_connected() { - return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED && this->wifi_sta_status_() == WL_CONNECTED && - !this->error_from_callback_; + return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED && + this->wifi_sta_connect_status_() == WiFiSTAConnectStatus::CONNECTED && !this->error_from_callback_; } void WiFiComponent::set_power_save_mode(WiFiPowerSaveMode power_save) { this->power_save_ = power_save; } @@ -641,7 +624,7 @@ void WiFiAP::set_password(const std::string &password) { this->password_ = passw void WiFiAP::set_eap(optional eap_auth) { this->eap_ = std::move(eap_auth); } #endif void WiFiAP::set_channel(optional channel) { this->channel_ = channel; } -void WiFiAP::set_manual_ip(optional manual_ip) { this->manual_ip_ = std::move(manual_ip); } +void WiFiAP::set_manual_ip(optional manual_ip) { this->manual_ip_ = manual_ip; } void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; } const std::string &WiFiAP::get_ssid() const { return this->ssid_; } const optional &WiFiAP::get_bssid() const { return this->bssid_; } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 3a4213c93c..d04f15695d 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -5,20 +5,20 @@ #include "esphome/core/defines.h" #include "esphome/core/automation.h" #include "esphome/core/helpers.h" +#include "esphome/components/network/ip_address.h" #include -#include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #include #include #endif -#ifdef ARDUINO_ARCH_ESP8266 -#include +#ifdef USE_ESP8266 #include +#include -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) +#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) extern "C" { #include }; @@ -54,13 +54,21 @@ enum WiFiComponentState { WIFI_COMPONENT_STATE_AP, }; +enum class WiFiSTAConnectStatus : int { + IDLE, + CONNECTING, + CONNECTED, + ERROR_NETWORK_NOT_FOUND, + ERROR_CONNECT_FAILED, +}; + /// Struct for setting static IPs in WiFiComponent. struct ManualIP { - IPAddress static_ip; - IPAddress gateway; - IPAddress subnet; - IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. - IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. + network::IPAddress static_ip; + network::IPAddress gateway; + network::IPAddress subnet; + network::IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. + network::IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. }; #ifdef USE_WIFI_WPA2_EAP @@ -153,6 +161,10 @@ enum WiFiPowerSaveMode { WIFI_POWER_SAVE_HIGH, }; +#ifdef USE_ESP_IDF +struct IDFWiFiEvent; +#endif + /// This component is responsible for managing the ESP WiFi interface. class WiFiComponent : public Component { public: @@ -207,13 +219,13 @@ class WiFiComponent : public Component { bool has_sta() const; bool has_ap() const; - IPAddress get_ip_address(); + network::IPAddress get_ip_address(); std::string get_use_address() const; void set_use_address(const std::string &use_address); const std::vector &get_scan_result() const { return scan_result_; } - IPAddress wifi_soft_ap_ip(); + network::IPAddress wifi_soft_ap_ip(); bool has_sta_priority(const bssid_t &bssid) { for (auto &it : this->sta_priorities_) @@ -239,36 +251,46 @@ class WiFiComponent : public Component { }); } + network::IPAddress wifi_sta_ip(); + std::string wifi_ssid(); + bssid_t wifi_bssid(); + + int8_t wifi_rssi(); + protected: static std::string format_mac_addr(const uint8_t mac[6]); void setup_ap_config_(); void print_connect_params_(); + void wifi_loop_(); bool wifi_mode_(optional sta, optional ap); bool wifi_sta_pre_setup_(); bool wifi_apply_output_power_(float output_power); bool wifi_apply_power_save_(); bool wifi_sta_ip_config_(optional manual_ip); - IPAddress wifi_sta_ip_(); bool wifi_apply_hostname_(); bool wifi_sta_connect_(const WiFiAP &ap); void wifi_pre_setup_(); - wl_status_t wifi_sta_status_(); + WiFiSTAConnectStatus wifi_sta_connect_status_(); bool wifi_scan_start_(); bool wifi_ap_ip_config_(optional manual_ip); bool wifi_start_ap_(const WiFiAP &ap); bool wifi_disconnect_(); + int32_t wifi_channel_(); + network::IPAddress wifi_subnet_mask_(); + network::IPAddress wifi_gateway_ip_(); + network::IPAddress wifi_dns_ip_(int num); bool is_captive_portal_active_(); bool is_esp32_improv_active_(); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 static void wifi_event_callback(System_Event_t *event); void wifi_scan_done_callback_(void *arg, STATUS status); static void s_wifi_scan_done_callback(void *arg, STATUS status); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #if ESP_IDF_VERSION_MAJOR >= 4 void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info); #else @@ -276,6 +298,9 @@ class WiFiComponent : public Component { #endif void wifi_scan_done_callback_(); #endif +#ifdef USE_ESP_IDF + void wifi_process_event_(IDFWiFiEvent *); +#endif std::string use_address_; std::vector sta_; diff --git a/esphome/components/wifi/wifi_component_esp32.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp similarity index 89% rename from esphome/components/wifi/wifi_component_esp32.cpp rename to esphome/components/wifi/wifi_component_esp32_arduino.cpp index 38c9e12bec..df2692ea81 100644 --- a/esphome/components/wifi/wifi_component_esp32.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -1,6 +1,6 @@ #include "wifi_component.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include @@ -15,7 +15,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/application.h" #include "esphome/core/util.h" @@ -24,6 +24,12 @@ namespace wifi { static const char *const TAG = "wifi_esp32"; +static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + bool WiFiComponent::wifi_mode_(optional sta, optional ap) { uint8_t current_mode = WiFiClass::getMode(); bool current_sta = current_mode & 0b01; @@ -139,12 +145,12 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return true; } -IPAddress WiFiComponent::wifi_sta_ip_() { +network::IPAddress WiFiComponent::wifi_sta_ip() { if (!this->has_sta()) - return IPAddress(); + return {}; tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); - return IPAddress(ip.ip.addr); + return {ip.ip.addr}; } bool WiFiComponent::wifi_apply_hostname_() { @@ -280,6 +286,12 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { return false; } + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + return true; } const char *get_auth_mode_str(uint8_t mode) { @@ -429,6 +441,7 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i buf[it.ssid_len] = '\0'; ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); + s_sta_connected = true; break; } case SYSTEM_EVENT_STA_DISCONNECTED: { @@ -442,10 +455,14 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i buf[it.ssid_len] = '\0'; if (it.reason == WIFI_REASON_NO_AP_FOUND) { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + s_sta_connect_not_found = true; } else { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); + s_sta_connect_error = true; } + s_sta_connected = false; + s_sta_connecting = false; break; } case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: { @@ -474,10 +491,12 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i auto it = info.got_ip.ip_info; ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(), format_ip4_addr(it.gw).c_str()); + s_sta_got_ip = true; break; } case SYSTEM_EVENT_STA_LOST_IP: { ESP_LOGV(TAG, "Event: Lost IP"); + s_sta_got_ip = false; break; } case SYSTEM_EVENT_AP_START: { @@ -559,7 +578,21 @@ void WiFiComponent::wifi_pre_setup_() { // Make sure WiFi is in clean state before anything starts this->wifi_mode_(false, false); } -wl_status_t WiFiComponent::wifi_sta_status_() { return WiFiClass::status(); } +WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { + if (s_sta_connected && s_sta_got_ip) { + return WiFiSTAConnectStatus::CONNECTED; + } + if (s_sta_connect_error) { + return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; + } + if (s_sta_connect_not_found) { + return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; + } + if (s_sta_connecting) { + return WiFiSTAConnectStatus::CONNECTING; + } + return WiFiSTAConnectStatus::IDLE; +} bool WiFiComponent::wifi_scan_start_() { // enable STA if (!this->wifi_mode_(true, {})) @@ -610,9 +643,9 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { info.gw.addr = static_cast(manual_ip->gateway); info.netmask.addr = static_cast(manual_ip->subnet); } else { - info.ip.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.gw.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.netmask.addr = static_cast(IPAddress(255, 255, 255, 0)); + info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); } tcpip_adapter_dhcp_status_t dhcp_status; tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); @@ -630,13 +663,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { dhcps_lease_t lease; lease.enable = true; - IPAddress start_address = info.ip.addr; + network::IPAddress start_address = info.ip.addr; start_address[3] += 99; lease.start_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); start_address[3] += 100; lease.end_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); err = tcpip_adapter_dhcps_option(TCPIP_ADAPTER_OP_SET, TCPIP_ADAPTER_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); if (err != ESP_OK) { @@ -694,14 +727,31 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return true; } -IPAddress WiFiComponent::wifi_soft_ap_ip() { +network::IPAddress WiFiComponent::wifi_soft_ap_ip() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); - return IPAddress(ip.ip.addr); + return {ip.ip.addr}; } bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); } +bssid_t WiFiComponent::wifi_bssid() { + bssid_t bssid{}; + uint8_t *raw_bssid = WiFi.BSSID(); + if (raw_bssid != nullptr) { + for (size_t i = 0; i < bssid.size(); i++) + bssid[i] = raw_bssid[i]; + } + return bssid; +} +std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } +int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } +int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } +network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } +void WiFiComponent::wifi_loop_() {} + } // namespace wifi } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 5a0ba92b09..5dcfe7a108 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -1,7 +1,7 @@ #include "wifi_component.h" #include "esphome/core/macros.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include @@ -30,7 +30,7 @@ extern "C" { #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/util.h" #include "esphome/core/application.h" @@ -39,6 +39,12 @@ namespace wifi { static const char *const TAG = "wifi_esp8266"; +static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + bool WiFiComponent::wifi_mode_(optional sta, optional ap) { uint8_t current_mode = wifi_get_opmode(); bool current_sta = current_mode & 0b01; @@ -177,7 +183,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return ret; } -IPAddress WiFiComponent::wifi_sta_ip_() { +network::IPAddress WiFiComponent::wifi_sta_ip() { if (!this->has_sta()) return {}; struct ip_info ip {}; @@ -319,6 +325,12 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { } } + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + return true; } @@ -453,6 +465,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { buf[it.ssid_len] = '\0'; ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=%s channel=%u", buf, format_mac_addr(it.bssid).c_str(), it.channel); + s_sta_connected = true; break; } case EVENT_STAMODE_DISCONNECTED: { @@ -462,10 +475,14 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { buf[it.ssid_len] = '\0'; if (it.reason == REASON_NO_AP_FOUND) { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + s_sta_connect_not_found = true; } else { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), LOG_STR_ARG(get_disconnect_reason_str(it.reason))); + s_sta_connect_error = true; } + s_sta_connected = false; + s_sta_connecting = false; break; } case EVENT_STAMODE_AUTHMODE_CHANGE: { @@ -487,6 +504,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { auto it = event->event_info.got_ip; ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s netmask=%s", format_ip_addr(it.ip).c_str(), format_ip_addr(it.gw).c_str(), format_ip_addr(it.mask).c_str()); + s_sta_got_ip = true; break; } case EVENT_STAMODE_DHCP_TIMEOUT: { @@ -563,21 +581,22 @@ void WiFiComponent::wifi_pre_setup_() { this->wifi_mode_(false, false); } -wl_status_t WiFiComponent::wifi_sta_status_() { +WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { station_status_t status = wifi_station_get_connect_status(); switch (status) { case STATION_GOT_IP: - return WL_CONNECTED; + return WiFiSTAConnectStatus::CONNECTED; case STATION_NO_AP_FOUND: - return WL_NO_SSID_AVAIL; + return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; + ; case STATION_CONNECT_FAIL: case STATION_WRONG_PASSWORD: - return WL_CONNECT_FAILED; - case STATION_IDLE: - return WL_IDLE_STATUS; + return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; case STATION_CONNECTING: + return WiFiSTAConnectStatus::CONNECTING; + case STATION_IDLE: default: - return WL_DISCONNECTED; + return WiFiSTAConnectStatus::IDLE; } } bool WiFiComponent::wifi_scan_start_() { @@ -656,9 +675,9 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { info.gw.addr = static_cast(manual_ip->gateway); info.netmask.addr = static_cast(manual_ip->subnet); } else { - info.ip.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.gw.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.netmask.addr = static_cast(IPAddress(255, 255, 255, 0)); + info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); } if (wifi_softap_dhcps_status() == DHCP_STARTED) { @@ -677,13 +696,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { #endif struct dhcps_lease lease {}; - IPAddress start_address = info.ip.addr; + network::IPAddress start_address = info.ip.addr; start_address[3] += 99; lease.start_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); start_address[3] += 100; lease.end_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); if (!wifi_softap_set_dhcps_lease(&lease)) { ESP_LOGV(TAG, "Setting SoftAP DHCP lease failed!"); return false; @@ -746,11 +765,27 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return true; } -IPAddress WiFiComponent::wifi_soft_ap_ip() { +network::IPAddress WiFiComponent::wifi_soft_ap_ip() { struct ip_info ip {}; wifi_get_ip_info(SOFTAP_IF, &ip); return {ip.ip.addr}; } +bssid_t WiFiComponent::wifi_bssid() { + bssid_t bssid{}; + uint8_t *raw_bssid = WiFi.BSSID(); + if (raw_bssid != nullptr) { + for (size_t i = 0; i < bssid.size(); i++) + bssid[i] = raw_bssid[i]; + } + return bssid; +} +std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } +int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } +int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } +network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } +void WiFiComponent::wifi_loop_() {} } // namespace wifi } // namespace esphome diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp new file mode 100644 index 0000000000..676b7f8fba --- /dev/null +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -0,0 +1,901 @@ +#include "wifi_component.h" + +#ifdef USE_ESP_IDF + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef USE_WIFI_WPA2_EAP +#include +#endif +#include "lwip/err.h" +#include "lwip/dns.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include "esphome/core/application.h" +#include "esphome/core/util.h" + +namespace esphome { +namespace wifi { + +static const char *const TAG = "wifi_esp32"; + +static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static xQueueHandle s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_ap_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_wifi_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +struct IDFWiFiEvent { + esp_event_base_t event_base; + int32_t event_id; + union { + wifi_event_sta_scan_done_t sta_scan_done; + wifi_event_sta_connected_t sta_connected; + wifi_event_sta_disconnected_t sta_disconnected; + wifi_event_sta_authmode_change_t sta_authmode_change; + wifi_event_ap_staconnected_t ap_staconnected; + wifi_event_ap_stadisconnected_t ap_stadisconnected; + wifi_event_ap_probe_req_rx_t ap_probe_req_rx; + wifi_event_bss_rssi_low_t bss_rssi_low; + ip_event_got_ip_t ip_got_ip; + ip_event_ap_staipassigned_t ip_ap_staipassigned; + } data; +}; + +// general design: event handler translates events and pushes them to a queue, +// events get processed in the main loop +void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + IDFWiFiEvent event; + memset(&event, 0, sizeof(IDFWiFiEvent)); + event.event_base = event_base; + event.event_id = event_id; + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { + memcpy(&event.data.sta_authmode_change, event_data, sizeof(wifi_event_sta_authmode_change_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { + memcpy(&event.data.sta_connected, event_data, sizeof(wifi_event_sta_connected_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + memcpy(&event.data.sta_disconnected, event_data, sizeof(wifi_event_sta_disconnected_t)); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + memcpy(&event.data.ip_got_ip, event_data, sizeof(ip_event_got_ip_t)); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { + memcpy(&event.data.sta_scan_done, event_data, sizeof(wifi_event_sta_scan_done_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_START) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STOP) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_PROBEREQRECVED) { + memcpy(&event.data.ap_probe_req_rx, event_data, sizeof(wifi_event_ap_probe_req_rx_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) { + memcpy(&event.data.ap_staconnected, event_data, sizeof(wifi_event_ap_staconnected_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) { + memcpy(&event.data.ap_stadisconnected, event_data, sizeof(wifi_event_ap_stadisconnected_t)); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_AP_STAIPASSIGNED) { + memcpy(&event.data.ip_ap_staipassigned, event_data, sizeof(ip_event_ap_staipassigned_t)); + } else { + // did not match any event, don't send anything + return; + } + + // copy to heap to keep queue object small + auto *to_send = new IDFWiFiEvent; // NOLINT(cppcoreguidelines-owning-memory) + memcpy(to_send, &event, sizeof(IDFWiFiEvent)); + // don't block, we may miss events but the core can handle that + if (xQueueSend(s_event_queue, &to_send, 0L) != pdPASS) { + delete to_send; // NOLINT(cppcoreguidelines-owning-memory) + } +} + +void WiFiComponent::wifi_pre_setup_() { + esp_err_t err = esp_netif_init(); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_netif_init failed: %s", esp_err_to_name(err)); + return; + } + s_wifi_event_group = xEventGroupCreate(); + if (s_wifi_event_group == nullptr) { + ESP_LOGE(TAG, "xEventGroupCreate failed"); + return; + } + // NOLINTNEXTLINE(bugprone-sizeof-expression) + s_event_queue = xQueueCreate(64, sizeof(IDFWiFiEvent *)); + if (s_event_queue == nullptr) { + ESP_LOGE(TAG, "xQueueCreate failed"); + return; + } + err = esp_event_loop_create_default(); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(err)); + return; + } + esp_event_handler_instance_t instance_wifi_id, instance_ip_id; + err = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, nullptr, &instance_wifi_id); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_event_handler_instance_register failed: %s", esp_err_to_name(err)); + return; + } + err = esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, nullptr, &instance_ip_id); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_event_handler_instance_register failed: %s", esp_err_to_name(err)); + return; + } + + s_sta_netif = esp_netif_create_default_wifi_sta(); + s_ap_netif = esp_netif_create_default_wifi_ap(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + // cfg.nvs_enable = false; + err = esp_wifi_init(&cfg); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_wifi_init failed: %s", esp_err_to_name(err)); + return; + } + err = esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_wifi_set_storage failed: %s", esp_err_to_name(err)); + return; + } +} + +bool WiFiComponent::wifi_mode_(optional sta, optional ap) { + esp_err_t err; + wifi_mode_t current_mode = WIFI_MODE_NULL; + if (s_wifi_started) { + err = esp_wifi_get_mode(¤t_mode); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_wifi_get_mode failed: %s", esp_err_to_name(err)); + return false; + } + } + bool current_sta = current_mode == WIFI_MODE_STA || current_mode == WIFI_MODE_APSTA; + bool current_ap = current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA; + + bool set_sta = sta.has_value() ? *sta : current_sta; + bool set_ap = ap.has_value() ? *ap : current_ap; + + wifi_mode_t set_mode; + if (set_sta && set_ap) + set_mode = WIFI_MODE_APSTA; + else if (set_sta && !set_ap) + set_mode = WIFI_MODE_STA; + else if (!set_sta && set_ap) + set_mode = WIFI_MODE_AP; + else + set_mode = WIFI_MODE_NULL; + + if (current_mode == set_mode) + return true; + + if (set_sta && !current_sta) { + ESP_LOGV(TAG, "Enabling STA."); + } else if (!set_sta && current_sta) { + ESP_LOGV(TAG, "Disabling STA."); + } + if (set_ap && !current_ap) { + ESP_LOGV(TAG, "Enabling AP."); + } else if (!set_ap && current_ap) { + ESP_LOGV(TAG, "Disabling AP."); + } + + if (set_mode == WIFI_MODE_NULL && s_wifi_started) { + err = esp_wifi_stop(); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_stop failed: %s", esp_err_to_name(err)); + return false; + } + s_wifi_started = false; + return true; + } + + err = esp_wifi_set_mode(set_mode); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_wifi_set_mode failed: %s", esp_err_to_name(err)); + return false; + } + + if (set_mode != WIFI_MODE_NULL && !s_wifi_started) { + err = esp_wifi_start(); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_start failed: %s", esp_err_to_name(err)); + return false; + } + s_wifi_started = true; + } + + return true; +} + +bool WiFiComponent::wifi_sta_pre_setup_() { return this->wifi_mode_(true, {}); } + +bool WiFiComponent::wifi_apply_output_power_(float output_power) { + int8_t val = static_cast(output_power * 4); + return esp_wifi_set_max_tx_power(val) == ESP_OK; +} + +bool WiFiComponent::wifi_apply_power_save_() { + wifi_ps_type_t power_save; + switch (this->power_save_) { + case WIFI_POWER_SAVE_LIGHT: + power_save = WIFI_PS_MIN_MODEM; + break; + case WIFI_POWER_SAVE_HIGH: + power_save = WIFI_PS_MAX_MODEM; + break; + case WIFI_POWER_SAVE_NONE: + default: + power_save = WIFI_PS_NONE; + break; + } + return esp_wifi_set_ps(power_save) == ESP_OK; +} + +bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { + // enable STA + if (!this->wifi_mode_(true, {})) + return false; + + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t + wifi_config_t conf; + memset(&conf, 0, sizeof(conf)); + strncpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid)); + strncpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password)); + + // The weakest authmode to accept in the fast scan mode + if (ap.get_password().empty()) { + conf.sta.threshold.authmode = WIFI_AUTH_OPEN; + } else { + conf.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK; + } + +#ifdef USE_WIFI_WPA2_EAP + if (ap.get_eap().has_value()) { + conf.sta.threshold.authmode = WIFI_AUTH_WPA2_ENTERPRISE; + } +#endif + + if (ap.get_bssid().has_value()) { + conf.sta.bssid_set = true; + memcpy(conf.sta.bssid, ap.get_bssid()->data(), 6); + } else { + conf.sta.bssid_set = false; + } + if (ap.get_channel().has_value()) { + conf.sta.channel = *ap.get_channel(); + conf.sta.scan_method = WIFI_FAST_SCAN; + } else { + conf.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + } + // Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set. + // Units: AP beacon intervals. Defaults to 3 if set to 0. + conf.sta.listen_interval = 0; + +#if ESP_IDF_VERSION_MAJOR >= 4 + // Protected Management Frame + // Device will prefer to connect in PMF mode if other device also advertizes PMF capability. + conf.sta.pmf_cfg.capable = true; + conf.sta.pmf_cfg.required = false; +#endif + + // note, we do our own filtering + // The minimum rssi to accept in the fast scan mode + conf.sta.threshold.rssi = -127; + + conf.sta.threshold.authmode = WIFI_AUTH_OPEN; + + wifi_config_t current_conf; + esp_err_t err; + err = esp_wifi_get_config(WIFI_IF_STA, ¤t_conf); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_wifi_get_config failed: %s", esp_err_to_name(err)); + // can continue + } + + if (memcmp(¤t_conf, &conf, sizeof(wifi_config_t)) != 0) { + err = esp_wifi_disconnect(); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_disconnect failed: %s", esp_err_to_name(err)); + return false; + } + } + + err = esp_wifi_set_config(WIFI_IF_STA, &conf); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(err)); + return false; + } + + if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) { + return false; + } + + // setup enterprise authentication if required +#ifdef USE_WIFI_WPA2_EAP + if (ap.get_eap().has_value()) { + // note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0. + EAPAuth eap = ap.get_eap().value(); + err = esp_wifi_sta_wpa2_ent_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length()); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", err); + } + int ca_cert_len = strlen(eap.ca_cert); + int client_cert_len = strlen(eap.client_cert); + int client_key_len = strlen(eap.client_key); + if (ca_cert_len) { + err = esp_wifi_sta_wpa2_ent_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", err); + } + } + // workout what type of EAP this is + // validation is not required as the config tool has already validated it + if (client_cert_len && client_key_len) { + // if we have certs, this must be EAP-TLS + err = esp_wifi_sta_wpa2_ent_set_cert_key((uint8_t *) eap.client_cert, client_cert_len + 1, + (uint8_t *) eap.client_key, client_key_len + 1, + (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str())); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", err); + } + } else { + // in the absence of certs, assume this is username/password based + err = esp_wifi_sta_wpa2_ent_set_username((uint8_t *) eap.username.c_str(), eap.username.length()); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", err); + } + err = esp_wifi_sta_wpa2_ent_set_password((uint8_t *) eap.password.c_str(), eap.password.length()); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err); + } + } + esp_wpa2_config_t wpa2_config = WPA2_CONFIG_INIT_DEFAULT(); + err = esp_wifi_sta_wpa2_ent_enable(&wpa2_config); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err); + } + } +#endif // USE_WIFI_WPA2_EAP + + err = esp_wifi_connect(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(err)); + return false; + } + + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + return true; +} + +bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { + // enable STA + if (!this->wifi_mode_(true, {})) + return false; + + tcpip_adapter_dhcp_status_t dhcp_status; + esp_err_t err = tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dhcp_status); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcpc_get_status failed: %s", esp_err_to_name(err)); + return false; + } + + if (!manual_ip.has_value()) { + // Use DHCP client + if (dhcp_status != TCPIP_ADAPTER_DHCP_STARTED) { + err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); + if (err != ESP_OK) { + ESP_LOGV(TAG, "Starting DHCP client failed! %d", err); + } + return err == ESP_OK; + } + return true; + } + + tcpip_adapter_ip_info_t info; + memset(&info, 0, sizeof(info)); + info.ip.addr = static_cast(manual_ip->static_ip); + info.gw.addr = static_cast(manual_ip->gateway); + info.netmask.addr = static_cast(manual_ip->subnet); + + err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcpc_stop failed: %s", esp_err_to_name(err)); + return false; + } + + err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_set_ip_info failed: %s", esp_err_to_name(err)); + return false; + } + + ip_addr_t dns; + dns.type = IPADDR_TYPE_V4; + if (uint32_t(manual_ip->dns1) != 0) { + dns.u_addr.ip4.addr = static_cast(manual_ip->dns1); + dns_setserver(0, &dns); + } + if (uint32_t(manual_ip->dns2) != 0) { + dns.u_addr.ip4.addr = static_cast(manual_ip->dns2); + dns_setserver(1, &dns); + } + + return true; +} + +network::IPAddress WiFiComponent::wifi_sta_ip() { + if (!this->has_sta()) + return {}; + tcpip_adapter_ip_info_t ip; + esp_err_t err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_get_ip_info failed: %s", esp_err_to_name(err)); + return false; + } + return {ip.ip.addr}; +} + +bool WiFiComponent::wifi_apply_hostname_() { + // setting is done in SYSTEM_EVENT_STA_START callback + return true; +} +const char *get_auth_mode_str(uint8_t mode) { + switch (mode) { + case WIFI_AUTH_OPEN: + return "OPEN"; + case WIFI_AUTH_WEP: + return "WEP"; + case WIFI_AUTH_WPA_PSK: + return "WPA PSK"; + case WIFI_AUTH_WPA2_PSK: + return "WPA2 PSK"; + case WIFI_AUTH_WPA_WPA2_PSK: + return "WPA/WPA2 PSK"; + case WIFI_AUTH_WPA2_ENTERPRISE: + return "WPA2 Enterprise"; + case WIFI_AUTH_WPA3_PSK: + return "WPA3 PSK"; + case WIFI_AUTH_WPA2_WPA3_PSK: + return "WPA2/WPA3 PSK"; + case WIFI_AUTH_WAPI_PSK: + return "WAPI PSK"; + default: + return "UNKNOWN"; + } +} + +std::string format_ip4_addr(const esp_ip4_addr_t &ip) { + char buf[20]; + snprintf(buf, sizeof(buf), "%u.%u.%u.%u", uint8_t(ip.addr >> 0), uint8_t(ip.addr >> 8), uint8_t(ip.addr >> 16), + uint8_t(ip.addr >> 24)); + return buf; +} +const char *get_disconnect_reason_str(uint8_t reason) { + switch (reason) { + case WIFI_REASON_AUTH_EXPIRE: + return "Auth Expired"; + case WIFI_REASON_AUTH_LEAVE: + return "Auth Leave"; + case WIFI_REASON_ASSOC_EXPIRE: + return "Association Expired"; + case WIFI_REASON_ASSOC_TOOMANY: + return "Too Many Associations"; + case WIFI_REASON_NOT_AUTHED: + return "Not Authenticated"; + case WIFI_REASON_NOT_ASSOCED: + return "Not Associated"; + case WIFI_REASON_ASSOC_LEAVE: + return "Association Leave"; + case WIFI_REASON_ASSOC_NOT_AUTHED: + return "Association not Authenticated"; + case WIFI_REASON_DISASSOC_PWRCAP_BAD: + return "Disassociate Power Cap Bad"; + case WIFI_REASON_DISASSOC_SUPCHAN_BAD: + return "Disassociate Supported Channel Bad"; + case WIFI_REASON_IE_INVALID: + return "IE Invalid"; + case WIFI_REASON_MIC_FAILURE: + return "Mic Failure"; + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: + return "4-Way Handshake Timeout"; + case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: + return "Group Key Update Timeout"; + case WIFI_REASON_IE_IN_4WAY_DIFFERS: + return "IE In 4-Way Handshake Differs"; + case WIFI_REASON_GROUP_CIPHER_INVALID: + return "Group Cipher Invalid"; + case WIFI_REASON_PAIRWISE_CIPHER_INVALID: + return "Pairwise Cipher Invalid"; + case WIFI_REASON_AKMP_INVALID: + return "AKMP Invalid"; + case WIFI_REASON_UNSUPP_RSN_IE_VERSION: + return "Unsupported RSN IE version"; + case WIFI_REASON_INVALID_RSN_IE_CAP: + return "Invalid RSN IE Cap"; + case WIFI_REASON_802_1X_AUTH_FAILED: + return "802.1x Authentication Failed"; + case WIFI_REASON_CIPHER_SUITE_REJECTED: + return "Cipher Suite Rejected"; + case WIFI_REASON_BEACON_TIMEOUT: + return "Beacon Timeout"; + case WIFI_REASON_NO_AP_FOUND: + return "AP Not Found"; + case WIFI_REASON_AUTH_FAIL: + return "Authentication Failed"; + case WIFI_REASON_ASSOC_FAIL: + return "Association Failed"; + case WIFI_REASON_HANDSHAKE_TIMEOUT: + return "Handshake Failed"; + case WIFI_REASON_CONNECTION_FAIL: + return "Connection Failed"; + case WIFI_REASON_UNSPECIFIED: + default: + return "Unspecified"; + } +} + +void WiFiComponent::wifi_loop_() { + while (true) { + IDFWiFiEvent *data; + if (xQueueReceive(s_event_queue, &data, 0L) != pdTRUE) { + // no event ready + break; + } + + // process event + wifi_process_event_(data); + + delete data; // NOLINT(cppcoreguidelines-owning-memory) + } +} +void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { + esp_err_t err; + if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_START) { + ESP_LOGV(TAG, "Event: WiFi STA start"); + // apply hostname + err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, App.get_name().c_str()); + if (err != ERR_OK) { + ESP_LOGW(TAG, "tcpip_adapter_set_hostname failed: %s", esp_err_to_name(err)); + } + + s_sta_started = true; + // re-apply power save mode + wifi_apply_power_save_(); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) { + ESP_LOGV(TAG, "Event: WiFi STA stop"); + s_sta_started = false; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { + const auto &it = data->data.sta_authmode_change; + ESP_LOGV(TAG, "Event: Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), + get_auth_mode_str(it.new_mode)); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_CONNECTED) { + const auto &it = data->data.sta_connected; + char buf[33]; + assert(it.ssid_len <= 32); + memcpy(buf, it.ssid, it.ssid_len); + buf[it.ssid_len] = '\0'; + ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, + format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); + s_sta_connected = true; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_DISCONNECTED) { + const auto &it = data->data.sta_disconnected; + char buf[33]; + assert(it.ssid_len <= 32); + memcpy(buf, it.ssid, it.ssid_len); + buf[it.ssid_len] = '\0'; + if (it.reason == WIFI_REASON_NO_AP_FOUND) { + ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + s_sta_connect_not_found = true; + + } else { + ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, + format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); + s_sta_connect_error = true; + } + s_sta_connected = false; + s_sta_connecting = false; + error_from_callback_ = true; + + } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) { + const auto &it = data->data.ip_got_ip; + ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(), + format_ip4_addr(it.ip_info.gw).c_str()); + s_sta_got_ip = true; + + } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) { + ESP_LOGV(TAG, "Event: Lost IP"); + s_sta_got_ip = false; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_SCAN_DONE) { + const auto &it = data->data.sta_scan_done; + ESP_LOGV(TAG, "Event: WiFi Scan Done status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id); + + scan_result_.clear(); + this->scan_done_ = true; + if (it.status != 0) { + // scan error + return; + } + + uint16_t number = it.number; + std::vector records(number); + err = esp_wifi_scan_get_ap_records(&number, records.data()); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_scan_get_ap_records failed: %s", esp_err_to_name(err)); + return; + } + records.resize(number); + + scan_result_.reserve(number); + for (int i = 0; i < number; i++) { + auto &record = records[i]; + bssid_t bssid; + std::copy(record.bssid, record.bssid + 6, bssid.begin()); + std::string ssid(reinterpret_cast(record.ssid)); + WiFiScanResult result(bssid, ssid, record.primary, record.rssi, record.authmode != WIFI_AUTH_OPEN, ssid.empty()); + scan_result_.push_back(result); + } + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_START) { + ESP_LOGV(TAG, "Event: WiFi AP start"); + s_ap_started = true; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STOP) { + ESP_LOGV(TAG, "Event: WiFi AP stop"); + s_ap_started = false; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) { + const auto &it = data->data.ap_probe_req_rx; + ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STACONNECTED) { + const auto &it = data->data.ap_staconnected; + ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(it.mac).c_str()); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STADISCONNECTED) { + const auto &it = data->data.ap_stadisconnected; + ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(it.mac).c_str()); + + } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_AP_STAIPASSIGNED) { + const auto &it = data->data.ip_ap_staipassigned; + ESP_LOGV(TAG, "Event: AP client assigned IP %s", format_ip4_addr(it.ip).c_str()); + } +} + +WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { + if (s_sta_connected && s_sta_got_ip) { + return WiFiSTAConnectStatus::CONNECTED; + } + if (s_sta_connect_error) { + return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; + } + if (s_sta_connect_not_found) { + return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; + } + if (s_sta_connecting) { + return WiFiSTAConnectStatus::CONNECTING; + } + return WiFiSTAConnectStatus::IDLE; +} +bool WiFiComponent::wifi_scan_start_() { + // enable STA + if (!this->wifi_mode_(true, {})) + return false; + + wifi_scan_config_t config{}; + config.ssid = nullptr; + config.bssid = nullptr; + config.channel = 0; + config.show_hidden = true; + config.scan_type = WIFI_SCAN_TYPE_ACTIVE; + config.scan_time.active.min = 100; + config.scan_time.active.max = 300; + + esp_err_t err = esp_wifi_scan_start(&config, false); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_scan_start failed: %s", esp_err_to_name(err)); + return false; + } + + scan_done_ = false; + return true; +} +bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { + esp_err_t err; + + // enable AP + if (!this->wifi_mode_({}, true)) + return false; + + tcpip_adapter_ip_info_t info; + memset(&info, 0, sizeof(info)); + if (manual_ip.has_value()) { + info.ip.addr = static_cast(manual_ip->static_ip); + info.gw.addr = static_cast(manual_ip->gateway); + info.netmask.addr = static_cast(manual_ip->subnet); + } else { + info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); + } + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); + err = tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcps_stop failed! %d", err); + return false; + } + + err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_AP, &info); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_set_ip_info failed! %d", err); + return false; + } + + dhcps_lease_t lease; + lease.enable = true; + network::IPAddress start_address = info.ip.addr; + start_address[3] += 99; + lease.start_ip.addr = static_cast(start_address); + ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); + start_address[3] += 100; + lease.end_ip.addr = static_cast(start_address); + ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); + err = tcpip_adapter_dhcps_option(TCPIP_ADAPTER_OP_SET, TCPIP_ADAPTER_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); + + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcps_option failed! %d", err); + return false; + } + + err = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); + + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcps_start failed! %d", err); + return false; + } + + return true; +} +bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { + // enable AP + if (!this->wifi_mode_({}, true)) + return false; + + wifi_config_t conf; + memset(&conf, 0, sizeof(conf)); + strncpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); + conf.ap.channel = ap.get_channel().value_or(1); + conf.ap.ssid_hidden = ap.get_ssid().size(); + conf.ap.max_connection = 5; + conf.ap.beacon_interval = 100; + + if (ap.get_password().empty()) { + conf.ap.authmode = WIFI_AUTH_OPEN; + *conf.ap.password = 0; + } else { + conf.ap.authmode = WIFI_AUTH_WPA2_PSK; + strncpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password)); + } + +#if ESP_IDF_VERSION_MAJOR >= 4 + // pairwise cipher of SoftAP, group cipher will be derived using this. + conf.ap.pairwise_cipher = WIFI_CIPHER_TYPE_CCMP; +#endif + + esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &conf); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_set_config failed! %d", err); + return false; + } + + if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!"); + return false; + } + + return true; +} +network::IPAddress WiFiComponent::wifi_soft_ap_ip() { + tcpip_adapter_ip_info_t ip; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); + return {ip.ip.addr}; +} +bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); } + +bssid_t WiFiComponent::wifi_bssid() { + wifi_ap_record_t info; + esp_err_t err = esp_wifi_sta_get_ap_info(&info); + bssid_t res{}; + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + return res; + } + std::copy(info.bssid, info.bssid + 6, res.begin()); + return res; +} +std::string WiFiComponent::wifi_ssid() { + wifi_ap_record_t info{}; + esp_err_t err = esp_wifi_sta_get_ap_info(&info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + return ""; + } + auto *ssid_s = reinterpret_cast(info.ssid); + size_t len = strnlen(ssid_s, sizeof(info.ssid)); + return {ssid_s, len}; +} +int8_t WiFiComponent::wifi_rssi() { + wifi_ap_record_t info; + esp_err_t err = esp_wifi_sta_get_ap_info(&info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + return 0; + } + return info.rssi; +} +int32_t WiFiComponent::wifi_channel_() { + uint8_t primary; + wifi_second_chan_t second; + esp_err_t err = esp_wifi_get_channel(&primary, &second); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_get_channel failed: %s", esp_err_to_name(err)); + return 0; + } + return primary; +} +network::IPAddress WiFiComponent::wifi_subnet_mask_() { + esp_netif_ip_info_t ip; + esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); + return {}; + } + return {ip.netmask.addr}; +} +network::IPAddress WiFiComponent::wifi_gateway_ip_() { + esp_netif_ip_info_t ip; + esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); + return {}; + } + return {ip.gw.addr}; +} +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { + const ip_addr_t *dns_ip = dns_getserver(num); + return {dns_ip->u_addr.ip4.addr}; +} + +} // namespace wifi +} // namespace esphome + +#endif diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index 6d2be08fa0..de1c7f71fc 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -10,10 +10,10 @@ namespace wifi_info { class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { public: void loop() override { - IPAddress ip = WiFi.localIP(); + auto ip = wifi::global_wifi_component->wifi_sta_ip(); if (ip != this->last_ip_) { this->last_ip_ = ip; - this->publish_state(ip.toString().c_str()); + this->publish_state(ip.str().c_str()); } } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } @@ -21,15 +21,15 @@ class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { void dump_config() override; protected: - IPAddress last_ip_; + network::IPAddress last_ip_; }; class SSIDWiFiInfo : public Component, public text_sensor::TextSensor { public: void loop() override { - String ssid = WiFi.SSID(); - if (this->last_ssid_ != ssid.c_str()) { - this->last_ssid_ = std::string(ssid.c_str()); + std::string ssid = wifi::global_wifi_component->wifi_ssid(); + if (this->last_ssid_ != ssid) { + this->last_ssid_ = ssid; this->publish_state(this->last_ssid_); } } @@ -44,9 +44,9 @@ class SSIDWiFiInfo : public Component, public text_sensor::TextSensor { class BSSIDWiFiInfo : public Component, public text_sensor::TextSensor { public: void loop() override { - uint8_t *bssid = WiFi.BSSID(); - if (memcmp(bssid, this->last_bssid_.data(), 6) != 0) { - std::copy(bssid, bssid + 6, this->last_bssid_.data()); + wifi::bssid_t bssid = wifi::global_wifi_component->wifi_bssid(); + if (memcmp(bssid.data(), last_bssid_.data(), 6) != 0) { + std::copy(bssid.begin(), bssid.end(), last_bssid_.begin()); char buf[30]; sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); this->publish_state(buf); diff --git a/esphome/components/wifi_signal/wifi_signal_sensor.h b/esphome/components/wifi_signal/wifi_signal_sensor.h index 8fe108a530..f797aaa590 100644 --- a/esphome/components/wifi_signal/wifi_signal_sensor.h +++ b/esphome/components/wifi_signal/wifi_signal_sensor.h @@ -10,7 +10,7 @@ namespace wifi_signal { class WiFiSignalSensor : public sensor::Sensor, public PollingComponent { public: - void update() override { this->publish_state(WiFi.RSSI()); } + void update() override { this->publish_state(wifi::global_wifi_component->wifi_rssi()); } void dump_config() override; std::string unique_id() override { return get_mac_address() + "-wifisignal"; } diff --git a/esphome/components/wled/__init__.py b/esphome/components/wled/__init__.py index c9e23bb7eb..2795529203 100644 --- a/esphome/components/wled/__init__.py +++ b/esphome/components/wled/__init__.py @@ -7,7 +7,7 @@ from esphome.const import CONF_NAME, CONF_PORT wled_ns = cg.esphome_ns.namespace("wled") WLEDLightEffect = wled_ns.class_("WLEDLightEffect", AddressableLightEffect) -CONFIG_SCHEMA = cv.Schema({}) +CONFIG_SCHEMA = cv.All(cv.Schema({}), cv.only_with_arduino) @register_addressable_effect( diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 500b9bbad2..8c68bca6e3 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -1,12 +1,14 @@ +#ifdef USE_ARDUINO + #include "wled_light_effect.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #include #endif @@ -246,3 +248,5 @@ bool WLEDLightEffect::parse_dnrgb_frame_(light::AddressableLight &it, const uint } // namespace wled } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/wled/wled_light_effect.h b/esphome/components/wled/wled_light_effect.h index 2a7654ec27..f0021ca978 100644 --- a/esphome/components/wled/wled_light_effect.h +++ b/esphome/components/wled/wled_light_effect.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/light/addressable_light_effect.h" @@ -39,3 +41,5 @@ class WLEDLightEffect : public light::AddressableLightEffect { } // namespace wled } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 3e7e591f9a..884969f793 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include "mbedtls/ccm.h" diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index 7681cbd89c..54ab9a144f 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -3,7 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_ble { diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp index cfbe986fff..97bbd6e6d6 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgd1.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgd1 { diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h index b9e05f857c..d05cffc4d1 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgd1 { diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp index 8f42eaecaf..a97ca93206 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgdk2.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgdk2 { diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h index 70f2ae9e2e..8fd9946537 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgdk2 { diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp index d0a91d23f0..e1f83e4ddd 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgg1.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgg1 { diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h index e1d812e929..966c05ac79 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgg1 { diff --git a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp index 75ed947c25..db63beea89 100644 --- a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp +++ b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgpr1.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgpr1 { diff --git a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h index 0b7369c798..eff4b1c6fb 100644 --- a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +++ b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h @@ -6,7 +6,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgpr1 { diff --git a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp index f626daad88..990346e01e 100644 --- a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp +++ b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp @@ -1,7 +1,7 @@ #include "xiaomi_gcls002.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_gcls002 { diff --git a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h index d800e2837d..08e1bd7e54 100644 --- a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +++ b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_gcls002 { diff --git a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp index 3f2cbf963a..30990b121d 100644 --- a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp +++ b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp @@ -1,7 +1,7 @@ #include "xiaomi_hhccjcy01.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccjcy01 { diff --git a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h index bd9d742b2d..aa99cc004a 100644 --- a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +++ b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccjcy01 { diff --git a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp index 92b7171004..3ae29088bb 100644 --- a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp +++ b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp @@ -1,7 +1,7 @@ #include "xiaomi_hhccpot002.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccpot002 { diff --git a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h index 1add8e27b1..ce746b9ee0 100644 --- a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +++ b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccpot002 { diff --git a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp index 646796525e..1efebc2849 100644 --- a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp +++ b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp @@ -1,7 +1,7 @@ #include "xiaomi_jqjcy01ym.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_jqjcy01ym { diff --git a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h index d750e1e97f..ca1ad0f27e 100644 --- a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +++ b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_jqjcy01ym { diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp index 1ecf0606a2..a6f27c58b9 100644 --- a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp @@ -1,7 +1,7 @@ #include "xiaomi_lywsd02.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd02 { diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h index ec00464cb5..641a02bd5a 100644 --- a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd02 { diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp index 3eb0b68318..547cc7c114 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp @@ -1,7 +1,7 @@ #include "xiaomi_lywsd03mmc.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd03mmc { diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h index c2828e3cd1..95710a1508 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd03mmc { diff --git a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp index f16fce296b..749ca83afb 100644 --- a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp +++ b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp @@ -1,7 +1,7 @@ #include "xiaomi_lywsdcgq.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsdcgq { diff --git a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h index 553b5965fd..cbc76f9dd3 100644 --- a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +++ b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsdcgq { diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp index fdbb799ea6..0cad5c67b2 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -1,7 +1,7 @@ #include "xiaomi_mhoc401.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mhoc401 { diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h index e80916f855..4ab882b2af 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mhoc401 { diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp index 36b4c8cc00..62378de72c 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp @@ -1,7 +1,7 @@ #include "xiaomi_miscale.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale { diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.h b/esphome/components/xiaomi_miscale/xiaomi_miscale.h index d9da4f9421..409fdeaf93 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.h +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale { diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp index 34c5a975c7..9ae95c5f97 100644 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp @@ -1,7 +1,7 @@ #include "xiaomi_miscale2.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale2 { diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h index ead522e1f2..9f7ebf6a1f 100644 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale2 { diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp index df1cede068..16c0b42279 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp @@ -1,7 +1,7 @@ #include "xiaomi_mjyd02yla.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mjyd02yla { diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h index 973b19a372..34b1fe4af0 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h @@ -6,7 +6,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mjyd02yla { diff --git a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp index 53429c1806..1a8e72bd2c 100644 --- a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp +++ b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp @@ -1,7 +1,7 @@ #include "xiaomi_mue4094rt.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mue4094rt { diff --git a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h index 31f913ec94..904c575ae6 100644 --- a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +++ b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mue4094rt { diff --git a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp index 7529bb7431..b57bf5cd05 100644 --- a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp +++ b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp @@ -1,7 +1,7 @@ #include "xiaomi_wx08zm.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_wx08zm { diff --git a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h index f3eba0e159..297c7ab47d 100644 --- a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +++ b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h @@ -6,7 +6,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_wx08zm { diff --git a/esphome/components/zyaura/sensor.py b/esphome/components/zyaura/sensor.py index 74f1f9ec61..28a708b866 100644 --- a/esphome/components/zyaura/sensor.py +++ b/esphome/components/zyaura/sensor.py @@ -26,12 +26,8 @@ ZyAuraSensor = zyaura_ns.class_("ZyAuraSensor", cg.PollingComponent) CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(ZyAuraSensor), - cv.Required(CONF_CLOCK_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), - cv.Required(CONF_DATA_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_CLOCK_PIN): cv.All(pins.internal_gpio_input_pin_schema), + cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema), cv.Optional(CONF_CO2): sensor.sensor_schema( unit_of_measurement=UNIT_PARTS_PER_MILLION, icon=ICON_MOLECULE_CO2, diff --git a/esphome/components/zyaura/zyaura.cpp b/esphome/components/zyaura/zyaura.cpp index 989791f098..11643a5c23 100644 --- a/esphome/components/zyaura/zyaura.cpp +++ b/esphome/components/zyaura/zyaura.cpp @@ -6,7 +6,7 @@ namespace zyaura { static const char *const TAG = "zyaura"; -bool ICACHE_RAM_ATTR ZaDataProcessor::decode(uint32_t ms, bool data) { +bool IRAM_ATTR ZaDataProcessor::decode(uint32_t ms, bool data) { // check if a new message has started, based on time since previous bit if ((ms - this->prev_ms_) > ZA_MAX_MS) { this->num_bits_ = 0; @@ -37,24 +37,24 @@ bool ICACHE_RAM_ATTR ZaDataProcessor::decode(uint32_t ms, bool data) { return false; } -void ZaSensorStore::setup(GPIOPin *pin_clock, GPIOPin *pin_data) { +void ZaSensorStore::setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data) { pin_clock->setup(); pin_data->setup(); this->pin_clock_ = pin_clock->to_isr(); this->pin_data_ = pin_data->to_isr(); - pin_clock->attach_interrupt(ZaSensorStore::interrupt, this, FALLING); + pin_clock->attach_interrupt(ZaSensorStore::interrupt, this, gpio::INTERRUPT_FALLING_EDGE); } -void ICACHE_RAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) { +void IRAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) { uint32_t now = millis(); - bool data_bit = arg->pin_data_->digital_read(); + bool data_bit = arg->pin_data_.digital_read(); if (arg->processor_.decode(now, data_bit)) { arg->set_data_(arg->processor_.message); } } -void ICACHE_RAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) { +void IRAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) { switch (message->type) { case HUMIDITY: this->humidity = (message->value > 10000) ? NAN : (message->value / 100.0f); @@ -82,7 +82,7 @@ bool ZyAuraSensor::publish_state_(sensor::Sensor *sensor, float *value) { sensor->publish_state(*value); // Sensor reported wrong value - if (isnan(*value)) { + if (std::isnan(*value)) { ESP_LOGW(TAG, "Sensor reported invalid data. Is the update interval too small?"); this->status_set_warning(); return false; diff --git a/esphome/components/zyaura/zyaura.h b/esphome/components/zyaura/zyaura.h index eed0c55c35..2b9e3fbb35 100644 --- a/esphome/components/zyaura/zyaura.h +++ b/esphome/components/zyaura/zyaura.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -46,12 +46,12 @@ class ZaSensorStore { float temperature = NAN; float humidity = NAN; - void setup(GPIOPin *pin_clock, GPIOPin *pin_data); + void setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data); static void interrupt(ZaSensorStore *arg); protected: - ISRInternalGPIOPin *pin_clock_; - ISRInternalGPIOPin *pin_data_; + ISRInternalGPIOPin pin_clock_; + ISRInternalGPIOPin pin_data_; ZaDataProcessor processor_; void set_data_(ZaMessage *message); @@ -60,8 +60,8 @@ class ZaSensorStore { /// Component for reading temperature/co2/humidity measurements from ZyAura sensors. class ZyAuraSensor : public PollingComponent { public: - void set_pin_clock(GPIOPin *pin) { pin_clock_ = pin; } - void set_pin_data(GPIOPin *pin) { pin_data_ = pin; } + void set_pin_clock(InternalGPIOPin *pin) { pin_clock_ = pin; } + void set_pin_data(InternalGPIOPin *pin) { pin_data_ = pin; } void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } @@ -73,8 +73,8 @@ class ZyAuraSensor : public PollingComponent { protected: ZaSensorStore store_; - GPIOPin *pin_clock_; - GPIOPin *pin_data_; + InternalGPIOPin *pin_clock_; + InternalGPIOPin *pin_data_; sensor::Sensor *co2_sensor_{nullptr}; sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; diff --git a/esphome/config.py b/esphome/config.py index e49293e6b2..af6c5b0b64 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -1,4 +1,6 @@ -import collections +import abc +import functools +import heapq import logging import re @@ -15,6 +17,7 @@ from esphome.const import ( CONF_PACKAGES, CONF_SUBSTITUTIONS, CONF_EXTERNAL_COMPONENTS, + TARGET_PLATFORMS, ) from esphome.core import CORE, EsphomeError from esphome.helpers import indent @@ -56,6 +59,27 @@ def _path_begins_with(path, other): # type: (ConfigPath, ConfigPath) -> bool return path[: len(other)] == other +@functools.total_ordering +class _ValidationStepTask: + def __init__(self, priority: float, id_number: int, step: "ConfigValidationStep"): + self.priority = priority + self.id_number = id_number + self.step = step + + @property + def _cmp_tuple(self) -> Tuple[float, int]: + return (-self.priority, self.id_number) + + def __eq__(self, other): + return self._cmp_tuple == other._cmp_tuple + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return self._cmp_tuple < other._cmp_tuple + + class Config(OrderedDict, fv.FinalValidateConfig): def __init__(self): super().__init__() @@ -68,6 +92,10 @@ class Config(OrderedDict, fv.FinalValidateConfig): # A list of components ids with the config path self.declare_ids = [] # type: List[Tuple[core.ID, ConfigPath]] self._data = {} + # Store pending validation tasks (in heap order) + self._validation_tasks: List[_ValidationStepTask] = [] + # ID to ensure stable order for keys with equal priority + self._validation_tasks_id = 0 def add_error(self, error): # type: (vol.Invalid) -> None @@ -83,6 +111,18 @@ class Config(OrderedDict, fv.FinalValidateConfig): error.path = error.path[last_root + 1 :] self.errors.append(error) + def add_validation_step(self, step: "ConfigValidationStep"): + id_num = self._validation_tasks_id + self._validation_tasks_id += 1 + heapq.heappush( + self._validation_tasks, _ValidationStepTask(step.priority, id_num, step) + ) + + def run_validation_steps(self): + while self._validation_tasks: + task = heapq.heappop(self._validation_tasks) + task.step.run(self) + @contextmanager def catch_error(self, path=None): path = path or [] @@ -206,92 +246,6 @@ def iter_ids(config, path=None): yield from iter_ids(value, path + [key]) -def do_id_pass(result): # type: (Config) -> None - from esphome.cpp_generator import MockObjClass - from esphome.cpp_types import Component - - searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]] - for id, path in iter_ids(result): - if id.is_declaration: - if id.id is not None: - # Look for duplicate definitions - match = next((v for v in result.declare_ids if v[0].id == id.id), None) - if match is not None: - opath = "->".join(str(v) for v in match[1]) - result.add_str_error(f"ID {id.id} redefined! Check {opath}", path) - continue - result.declare_ids.append((id, path)) - else: - searching_ids.append((id, path)) - # Resolve default ids after manual IDs - for id, _ in result.declare_ids: - id.resolve([v[0].id for v in result.declare_ids]) - if isinstance(id.type, MockObjClass) and id.type.inherits_from(Component): - CORE.component_ids.add(id.id) - - # Check searched IDs - for id, path in searching_ids: - if id.id is not None: - # manually declared - match = next((v[0] for v in result.declare_ids if v[0].id == id.id), None) - if match is None or not match.is_manual: - # No declared ID with this name - import difflib - - error = f"Couldn't find ID '{id.id}'. Please check you have defined an ID with that name in your configuration." - # Find candidates - matches = difflib.get_close_matches( - id.id, [v[0].id for v in result.declare_ids if v[0].is_manual] - ) - if matches: - matches_s = ", ".join(f'"{x}"' for x in matches) - error += f" These IDs look similar: {matches_s}." - result.add_str_error(error, path) - continue - if not isinstance(match.type, MockObjClass) or not isinstance( - id.type, MockObjClass - ): - continue - if not match.type.inherits_from(id.type): - result.add_str_error( - f"ID '{id.id}' of type {match.type} doesn't inherit from {id.type}. Please double check your ID is pointing to the correct value", - path, - ) - - if id.id is None and id.type is not None: - matches = [] - for v in result.declare_ids: - if v[0] is None or not isinstance(v[0].type, MockObjClass): - continue - inherits = v[0].type.inherits_from(id.type) - if inherits: - matches.append(v[0]) - - if len(matches) == 0: - result.add_str_error( - f"Couldn't find any component that can be used for '{id.type}'. Are you missing a hub declaration?", - path, - ) - elif len(matches) == 1: - id.id = matches[0].id - elif len(matches) > 1: - if str(id.type) == "time::RealTimeClock": - id.id = matches[0].id - else: - manual_declared_count = sum(1 for m in matches if m.is_manual) - if manual_declared_count > 0: - ids = ", ".join([f"'{m.id}'" for m in matches if m.is_manual]) - result.add_str_error( - f"Too many candidates found for '{path[-1]}' type '{id.type}' {'Some are' if manual_declared_count > 1 else 'One is'} {ids}", - path, - ) - else: - result.add_str_error( - f"Too many candidates found for '{path[-1]}' type '{id.type}' You must assign an explicit ID to the parent component you want to use.", - path, - ) - - def recursive_check_replaceme(value): if isinstance(value, list): return cv.Schema([recursive_check_replaceme])(value) @@ -310,6 +264,389 @@ def recursive_check_replaceme(value): return value +class ConfigValidationStep(abc.ABC): + """A step to for the validation phase.""" + + # Priority of this step, higher means run earlier + priority: float = 0.0 + + @abc.abstractmethod + def run(self, result: Config) -> None: + ... + + +class LoadValidationStep(ConfigValidationStep): + """Load step, this step is called once for each domain config fragment. + + Responsibilties: + - Load component code + - Ensure all AUTO_LOADs are added + - Set output paths of result + """ + + def __init__(self, domain: str, conf: ConfigType): + self.domain = domain + self.conf = conf + + def run(self, result: Config) -> None: + if self.domain.startswith("."): + # Ignore top-level keys starting with a dot + return + result.add_output_path([self.domain], self.domain) + result[self.domain] = self.conf + component = get_component(self.domain) + path = [self.domain] + if component is None: + result.add_str_error(f"Component not found: {self.domain}", path) + return + CORE.loaded_integrations.add(self.domain) + + # Process AUTO_LOAD + for load in component.auto_load: + if load not in result: + result.add_validation_step(AutoLoadValidationStep(load)) + + if not component.is_platform_component: + result.add_validation_step( + MetadataValidationStep([self.domain], self.domain, self.conf, component) + ) + return + + # This is a platform component, proceed to reading platform entries + # Remove this is as an output path + result.remove_output_path([self.domain], self.domain) + + # Ensure conf is a list + if not self.conf: + result[self.domain] = self.conf = [] + elif not isinstance(self.conf, list): + result[self.domain] = self.conf = [self.conf] + + for i, p_config in enumerate(self.conf): + path = [self.domain, i] + # Construct temporary unknown output path + p_domain = f"{self.domain}.unknown" + result.add_output_path(path, p_domain) + result[self.domain][i] = p_config + if not isinstance(p_config, dict): + result.add_str_error("Platform schemas must be key-value pairs.", path) + continue + p_name = p_config.get("platform") + if p_name is None: + result.add_str_error("No platform specified! See 'platform' key.", path) + continue + # Remove temp output path and construct new one + result.remove_output_path(path, p_domain) + p_domain = f"{self.domain}.{p_name}" + result.add_output_path(path, p_domain) + # Try Load platform + platform = get_platform(self.domain, p_name) + if platform is None: + result.add_str_error(f"Platform not found: '{p_domain}'", path) + continue + CORE.loaded_integrations.add(p_name) + + # Process AUTO_LOAD + for load in platform.auto_load: + if load not in result: + result.add_validation_step(AutoLoadValidationStep(load)) + + result.add_validation_step( + MetadataValidationStep(path, p_domain, p_config, platform) + ) + + +class AutoLoadValidationStep(ConfigValidationStep): + """Auto load step. This step is used to automatically load components if + a component requested that with AUTO_LOAD. + """ + + # Only load after all regular loads have taken place + priority = -1.0 + + def __init__(self, domain: str): + self.domain = domain + + def run(self, result: Config) -> None: + if self.domain in result: + # already loaded + return + result.add_validation_step(LoadValidationStep(self.domain, core.AutoLoad())) + + +class MetadataValidationStep(ConfigValidationStep): + """Validate component metadata + + Responsibilties: + - Config transformation (nullable, multi conf) + - Check dependencies + - Check conflicts + - Check supported target platforms + """ + + # All components need to be loaded first to ensure dependency check works + priority = -2.0 + + def __init__( + self, + path: ConfigPath, + domain: str, + conf: ConfigType, + component: ComponentManifest, + ) -> None: + self.path = path + self.domain = domain + self.conf = conf + self.comp = component + + def run(self, result: Config) -> None: + if self.conf is None: + result[self.domain] = self.conf = {} + + success = True + for dependency in self.comp.dependencies: + if dependency not in result: + result.add_str_error( + f"Component {self.domain} requires component {dependency}", + self.path, + ) + success = False + if not success: + return + + success = True + for conflict in self.comp.conflicts_with: + if conflict in result: + result.add_str_error( + f"Component {self.domain} cannot be used together with component {conflict}", + self.path, + ) + success = False + if not success: + return + + if ( + not self.comp.is_platform_component + and self.comp.config_schema is None + and not isinstance(self.conf, core.AutoLoad) + ): + result.add_str_error( + f"Component {self.domain} cannot be loaded via YAML " + "(no CONFIG_SCHEMA).", + self.path, + ) + return + + if self.comp.multi_conf: + if not isinstance(self.conf, list): + result[self.domain] = self.conf = [self.conf] + if ( + not isinstance(self.comp.multi_conf, bool) + and len(self.conf) > self.comp.multi_conf + ): + result.add_str_error( + f"Component {self.domain} supports a maximum of {self.comp.multi_conf} " + f"entries ({len(self.conf)} found).", + self.path, + ) + return + for i, part_conf in enumerate(self.conf): + result.add_validation_step( + SchemaValidationStep( + self.domain, self.path + [i], part_conf, self.comp + ) + ) + return + + result.add_validation_step( + SchemaValidationStep(self.domain, self.path, self.conf, self.comp) + ) + + +class SchemaValidationStep(ConfigValidationStep): + """Schema validation step. + + During this step all CONFIG_SCHEMAs are checked against the configs. + """ + + def __init__( + self, domain: str, path: ConfigPath, conf: ConfigType, comp: ComponentManifest + ): + self.path = path + self.conf = conf + self.comp = comp + + def run(self, result: Config) -> None: + if self.comp.config_schema is None: + return + with result.catch_error(self.path): + if self.comp.is_platform: + # Remove 'platform' key for validation + input_conf = OrderedDict(self.conf) + platform_val = input_conf.pop("platform") + schema = cv.Schema(self.comp.config_schema) + validated = schema(input_conf) + # Ensure result is OrderedDict so we can call move_to_end + if not isinstance(validated, OrderedDict): + validated = OrderedDict(validated) + validated["platform"] = platform_val + validated.move_to_end("platform", last=False) + result.set_by_path(self.path, validated) + else: + schema = cv.Schema(self.comp.config_schema) + validated = schema(self.conf) + result.set_by_path(self.path, validated) + + result.add_validation_step(FinalValidateValidationStep(self.path, self.comp)) + + +class IDPassValidationStep(ConfigValidationStep): + """ID Pass step. + + During this step all ID references are checked. + + If an automatic ID reference is used, a fitting declared ID is automatically searched. + Also checks duplicate ID names, and that referenced IDs are declared. + """ + + # Has to happen after all schemas validated + priority = -10.0 + + def __init__(self) -> None: + pass + + def run(self, result: Config) -> None: + from esphome.cpp_generator import MockObjClass + from esphome.cpp_types import Component + + if result.errors: + # If result already has errors, skip this step + # Otherwise the user will get a bunch of missing ID warnings + # because the component that did not validate doesn't have any IDs set + return + + searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]] + for id, path in iter_ids(result): + if id.is_declaration: + if id.id is not None: + # Look for duplicate definitions + match = next( + (v for v in result.declare_ids if v[0].id == id.id), None + ) + if match is not None: + opath = "->".join(str(v) for v in match[1]) + result.add_str_error( + f"ID {id.id} redefined! Check {opath}", path + ) + continue + result.declare_ids.append((id, path)) + else: + searching_ids.append((id, path)) + + # Resolve default ids after manual IDs + for id, _ in result.declare_ids: + id.resolve([v[0].id for v in result.declare_ids]) + if isinstance(id.type, MockObjClass) and id.type.inherits_from(Component): + CORE.component_ids.add(id.id) + + # Check searched IDs + for id, path in searching_ids: + if id.id is not None: + # manually declared + match = next( + (v[0] for v in result.declare_ids if v[0].id == id.id), None + ) + if match is None or not match.is_manual: + # No declared ID with this name + import difflib + + error = ( + f"Couldn't find ID '{id.id}'. Please check you have defined " + "an ID with that name in your configuration." + ) + # Find candidates + matches = difflib.get_close_matches( + id.id, [v[0].id for v in result.declare_ids if v[0].is_manual] + ) + if matches: + matches_s = ", ".join(f'"{x}"' for x in matches) + error += f" These IDs look similar: {matches_s}." + result.add_str_error(error, path) + continue + if not isinstance(match.type, MockObjClass) or not isinstance( + id.type, MockObjClass + ): + continue + if not match.type.inherits_from(id.type): + result.add_str_error( + f"ID '{id.id}' of type {match.type} doesn't inherit from {id.type}. " + "Please double check your ID is pointing to the correct value", + path, + ) + + if id.id is None and id.type is not None: + matches = [] + for v in result.declare_ids: + if v[0] is None or not isinstance(v[0].type, MockObjClass): + continue + inherits = v[0].type.inherits_from(id.type) + if inherits: + matches.append(v[0]) + + if len(matches) == 0: + result.add_str_error( + f"Couldn't find any component that can be used for '{id.type}'. Are you missing a hub declaration?", + path, + ) + elif len(matches) == 1: + id.id = matches[0].id + elif len(matches) > 1: + if str(id.type) == "time::RealTimeClock": + id.id = matches[0].id + else: + manual_declared_count = sum(1 for m in matches if m.is_manual) + if manual_declared_count > 0: + ids = ", ".join( + [f"'{m.id}'" for m in matches if m.is_manual] + ) + result.add_str_error( + f"Too many candidates found for '{path[-1]}' type '{id.type}' {'Some are' if manual_declared_count > 1 else 'One is'} {ids}", + path, + ) + else: + result.add_str_error( + f"Too many candidates found for '{path[-1]}' type '{id.type}' You must assign an explicit ID to the parent component you want to use.", + path, + ) + + +class FinalValidateValidationStep(ConfigValidationStep): + """Run final_validate_schema for all components.""" + + # Has to happen after ID pass validated + priority = -20.0 + + def __init__(self, path: ConfigPath, comp: ComponentManifest) -> None: + self.path = path + self.comp = comp + + def run(self, result: Config) -> None: + if result.errors: + # If result already has errors, skip this step + return + + if self.comp.final_validate_schema is None: + return + + token = fv.full_config.set(result) + + conf = result.get_nested_item(self.path) + with result.catch_error(self.path): + self.comp.final_validate_schema(conf) + + fv.full_config.reset(token) + + def validate_config(config, command_line_substitutions): result = Config() @@ -384,217 +721,31 @@ def validate_config(config, command_line_substitutions): result[CONF_ESPHOME] = config[CONF_ESPHOME] result.add_output_path([CONF_ESPHOME], CONF_ESPHOME) try: - core_config.preload_core_config(config) + core_config.preload_core_config(config, result) except vol.Invalid as err: result.add_error(err) return result # Remove temporary esphome config path again, it will be reloaded later result.remove_output_path([CONF_ESPHOME], CONF_ESPHOME) - # 3. Load components. - # Load components (also AUTO_LOAD) and set output paths of result - # Queue of items to load, FIFO - load_queue = collections.deque() + # First run platform validation steps + for key in TARGET_PLATFORMS: + if key in config: + result.add_validation_step(LoadValidationStep(key, config[key])) + result.run_validation_steps() + + if result.errors: + return result + for domain, conf in config.items(): - load_queue.append((domain, conf)) + result.add_validation_step(LoadValidationStep(domain, conf)) + result.add_validation_step(IDPassValidationStep()) - # List of items to enter next stage - check_queue = ( - [] - ) # type: List[Tuple[ConfigPath, str, ConfigType, ComponentManifest]] - - # This step handles: - # - Adding output path - # - Auto Load - # - Loading configs into result - - while load_queue: - domain, conf = load_queue.popleft() - if domain.startswith("."): - # Ignore top-level keys starting with a dot - continue - result.add_output_path([domain], domain) - result[domain] = conf - component = get_component(domain) - path = [domain] - if component is None: - result.add_str_error(f"Component not found: {domain}", path) - continue - CORE.loaded_integrations.add(domain) - - # Process AUTO_LOAD - for load in component.auto_load: - if load not in config: - load_conf = core.AutoLoad() - config[load] = load_conf - load_queue.append((load, load_conf)) - - if not component.is_platform_component: - check_queue.append(([domain], domain, conf, component)) - continue - - # This is a platform component, proceed to reading platform entries - # Remove this is as an output path - result.remove_output_path([domain], domain) - - # Ensure conf is a list - if not conf: - result[domain] = conf = [] - elif not isinstance(conf, list): - result[domain] = conf = [conf] - - for i, p_config in enumerate(conf): - path = [domain, i] - # Construct temporary unknown output path - p_domain = f"{domain}.unknown" - result.add_output_path(path, p_domain) - result[domain][i] = p_config - if not isinstance(p_config, dict): - result.add_str_error("Platform schemas must be key-value pairs.", path) - continue - p_name = p_config.get("platform") - if p_name is None: - result.add_str_error("No platform specified! See 'platform' key.", path) - continue - # Remove temp output path and construct new one - result.remove_output_path(path, p_domain) - p_domain = f"{domain}.{p_name}" - result.add_output_path(path, p_domain) - # Try Load platform - platform = get_platform(domain, p_name) - if platform is None: - result.add_str_error(f"Platform not found: '{p_domain}'", path) - continue - CORE.loaded_integrations.add(p_name) - - # Process AUTO_LOAD - for load in platform.auto_load: - if load not in config: - load_conf = core.AutoLoad() - config[load] = load_conf - load_queue.append((load, load_conf)) - - check_queue.append((path, p_domain, p_config, platform)) - - # 4. Validate component metadata, including - # - Transformation (nullable, multi conf) - # - Dependencies - # - Conflicts - # - Supported ESP Platform - - # List of items to proceed to next stage - validate_queue = [] # type: List[Tuple[ConfigPath, ConfigType, ComponentManifest]] - for path, domain, conf, comp in check_queue: - if conf is None: - result[domain] = conf = {} - - success = True - for dependency in comp.dependencies: - if dependency not in config: - result.add_str_error( - f"Component {domain} requires component {dependency}", - path, - ) - success = False - if not success: - continue - - success = True - for conflict in comp.conflicts_with: - if conflict in config: - result.add_str_error( - f"Component {domain} cannot be used together with component {conflict}", - path, - ) - success = False - if not success: - continue - - if CORE.esp_platform not in comp.esp_platforms: - result.add_str_error( - f"Component {domain} doesn't support {CORE.esp_platform}.", - path, - ) - continue - - if ( - not comp.is_platform_component - and comp.config_schema is None - and not isinstance(conf, core.AutoLoad) - ): - result.add_str_error( - f"Component {domain} cannot be loaded via YAML (no CONFIG_SCHEMA).", - path, - ) - continue - - if comp.multi_conf: - if not isinstance(conf, list): - result[domain] = conf = [conf] - if not isinstance(comp.multi_conf, bool) and len(conf) > comp.multi_conf: - result.add_str_error( - f"Component {domain} supports a maximum of {comp.multi_conf} entries ({len(conf)} found).", - path, - ) - continue - for i, part_conf in enumerate(conf): - validate_queue.append((path + [i], part_conf, comp)) - continue - - validate_queue.append((path, conf, comp)) - - # 5. Validate configuration schema - for path, conf, comp in validate_queue: - if comp.config_schema is None: - continue - with result.catch_error(path): - if comp.is_platform: - # Remove 'platform' key for validation - input_conf = OrderedDict(conf) - platform_val = input_conf.pop("platform") - validated = comp.config_schema(input_conf) - # Ensure result is OrderedDict so we can call move_to_end - if not isinstance(validated, OrderedDict): - validated = OrderedDict(validated) - validated["platform"] = platform_val - validated.move_to_end("platform", last=False) - result.set_by_path(path, validated) - else: - validated = comp.config_schema(conf) - result.set_by_path(path, validated) - - # 6. If no validation errors, check IDs - if not result.errors: - # Only parse IDs if no validation error. Otherwise - # user gets confusing messages - do_id_pass(result) - - # 7. Final validation - if not result.errors: - # Inter - components validation - token = fv.full_config.set(result) - - for path, _, comp in validate_queue: - if comp.final_validate_schema is None: - continue - conf = result.get_nested_item(path) - with result.catch_error(path): - comp.final_validate_schema(conf) - - fv.full_config.reset(token) + result.run_validation_steps() return result -def _nested_getitem(data, path): - for item_index in path: - try: - data = data[item_index] - except (KeyError, IndexError, TypeError): - return None - return data - - def humanize_error(config, validation_error): validation_error = str(validation_error) m = re.match( @@ -661,7 +812,6 @@ def _load_config(command_line_substitutions): config = yaml_util.load_yaml(CORE.config_path) except EsphomeError as e: raise InvalidYAMLError(e) from e - CORE.raw_config = config try: result = validate_config(config, command_line_substitutions) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index a864c65dc7..0d8f4f3b64 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1,5 +1,6 @@ """Helpers for config validation using voluptuous.""" +from dataclasses import dataclass import logging import os import re @@ -33,6 +34,9 @@ from esphome.const import ( CONF_UPDATE_INTERVAL, CONF_TYPE_ID, CONF_TYPE, + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, ) from esphome.core import ( CORE, @@ -492,20 +496,37 @@ def templatable(other_validators): def only_on(platforms): - """Validate that this option can only be specified on the given ESP platforms.""" + """Validate that this option can only be specified on the given target platforms.""" if not isinstance(platforms, list): platforms = [platforms] def validator_(obj): - if CORE.esp_platform not in platforms: + if CORE.target_platform not in platforms: raise Invalid(f"This feature is only available on {platforms}") return obj return validator_ -only_on_esp32 = only_on("ESP32") -only_on_esp8266 = only_on("ESP8266") +def only_with_framework(frameworks): + """Validate that this option can only be specified on the given frameworks.""" + if not isinstance(frameworks, list): + frameworks = [frameworks] + + def validator_(obj): + if CORE.target_framework not in frameworks: + raise Invalid( + f"This feature is only available with frameworks {frameworks}" + ) + return obj + + return validator_ + + +only_on_esp32 = only_on("esp32") +only_on_esp8266 = only_on("esp8266") +only_with_arduino = only_with_framework("arduino") +only_with_esp_idf = only_with_framework("esp-idf") # Adapted from: @@ -1025,7 +1046,7 @@ def requires_component(comp): # pylint: disable=unsupported-membership-test def validator(value): # pylint: disable=unsupported-membership-test - if comp not in CORE.raw_config: + if comp not in CORE.loaded_integrations: raise Invalid(f"This option requires component {comp}") return value @@ -1401,18 +1422,32 @@ class GenerateID(Optional): class SplitDefault(Optional): """Mark this key to have a split default for ESP8266/ESP32.""" - def __init__(self, key, esp8266=vol.UNDEFINED, esp32=vol.UNDEFINED): + def __init__( + self, + key, + esp8266=vol.UNDEFINED, + esp32=vol.UNDEFINED, + esp32_arduino=vol.UNDEFINED, + esp32_idf=vol.UNDEFINED, + ): super().__init__(key) self._esp8266_default = vol.default_factory(esp8266) - self._esp32_default = vol.default_factory(esp32) + self._esp32_arduino_default = vol.default_factory( + esp32_arduino if esp32 is vol.UNDEFINED else esp32 + ) + self._esp32_idf_default = vol.default_factory( + esp32_idf if esp32 is vol.UNDEFINED else esp32 + ) @property def default(self): if CORE.is_esp8266: return self._esp8266_default - if CORE.is_esp32: - return self._esp32_default - raise ValueError + if CORE.is_esp32 and CORE.using_arduino: + return self._esp32_arduino_default + if CORE.is_esp32 and CORE.using_esp_idf: + return self._esp32_idf_default + raise NotImplementedError @default.setter def default(self, value): @@ -1431,7 +1466,7 @@ class OnlyWith(Optional): @property def default(self): # pylint: disable=unsupported-membership-test - if self._component in CORE.raw_config: + if self._component in CORE.loaded_integrations: return self._default return vol.UNDEFINED @@ -1613,3 +1648,73 @@ def source_refresh(value: str): if value.lower() == "never": return source_refresh("1000y") return positive_time_period_seconds(value) + + +@dataclass(frozen=True, order=True) +class Version: + major: int + minor: int + patch: int + + def __str__(self): + return f"{self.major}.{self.minor}.{self.patch}" + + @classmethod + def parse(cls, value: str) -> "Version": + match = re.match(r"(\d+).(\d+).(\d+)", value) + if match is None: + raise ValueError(f"Not a valid version number {value}") + major = int(match[1]) + minor = int(match[2]) + patch = int(match[3]) + return Version(major=major, minor=minor, patch=patch) + + +def version_number(value): + value = string_strict(value) + try: + return str(Version.parse(value)) + except ValueError as e: + raise Invalid("Not a version number") from e + + +def require_framework_version( + *, + esp_idf=None, + esp32_arduino=None, + esp8266_arduino=None, +): + def validator(value): + core_data = CORE.data[KEY_CORE] + framework = core_data[KEY_TARGET_FRAMEWORK] + if framework == "esp-idf": + if esp_idf is None: + raise Invalid("This feature is incompatible with esp-idf") + required = esp_idf + elif CORE.is_esp32 and framework == "arduino": + if esp32_arduino is None: + raise Invalid( + "This feature is incompatible with ESP32 using arduino framework" + ) + required = esp32_arduino + elif CORE.is_esp8266 and framework == "arduino": + if esp8266_arduino is None: + raise Invalid("This feature is incompatible with ESP8266") + required = esp8266_arduino + else: + raise NotImplementedError + if core_data[KEY_FRAMEWORK_VERSION] < required: + raise Invalid( + f"This feature requires at least framework version {required}" + ) + return value + + return validator + + +@contextmanager +def suppress_invalid(): + try: + yield + except vol.Invalid: + pass diff --git a/esphome/const.py b/esphome/const.py index 598b6351b4..6a3296bcea 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -2,25 +2,11 @@ __version__ = "2021.10.0-dev" -ESP_PLATFORM_ESP32 = "ESP32" -ESP_PLATFORM_ESP8266 = "ESP8266" -ESP_PLATFORMS = [ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266] - ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" -# Lookup table from ESP32 arduino framework version to latest platformio -# package with that version -# See also https://github.com/platformio/platform-espressif32/releases -ARDUINO_VERSION_ESP32 = { - "dev": "https://github.com/platformio/platform-espressif32.git", - "1.0.6": "platformio/espressif32@3.2.0", - "1.0.5": "platformio/espressif32@3.1.1", - "1.0.4": "platformio/espressif32@3.0.0", - "1.0.3": "platformio/espressif32@1.10.0", - "1.0.2": "platformio/espressif32@1.9.0", - "1.0.1": "platformio/espressif32@1.7.0", - "1.0.0": "platformio/espressif32@1.5.0", -} +TARGET_PLATFORMS = ["esp32", "esp8266"] +TARGET_FRAMEWORKS = ["arduino", "esp-idf"] + # See also https://github.com/platformio/platform-espressif8266/releases ARDUINO_VERSION_ESP8266 = { "dev": "https://github.com/platformio/platform-espressif8266.git", @@ -204,13 +190,11 @@ CONF_ECO2 = "eco2" CONF_EFFECT = "effect" CONF_EFFECTS = "effects" CONF_ELSE = "else" -CONF_ENABLE_MDNS = "enable_mdns" CONF_ENABLE_PIN = "enable_pin" CONF_ENABLE_TIME = "enable_time" CONF_ENERGY = "energy" CONF_ENTITY_ID = "entity_id" CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support" -CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONF_ESPHOME = "esphome" CONF_ETHERNET = "ethernet" CONF_EVENT = "event" @@ -253,6 +237,7 @@ CONF_FORCE_UPDATE = "force_update" CONF_FORMALDEHYDE = "formaldehyde" CONF_FORMAT = "format" CONF_FORWARD_ACTIVE_ENERGY = "forward_active_energy" +CONF_FRAMEWORK = "framework" CONF_FREQUENCY = "frequency" CONF_FROM = "from" CONF_FULL_SPECTRUM = "full_spectrum" @@ -307,6 +292,7 @@ CONF_INFRARED = "infrared" CONF_INITIAL_MODE = "initial_mode" CONF_INITIAL_OPTION = "initial_option" CONF_INITIAL_VALUE = "initial_value" +CONF_INPUT = "input" CONF_INTEGRATION_TIME = "integration_time" CONF_INTENSITY = "intensity" CONF_INTERLOCK = "interlock" @@ -447,6 +433,7 @@ CONF_ON_VALUE = "on_value" CONF_ON_VALUE_RANGE = "on_value_range" CONF_ONE = "one" CONF_OPEN_ACTION = "open_action" +CONF_OPEN_DRAIN = "open_drain" CONF_OPEN_DRAIN_INTERRUPT = "open_drain_interrupt" CONF_OPEN_DURATION = "open_duration" CONF_OPEN_ENDSTOP = "open_endstop" @@ -520,6 +507,8 @@ CONF_PRIORITY = "priority" CONF_PROJECT = "project" CONF_PROTOCOL = "protocol" CONF_PULL_MODE = "pull_mode" +CONF_PULLDOWN = "pulldown" +CONF_PULLUP = "pullup" CONF_PULSE_LENGTH = "pulse_length" CONF_QOS = "qos" CONF_RADON = "radon" @@ -890,3 +879,8 @@ STATE_CLASS_MEASUREMENT = "measurement" # The state represents a total that only increases, a decrease is considered a reset. STATE_CLASS_TOTAL_INCREASING = "total_increasing" + +KEY_CORE = "core" +KEY_TARGET_PLATFORM = "target_platform" +KEY_TARGET_FRAMEWORK = "target_framework" +KEY_FRAMEWORK_VERSION = "framework_version" diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index e89f64d14c..9ec7fe358d 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -2,16 +2,17 @@ import logging import math import os import re -from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union from esphome.const import ( - CONF_ARDUINO_VERSION, CONF_COMMENT, CONF_ESPHOME, CONF_USE_ADDRESS, CONF_ETHERNET, CONF_WIFI, - SOURCE_FILE_EXTENSIONS, + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, ) from esphome.coroutine import FakeAwaitable as _FakeAwaitable from esphome.coroutine import FakeEventLoop as _FakeEventLoop @@ -20,7 +21,6 @@ from esphome.coroutine import FakeEventLoop as _FakeEventLoop from esphome.coroutine import coroutine, coroutine_with_priority # noqa from esphome.helpers import ensure_unique_string, is_hassio from esphome.util import OrderedDict -from esphome import boards if TYPE_CHECKING: from ..cpp_generator import MockObj, MockObjClass, Statement @@ -441,19 +441,6 @@ class Library: return NotImplemented -def find_source_files(file): - files = set() - directory = os.path.abspath(os.path.dirname(file)) - for f in os.listdir(directory): - if not os.path.isfile(os.path.join(directory, f)): - continue - _, ext = os.path.splitext(f) - if ext.lower() not in SOURCE_FILE_EXTENSIONS: - continue - files.add(f) - return files - - # pylint: disable=too-many-instance-attributes,too-many-public-methods class EsphomeCore: def __init__(self): @@ -464,16 +451,13 @@ class EsphomeCore: self.ace = False # The name of the node self.name: Optional[str] = None + # Additional data components can store temporary data in + # The first key to this dict should always be the integration name + self.data = {} # The relative path to the configuration YAML self.config_path: Optional[str] = None # The relative path to where all build files are stored self.build_path: Optional[str] = None - # The platform (ESP8266, ESP32) of this device - self.esp_platform: Optional[str] = None - # The board that's used (for example nodemcuv2) - self.board: Optional[str] = None - # The full raw configuration - self.raw_config: Optional["ConfigType"] = None # The validated configuration, this is None until the config has been validated self.config: Optional["ConfigType"] = None # The pending tasks in the task queue (mostly for C++ generation) @@ -494,6 +478,8 @@ class EsphomeCore: self.build_flags: Set[str] = set() # A set of defines to set for the compile process in esphome/core/defines.h self.defines: Set["Define"] = set() + # A map of all platformio options to apply + self.platformio_options: Dict[str, Union[str, List[str]]] = {} # A set of strings of names of loaded integrations, used to find namespace ID conflicts self.loaded_integrations = set() # A set of component IDs to track what Component subclasses are declared @@ -504,11 +490,9 @@ class EsphomeCore: def reset(self): self.dashboard = False self.name = None + self.data = {} self.config_path = None self.build_path = None - self.esp_platform = None - self.board = None - self.raw_config = None self.config = None self.event_loop = _FakeEventLoop() self.task_counter = 0 @@ -518,6 +502,7 @@ class EsphomeCore: self.libraries = [] self.build_flags = set() self.defines = set() + self.platformio_options = {} self.loaded_integrations = set() self.component_ids = set() @@ -544,13 +529,6 @@ class EsphomeCore: return None - @property - def arduino_version(self) -> str: - if self.config is None: - raise ValueError("Config has not been loaded yet") - - return self.config[CONF_ESPHOME][CONF_ARDUINO_VERSION] - @property def config_dir(self): return os.path.dirname(self.config_path) @@ -586,27 +564,29 @@ class EsphomeCore: def firmware_bin(self): return self.relative_pioenvs_path(self.name, "firmware.bin") + @property + def target_platform(self): + return self.data[KEY_CORE][KEY_TARGET_PLATFORM] + @property def is_esp8266(self): - if self.esp_platform is None: - raise ValueError("No platform specified") - return self.esp_platform == "ESP8266" + return self.target_platform == "esp8266" @property def is_esp32(self): - """Check if the ESP32 platform is used. - - This checks if the ESP32 platform is in use, which - support ESP32 as well as other chips such as ESP32-C3 - """ - if self.esp_platform is None: - raise ValueError("No platform specified") - return self.esp_platform == "ESP32" + return self.target_platform == "esp32" @property - def is_esp32_c3(self): - """Check if the ESP32-C3 SoC is being used.""" - return self.is_esp32 and self.board in boards.ESP32_C3_BOARD_PINS + def target_framework(self): + return self.data[KEY_CORE][KEY_TARGET_FRAMEWORK] + + @property + def using_arduino(self): + return self.target_framework == "arduino" + + @property + def using_esp_idf(self): + return self.target_framework == "esp-idf" def add_job(self, func, *args, **kwargs): self.event_loop.add_job(func, *args, **kwargs) @@ -703,6 +683,14 @@ class EsphomeCore: _LOGGER.debug("Adding define: %s", define) return define + def add_platformio_option(self, key: str, value: Union[str, List[str]]) -> None: + new_val = value + old_val = self.platformio_options.get(key) + if isinstance(old_val, list): + assert isinstance(value, list) + new_val = old_val + value + self.platformio_options[key] = new_val + def _get_variable_generator(self, id): while True: try: diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index d59ad23a5e..a4d61f819c 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -1,7 +1,7 @@ #include "esphome/core/application.h" #include "esphome/core/log.h" #include "esphome/core/version.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #ifdef USE_STATUS_LED #include "esphome/components/status_led/status_led.h" @@ -60,9 +60,6 @@ void Application::setup() { ESP_LOGI(TAG, "setup() finished successfully!"); this->schedule_dump_config(); this->calculate_looping_components_(); - - // Dummy function to link some symbols into the binary. - force_link_symbols(); } void Application::loop() { uint32_t new_app_state = 0; @@ -110,11 +107,11 @@ void Application::loop() { } } -void ICACHE_RAM_ATTR HOT Application::feed_wdt() { +void IRAM_ATTR HOT Application::feed_wdt() { static uint32_t last_feed = 0; uint32_t now = millis(); if (now - last_feed > 3) { - this->feed_wdt_arch_(); + arch_feed_wdt(); last_feed = now; #ifdef USE_STATUS_LED if (status_led::global_status_led != nullptr) { @@ -127,11 +124,7 @@ void Application::reboot() { ESP_LOGI(TAG, "Forcing a reboot..."); for (auto *comp : this->components_) comp->on_shutdown(); - ESP.restart(); // NOLINT(readability-static-accessed-through-instance) - // restart() doesn't always end execution - while (true) { - yield(); - } + arch_restart(); } void Application::safe_reboot() { ESP_LOGI(TAG, "Rebooting safely..."); @@ -139,11 +132,7 @@ void Application::safe_reboot() { comp->on_safe_shutdown(); for (auto *comp : this->components_) comp->on_shutdown(); - ESP.restart(); // NOLINT(readability-static-accessed-through-instance) - // restart() doesn't always end execution - while (true) { - yield(); - } + arch_restart(); } void Application::calculate_looping_components_() { diff --git a/esphome/core/application.h b/esphome/core/application.h index e5f686a320..5c1483d301 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -51,7 +51,6 @@ class Application { this->name_ = name; } this->compilation_time_ = compilation_time; - global_preferences.begin(); } #ifdef USE_BINARY_SENSOR diff --git a/esphome/core/application_esp32.cpp b/esphome/core/application_esp32.cpp deleted file mode 100644 index 9f084428bb..0000000000 --- a/esphome/core/application_esp32.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "esphome/core/application.h" - -#ifdef ARDUINO_ARCH_ESP32 - -namespace esphome { - -static const char *const TAG = "app_esp32"; - -void ICACHE_RAM_ATTR HOT Application::feed_wdt_arch_() { -#if CONFIG_ARDUINO_RUNNING_CORE == 0 -#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 - // ESP32 uses "Task Watchdog" which is hooked to the FreeRTOS idle task. - // To cause the Watchdog to be triggered we need to put the current task - // to sleep to get the idle task scheduled. - delay(1); -#endif -#endif -} - -} // namespace esphome -#endif diff --git a/esphome/core/application_esp8266.cpp b/esphome/core/application_esp8266.cpp deleted file mode 100644 index ee32417634..0000000000 --- a/esphome/core/application_esp8266.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "esphome/core/application.h" - -#ifdef ARDUINO_ARCH_ESP8266 - -namespace esphome { - -static const char *const TAG = "app_esp8266"; - -void ICACHE_RAM_ATTR HOT Application::feed_wdt_arch_() { - ESP.wdtFeed(); // NOLINT(readability-static-accessed-through-instance) -} - -} // namespace esphome -#endif diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index e3b32978cd..c85b445b08 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -1,7 +1,7 @@ #include "esphome/core/component.h" #include "esphome/core/application.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include @@ -144,7 +144,7 @@ void Component::status_momentary_error(const std::string &name, uint32_t length) } void Component::dump_config() {} float Component::get_actual_setup_priority() const { - if (isnan(this->setup_priority_override_)) + if (std::isnan(this->setup_priority_override_)) return this->get_setup_priority(); return this->setup_priority_override_; } diff --git a/esphome/core/component.h b/esphome/core/component.h index 2654504fe8..85256c0f0f 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -2,7 +2,7 @@ #include #include -#include "Arduino.h" +#include #include "esphome/core/optional.h" diff --git a/esphome/core/config.py b/esphome/core/config.py index 222a775ee6..71add56b13 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -4,7 +4,7 @@ import re import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation, boards +from esphome import automation from esphome.const import ( CONF_ARDUINO_VERSION, CONF_BOARD, @@ -12,6 +12,7 @@ from esphome.const import ( CONF_BUILD_PATH, CONF_COMMENT, CONF_ESPHOME, + CONF_FRAMEWORK, CONF_INCLUDES, CONF_LIBRARIES, CONF_NAME, @@ -23,11 +24,10 @@ from esphome.const import ( CONF_PRIORITY, CONF_PROJECT, CONF_TRIGGER_ID, - CONF_ESP8266_RESTORE_FROM_FLASH, - ARDUINO_VERSION_ESP8266, - ARDUINO_VERSION_ESP32, + CONF_TYPE, CONF_VERSION, - ESP_PLATFORMS, + KEY_CORE, + TARGET_PLATFORMS, ) from esphome.core import CORE, coroutine_with_priority from esphome.helpers import copy_file_if_changed, walk_files @@ -50,79 +50,6 @@ VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$") CONF_NAME_ADD_MAC_SUFFIX = "name_add_mac_suffix" -def validate_board(value: str): - if CORE.is_esp8266: - boardlist = boards.ESP8266_BOARD_PINS.keys() - elif CORE.is_esp32: - boardlist = list(boards.ESP32_BOARD_PINS.keys()) - boardlist += list(boards.ESP32_C3_BOARD_PINS.keys()) - else: - raise NotImplementedError - - if value not in boardlist: - raise cv.Invalid( - f"Could not find board '{value}'. Valid boards are {', '.join(sorted(boardlist))}" - ) - return value - - -validate_platform = cv.one_of(*ESP_PLATFORMS, upper=True) - -PLATFORMIO_ESP8266_LUT = { - **ARDUINO_VERSION_ESP8266, - # Keep this in mind when updating the recommended version: - # * New framework historically have had some regressions, especially for WiFi, BLE and the - # bootloader system. The new version needs to be thoroughly validated before changing the - # recommended version as otherwise a bunch of devices could be bricked - # * The docker images need to be updated to ship the new recommended version, in order not - # to DDoS platformio servers. - # Update this file: https://github.com/esphome/esphome-docker-base/blob/main/platformio.ini - "RECOMMENDED": ARDUINO_VERSION_ESP8266["2.7.4"], - "LATEST": "espressif8266", - "DEV": ARDUINO_VERSION_ESP8266["dev"], -} - -PLATFORMIO_ESP32_LUT = { - **ARDUINO_VERSION_ESP32, - # See PLATFORMIO_ESP8266_LUT for considerations when changing the recommended version - "RECOMMENDED": ARDUINO_VERSION_ESP32["1.0.6"], - "LATEST": "espressif32", - "DEV": ARDUINO_VERSION_ESP32["dev"], -} - - -def validate_arduino_version(value): - value = cv.string_strict(value) - value_ = value.upper() - if CORE.is_esp8266: - if ( - VERSION_REGEX.match(value) is not None - and value_ not in PLATFORMIO_ESP8266_LUT - ): - raise cv.Invalid( - f"Unfortunately the arduino framework version '{value}' is unsupported at this time. You can override this by manually using espressif8266@" - ) - if value_ in PLATFORMIO_ESP8266_LUT: - return PLATFORMIO_ESP8266_LUT[value_] - return value - if CORE.is_esp32: - if ( - VERSION_REGEX.match(value) is not None - and value_ not in PLATFORMIO_ESP32_LUT - ): - raise cv.Invalid( - f"Unfortunately the arduino framework version '{value}' is unsupported at this time. You can override this by manually using espressif32@" - ) - if value_ in PLATFORMIO_ESP32_LUT: - return PLATFORMIO_ESP32_LUT[value_] - return value - raise NotImplementedError - - -def default_build_path(): - return CORE.name - - VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"} @@ -149,27 +76,17 @@ def valid_project_name(value: str): return value +CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_NAME): cv.hostname, - cv.Required(CONF_PLATFORM): cv.one_of("ESP8266", "ESP32", upper=True), - cv.Required(CONF_BOARD): validate_board, cv.Optional(CONF_COMMENT): cv.string, - cv.Optional( - CONF_ARDUINO_VERSION, default="recommended" - ): validate_arduino_version, - cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, + cv.Required(CONF_BUILD_PATH): cv.string, cv.Optional(CONF_PLATFORMIO_OPTIONS, default={}): cv.Schema( { cv.string_strict: cv.Any([cv.string], cv.string), } ), - cv.SplitDefault(CONF_ESP8266_RESTORE_FROM_FLASH, esp8266=False): cv.All( - cv.only_on_esp8266, cv.boolean - ), - cv.SplitDefault(CONF_BOARD_FLASH_MODE, esp8266="dout"): cv.one_of( - *BUILD_FLASH_MODES, lower=True - ), cv.Optional(CONF_ON_BOOT): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger), @@ -201,38 +118,78 @@ CONFIG_SCHEMA = cv.Schema( PRELOAD_CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_NAME): cv.valid_name, - cv.Required(CONF_PLATFORM): validate_platform, + cv.Optional(CONF_BUILD_PATH): cv.string, + # Compat options, these were moved to target-platform specific sections + # but we'll keep these around for a long time because every config would + # be impacted + cv.Optional(CONF_PLATFORM): cv.one_of(*TARGET_PLATFORMS, lower=True), + cv.Optional(CONF_BOARD): cv.string_strict, + cv.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): cv.valid, + cv.Optional(CONF_BOARD_FLASH_MODE): cv.valid, + cv.Optional(CONF_ARDUINO_VERSION): cv.valid, }, extra=cv.ALLOW_EXTRA, ) -PRELOAD_CONFIG_SCHEMA2 = PRELOAD_CONFIG_SCHEMA.extend( - { - cv.Required(CONF_BOARD): validate_board, - cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, - } -) +def preload_core_config(config, result): + with cv.prepend_path(CONF_ESPHOME): + conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) -def preload_core_config(config): - core_key = "esphome" - if "esphomeyaml" in config: - _LOGGER.warning( - "The esphomeyaml section has been renamed to esphome in 1.11.0. " - "Please replace 'esphomeyaml:' in your configuration with 'esphome:'." + CORE.name = conf[CONF_NAME] + CORE.data[KEY_CORE] = {} + + if CONF_BUILD_PATH not in conf: + conf[CONF_BUILD_PATH] = CORE.name + CORE.build_path = CORE.relative_config_path(conf[CONF_BUILD_PATH]) + + has_oldstyle = CONF_PLATFORM in conf + newstyle_found = [key for key in TARGET_PLATFORMS if key in config] + oldstyle_opts = [ + CONF_ESP8266_RESTORE_FROM_FLASH, + CONF_BOARD_FLASH_MODE, + CONF_ARDUINO_VERSION, + CONF_BOARD, + ] + + if not has_oldstyle and not newstyle_found: + raise cv.Invalid("Platform missing for core options!", [CONF_ESPHOME]) + if has_oldstyle and newstyle_found: + raise cv.Invalid( + f"Please remove the `platform` key from the [esphome] block. You're already using the new style with the [{conf[CONF_PLATFORM]}] block", + [CONF_ESPHOME, CONF_PLATFORM], ) - config[CONF_ESPHOME] = config.pop("esphomeyaml") - core_key = "esphomeyaml" - if CONF_ESPHOME not in config: - raise cv.RequiredFieldInvalid("required key not provided", CONF_ESPHOME) - with cv.prepend_path(core_key): - out = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) - CORE.name = out[CONF_NAME] - CORE.esp_platform = out[CONF_PLATFORM] - with cv.prepend_path(core_key): - out2 = PRELOAD_CONFIG_SCHEMA2(config[CONF_ESPHOME]) - CORE.board = out2[CONF_BOARD] - CORE.build_path = CORE.relative_config_path(out2[CONF_BUILD_PATH]) + if len(newstyle_found) > 1: + raise cv.Invalid( + f"Found multiple target platform blocks: {', '.join(newstyle_found)}. Only one is allowed.", + [newstyle_found[0]], + ) + if newstyle_found: + # Convert to newstyle + for key in oldstyle_opts: + if key in conf: + raise cv.Invalid( + f"Please move {key} to the [{newstyle_found[0]}] block.", + [CONF_ESPHOME, key], + ) + + if has_oldstyle: + plat = conf.pop(CONF_PLATFORM) + plat_conf = {} + if CONF_ESP8266_RESTORE_FROM_FLASH in conf: + plat_conf["restore_from_flash"] = conf.pop(CONF_ESP8266_RESTORE_FROM_FLASH) + if CONF_BOARD_FLASH_MODE in conf: + plat_conf[CONF_BOARD_FLASH_MODE] = conf.pop(CONF_BOARD_FLASH_MODE) + if CONF_ARDUINO_VERSION in conf: + plat_conf[CONF_FRAMEWORK] = { + CONF_TYPE: "arduino", + CONF_VERSION: conf.pop(CONF_ARDUINO_VERSION), + } + if CONF_BOARD in conf: + plat_conf[CONF_BOARD] = conf.pop(CONF_BOARD) + # Insert generated target platform config to main config + config[plat] = plat_conf + config[CONF_ESPHOME] = conf def include_file(path, basename): @@ -263,25 +220,10 @@ async def add_includes(includes): @coroutine_with_priority(-1000.0) -async def _esp8266_add_lwip_type(): - # If any component has already set this, do not change it - if any( - flag.startswith("-DPIO_FRAMEWORK_ARDUINO_LWIP2_") for flag in CORE.build_flags - ): - return - - # Default for platformio is LWIP2_LOW_MEMORY with: - # - MSS=536 - # - LWIP_FEATURES enabled - # - this only adds some optional features like IP incoming packet reassembly and NAPT - # see also: - # https://github.com/esp8266/Arduino/blob/master/tools/sdk/lwip2/include/lwipopts.h - - # Instead we use LWIP2_HIGHER_BANDWIDTH_LOW_FLASH with: - # - MSS=1460 - # - LWIP_FEATURES disabled (because we don't need them) - # Other projects like Tasmota & ESPEasy also use this - cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH") +async def _add_platformio_options(pio_options): + # Add includes at the very end, so that they override everything + for key, val in pio_options.items(): + cg.add_platformio_option(key, val) @coroutine_with_priority(30.0) @@ -315,10 +257,6 @@ async def to_code(config): CORE.add_job(_add_automations, config) - # Set LWIP build constants for ESP8266 - if CORE.is_esp8266: - CORE.add_job(_esp8266_add_lwip_type) - cg.add_build_flag("-fno-exceptions") # Libraries @@ -337,25 +275,16 @@ async def to_code(config): else: cg.add_library(lib, None) - if CORE.is_esp8266: - # Arduino 2 has a non-standards conformant new that returns a nullptr instead of failing when - # out of memory and exceptions are disabled. Since Arduino 2.6.0, this flag can be used to make - # new abort instead. Use it so that OOM fails early (on allocation) instead of on dereference of - # a NULL pointer (so the stacktrace makes more sense), and for consistency with Arduino 3, - # which always aborts if exceptions are disabled. - # For cases where nullptrs can be handled, use nothrow: `new (std::nothrow) T;` - cg.add_build_flag("-DNEW_OOM_ABORT") - cg.add_build_flag("-Wno-unused-variable") cg.add_build_flag("-Wno-unused-but-set-variable") cg.add_build_flag("-Wno-sign-compare") - if config.get(CONF_ESP8266_RESTORE_FROM_FLASH, False): - cg.add_define("USE_ESP8266_PREFERENCES_FLASH") if config[CONF_INCLUDES]: CORE.add_job(add_includes, config[CONF_INCLUDES]) - cg.add_define("ESPHOME_BOARD", CORE.board) if CONF_PROJECT in config: cg.add_define("ESPHOME_PROJECT_NAME", config[CONF_PROJECT][CONF_NAME]) cg.add_define("ESPHOME_PROJECT_VERSION", config[CONF_PROJECT][CONF_VERSION]) + + if config[CONF_PLATFORMIO_OPTIONS]: + CORE.add_job(_add_platformio_options, config[CONF_PLATFORMIO_OPTIONS]) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 89010ce246..ac40921a4a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -13,7 +13,9 @@ // Feature flags #define USE_API #define USE_BINARY_SENSOR +#ifdef USE_ARDUINO #define USE_CAPTIVE_PORTAL +#endif #define USE_CLIMATE #define USE_COVER #define USE_DEEP_SLEEP @@ -21,13 +23,17 @@ #define USE_FAN #define USE_GRAPH #define USE_HOMEASSISTANT_TIME -#define USE_HTTP_REQUEST_ESP8266_HTTPS -#define USE_I2C_MULTIPLEXER + +#ifdef USE_ARDUINO #define USE_JSON +#define USE_NEXTION_TFT_UPLOAD +#define USE_MQTT +#define USE_CAPTIVE_PORTAL +#endif // USE_ARDUINO + #define USE_LIGHT #define USE_LOGGER #define USE_MDNS -#define USE_MQTT #define USE_NUMBER #define USE_OTA_STATE_CALLBACK #define USE_POWER_SUPPLY @@ -37,22 +43,29 @@ #define USE_STATUS_LED #define USE_SWITCH #define USE_TEXT_SENSOR -#define USE_TFT_UPLOAD + #define USE_TIME #define USE_WIFI +#ifdef USE_ARDUINO #define USE_WIFI_WPA2_EAP - -#ifdef ARDUINO_ARCH_ESP32 -#define USE_ESP32_BLE_SERVER -#define USE_ESP32_CAMERA -#define USE_ETHERNET -#define USE_IMPROV -#define USE_SOCKET_IMPL_BSD_SOCKETS #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP32 +#define USE_ESP32_BLE_SERVER +#define USE_ESP32_CAMERA + +#ifdef USE_ARDUINO +#define USE_ETHERNET +#endif // USE_ARDUINO + +#define USE_IMPROV +#define USE_SOCKET_IMPL_BSD_SOCKETS +#endif // USE_ESP32 + +#ifdef USE_ESP8266 #define USE_ADC_SENSOR_VCC #define USE_SOCKET_IMPL_LWIP_TCP +#define USE_HTTP_REQUEST_ESP8266_HTTPS #endif #define USE_API_PLAINTEXT @@ -60,3 +73,9 @@ // Disabled feature flags //#define USE_BSEC // Requires a library with proprietary license. +#define USE_TIME +#define USE_DEEP_SLEEP +#define ESPHOME_BOARD "dummy_board" +#define USE_MDNS +#define USE_API_NOISE +#define USE_API_PLAINTEXT diff --git a/esphome/core/esphal.cpp b/esphome/core/esphal.cpp deleted file mode 100644 index 849fdf95ad..0000000000 --- a/esphome/core/esphal.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include "esphome/core/esphal.h" -#include "esphome/core/macros.h" -#include "esphome/core/helpers.h" -#include "esphome/core/defines.h" -#include "esphome/core/log.h" - -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -typedef struct { // NOLINT - void *interruptInfo; // NOLINT - void *functionInfo; // NOLINT -} ArgStructure; - -void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, void (*)(void *), void *fp, // NOLINT - int mode); -void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin); // NOLINT -}; -#endif - -namespace esphome { - -static const char *const TAG = "esphal"; - -GPIOPin::GPIOPin(uint8_t pin, uint8_t mode, bool inverted) - : pin_(pin), - mode_(mode), - inverted_(inverted), -#ifdef ARDUINO_ARCH_ESP8266 - gpio_read_(pin < 16 ? &GPI : &GP16I), - gpio_mask_(pin < 16 ? (1UL << pin) : 1) -#elif ARDUINO_ARCH_ESP32 -#ifdef CONFIG_IDF_TARGET_ESP32C3 - gpio_set_(&GPIO.out_w1ts.val), - gpio_clear_(&GPIO.out_w1tc.val), - gpio_read_(&GPIO.in.val), -#else - gpio_set_(pin < 32 ? &GPIO.out_w1ts : &GPIO.out1_w1ts.val), - gpio_clear_(pin < 32 ? &GPIO.out_w1tc : &GPIO.out1_w1tc.val), - gpio_read_(pin < 32 ? &GPIO.in : &GPIO.in1.val), -#endif - gpio_mask_(pin < 32 ? (1UL << pin) : (1UL << (pin - 32))) -#endif -{ -} - -const LogString *GPIOPin::get_pin_mode_name() const { - const char *mode_s; - switch (this->mode_) { - case INPUT: - return LOG_STR("INPUT"); - case OUTPUT: - return LOG_STR("OUTPUT"); - case INPUT_PULLUP: - return LOG_STR("INPUT_PULLUP"); - case OUTPUT_OPEN_DRAIN: - return LOG_STR("OUTPUT_OPEN_DRAIN"); - case SPECIAL: - return LOG_STR("SPECIAL"); - case FUNCTION_1: - return LOG_STR("FUNCTION_1"); - case FUNCTION_2: - return LOG_STR("FUNCTION_2"); - case FUNCTION_3: - return LOG_STR("FUNCTION_3"); - case FUNCTION_4: - return LOG_STR("FUNCTION_4"); -#ifdef ARDUINO_ARCH_ESP32 - case PULLUP: - return LOG_STR("PULLUP"); - case PULLDOWN: - return LOG_STR("PULLDOWN"); - case INPUT_PULLDOWN: - return LOG_STR("INPUT_PULLDOWN"); - case OPEN_DRAIN: - return LOG_STR("OPEN_DRAIN"); - case FUNCTION_5: - return LOG_STR("FUNCTION_5"); - case FUNCTION_6: - return LOG_STR("FUNCTION_6"); - case ANALOG: - return LOG_STR("ANALOG"); -#endif -#ifdef ARDUINO_ARCH_ESP8266 - case FUNCTION_0: - return LOG_STR("FUNCTION_0"); - case WAKEUP_PULLUP: - return LOG_STR("WAKEUP_PULLUP"); - case WAKEUP_PULLDOWN: - return LOG_STR("WAKEUP_PULLDOWN"); - case INPUT_PULLDOWN_16: - return LOG_STR("INPUT_PULLDOWN_16"); -#endif - default: - return LOG_STR("UNKNOWN"); - } -} - -unsigned char GPIOPin::get_pin() const { return this->pin_; } -unsigned char GPIOPin::get_mode() const { return this->mode_; } - -bool GPIOPin::is_inverted() const { return this->inverted_; } -void GPIOPin::setup() { this->pin_mode(this->mode_); } -bool ICACHE_RAM_ATTR HOT GPIOPin::digital_read() { - return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_; -} -bool ICACHE_RAM_ATTR HOT ISRInternalGPIOPin::digital_read() { - return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_; -} -void ICACHE_RAM_ATTR HOT GPIOPin::digital_write(bool value) { -#ifdef ARDUINO_ARCH_ESP8266 - if (this->pin_ != 16) { - if (value != this->inverted_) { - GPOS = this->gpio_mask_; - } else { - GPOC = this->gpio_mask_; - } - } else { - if (value != this->inverted_) { - GP16O |= 1; - } else { - GP16O &= ~1; - } - } -#endif -#ifdef ARDUINO_ARCH_ESP32 - if (value != this->inverted_) { - (*this->gpio_set_) = this->gpio_mask_; - } else { - (*this->gpio_clear_) = this->gpio_mask_; - } -#endif -} -void ICACHE_RAM_ATTR HOT ISRInternalGPIOPin::digital_write(bool value) { -#ifdef ARDUINO_ARCH_ESP8266 - if (this->pin_ != 16) { - if (value != this->inverted_) { - GPOS = this->gpio_mask_; - } else { - GPOC = this->gpio_mask_; - } - } else { - if (value != this->inverted_) { - GP16O |= 1; - } else { - GP16O &= ~1; - } - } -#endif -#ifdef ARDUINO_ARCH_ESP32 - if (value != this->inverted_) { - (*this->gpio_set_) = this->gpio_mask_; - } else { - (*this->gpio_clear_) = this->gpio_mask_; - } -#endif -} -ISRInternalGPIOPin::ISRInternalGPIOPin(uint8_t pin, -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set, -#endif - volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted) - : pin_(pin), - inverted_(inverted), - gpio_read_(gpio_read), - gpio_mask_(gpio_mask) -#ifdef ARDUINO_ARCH_ESP32 - , - gpio_clear_(gpio_clear), - gpio_set_(gpio_set) -#endif -{ -} -void ICACHE_RAM_ATTR ISRInternalGPIOPin::clear_interrupt() { -#ifdef ARDUINO_ARCH_ESP8266 - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, this->gpio_mask_); -#endif -#ifdef ARDUINO_ARCH_ESP32 -#ifdef CONFIG_IDF_TARGET_ESP32C3 - GPIO.status_w1tc.val = this->gpio_mask_; -#else - if (this->pin_ < 32) { - GPIO.status_w1tc = this->gpio_mask_; - } else { - GPIO.status1_w1tc.intr_st = this->gpio_mask_; - } -#endif -#endif -} - -void ICACHE_RAM_ATTR HOT GPIOPin::pin_mode(uint8_t mode) { -#ifdef ARDUINO_ARCH_ESP8266 - if (this->pin_ == 16 && mode == INPUT_PULLUP) { - // pullups are not available on GPIO16, manually override with - // input mode. - pinMode(16, INPUT); - return; - } -#endif - pinMode(this->pin_, mode); -} - -#ifdef ARDUINO_ARCH_ESP8266 -struct ESPHomeInterruptFuncInfo { - void (*func)(void *); - void *arg; -}; - -void ICACHE_RAM_ATTR interrupt_handler(void *arg) { - ArgStructure *as = static_cast(arg); - auto *info = static_cast(as->functionInfo); - info->func(info->arg); -} -#endif - -void GPIOPin::detach_interrupt() const { this->detach_interrupt_(); } -void GPIOPin::detach_interrupt_() const { -#ifdef ARDUINO_ARCH_ESP8266 - __detachInterrupt(get_pin()); -#endif -#ifdef ARDUINO_ARCH_ESP32 - detachInterrupt(get_pin()); -#endif -} -void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const { - if (this->inverted_) { - if (mode == RISING) { - mode = FALLING; - } else if (mode == FALLING) { - mode = RISING; - } - } -#ifdef ARDUINO_ARCH_ESP8266 - ArgStructure *as = new ArgStructure; // NOLINT - as->interruptInfo = nullptr; - - as->functionInfo = new ESPHomeInterruptFuncInfo{ - // NOLINT - .func = func, - .arg = arg, - }; - - __attachInterruptArg(this->pin_, interrupt_handler, as, mode); -#endif -#ifdef ARDUINO_ARCH_ESP32 - // work around issue https://github.com/espressif/arduino-esp32/pull/1776 in arduino core - // yet again proves how horrible code is there :( - how could that have been accepted... - auto *attach = reinterpret_cast(attachInterruptArg); - attach(this->pin_, func, arg, mode); -#endif -} - -ISRInternalGPIOPin *GPIOPin::to_isr() const { - return new ISRInternalGPIOPin(this->pin_, // NOLINT -#ifdef ARDUINO_ARCH_ESP32 - this->gpio_clear_, this->gpio_set_, -#endif - this->gpio_read_, this->gpio_mask_, this->inverted_); -} - -void force_link_symbols() { -#ifdef ARDUINO_ARCH_ESP8266 - // Tasmota uses magic bytes in the binary to check if an OTA firmware is compatible - // with their settings - ESPHome uses a different settings system (that can also survive - // erases). So set magic bytes indicating all tasmota versions are supported. - // This only adds 12 bytes of binary size, which is an acceptable price to pay for easier support - // for Tasmota. - // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/settings.ino#L346-L380 - // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/i18n.h#L652-L654 - const static uint32_t TASMOTA_MAGIC_BYTES[] PROGMEM = {0x5AA55AA5, 0xFFFFFFFF, 0xA55AA55A}; - // Force link symbol by using a volatile integer (GCC attribute used does not work because of LTO) - volatile int x = 0; - x = TASMOTA_MAGIC_BYTES[x]; -#endif -} - -} // namespace esphome - -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) -// Fix 2.3.0 std missing memchr -extern "C" { -void *memchr(const void *s, int c, size_t n) { - if (n == 0) - return nullptr; - const uint8_t *p = reinterpret_cast(s); - do { - if (*p++ == c) - return const_cast(reinterpret_cast(p - 1)); - } while (--n != 0); - return nullptr; -} -}; -#endif - -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -extern void resetPins() { // NOLINT - // Added in framework 2.7.0 - // usually this sets up all pins to be in INPUT mode - // however, not strictly needed as we set up the pins properly - // ourselves and this causes pins to toggle during reboot. -} -} -#endif diff --git a/esphome/core/esphal.h b/esphome/core/esphal.h deleted file mode 100644 index 1b92f816b1..0000000000 --- a/esphome/core/esphal.h +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once - -#include "Arduino.h" - -// Fix some arduino defs -#ifdef round -#undef round -#endif -#ifdef bool -#undef bool -#endif -#ifdef true -#undef true -#endif -#ifdef false -#undef false -#endif -#ifdef min -#undef min -#endif -#ifdef max -#undef max -#endif -#ifdef abs -#undef abs -#endif - -#include "esphome/core/log.h" - -namespace esphome { - -#define LOG_PIN(prefix, pin) \ - if ((pin) != nullptr) { \ - ESP_LOGCONFIG(TAG, prefix LOG_PIN_PATTERN, LOG_PIN_ARGS(pin)); \ - } -#define LOG_PIN_PATTERN "GPIO%u (Mode: %s%s)" -#define LOG_PIN_ARGS(pin) \ - (pin)->get_pin(), LOG_STR_ARG((pin)->get_pin_mode_name()), ((pin)->is_inverted() ? ", INVERTED" : "") - -/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions) -class ISRInternalGPIOPin { - public: - ISRInternalGPIOPin(uint8_t pin, -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set, -#endif - volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted); - bool digital_read(); - void digital_write(bool value); - void clear_interrupt(); - - protected: - const uint8_t pin_; - const bool inverted_; - volatile uint32_t *const gpio_read_; - const uint32_t gpio_mask_; -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *const gpio_clear_; - volatile uint32_t *const gpio_set_; -#endif -}; - -/** A high-level abstraction class that can expose a pin together with useful options like pinMode. - * - * Set the parameters for this at construction time and use setup() to apply them. The inverted parameter will - * automatically invert the input/output for you. - * - * Use read_value() and write_value() to use digitalRead() and digitalWrite(), respectively. - */ -class GPIOPin { - public: - /** Construct the GPIOPin instance. - * - * @param pin The GPIO pin number of this instance. - * @param mode The Arduino pinMode that this pin should be put into at setup(). - * @param inverted Whether all digitalRead/digitalWrite calls should be inverted. - */ - GPIOPin(uint8_t pin, uint8_t mode, bool inverted = false); - - /// Setup the pin mode. - virtual void setup(); - /// Read the binary value from this pin using digitalRead (and inverts automatically). - virtual bool digital_read(); - /// Write the binary value to this pin using digitalWrite (and inverts automatically). - virtual void digital_write(bool value); - /// Set the pin mode - virtual void pin_mode(uint8_t mode); - - /// Get the GPIO pin number. - uint8_t get_pin() const; - const LogString *get_pin_mode_name() const; - /// Get the pinMode of this pin. - uint8_t get_mode() const; - /// Return whether this pin shall be treated as inverted. (for example active-low) - bool is_inverted() const; - - template void attach_interrupt(void (*func)(T *), T *arg, int mode) const; - void detach_interrupt() const; - - ISRInternalGPIOPin *to_isr() const; - - protected: - void attach_interrupt_(void (*func)(void *), void *arg, int mode) const; - void detach_interrupt_() const; - - const uint8_t pin_; - const uint8_t mode_; - const bool inverted_; -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *const gpio_set_; - volatile uint32_t *const gpio_clear_; -#endif - volatile uint32_t *const gpio_read_; - const uint32_t gpio_mask_; -}; - -template void GPIOPin::attach_interrupt(void (*func)(T *), T *arg, int mode) const { - this->attach_interrupt_(reinterpret_cast(func), arg, mode); -} -/** This function can be used by the HAL to force-link specific symbols - * into the generated binary without modifying the linker script. - * - * It is called by the application very early on startup and should not be used for anything - * other than forcing symbols to be linked. - */ -void force_link_symbols(); - -} // namespace esphome diff --git a/esphome/core/gpio.h b/esphome/core/gpio.h new file mode 100644 index 0000000000..25d56b9020 --- /dev/null +++ b/esphome/core/gpio.h @@ -0,0 +1,98 @@ +#pragma once +#include +#include + +namespace esphome { + +#define LOG_PIN(prefix, pin) \ + if ((pin) != nullptr) { \ + ESP_LOGCONFIG(TAG, prefix "%s", pin->dump_summary().c_str()); \ + } + +// put GPIO flags in a namepsace to not pollute esphome namespace +namespace gpio { + +enum Flags : uint8_t { + // Can't name these just INPUT because of Arduino defines :( + FLAG_NONE = 0x00, + FLAG_INPUT = 0x01, + FLAG_OUTPUT = 0x02, + FLAG_OPEN_DRAIN = 0x04, + FLAG_PULLUP = 0x08, + FLAG_PULLDOWN = 0x10, +}; + +class FlagsHelper { + public: + constexpr FlagsHelper(Flags val) : val_(val) {} + constexpr operator Flags() const { return val_; } + + protected: + Flags val_; +}; +constexpr FlagsHelper operator&(Flags lhs, Flags rhs) { + return static_cast(static_cast(lhs) & static_cast(rhs)); +} +constexpr FlagsHelper operator|(Flags lhs, Flags rhs) { + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +enum InterruptType : uint8_t { + INTERRUPT_RISING_EDGE = 1, + INTERRUPT_FALLING_EDGE = 2, + INTERRUPT_ANY_EDGE = 3, + INTERRUPT_LOW_LEVEL = 4, + INTERRUPT_HIGH_LEVEL = 5, +}; + +} // namespace gpio + +class GPIOPin { + public: + virtual void setup() = 0; + + virtual void pin_mode(gpio::Flags flags) = 0; + + virtual bool digital_read() = 0; + + virtual void digital_write(bool value) = 0; + + virtual std::string dump_summary() const = 0; + + virtual bool is_internal() { return false; } +}; + +/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions) +class ISRInternalGPIOPin { + public: + ISRInternalGPIOPin() = default; + ISRInternalGPIOPin(void *arg) : arg_(arg) {} + bool digital_read(); + void digital_write(bool value); + void clear_interrupt(); + + protected: + void *arg_ = nullptr; +}; + +class InternalGPIOPin : public GPIOPin { + public: + template void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const { + this->attach_interrupt_(reinterpret_cast(func), arg, type); + } + + virtual void detach_interrupt() const = 0; + + virtual ISRInternalGPIOPin to_isr() const = 0; + + virtual uint8_t get_pin() const = 0; + + bool is_internal() override { return true; } + + virtual bool is_inverted() const = 0; + + protected: + virtual void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const = 0; +}; + +} // namespace esphome diff --git a/esphome/core/hal.h b/esphome/core/hal.h new file mode 100644 index 0000000000..2843bb9b15 --- /dev/null +++ b/esphome/core/hal.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include "gpio.h" + +#if defined(USE_ESP32_FRAMEWORK_ESP_IDF) +#include +#ifndef PROGMEM +#define PROGMEM +#endif + +#elif defined(USE_ESP32_FRAMEWORK_ARDUINO) + +#include + +#ifndef PROGMEM +#define PROGMEM +#endif + +#elif defined(USE_ESP8266) + +#include +#ifndef PROGMEM +#define PROGMEM ICACHE_RODATA_ATTR +#endif + +#else + +#define IRAM_ATTR +#define PROGMEM + +#endif + +namespace esphome { + +void yield(); +uint32_t millis(); +uint32_t micros(); +void delay(uint32_t ms); +void delayMicroseconds(uint32_t us); +void __attribute__((noreturn)) arch_restart(); +void arch_feed_wdt(); +uint32_t arch_get_cpu_cycle_count(); +uint32_t arch_get_cpu_freq_hz(); +uint8_t progmem_read_byte(const uint8_t *addr); + +} // namespace esphome diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 53c7d1de43..a90eb74be2 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -1,15 +1,22 @@ #include "esphome/core/helpers.h" #include #include +#include +#include -#ifdef ARDUINO_ARCH_ESP8266 +#if defined(USE_ESP8266) #include -#else +#include +#elif defined(USE_ESP32_FRAMEWORK_ARDUINO) #include +#elif defined(USE_ESP_IDF) +#include "esp_system.h" +#include +#include #endif #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { @@ -18,10 +25,10 @@ static const char *const TAG = "helpers"; std::string get_mac_address() { char tmp[20]; uint8_t mac[6]; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_efuse_mac_get_default(mac); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 WiFi.macAddress(mac); #endif sprintf(tmp, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -31,10 +38,10 @@ std::string get_mac_address() { std::string get_mac_address_pretty() { char tmp[20]; uint8_t mac[6]; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_efuse_mac_get_default(mac); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 WiFi.macAddress(mac); #endif sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -44,9 +51,9 @@ std::string get_mac_address_pretty() { std::string generate_hostname(const std::string &base) { return base + std::string("-") + get_mac_address(); } uint32_t random_uint32() { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 return esp_random(); -#else +#elif defined(USE_ESP8266) return os_random(); #endif } @@ -56,11 +63,13 @@ double random_double() { return random_uint32() / double(UINT32_MAX); } float random_float() { return float(random_double()); } void fill_random(uint8_t *data, size_t len) { -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP_IDF) || defined(USE_ESP32_FRAMEWORK_ARDUINO) esp_fill_random(data, len); -#else +#elif defined(USE_ESP8266) int err = os_get_random(data, len); assert(err == 0); +#else +#error "No random source for this system config" #endif } @@ -123,10 +132,13 @@ std::string truncate_string(const std::string &s, size_t length) { } std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) { - auto multiplier = float(powf(10.0f, accuracy_decimals)); - float value_rounded = roundf(value * multiplier) / multiplier; + if (accuracy_decimals < 0) { + auto multiplier = powf(10.0f, accuracy_decimals); + value = roundf(value * multiplier) / multiplier; + accuracy_decimals = 0; + } char tmp[32]; // should be enough, but we should maybe improve this at some point. - dtostrf(value_rounded, 0, uint8_t(std::max(0, int(accuracy_decimals))), tmp); + snprintf(tmp, sizeof(tmp), "%.*f", accuracy_decimals, value); return std::string(tmp); } std::string uint64_to_string(uint64_t num) { @@ -334,13 +346,13 @@ std::string hexencode(const uint8_t *data, uint32_t len) { return res; } -#ifdef ARDUINO_ARCH_ESP8266 -ICACHE_RAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } -ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } +#ifdef USE_ESP8266 +IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } +IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } #endif -#ifdef ARDUINO_ARCH_ESP32 -ICACHE_RAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } -ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); } +#ifdef USE_ESP32_FRAMEWORK_ARDUINO +IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } +IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); } #endif } // namespace esphome diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 6c0ee8399e..40d94005e7 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -6,7 +6,7 @@ #include #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include "esp32-hal-psram.h" #endif @@ -151,7 +151,7 @@ uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb); * This behaves like std::lock_guard. As long as the value is visible in the current stack, all interrupts * (including flash reads) will be disabled. * - * Please note all functions called when the interrupt lock must be marked ICACHE_RAM_ATTR (loading code into + * Please note all functions called when the interrupt lock must be marked IRAM_ATTR (loading code into * instruction cache is done via interrupts; disabling interrupts prevents data not already in cache from being * pulled from flash). * @@ -173,7 +173,7 @@ class InterruptLock { ~InterruptLock(); protected: -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 uint32_t xt_state_; #endif }; @@ -277,8 +277,8 @@ template class TemplatableValue { LAMBDA, } type_; - T value_; - std::function f_; + T value_{}; + std::function f_{}; }; template class TemplatableStringValue : public TemplatableValue { @@ -329,7 +329,7 @@ uint32_t fnv1_hash(const std::string &str); template T *new_buffer(size_t length) { T *buffer; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO if (psramFound()) { buffer = (T *) ps_malloc(length); } else { diff --git a/esphome/core/log.cpp b/esphome/core/log.cpp index 9b49a4c6ba..424154d253 100644 --- a/esphome/core/log.cpp +++ b/esphome/core/log.cpp @@ -46,7 +46,7 @@ void HOT esp_log_vprintf_(int level, const char *tag, int line, const __FlashStr } #endif -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) int HOT esp_idf_log_vprintf_(const char *format, va_list args) { // NOLINT #ifdef USE_LOGGER auto *log = logger::global_logger; diff --git a/esphome/core/log.h b/esphome/core/log.h index 3c2f1e6999..590ad26032 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -8,10 +8,14 @@ #include "WString.h" #endif -// Both the ESP-IDF and Arduino also define ESP_LOG* macros. Include them here, so that they won't -// be reincluded later on and redefine our macros. -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/macros.h" + +// Include ESP-IDF/Arduino based logging methods here so they don't undefine ours later +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) +#include #include +#endif +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #endif @@ -58,7 +62,7 @@ void esp_log_vprintf_(int level, const char *tag, int line, const char *format, #ifdef USE_STORE_LOG_STR_IN_FLASH void esp_log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, va_list args); #endif -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #endif diff --git a/esphome/core/macros.h b/esphome/core/macros.h index 59b52bf7a1..b0027a276c 100644 --- a/esphome/core/macros.h +++ b/esphome/core/macros.h @@ -2,7 +2,7 @@ #define VERSION_CODE(major, minor, patch) ((major) << 16 | (minor) << 8 | (patch)) -#if defined(ARDUINO_ARCH_ESP8266) +#if defined(USE_ESP8266) #include #if defined(ARDUINO_ESP8266_MAJOR) && defined(ARDUINO_ESP8266_MINOR) && defined(ARDUINO_ESP8266_REVISION) // v3.0.1+ @@ -43,7 +43,7 @@ #warning "Could not determine Arduino framework version, update esphome/core/macros.h!" #endif -#elif defined(ARDUINO_ARCH_ESP32) +#elif defined(USE_ESP32_FRAMEWORK_ARDUINO) #if defined(IDF_VER) // identifies v2, needed since v1 doesn't have the esp_arduino_version.h header #include diff --git a/esphome/core/preferences.cpp b/esphome/core/preferences.cpp deleted file mode 100644 index f051ce0e8a..0000000000 --- a/esphome/core/preferences.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include "esphome/core/preferences.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/application.h" - -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -#include "spi_flash.h" -} -#endif -#ifdef ARDUINO_ARCH_ESP32 -#include "nvs.h" -#include "nvs_flash.h" -#endif - -namespace esphome { - -static const char *const TAG = "preferences"; - -ESPPreferenceObject::ESPPreferenceObject(size_t offset, size_t length, uint32_t type) - : offset_(offset), length_words_(length), type_(type), data_(length + 1) {} -bool ESPPreferenceObject::load_() { - if (!this->is_initialized()) { - ESP_LOGV(TAG, "Load Pref Not initialized!"); - return false; - } - if (!this->load_internal_()) - return false; - - bool valid = this->data_[this->length_words_] == this->calculate_crc_(); - - ESP_LOGVV(TAG, "LOAD %u: valid=%s, 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->offset_, // NOLINT - YESNO(valid), this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); - return valid; -} -bool ESPPreferenceObject::save_() { - if (!this->is_initialized()) { - ESP_LOGV(TAG, "Save Pref Not initialized!"); - return false; - } - - this->data_[this->length_words_] = this->calculate_crc_(); - if (!this->save_internal_()) - return false; - ESP_LOGVV(TAG, "SAVE %u: 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->offset_, // NOLINT - this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); - return true; -} - -#ifdef ARDUINO_ARCH_ESP8266 - -static const uint32_t ESP_RTC_USER_MEM_START = 0x60001200; -#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START) -static const uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128; -static const uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4; - -#ifdef USE_ESP8266_PREFERENCES_FLASH -static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 128; -#else -static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 64; -#endif - -static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) { - if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { - return false; - } - *dest = ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) - return true; -} - -static bool esp8266_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { - if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { - return false; - } - if (index < 32 && global_preferences.is_prevent_write()) { - return false; - } - - auto *ptr = &ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) - *ptr = value; - return true; -} - -extern "C" uint32_t _SPIFFS_end; // NOLINT - -static const uint32_t get_esp8266_flash_sector() { - union { - uint32_t *ptr; - uint32_t uint; - } data{}; - data.ptr = &_SPIFFS_end; - return (data.uint - 0x40200000) / SPI_FLASH_SEC_SIZE; -} -static const uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; } - -void ESPPreferences::save_esp8266_flash_() { - if (!esp8266_flash_dirty) - return; - - ESP_LOGVV(TAG, "Saving preferences to flash..."); - SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; - { - InterruptLock lock; - erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); - if (erase_res == SPI_FLASH_RESULT_OK) { - write_res = spi_flash_write(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); - } - } - if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); - return; - } - if (write_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Write ESP8266 flash failed!"); - return; - } - - esp8266_flash_dirty = false; -} - -bool ESPPreferenceObject::save_internal_() { - if (this->in_flash_) { - for (uint32_t i = 0; i <= this->length_words_; i++) { - uint32_t j = this->offset_ + i; - if (j >= ESP8266_FLASH_STORAGE_SIZE) - return false; - uint32_t v = this->data_[i]; - uint32_t *ptr = &global_preferences.flash_storage_[j]; - if (*ptr != v) - esp8266_flash_dirty = true; - *ptr = v; - } - global_preferences.save_esp8266_flash_(); - return true; - } - - for (uint32_t i = 0; i <= this->length_words_; i++) { - if (!esp_rtc_user_mem_write(this->offset_ + i, this->data_[i])) - return false; - } - - return true; -} -bool ESPPreferenceObject::load_internal_() { - if (this->in_flash_) { - for (uint32_t i = 0; i <= this->length_words_; i++) { - uint32_t j = this->offset_ + i; - if (j >= ESP8266_FLASH_STORAGE_SIZE) - return false; - this->data_[i] = global_preferences.flash_storage_[j]; - } - - return true; - } - - for (uint32_t i = 0; i <= this->length_words_; i++) { - if (!esp_rtc_user_mem_read(this->offset_ + i, &this->data_[i])) - return false; - } - return true; -} -ESPPreferences::ESPPreferences() - // offset starts from start of user RTC mem (64 words before that are reserved for system), - // an additional 32 words at the start of user RTC are for eboot (OTA, see eboot_command.h), - // which will be reset each time OTA occurs - : current_offset_(0) {} - -void ESPPreferences::begin() { - this->flash_storage_ = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT - ESP_LOGVV(TAG, "Loading preferences from flash..."); - - { - InterruptLock lock; - spi_flash_read(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); - } -} - -ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type, bool in_flash) { - if (in_flash) { - uint32_t start = this->current_flash_offset_; - uint32_t end = start + length + 1; - if (end > ESP8266_FLASH_STORAGE_SIZE) - return {}; - auto pref = ESPPreferenceObject(start, length, type); - pref.in_flash_ = true; - this->current_flash_offset_ = end; - return pref; - } - - uint32_t start = this->current_offset_; - uint32_t end = start + length + 1; - bool in_normal = start < 96; - // Normal: offset 0-95 maps to RTC offset 32 - 127, - // Eboot: offset 96-127 maps to RTC offset 0 - 31 words - if (in_normal && end > 96) { - // start is in normal but end is not -> switch to Eboot - this->current_offset_ = start = 96; - end = start + length + 1; - in_normal = false; - } - - if (end > 128) { - // Doesn't fit in data, return uninitialized preference obj. - return {}; - } - - uint32_t rtc_offset; - if (in_normal) { - rtc_offset = start + 32; - } else { - rtc_offset = start - 96; - } - - auto pref = ESPPreferenceObject(rtc_offset, length, type); - this->current_offset_ += length + 1; - return pref; -} -void ESPPreferences::prevent_write(bool prevent) { this->prevent_write_ = prevent; } -bool ESPPreferences::is_prevent_write() { return this->prevent_write_; } -#endif - -#ifdef ARDUINO_ARCH_ESP32 -bool ESPPreferenceObject::save_internal_() { - if (global_preferences.nvs_handle_ == 0) - return false; - - char key[32]; - sprintf(key, "%u", this->offset_); - uint32_t len = (this->length_words_ + 1) * 4; - esp_err_t err = nvs_set_blob(global_preferences.nvs_handle_, key, this->data_.data(), len); - if (err) { - ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", key, len, esp_err_to_name(err)); - return false; - } - err = nvs_commit(global_preferences.nvs_handle_); - if (err) { - ESP_LOGV(TAG, "nvs_commit('%s', len=%u) failed: %s", key, len, esp_err_to_name(err)); - return false; - } - return true; -} -bool ESPPreferenceObject::load_internal_() { - if (global_preferences.nvs_handle_ == 0) - return false; - - char key[32]; - sprintf(key, "%u", this->offset_); - size_t len = (this->length_words_ + 1) * 4; - - size_t actual_len; - esp_err_t err = nvs_get_blob(global_preferences.nvs_handle_, key, nullptr, &actual_len); - if (err) { - ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key, esp_err_to_name(err)); - return false; - } - if (actual_len != len) { - ESP_LOGVV(TAG, "NVS length does not match. Assuming key changed (%u!=%u)", actual_len, len); - return false; - } - err = nvs_get_blob(global_preferences.nvs_handle_, key, this->data_.data(), &len); - if (err) { - ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key, esp_err_to_name(err)); - return false; - } - return true; -} -ESPPreferences::ESPPreferences() : current_offset_(0) {} -void ESPPreferences::begin() { - auto ns = truncate_string(App.get_name(), 15); - esp_err_t err = nvs_open(ns.c_str(), NVS_READWRITE, &this->nvs_handle_); - if (err) { - ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err)); - nvs_flash_deinit(); - nvs_flash_erase(); - nvs_flash_init(); - - err = nvs_open(ns.c_str(), NVS_READWRITE, &this->nvs_handle_); - if (err) { - this->nvs_handle_ = 0; - } - } -} - -ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type, bool in_flash) { - auto pref = ESPPreferenceObject(this->current_offset_, length, type); - this->current_offset_++; - return pref; -} -#endif -uint32_t ESPPreferenceObject::calculate_crc_() const { - uint32_t crc = this->type_; - for (size_t i = 0; i < this->length_words_; i++) { - crc ^= (this->data_[i] * 2654435769UL) >> 1; - } - return crc; -} -bool ESPPreferenceObject::is_initialized() const { return !this->data_.empty(); } - -ESPPreferences global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -} // namespace esphome diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index 7ed9a2c8c5..ff7911ed3a 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -1,112 +1,61 @@ #pragma once -#include -#include -#include #include - -#include "esphome/core/defines.h" +#include +#include namespace esphome { +class ESPPreferenceBackend { + public: + virtual bool save(const uint8_t *data, size_t len) = 0; + virtual bool load(uint8_t *data, size_t len) = 0; +}; + class ESPPreferenceObject { public: ESPPreferenceObject() = default; - ESPPreferenceObject(size_t offset, size_t length, uint32_t type); + ESPPreferenceObject(ESPPreferenceBackend *backend) : backend_(backend) {} - template bool save(T *src); + template bool save(const T *src) { + if (backend_ == nullptr) + return false; + return backend_->save(reinterpret_cast(src), sizeof(T)); + } - template bool load(T *dest); - - bool is_initialized() const; + template bool load(T *dest) { + if (backend_ == nullptr) + return false; + return backend_->load(reinterpret_cast(dest), sizeof(T)); + } protected: - friend class ESPPreferences; - - bool save_(); - bool load_(); - bool save_internal_(); - bool load_internal_(); - - uint32_t calculate_crc_() const; - - size_t offset_ = 0; - size_t length_words_ = 0; - uint32_t type_ = 0; - std::vector data_; -#ifdef ARDUINO_ARCH_ESP8266 - bool in_flash_ = false; -#endif + ESPPreferenceBackend *backend_; }; -#ifdef ARDUINO_ARCH_ESP8266 -#ifdef USE_ESP8266_PREFERENCES_FLASH -static const bool DEFAULT_IN_FLASH = true; -#else -static const bool DEFAULT_IN_FLASH = false; -#endif -#endif - -#ifdef ARDUINO_ARCH_ESP32 -static const bool DEFAULT_IN_FLASH = true; -#endif - class ESPPreferences { public: - ESPPreferences(); - void begin(); - ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash = DEFAULT_IN_FLASH); - template ESPPreferenceObject make_preference(uint32_t type, bool in_flash = DEFAULT_IN_FLASH); - -#ifdef ARDUINO_ARCH_ESP8266 - /** On the ESP8266, we can't override the first 128 bytes during OTA uploads - * as the eboot parameters are stored there. Writing there during an OTA upload - * would invalidate applying the new firmware. During normal operation, we use - * this part of the RTC user memory, but stop writing to it during OTA uploads. - * - * @param prevent Whether to prevent writing to the first 32 words of RTC user memory. - */ - void prevent_write(bool prevent); - bool is_prevent_write(); + virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) = 0; + virtual ESPPreferenceObject make_preference(size_t length, uint32_t type) = 0; +#ifndef USE_ESP8266 + template::value, bool>::type = true> +#else + // esp8266 toolchain doesn't have is_trivially_copyable + template #endif - - protected: - friend ESPPreferenceObject; - - uint32_t current_offset_; -#ifdef ARDUINO_ARCH_ESP32 - uint32_t nvs_handle_; -#endif -#ifdef ARDUINO_ARCH_ESP8266 - void save_esp8266_flash_(); - bool prevent_write_{false}; - uint32_t *flash_storage_; - uint32_t current_flash_offset_; + ESPPreferenceObject make_preference(uint32_t type, bool in_flash) { + return this->make_preference(sizeof(T), type, in_flash); + } +#ifndef USE_ESP8266 + template::value, bool>::type = true> +#else + template #endif + ESPPreferenceObject make_preference(uint32_t type) { + return this->make_preference(sizeof(T), type); + } }; -extern ESPPreferences global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -template ESPPreferenceObject ESPPreferences::make_preference(uint32_t type, bool in_flash) { - return this->make_preference((sizeof(T) + 3) / 4, type, in_flash); -} - -template bool ESPPreferenceObject::save(T *src) { - if (!this->is_initialized()) - return false; - // ensure all bytes are 0 (in case sizeof(T) is not multiple of 4) - std::fill_n(data_.begin(), length_words_, 0); - memcpy(data_.data(), src, sizeof(T)); - return this->save_(); -} - -template bool ESPPreferenceObject::load(T *dest) { - std::fill_n(data_.begin(), length_words_, 0); - if (!this->load_()) - return false; - - memcpy(dest, data_.data(), sizeof(T)); - return true; -} +extern ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 5718e3b396..a6d3e0307e 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -1,6 +1,7 @@ #include "scheduler.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" #include namespace esphome { @@ -81,7 +82,7 @@ optional HOT Scheduler::next_schedule_in() { return 0; return next_time - now; } -void ICACHE_RAM_ATTR HOT Scheduler::call() { +void IRAM_ATTR HOT Scheduler::call() { const uint32_t now = this->millis_(); this->process_to_add(); diff --git a/esphome/core/util.cpp b/esphome/core/util.cpp index e0132e2e4a..996cf8e310 100644 --- a/esphome/core/util.cpp +++ b/esphome/core/util.cpp @@ -4,47 +4,16 @@ #include "esphome/core/version.h" #include "esphome/core/log.h" -#ifdef USE_WIFI -#include "esphome/components/wifi/wifi_component.h" -#endif - #ifdef USE_API #include "esphome/components/api/api_server.h" #endif -#ifdef USE_ETHERNET -#include "esphome/components/ethernet/ethernet_component.h" -#endif - #ifdef USE_MQTT #include "esphome/components/mqtt/mqtt_client.h" #endif -#ifdef USE_MDNS -#ifdef ARDUINO_ARCH_ESP32 -#include -#endif -#ifdef ARDUINO_ARCH_ESP8266 -#include -#endif -#endif - namespace esphome { -bool network_is_connected() { -#ifdef USE_ETHERNET - if (ethernet::global_eth_component != nullptr && ethernet::global_eth_component->is_connected()) - return true; -#endif - -#ifdef USE_WIFI - if (wifi::global_wifi_component != nullptr) - return wifi::global_wifi_component->is_connected(); -#endif - - return false; -} - bool api_is_connected() { #ifdef USE_API if (api::global_api_server != nullptr) { @@ -65,78 +34,4 @@ bool mqtt_is_connected() { bool remote_is_connected() { return api_is_connected() || mqtt_is_connected(); } -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) -static bool mdns_setup; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -#endif - -#ifndef WEBSERVER_PORT -static const uint8_t WEBSERVER_PORT = 80; -#endif - -#ifdef USE_MDNS -#ifdef ARDUINO_ARCH_ESP8266 -void network_setup_mdns(const IPAddress &address, int interface) { - // Latest arduino framework breaks mDNS for AP interface - // see https://github.com/esp8266/Arduino/issues/6114 - if (interface == 1) - return; - MDNS.begin(App.get_name().c_str(), address); - mdns_setup = true; -#endif -#ifdef ARDUINO_ARCH_ESP32 - void network_setup_mdns() { - MDNS.begin(App.get_name().c_str()); -#endif -#ifdef USE_API - if (api::global_api_server != nullptr) { - MDNS.addService("esphomelib", "tcp", api::global_api_server->get_port()); - // DNS-SD (!=mDNS !) requires at least one TXT record for service discovery - let's add version - MDNS.addServiceTxt("esphomelib", "tcp", "version", ESPHOME_VERSION); - MDNS.addServiceTxt("esphomelib", "tcp", "address", network_get_address().c_str()); - MDNS.addServiceTxt("esphomelib", "tcp", "mac", get_mac_address().c_str()); -#ifdef ARDUINO_ARCH_ESP8266 - MDNS.addServiceTxt("esphomelib", "tcp", "platform", "ESP8266"); -#endif -#ifdef ARDUINO_ARCH_ESP32 - MDNS.addServiceTxt("esphomelib", "tcp", "platform", "ESP32"); -#endif - MDNS.addServiceTxt("esphomelib", "tcp", "board", ESPHOME_BOARD); -#ifdef ESPHOME_PROJECT_NAME - MDNS.addServiceTxt("esphomelib", "tcp", "project_name", ESPHOME_PROJECT_NAME); - MDNS.addServiceTxt("esphomelib", "tcp", "project_version", ESPHOME_PROJECT_VERSION); -#endif - } else { -#endif - // Publish "http" service if not using native API nor the webserver component - // This is just to have *some* mDNS service so that .local resolution works - MDNS.addService("http", "tcp", WEBSERVER_PORT); - MDNS.addServiceTxt("http", "tcp", "version", ESPHOME_VERSION); -#ifdef USE_API - } -#endif -#ifdef USE_PROMETHEUS - MDNS.addService("prometheus-http", "tcp", WEBSERVER_PORT); -#endif - } -#endif - - void network_tick_mdns() { -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) - if (mdns_setup) - MDNS.update(); -#endif - } - - std::string network_get_address() { -#ifdef USE_ETHERNET - if (ethernet::global_eth_component != nullptr) - return ethernet::global_eth_component->get_use_address(); -#endif -#ifdef USE_WIFI - if (wifi::global_wifi_component != nullptr) - return wifi::global_wifi_component->get_use_address(); -#endif - return ""; - } - } // namespace esphome diff --git a/esphome/core/util.h b/esphome/core/util.h index 764c6aaf03..1ca0173eab 100644 --- a/esphome/core/util.h +++ b/esphome/core/util.h @@ -1,15 +1,8 @@ #pragma once #include -#include "IPAddress.h" - namespace esphome { -/// Return whether the node is connected to the network (through wifi, eth, ...) -bool network_is_connected(); -/// Get the active network hostname -std::string network_get_address(); - /// Return whether the node has at least one client connected to the native API bool api_is_connected(); @@ -19,14 +12,4 @@ bool mqtt_is_connected(); /// Return whether the node has any form of "remote" connection via the API or to an MQTT broker bool remote_is_connected(); -/// Manually set up the network stack (outside of the App.setup() loop, for example in OTA safe mode) -#ifdef ARDUINO_ARCH_ESP8266 -void network_setup_mdns(const IPAddress &address, int interface); -#endif -#ifdef ARDUINO_ARCH_ESP32 -void network_setup_mdns(); -#endif - -void network_tick_mdns(); - } // namespace esphome diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 0442e2633b..691f45f91f 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -566,6 +566,10 @@ def add_define(name: str, value: SafeExpType = None): CORE.add_define(Define(name, safe_exp(value))) +def add_platformio_option(key: str, value: Union[str, List[str]]): + CORE.add_platformio_option(key, value) + + async def get_variable(id_: ID) -> "MockObj": """ Wait for the given ID to be defined in the code generation and diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 33fb485c2b..a2eafaa0e8 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -1,9 +1,6 @@ import logging from esphome.const import ( - CONF_INVERTED, - CONF_MODE, - CONF_NUMBER, CONF_SETUP_PRIORITY, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, @@ -12,8 +9,8 @@ from esphome.const import ( # pylint: disable=unused-import from esphome.core import coroutine, ID, CORE from esphome.types import ConfigType -from esphome.cpp_generator import RawExpression, add, get_variable -from esphome.cpp_types import App, GPIOPin +from esphome.cpp_generator import add, get_variable +from esphome.cpp_types import App from esphome.util import Registry, RegistryEntry @@ -26,17 +23,13 @@ async def gpio_pin_expression(conf): This is a coroutine, you must await it with a 'await' expression! """ if conf is None: - return + return None from esphome import pins for key, (func, _) in pins.PIN_SCHEMA_REGISTRY.items(): if key in conf: return await coroutine(func)(conf) - - number = conf[CONF_NUMBER] - mode = conf[CONF_MODE] - inverted = conf.get(CONF_INVERTED) - return GPIOPin.new(number, RawExpression(mode), inverted) + return await coroutine(pins.PIN_SCHEMA_REGISTRY[CORE.target_platform][0])(conf) async def register_component(var, config): diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index ee606ec7b3..7a8eb8e04c 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -30,5 +30,7 @@ JsonObject = arduino_json_ns.class_("JsonObject") JsonObjectRef = JsonObject.operator("ref") JsonObjectConstRef = JsonObjectRef.operator("const") Controller = esphome_ns.class_("Controller") - GPIOPin = esphome_ns.class_("GPIOPin") +InternalGPIOPin = esphome_ns.class_("InternalGPIOPin", GPIOPin) +gpio_ns = esphome_ns.namespace("gpio") +gpio_Flags = gpio_ns.enum("Flags", is_class=True) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 38689675af..bfe6808729 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -441,16 +441,10 @@ class DashboardEntry: return self.storage.comment @property - def esp_platform(self): + def target_platform(self): if self.storage is None: return None - return self.storage.esp_platform - - @property - def board(self): - if self.storage is None: - return None - return self.storage.board + return self.storage.target_platform @property def update_available(self): diff --git a/esphome/final_validate.py b/esphome/final_validate.py index 199c68210e..96dd2fd651 100644 --- a/esphome/final_validate.py +++ b/esphome/final_validate.py @@ -4,13 +4,6 @@ import contextvars from esphome.types import ConfigFragmentType, ID, ConfigPathType import esphome.config_validation as cv -from esphome.const import ( - ARDUINO_VERSION_ESP32, - ARDUINO_VERSION_ESP8266, - CONF_ESPHOME, - CONF_ARDUINO_VERSION, -) -from esphome.core import CORE class FinalValidateConfig(ABC): @@ -63,20 +56,3 @@ def id_declaration_match_schema(schema): return schema(declaration_config) return validator - - -def get_arduino_framework_version(): - path = [CONF_ESPHOME, CONF_ARDUINO_VERSION] - # This is run after core validation, so the property is set even if user didn't - version: str = full_config.get().get_config_for_path(path) - - if CORE.is_esp32: - version_map = ARDUINO_VERSION_ESP32 - elif CORE.is_esp8266: - version_map = ARDUINO_VERSION_ESP8266 - else: - raise ValueError("Platform not supported yet for this validator") - - reverse_map = {v: k for k, v in version_map.items()} - framework_version = reverse_map.get(version) - return framework_version diff --git a/esphome/helpers.py b/esphome/helpers.py index 3420b2cc12..1193d61eaa 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -209,15 +209,21 @@ def write_file(path: Union[Path, str], text: str): raise EsphomeError(f"Could not write file at {path}") from err -def write_file_if_changed(path: Union[Path, str], text: str): +def write_file_if_changed(path: Union[Path, str], text: str) -> bool: + """Write text to the given path, but not if the contents match already. + + Returns true if the file was changed. + """ if not isinstance(path, Path): path = Path(path) src_content = None if path.is_file(): src_content = read_file(path) - if src_content != text: - write_file(path, text) + if src_content == text: + return False + write_file(path, text) + return True def copy_file_if_changed(src: os.PathLike, dst: os.PathLike) -> None: diff --git a/esphome/loader.py b/esphome/loader.py index f74fc6367d..05d2e5a213 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -1,6 +1,5 @@ import logging -import typing -from typing import Callable, List, Optional, Dict, Any, ContextManager +from typing import Callable, List, Optional, Any, ContextManager from types import ModuleType import importlib import importlib.util @@ -8,8 +7,9 @@ import importlib.resources import importlib.abc import sys from pathlib import Path +from dataclasses import dataclass -from esphome.const import ESP_PLATFORMS, SOURCE_FILE_EXTENSIONS +from esphome.const import SOURCE_FILE_EXTENSIONS import esphome.core.config from esphome.core import CORE from esphome.types import ConfigType @@ -17,20 +17,13 @@ from esphome.types import ConfigType _LOGGER = logging.getLogger(__name__) -class SourceFile: - def __init__( - self, - package: importlib.resources.Package, - resource: importlib.resources.Resource, - ) -> None: - self._package = package - self._resource = resource - - def open_binary(self) -> typing.BinaryIO: - return importlib.resources.open_binary(self._package, self._resource) +@dataclass(frozen=True, order=True) +class FileResource: + package: str + resource: str def path(self) -> ContextManager[Path]: - return importlib.resources.path(self._package, self._resource) + return importlib.resources.path(self.package, self.resource) class ComponentManifest: @@ -39,6 +32,13 @@ class ComponentManifest: @property def package(self) -> str: + """Return the package name the module is contained in. + + Examples: + - esphome/components/gpio/__init__.py -> esphome.components.gpio + - esphome/components/gpio/switch/__init__.py -> esphome.components.gpio.switch + - esphome/components/a4988/stepper.py -> esphome.components.a4988 + """ return self.module.__package__ @property @@ -61,10 +61,6 @@ class ComponentManifest: def to_code(self) -> Optional[Callable[[Any], None]]: return getattr(self.module, "to_code", None) - @property - def esp_platforms(self) -> List[str]: - return getattr(self.module, "ESP_PLATFORMS", ESP_PLATFORMS) - @property def dependencies(self) -> List[str]: return getattr(self.module, "DEPENDENCIES", []) @@ -91,17 +87,20 @@ class ComponentManifest: return getattr(self.module, "FINAL_VALIDATE_SCHEMA", None) @property - def source_files(self) -> Dict[Path, SourceFile]: - ret = {} + def resources(self) -> List[FileResource]: + """Return a list of all file resources defined in the package of this component. + + This will return all cpp source files that are located in the same folder as the + loaded .py file (does not look through subdirectories) + """ + ret = [] for resource in importlib.resources.contents(self.package): if Path(resource).suffix not in SOURCE_FILE_EXTENSIONS: continue if not importlib.resources.is_resource(self.package, resource): # Not a resource = this is a directory (yeah this is confusing) continue - # Always use / for C++ include names - target_path = Path(*self.package.split(".")) / resource - ret[target_path] = SourceFile(self.package, resource) + ret.append(FileResource(self.package, resource)) return ret diff --git a/esphome/pins.py b/esphome/pins.py index c717424ff3..ae762c1a1a 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -1,330 +1,143 @@ -import logging +import operator +from functools import reduce -import esphome.config_validation as cv -from esphome.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER -from esphome.core import CORE +from esphome.const import ( + CONF_INPUT, + CONF_MODE, + CONF_NUMBER, + CONF_OPEN_DRAIN, + CONF_OUTPUT, + CONF_PULLDOWN, + CONF_PULLUP, +) from esphome.util import SimpleRegistry -from esphome import boards - -_LOGGER = logging.getLogger(__name__) - - -def _lookup_pin(value): - if CORE.is_esp8266: - board_pins_dict = boards.ESP8266_BOARD_PINS - base_pins = boards.ESP8266_BASE_PINS - elif CORE.is_esp32: - if CORE.board in boards.ESP32_C3_BOARD_PINS: - board_pins_dict = boards.ESP32_C3_BOARD_PINS - base_pins = boards.ESP32_C3_BASE_PINS - else: - board_pins_dict = boards.ESP32_BOARD_PINS - base_pins = boards.ESP32_BASE_PINS - else: - raise NotImplementedError - - board_pins = board_pins_dict.get(CORE.board, {}) - - # Resolved aliased board pins (shorthand when two boards have the same pin configuration) - while isinstance(board_pins, str): - board_pins = board_pins_dict[board_pins] - - if value in board_pins: - return board_pins[value] - if value in base_pins: - return base_pins[value] - raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {CORE.board}.") - - -def _translate_pin(value): - if isinstance(value, dict) or value is None: - raise cv.Invalid( - "This variable only supports pin numbers, not full pin schemas " - "(with inverted and mode)." - ) - if isinstance(value, int): - return value - try: - return int(value) - except ValueError: - pass - if value.startswith("GPIO"): - return cv.Coerce(int)(value[len("GPIO") :].strip()) - return _lookup_pin(value) - - -_ESP_SDIO_PINS = { - 6: "Flash Clock", - 7: "Flash Data 0", - 8: "Flash Data 1", - 11: "Flash Command", -} - -_ESP32C3_SDIO_PINS = { - 12: "Flash IO3/HOLD#", - 13: "Flash IO2/WP#", - 14: "Flash CS#", - 15: "Flash CLK", - 16: "Flash IO0/DI", - 17: "Flash IO1/DO", -} - - -def validate_gpio_pin(value): - value = _translate_pin(value) - if CORE.is_esp32_c3: - if value < 0 or value > 22: - raise cv.Invalid(f"ESP32-C3: Invalid pin number: {value}") - if value in _ESP32C3_SDIO_PINS: - raise cv.Invalid( - f"This pin cannot be used on ESP32-C3s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" - ) - return value - if CORE.is_esp32: - if value < 0 or value > 39: - raise cv.Invalid(f"ESP32: Invalid pin number: {value}") - if value in _ESP_SDIO_PINS: - raise cv.Invalid( - f"This pin cannot be used on ESP32s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" - ) - if 9 <= value <= 10: - _LOGGER.warning( - "ESP32: Pin %s (9-10) might already be used by the " - "flash interface in QUAD IO flash mode.", - value, - ) - if value in (20, 24, 28, 29, 30, 31): - # These pins are not exposed in GPIO mux (reason unknown) - # but they're missing from IO_MUX list in datasheet - raise cv.Invalid(f"The pin GPIO{value} is not usable on ESP32s.") - return value - if CORE.is_esp8266: - if value < 0 or value > 17: - raise cv.Invalid(f"ESP8266: Invalid pin number: {value}") - if value in _ESP_SDIO_PINS: - raise cv.Invalid( - f"This pin cannot be used on ESP8266s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" - ) - if 9 <= value <= 10: - _LOGGER.warning( - "ESP8266: Pin %s (9-10) might already be used by the " - "flash interface in QUAD IO flash mode.", - value, - ) - return value - raise NotImplementedError - - -def input_pin(value): - value = validate_gpio_pin(value) - if CORE.is_esp8266 and value == 17: - raise cv.Invalid("GPIO17 (TOUT) is an analog-only pin on the ESP8266.") - return value - - -def input_pullup_pin(value): - value = input_pin(value) - if CORE.is_esp32: - return output_pin(value) - if CORE.is_esp8266: - if value == 0: - raise cv.Invalid( - "GPIO Pin 0 does not support pullup pin mode. " - "Please choose another pin." - ) - return value - raise NotImplementedError - - -def output_pin(value): - value = validate_gpio_pin(value) - if CORE.is_esp32: - if 34 <= value <= 39: - raise cv.Invalid( - f"ESP32: GPIO{value} (34-39) can only be used as an input pin." - ) - return value - if CORE.is_esp8266: - if value == 17: - raise cv.Invalid("GPIO17 (TOUT) is an analog-only pin on the ESP8266.") - return value - raise NotImplementedError - - -def analog_pin(value): - value = validate_gpio_pin(value) - if CORE.is_esp32: - if CORE.is_esp32_c3: - if 0 <= value <= 4: # ADC1 - return value - raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.") - if 32 <= value <= 39: # ADC1 - return value - raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.") - if CORE.is_esp8266: - if value == 17: # A0 - return value - raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.") - raise NotImplementedError - - -input_output_pin = cv.All(input_pin, output_pin) - -PIN_MODES_ESP8266 = [ - "INPUT", - "OUTPUT", - "INPUT_PULLUP", - "OUTPUT_OPEN_DRAIN", - "SPECIAL", - "FUNCTION_1", - "FUNCTION_2", - "FUNCTION_3", - "FUNCTION_4", - "FUNCTION_0", - "WAKEUP_PULLUP", - "WAKEUP_PULLDOWN", - "INPUT_PULLDOWN_16", -] -PIN_MODES_ESP32 = [ - "INPUT", - "OUTPUT", - "INPUT_PULLUP", - "OUTPUT_OPEN_DRAIN", - "SPECIAL", - "FUNCTION_1", - "FUNCTION_2", - "FUNCTION_3", - "FUNCTION_4", - "PULLUP", - "PULLDOWN", - "INPUT_PULLDOWN", - "OPEN_DRAIN", - "FUNCTION_5", - "FUNCTION_6", - "ANALOG", -] - - -def pin_mode(value): - if CORE.is_esp32: - return cv.one_of(*PIN_MODES_ESP32, upper=True)(value) - if CORE.is_esp8266: - return cv.one_of(*PIN_MODES_ESP8266, upper=True)(value) - raise NotImplementedError - - -GPIO_FULL_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): output_pin, - cv.Optional(CONF_MODE, default="OUTPUT"): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - -GPIO_FULL_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): input_pin, - cv.Optional(CONF_MODE, default="INPUT"): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - -GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): input_pin, - cv.Optional(CONF_MODE, default="INPUT_PULLUP"): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - -GPIO_FULL_ANALOG_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): analog_pin, - cv.Optional(CONF_MODE, default="INPUT"): pin_mode, - } -) - - -def shorthand_output_pin(value): - value = output_pin(value) - return GPIO_FULL_OUTPUT_PIN_SCHEMA({CONF_NUMBER: value}) - - -def shorthand_input_pin(value): - value = input_pin(value) - return GPIO_FULL_INPUT_PIN_SCHEMA({CONF_NUMBER: value}) - - -def shorthand_input_pullup_pin(value): - value = input_pullup_pin(value) - return GPIO_FULL_INPUT_PIN_SCHEMA( - { - CONF_NUMBER: value, - CONF_MODE: "INPUT_PULLUP", - } - ) - - -def shorthand_analog_pin(value): - value = analog_pin(value) - return GPIO_FULL_ANALOG_PIN_SCHEMA({CONF_NUMBER: value}) - - -def validate_has_interrupt(value): - if CORE.is_esp8266: - if value[CONF_NUMBER] >= 16: - raise cv.Invalid( - f"Pins GPIO16 and GPIO17 do not support interrupts and cannot be used here, got {value[CONF_NUMBER]}" - ) - return value +from esphome.core import CORE PIN_SCHEMA_REGISTRY = SimpleRegistry() -def internal_gpio_output_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_OUTPUT_PIN_SCHEMA(value) - return shorthand_output_pin(value) +def _set_mode(value, default_mode): + import esphome.config_validation as cv + + if CONF_MODE not in value: + return {**value, CONF_MODE: default_mode} + mode = value[CONF_MODE] + if not isinstance(mode, str): + return value + # mode is a string, try parsing it like arduino pin modes + PIN_MODES = { + "INPUT": { + CONF_INPUT: True, + }, + "OUTPUT": { + CONF_OUTPUT: True, + }, + "INPUT_PULLUP": { + CONF_INPUT: True, + CONF_PULLUP: True, + }, + "OUTPUT_OPEN_DRAIN": { + CONF_OUTPUT: True, + CONF_OPEN_DRAIN: True, + }, + "INPUT_PULLDOWN_16": { + CONF_INPUT: True, + CONF_PULLDOWN: True, + }, + "INPUT_PULLDOWN": { + CONF_INPUT: True, + CONF_PULLDOWN: True, + }, + } + if mode.upper() not in PIN_MODES: + raise cv.Invalid(f"Unknown pin mode {mode}", [CONF_MODE]) + return {**value, CONF_MODE: PIN_MODES[mode.upper()]} -def gpio_output_pin_schema(value): - if isinstance(value, dict): - for key, entry in PIN_SCHEMA_REGISTRY.items(): - if key in value: - return entry[1][0](value) - return internal_gpio_output_pin_schema(value) +def _schema_creator(default_mode, internal: bool = False): + def validator(value): + if not isinstance(value, dict): + return validator({CONF_NUMBER: value}) + value = _set_mode(value, default_mode) + if not internal: + for key, entry in PIN_SCHEMA_REGISTRY.items(): + if key != CORE.target_platform and key in value: + return entry[1](value) + return PIN_SCHEMA_REGISTRY[CORE.target_platform][1](value) + + return validator -def internal_gpio_input_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_INPUT_PIN_SCHEMA(value) - return shorthand_input_pin(value) +def _internal_number_creator(mode): + def validator(value): + value_d = {CONF_NUMBER: value} + value_d = _set_mode(value_d, mode) + return PIN_SCHEMA_REGISTRY[CORE.target_platform][1](value_d)[CONF_NUMBER] + + return validator -def internal_gpio_analog_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_ANALOG_PIN_SCHEMA(value) - return shorthand_analog_pin(value) +def gpio_flags_expr(mode): + """Convert the given mode dict to a gpio Flags expression""" + import esphome.codegen as cg + + FLAGS_MAPPING = { + CONF_INPUT: cg.gpio_Flags.FLAG_INPUT, + CONF_OUTPUT: cg.gpio_Flags.FLAG_OUTPUT, + CONF_OPEN_DRAIN: cg.gpio_Flags.FLAG_OPEN_DRAIN, + CONF_PULLUP: cg.gpio_Flags.FLAG_PULLUP, + CONF_PULLDOWN: cg.gpio_Flags.FLAG_PULLDOWN, + } + active_flags = [v for k, v in FLAGS_MAPPING.items() if mode.get(k)] + if active_flags: + return cg.gpio_Flags.FLAG_NONE + + return reduce(operator.or_, active_flags) -def gpio_input_pin_schema(value): - if isinstance(value, dict): - for key, entry in PIN_SCHEMA_REGISTRY.items(): - if key in value: - return entry[1][1](value) - return internal_gpio_input_pin_schema(value) - - -def internal_gpio_input_pullup_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA(value) - return shorthand_input_pullup_pin(value) - - -def gpio_input_pullup_pin_schema(value): - if isinstance(value, dict): - for key, entry in PIN_SCHEMA_REGISTRY.items(): - if key in value: - return entry[1][1](value) - return internal_gpio_input_pullup_pin_schema(value) +gpio_pin_schema = _schema_creator +internal_gpio_pin_number = _internal_number_creator +gpio_output_pin_schema = _schema_creator( + { + CONF_OUTPUT: True, + } +) +gpio_input_pin_schema = _schema_creator( + { + CONF_INPUT: True, + } +) +gpio_input_pullup_pin_schema = _schema_creator( + { + CONF_INPUT: True, + CONF_PULLUP: True, + } +) +internal_gpio_output_pin_schema = _schema_creator( + { + CONF_OUTPUT: True, + }, + internal=True, +) +internal_gpio_output_pin_number = _internal_number_creator({CONF_OUTPUT: True}) +internal_gpio_input_pin_schema = _schema_creator( + { + CONF_INPUT: True, + }, + internal=True, +) +internal_gpio_input_pin_number = _internal_number_creator({CONF_INPUT: True}) +internal_gpio_input_pullup_pin_schema = _schema_creator( + { + CONF_INPUT: True, + CONF_PULLUP: True, + }, + internal=True, +) +internal_gpio_input_pullup_pin_number = _internal_number_creator( + { + CONF_INPUT: True, + CONF_PULLUP: True, + } +) diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index a99081a650..100def3310 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -196,7 +196,7 @@ def _parse_register(config, regex, line): STACKTRACE_ESP8266_EXCEPTION_TYPE_RE = re.compile(r"[eE]xception \((\d+)\):") STACKTRACE_ESP8266_PC_RE = re.compile(r"epc1=0x(4[0-9a-fA-F]{7})") STACKTRACE_ESP8266_EXCVADDR_RE = re.compile(r"excvaddr=0x(4[0-9a-fA-F]{7})") -STACKTRACE_ESP32_PC_RE = re.compile(r"PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") +STACKTRACE_ESP32_PC_RE = re.compile(r".*PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7}).*") STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r"EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") STACKTRACE_ESP32_C3_PC_RE = re.compile(r"MEPC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") STACKTRACE_ESP32_C3_RA_RE = re.compile(r"RA\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") @@ -204,7 +204,7 @@ STACKTRACE_BAD_ALLOC_RE = re.compile( r"^last failed alloc call: (4[0-9a-fA-F]{7})\((\d+)\)$" ) STACKTRACE_ESP32_BACKTRACE_RE = re.compile( - r"Backtrace:(?:\s+0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+" + r"Backtrace:(?:\s*0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+" ) STACKTRACE_ESP32_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}") STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}") diff --git a/esphome/storage_json.py b/esphome/storage_json.py index 517bb508ba..e70d3d6c93 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -40,10 +40,8 @@ class StorageJSON: comment, esphome_version, src_version, - arduino_version, address, - esp_platform, - board, + target_platform, build_path, firmware_bin_path, loaded_integrations, @@ -60,15 +58,10 @@ class StorageJSON: # The version of the file in src/main.cpp - Used to migrate the file assert src_version is None or isinstance(src_version, int) self.src_version = src_version # type: int - # The version of the Arduino framework, the build files need to be cleared each time - # this changes - self.arduino_version = arduino_version # type: str # Address of the ESP, for example livingroom.local or a static IP self.address = address # type: str # The type of ESP in use, either ESP32 or ESP8266 - self.esp_platform = esp_platform # type: str - # The ESP board used, for example nodemcuv2 - self.board = board # type: str + self.target_platform = target_platform # type: str # The absolute path to the platformio project self.build_path = build_path # type: str # The absolute path to the firmware binary @@ -84,10 +77,8 @@ class StorageJSON: "comment": self.comment, "esphome_version": self.esphome_version, "src_version": self.src_version, - "arduino_version": self.arduino_version, "address": self.address, - "esp_platform": self.esp_platform, - "board": self.board, + "esp_platform": self.target_platform, "build_path": self.build_path, "firmware_bin_path": self.firmware_bin_path, "loaded_integrations": self.loaded_integrations, @@ -109,28 +100,24 @@ class StorageJSON: comment=esph.comment, esphome_version=const.__version__, src_version=1, - arduino_version=esph.arduino_version, address=esph.address, - esp_platform=esph.esp_platform, - board=esph.board, + target_platform=esph.target_platform, build_path=esph.build_path, firmware_bin_path=esph.firmware_bin, loaded_integrations=list(esph.loaded_integrations), ) @staticmethod - def from_wizard(name, address, esp_platform, board): - # type: (str, str, str, str) -> StorageJSON + def from_wizard(name, address, esp_platform): + # type: (str, str, str) -> StorageJSON return StorageJSON( storage_version=1, name=name, comment=None, esphome_version=const.__version__, src_version=1, - arduino_version=None, address=address, - esp_platform=esp_platform, - board=board, + target_platform=esp_platform, build_path=None, firmware_bin_path=None, loaded_integrations=[], @@ -147,10 +134,8 @@ class StorageJSON: "esphome_version", storage.get("esphomeyaml_version") ) src_version = storage.get("src_version") - arduino_version = storage.get("arduino_version") address = storage.get("address") esp_platform = storage.get("esp_platform") - board = storage.get("board") build_path = storage.get("build_path") firmware_bin_path = storage.get("firmware_bin_path") loaded_integrations = storage.get("loaded_integrations", []) @@ -160,10 +145,8 @@ class StorageJSON: comment, esphome_version, src_version, - arduino_version, address, esp_platform, - board, build_path, firmware_bin_path, loaded_integrations, diff --git a/esphome/wizard.py b/esphome/wizard.py index abb17e3119..5c35fac73a 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -10,7 +10,6 @@ from esphome.helpers import get_bool_env, write_file from esphome.log import color, Fore # pylint: disable=anomalous-backslash-in-string -from esphome.boards import ESP32_BOARD_PINS, ESP8266_BOARD_PINS from esphome.storage_json import StorageJSON, ext_storage_path from esphome.util import safe_print from esphome.const import ALLOWED_NAME_CHARS, ENV_QUICKWIZARD @@ -116,6 +115,8 @@ captive_portal: def wizard_write(path, **kwargs): + from esphome.components.esp8266 import boards as esp8266_boards + name = kwargs["name"] board = kwargs["board"] @@ -124,11 +125,13 @@ def wizard_write(path, **kwargs): kwargs[key] = sanitize_double_quotes(kwargs[key]) if "platform" not in kwargs: - kwargs["platform"] = "ESP8266" if board in ESP8266_BOARD_PINS else "ESP32" + kwargs["platform"] = ( + "ESP8266" if board in esp8266_boards.ESP8266_BOARD_PINS else "ESP32" + ) platform = kwargs["platform"] write_file(path, wizard_file(**kwargs)) - storage = StorageJSON.from_wizard(name, f"{name}.local", platform, board) + storage = StorageJSON.from_wizard(name, f"{name}.local", platform) storage_path = ext_storage_path(os.path.dirname(path), os.path.basename(path)) storage.save(storage_path) @@ -168,6 +171,9 @@ def strip_accents(value): def wizard(path): + from esphome.components.esp32 import boards as esp32_boards + from esphome.components.esp8266 import boards as esp8266_boards + if not path.endswith(".yaml") and not path.endswith(".yml"): safe_print( f"Please make your configuration file {color(Fore.CYAN, path)} have the extension .yaml or .yml" @@ -266,10 +272,10 @@ def wizard(path): # Don't sleep because user needs to copy link if platform == "ESP32": safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcu-32s')}\".") - boards = list(ESP32_BOARD_PINS.keys()) + boards = list(esp32_boards.ESP32_BOARD_PINS.keys()) else: safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcuv2')}\".") - boards = list(ESP8266_BOARD_PINS.keys()) + boards = list(esp8266_boards.ESP8266_BOARD_PINS.keys()) safe_print(f"Options: {', '.join(sorted(boards))}") while True: diff --git a/esphome/writer.py b/esphome/writer.py index 2e1e19de54..29532d4f64 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -2,17 +2,13 @@ import logging import os import re from pathlib import Path -from typing import Dict +from typing import Dict, List, Union from esphome.config import iter_components from esphome.const import ( - CONF_BOARD_FLASH_MODE, - CONF_ESPHOME, - CONF_PLATFORMIO_OPTIONS, HEADER_FILE_EXTENSIONS, SOURCE_FILE_EXTENSIONS, __version__, - ARDUINO_VERSION_ESP8266, ENV_NOGITIGNORE, ) from esphome.core import CORE, EsphomeError @@ -25,7 +21,6 @@ from esphome.helpers import ( get_bool_env, ) from esphome.storage_json import StorageJSON, storage_path -from esphome.boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS from esphome import loader _LOGGER = logging.getLogger(__name__) @@ -72,7 +67,9 @@ upload_flags = """, ) -UPLOAD_SPEED_OVERRIDE = {"esp210": 57600} +UPLOAD_SPEED_OVERRIDE = { + "esp210": 57600, +} def get_flags(key): @@ -166,10 +163,6 @@ def storage_should_clean(old, new): # type: (StorageJSON, StorageJSON) -> bool if old.src_version != new.src_version: return True - if old.arduino_version != new.arduino_version: - return True - if old.board != new.board: - return True if old.build_path != new.build_path: return True return False @@ -192,10 +185,10 @@ def update_storage_json(): new.save(path) -def format_ini(data): +def format_ini(data: Dict[str, Union[str, List[str]]]) -> str: content = "" for key, value in sorted(data.items()): - if isinstance(value, (list, set, tuple)): + if isinstance(value, list): content += f"{key} =\n" for x in value: content += f" {x}\n" @@ -204,82 +197,15 @@ def format_ini(data): return content -def gather_lib_deps(): - return [x.as_lib_dep for x in CORE.libraries] - - -def gather_build_flags(overrides): - build_flags = list(CORE.build_flags) - build_flags += [overrides] if isinstance(overrides, str) else overrides - - # avoid changing build flags order - return list(sorted(build_flags)) - - -ESP32_LARGE_PARTITIONS_CSV = """\ -nvs, data, nvs, 0x009000, 0x005000, -otadata, data, ota, 0x00e000, 0x002000, -app0, app, ota_0, 0x010000, 0x1C0000, -app1, app, ota_1, 0x1D0000, 0x1C0000, -eeprom, data, 0x99, 0x390000, 0x001000, -spiffs, data, spiffs, 0x391000, 0x00F000 -""" - - def get_ini_content(): - overrides = CORE.config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS, {}) - - lib_deps = gather_lib_deps() - build_flags = gather_build_flags(overrides.pop("build_flags", [])) - - data = { - "platform": CORE.arduino_version, - "board": CORE.board, - "framework": "arduino", - "lib_deps": lib_deps + ["${common.lib_deps}"], - "build_flags": build_flags + ["${common.build_flags}"], - "upload_speed": UPLOAD_SPEED_OVERRIDE.get(CORE.board, 115200), - } - - if CORE.is_esp32: - data["board_build.partitions"] = "partitions.csv" - partitions_csv = CORE.relative_build_path("partitions.csv") - write_file_if_changed(partitions_csv, ESP32_LARGE_PARTITIONS_CSV) - - # pylint: disable=unsubscriptable-object - if CONF_BOARD_FLASH_MODE in CORE.config[CONF_ESPHOME]: - flash_mode = CORE.config[CONF_ESPHOME][CONF_BOARD_FLASH_MODE] - data["board_build.flash_mode"] = flash_mode - - # Build flags - if CORE.is_esp8266 and CORE.board in ESP8266_FLASH_SIZES: - flash_size = ESP8266_FLASH_SIZES[CORE.board] - ld_scripts = ESP8266_LD_SCRIPTS[flash_size] - - versions_with_old_ldscripts = [ - ARDUINO_VERSION_ESP8266["2.4.0"], - ARDUINO_VERSION_ESP8266["2.4.1"], - ARDUINO_VERSION_ESP8266["2.4.2"], - ] - if CORE.arduino_version == ARDUINO_VERSION_ESP8266["2.3.0"]: - # No ld script support - ld_script = None - if CORE.arduino_version in versions_with_old_ldscripts: - # Old ld script path - ld_script = ld_scripts[0] - else: - ld_script = ld_scripts[1] - - if ld_script is not None: - data["board_build.ldscript"] = ld_script - - # Ignore libraries that are not explicitly used, but may - # be added by LDF - # data['lib_ldf_mode'] = 'chain' - data.update(overrides) + CORE.add_platformio_option( + "lib_deps", [x.as_lib_dep for x in CORE.libraries] + ["${common.lib_deps}"] + ) + # Sort to avoid changing build flags order + CORE.add_platformio_option("build_flags", sorted(CORE.build_flags)) content = f"[env:{CORE.name}]\n" - content += format_ini(data) + content += format_ini(CORE.platformio_options) return content @@ -361,12 +287,15 @@ or use the custom_components folder. def copy_src_tree(): - source_files: Dict[Path, loader.SourceFile] = {} + source_files: List[loader.FileResource] = [] for _, component, _ in iter_components(CORE.config): - source_files.update(component.source_files) + source_files += component.resources + source_files_map = { + Path(x.package.replace(".", "/") + "/" + x.resource): x for x in source_files + } # Convert to list and sort - source_files_l = list(source_files.items()) + source_files_l = list(source_files_map.items()) source_files_l.sort() # Build #include list for esphome.h @@ -377,7 +306,7 @@ def copy_src_tree(): include_l.append("") include_s = "\n".join(include_l) - source_files_copy = source_files.copy() + source_files_copy = source_files_map.copy() ignore_targets = [Path(x) for x in (DEFINES_H_TARGET, VERSION_H_TARGET)] for t in ignore_targets: source_files_copy.pop(t) @@ -420,6 +349,11 @@ def copy_src_tree(): CORE.relative_src_path("esphome", "core", "version.h"), generate_version_h() ) + if CORE.is_esp32: + from esphome.components.esp32 import copy_files + + copy_files() + def generate_defines_h(): define_content_l = [x.as_macro for x in CORE.defines] diff --git a/platformio.ini b/platformio.ini index 89e9ce9374..0e2649e63a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -5,7 +5,7 @@ [platformio] default_envs = esp8266, esp32 -src_dir = . +src_dir = esphome include_dir = [runtime] @@ -47,7 +47,11 @@ src_filter = [common:esp8266] extends = common -platform = platformio/espressif8266@3.1.0 +; when changing this also copy it to esphome-docker-base images +platform = platformio/espressif8266 @ 3.2.0 +platform_packages = + platformio/framework-arduinoespressif8266 @ ~3.30002.0 + framework = arduino board = nodemcuv2 lib_deps = @@ -55,29 +59,88 @@ lib_deps = ESP8266WiFi ; wifi (Arduino built-in) Update ; ota (Arduino built-in) ottowinter/ESPAsyncTCP-esphome@1.2.3 ; async_tcp +build_flags = + ${common.build_flags} + -DUSE_ESP8266 [common:esp32] extends = common -platform = platformio/espressif32@3.2.0 +; when changing this also copy it to esphome-docker-base images +platform = platformio/espressif32 @ 3.3.2 +platform_packages = + platformio/framework-arduinoespressif32 @ ~3.10006.0 + framework = arduino board = nodemcu-32s lib_deps = ${common.lib_deps} Hash ; ota (Arduino built-in) esphome/AsyncTCP-esphome@1.2.2 ; async_tcp +build_flags = + ${common.build_flags} + -DUSE_ESP32 +src_filter = ${common.src_filter} + +[common:espidf] +; when changing this also copy it to esphome-docker-base images +platform = platformio/espressif32 @ 3.3.2 +platform_packages = + platformio/framework-espidf @ ~3.40300.0 + +framework = espidf +board = nodemcu-32s +lib_deps = + esphome/noise-c@0.1.1 ; used by api + espressif/esp32-camera@1.0.0 ; used by esp32_camera + NeoPixelBus@2.6.7 +build_flags = + ${common.build_flags} + ${common:esp32.build_flags} + -DUSE_ESP_IDF + -DUSE_ESP32_FRAMEWORK_ESP_IDF +src_filter = ${common:esp32.src_filter} [env:esp8266] extends = common:esp8266 -build_flags = ${common:esp8266.build_flags} ${runtime.build_flags} +build_flags = + ${common:esp8266.build_flags} + ${runtime.build_flags} + -DUSE_ARDUINO + -DUSE_ESP8266_FRAMEWORK_ARDUINO [env:esp8266-tidy] extends = common:esp8266 -build_flags = ${common:esp8266.build_flags} ${clangtidy.build_flags} +build_flags = + ${common:esp8266.build_flags} + ${clangtidy.build_flags} + -DUSE_ARDUINO + -DUSE_ESP8266_FRAMEWORK_ARDUINO [env:esp32] extends = common:esp32 -build_flags = ${common:esp32.build_flags} ${runtime.build_flags} +build_flags = + ${common:esp32.build_flags} + ${runtime.build_flags} + -DUSE_ARDUINO + -DUSE_ESP32_FRAMEWORK_ARDUINO [env:esp32-tidy] extends = common:esp32 -build_flags = ${common:esp32.build_flags} ${clangtidy.build_flags} +build_flags = + ${common:esp32.build_flags} + ${clangtidy.build_flags} + -DUSE_ARDUINO + -DUSE_ESP32_FRAMEWORK_ARDUINO + +[env:esp32-idf] +extends = common:espidf +build_flags = + ${common:espidf.build_flags} + ${runtime.build_flags} + + +[env:esp32-idf-tidy] +extends = common:espidf +build_flags = + ${common:espidf.build_flags} + ${clangtidy.build_flags} diff --git a/requirements.txt b/requirements.txt index 64ffd366e1..c6d967fc33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,7 @@ esptool==3.1 click==8.0.1 esphome-dashboard==20210908.0 aioesphomeapi==9.1.0 + +# esp-idf requires this, but doesn't bundle it by default +# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 +kconfiglib==13.7.1 diff --git a/script/ci-custom.py b/script/ci-custom.py index 3191842d8c..8e9ca487a6 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -408,7 +408,6 @@ ARDUINO_FORBIDDEN_RE = r"[^\w\d](" + r"|".join(ARDUINO_FORBIDDEN) + r")\(.*" exclude=[ "esphome/components/mqtt/custom_mqtt_device.h", "esphome/components/sun/sun.cpp", - "esphome/core/esphal.*", ], ) def lint_no_arduino_framework_functions(fname, match): @@ -422,6 +421,28 @@ def lint_no_arduino_framework_functions(fname, match): ) +IDF_CONVERSION_FORBIDDEN = { + "ARDUINO_ARCH_ESP32": "USE_ESP32", + "ARDUINO_ARCH_ESP8266": "USE_ESP8266", + "pgm_read_byte": "progmem_read_byte", + "ICACHE_RAM_ATTR": "IRAM_ATTR", + "esphome/core/esphal.h": "esphome/core/hal.h", +} +IDF_CONVERSION_FORBIDDEN_RE = r"(" + r"|".join(IDF_CONVERSION_FORBIDDEN) + r").*" + + +@lint_re_check( + IDF_CONVERSION_FORBIDDEN_RE, + include=cpp_include, +) +def lint_no_removed_in_idf_conversions(fname, match): + replacement = IDF_CONVERSION_FORBIDDEN[match.group(1)] + return ( + f"The macro {highlight(match.group(1))} can no longer be used in ESPHome directly. " + f"Plese use {highlight(replacement)} instead." + ) + + @lint_re_check( r"[^\w\d]byte\s+[\w\d]+\s*=", include=cpp_include, @@ -498,6 +519,8 @@ def lint_relative_py_import(fname): ], exclude=[ "esphome/components/socket/headers.h", + "esphome/components/esp32/core.cpp", + "esphome/components/esp8266/core.cpp", ], ) def lint_namespace(fname, content): @@ -575,7 +598,7 @@ def lint_inclusive_language(fname, match): "esphome/components/text_sensor/text_sensor.h", "esphome/components/climate/climate.h", "esphome/core/component.h", - "esphome/core/esphal.h", + "esphome/core/gpio.h", "esphome/core/log.h", "tests/custom.h", ], diff --git a/script/clang-tidy b/script/clang-tidy index 463959fa5d..8a1baf8a22 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -45,7 +45,11 @@ def clang_options(idedata): # pretend we're an Xtensa compiler, which gates some features in the headers '-D__XTENSA__', # allow to condition code on the presence of clang-tidy - '-DCLANG_TIDY' + '-DCLANG_TIDY', + # (esp-idf) Disable this header because they use asm with registers clang-tidy doesn't know + '-D__XTENSA_API_H__', + # (esp-idf) Fix __once_callable in some libstdc++ headers + '-D_GLIBCXX_HAVE_TLS', ] # copy compiler flags, except those clang doesn't understand. @@ -57,7 +61,8 @@ def clang_options(idedata): # add include directories, using -isystem for dependencies to suppress their errors for directory in idedata['includes']['toolchain']: - cmd.extend(['-isystem', directory]) + if 'xtensa-esp32s2-elf' not in directory: + cmd.extend(['-isystem', directory]) for directory in sorted(set(idedata['includes']['build'])): dependency = "framework-arduino" in directory or "/libdeps/" in directory cmd.extend(['-isystem' if dependency else '-I', directory]) diff --git a/script/helpers.py b/script/helpers.py index d32e0eafbe..2c40d47e04 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -112,15 +112,36 @@ def git_ls_files(patterns=None): return {s[3].strip(): int(s[0]) for s in lines} +IDF_TIDY_SDKCONFIG = """\ +CONFIG_BT_ENABLED=y +""" + + def load_idedata(environment): platformio_ini = Path(root_path) / "platformio.ini" temp_idedata = Path(temp_folder) / f"idedata-{environment}.json" + changed = False if not platformio_ini.is_file() or not temp_idedata.is_file(): changed = True elif platformio_ini.stat().st_mtime >= temp_idedata.stat().st_mtime: changed = True - else: - changed = False + + if environment == "esp32-idf-tidy": + # sdkconfig needs to be written before idedata is run + # but the file is also modified by the build process, so + # store a temp file to keep track of the + + sdk_internal = Path(temp_folder) / f"{environment}-internal-sdkconfig" + sdkconfig = Path(root_path) / f"sdkconfig.{environment}" + if ( + changed + or not sdk_internal.is_file() + or sdk_internal.read_text() != IDF_TIDY_SDKCONFIG + ): + changed = True + sdkconfig.write_text(IDF_TIDY_SDKCONFIG) + sdk_internal.parent.mkdir(exist_ok=True) + sdk_internal.write_text(IDF_TIDY_SDKCONFIG) if not changed: return json.loads(temp_idedata.read_text()) diff --git a/tests/component_tests/binary_sensor/test_binary_sensor.py b/tests/component_tests/binary_sensor/test_binary_sensor.py index 8da93a476e..514bc6ee5f 100644 --- a/tests/component_tests/binary_sensor/test_binary_sensor.py +++ b/tests/component_tests/binary_sensor/test_binary_sensor.py @@ -30,7 +30,7 @@ def test_binary_sensor_sets_mandatory_fields(generate_main): # Then assert 'bs_1->set_name("test bs1");' in main_cpp - assert "bs_1->set_pin(new GPIOPin" in main_cpp + assert "bs_1->set_pin(" in main_cpp def test_binary_sensor_config_value_internal_set(generate_main): diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index f9ba868096..d956387665 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -27,12 +27,6 @@ void setup() { auto *ota = new ota::OTAComponent(); // NOLINT ota->set_port(8266); - auto *gpio = new gpio::GPIOSwitch(); // NOLINT - gpio->set_name("GPIO Switch"); - gpio->set_pin(new GPIOPin(8, OUTPUT, false)); // NOLINT - App.register_component(gpio); - App.register_switch(gpio); - App.setup(); } diff --git a/tests/test1.yaml b/tests/test1.yaml index cd4179f394..ab089dbc15 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -71,7 +71,6 @@ wifi: password: '' channel: 14 bssid: 'A1:63:95:47:D3:1D' - enable_mdns: true manual_ip: static_ip: 192.168.178.230 gateway: 192.168.178.1 @@ -82,6 +81,9 @@ wifi: reboot_timeout: 120s power_save_mode: light +mdns: + disabled: false + http_request: useragent: esphome/device timeout: 10s @@ -170,6 +172,7 @@ i2c: scan: True frequency: 100kHz setup_priority: -100 + id: i2c_bus spi: clk_pin: GPIO21 @@ -177,15 +180,18 @@ spi: miso_pin: GPIO23 uart: - - tx_pin: GPIO22 - rx_pin: GPIO23 + - tx_pin: + number: GPIO22 + inverted: yes + rx_pin: + number: GPIO23 + inverted: yes baud_rate: 115200 id: uart0 parity: NONE data_bits: 8 stop_bits: 1 rx_buffer_size: 512 - invert: false - id: adalight_uart tx_pin: GPIO25 @@ -246,6 +252,7 @@ deep_sleep: ads1115: address: 0x48 + i2c_id: i2c_bus dallas: pin: GPIO23 @@ -436,6 +443,7 @@ sensor: availability: state_topic: livingroom/custom_state_topic measurement_duration: 31 + i2c_id: i2c_bus - platform: bme280 temperature: name: 'Outside Temperature' @@ -449,6 +457,7 @@ sensor: address: 0x77 iir_filter: 16x update_interval: 15s + i2c_id: i2c_bus - platform: bme680 temperature: name: 'Outside Temperature' @@ -464,6 +473,7 @@ sensor: temperature: 320 duration: 150ms update_interval: 15s + i2c_id: i2c_bus - platform: bmp085 temperature: name: 'Outside Temperature' @@ -473,6 +483,7 @@ sensor: - lambda: >- return x / powf(1.0 - (x / 44330.0), 5.255); update_interval: 15s + i2c_id: i2c_bus - platform: bmp280 temperature: name: 'Outside Temperature' @@ -482,6 +493,7 @@ sensor: address: 0x77 update_interval: 15s iir_filter: 16x + i2c_id: i2c_bus - platform: dallas address: 0x1C0000031EDD2A28 name: 'Living Room Temperature' @@ -503,6 +515,7 @@ sensor: humidity: name: 'Living Room Humidity 4' update_interval: 15s + i2c_id: i2c_bus - platform: duty_cycle pin: GPIO25 name: Duty Cycle Sensor @@ -515,6 +528,7 @@ sensor: humidity: name: 'Living Room Pressure 5' update_interval: 15s + i2c_id: i2c_bus - platform: hlw8012 sel_pin: 5 cf_pin: 14 @@ -560,6 +574,7 @@ sensor: range: 130uT oversampling: 8x update_interval: 15s + i2c_id: i2c_bus - platform: qmc5883l address: 0x0D field_strength_x: @@ -573,6 +588,7 @@ sensor: range: 800uT oversampling: 256x update_interval: 15s + i2c_id: i2c_bus - platform: hx711 name: 'HX711 Value' dout_pin: GPIO23 @@ -593,6 +609,7 @@ sensor: max_voltage: 32.0V max_current: 3.2A update_interval: 15s + i2c_id: i2c_bus - platform: ina226 address: 0x40 shunt_resistance: 0.1 ohm @@ -606,6 +623,7 @@ sensor: name: 'INA226 Shunt Voltage' max_current: 3.2A update_interval: 15s + i2c_id: i2c_bus - platform: ina3221 address: 0x40 channel_1: @@ -619,12 +637,14 @@ sensor: shunt_voltage: name: 'INA3221 Channel 1 Shunt Voltage' update_interval: 15s + i2c_id: i2c_bus - platform: htu21d temperature: name: 'Living Room Temperature 6' humidity: name: 'Living Room Humidity 6' update_interval: 15s + i2c_id: i2c_bus - platform: max6675 name: 'Living Room Temperature' cs_pin: GPIO23 @@ -670,6 +690,7 @@ sensor: name: 'MPU6050 Gyro z' temperature: name: 'MPU6050 Temperature' + i2c_id: i2c_bus - platform: ms5611 temperature: name: 'Outside Temperature' @@ -677,6 +698,7 @@ sensor: name: 'Outside Pressure' address: 0x77 update_interval: 15s + i2c_id: i2c_bus - platform: pmsa003i pm_1_0: name: "PMSA003i PM1.0" @@ -698,6 +720,7 @@ sensor: name: "PMSA003i PMC <10µm" address: 0x12 standard_units: True + i2c_id: i2c_bus - platform: pulse_counter name: 'Pulse Counter' pin: GPIO12 @@ -768,10 +791,12 @@ sensor: humidity: name: 'Living Room Humidity 8' address: 0x44 + i2c_id: i2c_bus update_interval: 15s - platform: sts3x name: 'Living Room Temperature 9' address: 0x4A + i2c_id: i2c_bus - platform: scd30 co2: name: 'Living Room CO2 9' @@ -785,6 +810,7 @@ sensor: altitude_compensation: 10m ambient_pressure_compensation: 961mBar temperature_offset: 4.2C + i2c_id: i2c_bus - platform: sgp30 eco2: name: 'Workshop eCO2' @@ -794,6 +820,7 @@ sensor: accuracy_decimals: 1 address: 0x58 update_interval: 5s + i2c_id: i2c_bus - platform: sps30 pm_1_0: name: 'Workshop PM <1µm Weight concentration' @@ -824,6 +851,7 @@ sensor: id: 'workshop_PMC_10_0' address: 0x69 update_interval: 10s + i2c_id: i2c_bus - platform: sht4x temperature: name: 'SHT4X Temperature' @@ -831,6 +859,7 @@ sensor: name: 'SHT4X Humidity' address: 0x44 update_interval: 15s + i2c_id: i2c_bus - platform: shtcx temperature: name: 'Living Room Temperature 10' @@ -838,6 +867,7 @@ sensor: name: 'Living Room Humidity 10' address: 0x70 update_interval: 15s + i2c_id: i2c_bus - platform: template name: 'Template Sensor' state_class: measurement @@ -863,6 +893,7 @@ sensor: is_cs_package: true integration_time: 402ms gain: 16x + i2c_id: i2c_bus - platform: tsl2591 id: this_little_light_of_mine address: 0x29 @@ -882,6 +913,7 @@ sensor: calculated_lux: name: "tsl2591 calculated_lux" id: tsl2591_cl + i2c_id: i2c_bus - platform: ultrasonic trigger_pin: GPIO25 echo_pin: @@ -921,6 +953,7 @@ sensor: name: CCS811 TVOC update_interval: 30s baseline: 0x4242 + i2c_id: i2c_bus - platform: tx20 wind_speed: name: 'Windspeed' @@ -946,6 +979,7 @@ sensor: - platform: tmp117 name: 'TMP117 Temperature' update_interval: 5s + i2c_id: i2c_bus - platform: hm3301 pm_1_0: name: 'PM1.0' @@ -956,6 +990,7 @@ sensor: aqi: name: 'AQI' calculation_type: 'CAQI' + i2c_id: i2c_bus - platform: teleinfo tag_name: "HCHC" name: "hchc" @@ -965,15 +1000,18 @@ sensor: - platform: mcp9808 name: 'MCP9808 Temperature' update_interval: 15s + i2c_id: i2c_bus - platform: ezo id: ph_ezo address: 99 unit_of_measurement: 'pH' + i2c_id: i2c_bus - platform: sdp3x name: "HVAC Filter Pressure drop" id: filter_pressure update_interval: 5s accuracy_decimals: 3 + i2c_id: i2c_bus - platform: cs5460a id: cs5460a1 current: @@ -1216,14 +1254,18 @@ binary_sensor: pca9685: frequency: 500 address: 0x0 + i2c_id: i2c_bus tlc59208f: - address: 0x20 id: tlc59208f_1 + i2c_id: i2c_bus - address: 0x22 id: tlc59208f_2 + i2c_id: i2c_bus - address: 0x24 id: tlc59208f_3 + i2c_id: i2c_bus my9231: data_pin: GPIO12 @@ -1363,6 +1405,7 @@ output: id: dac_output - platform: mcp4725 id: mcp4725_dac_output + i2c_id: i2c_bus e131: @@ -1992,6 +2035,7 @@ display: address: 0x3F lambda: |- it.print("Hello World!"); + i2c_id: i2c_bus - platform: max7219 cs_pin: GPIO23 num_chips: 1 @@ -2039,6 +2083,7 @@ display: then: lambda: |- ESP_LOGD("display", "1 -> 2"); + i2c_id: i2c_bus - platform: ssd1306_spi model: 'SSD1306 128x64' cs_pin: GPIO23 @@ -2073,6 +2118,7 @@ display: - id: page13272 lambda: |- // Nothing + i2c_id: i2c_bus - platform: ssd1327_spi model: 'SSD1327 128x128' cs_pin: GPIO23 @@ -2151,6 +2197,7 @@ pn532_spi: payload: !lambda 'return x;' pn532_i2c: + i2c_id: i2c_bus rdm6300: uart_id: uart0 @@ -2167,11 +2214,13 @@ rc522_i2c: on_tag: - lambda: |- ESP_LOGD("main", "Found tag %s", x.c_str()); + i2c_id: i2c_bus - update_interval: 1s on_tag: - lambda: |- ESP_LOGD("main", "Found tag %s", x.c_str()); + i2c_id: i2c_bus gps: uart_id: uart0 @@ -2198,6 +2247,7 @@ time: on_time: seconds: 0 then: ds1307.read_time + i2c_id: i2c_bus cover: - platform: template @@ -2225,31 +2275,35 @@ debug: tca9548a: - address: 0x70 id: multiplex0 - scan: True + channels: + - bus_id: multiplex0_chan0 + channel: 0 + i2c_id: i2c_bus - address: 0x71 id: multiplex1 - scan: True - multiplexer: - id: multiplex0 - channel: 0 + i2c_id: multiplex0_chan0 pcf8574: - id: 'pcf8574_hub' address: 0x21 pcf8575: False + i2c_id: i2c_bus mcp23017: - id: 'mcp23017_hub' open_drain_interrupt: 'true' + i2c_id: i2c_bus mcp23008: - id: 'mcp23008_hub' address: 0x22 open_drain_interrupt: 'true' + i2c_id: i2c_bus mcp23016: - id: 'mcp23016_hub' address: 0x23 + i2c_id: i2c_bus stepper: - platform: a4988 diff --git a/tests/test2.yaml b/tests/test2.yaml index 54932953d5..e6df4d513e 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -14,13 +14,15 @@ ethernet: clk_mode: GPIO0_IN phy_addr: 0 power_pin: GPIO25 - enable_mdns: false manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 subnet: 255.255.255.0 domain: .local +mdns: + disabled: true + api: i2c: @@ -360,7 +362,7 @@ esp32_ble_tracker: ble_client: - mac_address: 01:02:03:04:05:06 id: airthings01 - + airthings_ble: #esp32_ble_beacon: diff --git a/tests/test3.yaml b/tests/test3.yaml index 5602481c36..de46cec99c 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -259,7 +259,7 @@ ota: logger: hardware_uart: UART1 level: DEBUG - esp8266_store_log_strings_in_flash: false + esp8266_store_log_strings_in_flash: true web_server: diff --git a/tests/test4.yaml b/tests/test4.yaml index e52e8cc33c..a205f0802e 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -379,7 +379,7 @@ display: it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); rotation: 0° update_interval: 16ms - + - platform: waveshare_epaper cs_pin: GPIO23 dc_pin: GPIO23 @@ -407,6 +407,23 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: inkplate6 + id: inkplate_display + greyscale: false + partial_updating: false + update_interval: 60s + + ckv_pin: GPIO1 + sph_pin: GPIO1 + gmod_pin: GPIO1 + gpio0_enable_pin: GPIO1 + oe_pin: GPIO1 + spv_pin: GPIO1 + powerup_pin: GPIO1 + wakeup_pin: GPIO1 + vcom_pin: GPIO1 + + text_sensor: - platform: pipsolar diff --git a/tests/test5.yaml b/tests/test5.yaml index 84d4a5a44e..f165617551 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -1,12 +1,15 @@ esphome: name: test5 - platform: ESP32 - board: nodemcu-32s build_path: build/test5 project: name: esphome.test5_project version: "1.0.0" +esp32: + board: nodemcu-32s + framework: + type: esp-idf + wifi: networks: - ssid: 'MySSID' @@ -151,20 +154,3 @@ sensor: uart_id: uart2 co2: name: CO2 Sensor - -display: - - platform: inkplate6 - id: inkplate_display - greyscale: false - partial_updating: false - update_interval: 60s - - ckv_pin: GPIO1 - sph_pin: GPIO1 - gmod_pin: GPIO1 - gpio0_enable_pin: GPIO1 - oe_pin: GPIO1 - spv_pin: GPIO1 - powerup_pin: GPIO1 - wakeup_pin: GPIO1 - vcom_pin: GPIO1 diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index 37b4d6db57..9a15bf0b9c 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -531,13 +531,13 @@ class TestEsphomeCore: assert target.address == "4.3.2.1" def test_is_esp32(self, target): - target.esp_platform = "ESP32" + target.data[const.KEY_CORE] = {const.KEY_TARGET_PLATFORM: "esp32"} assert target.is_esp32 is True assert target.is_esp8266 is False def test_is_esp8266(self, target): - target.esp_platform = "ESP8266" + target.data[const.KEY_CORE] = {const.KEY_TARGET_PLATFORM: "esp8266"} assert target.is_esp32 is False assert target.is_esp8266 is True diff --git a/tests/unit_tests/test_cpp_helpers.py b/tests/unit_tests/test_cpp_helpers.py index ae7a61e01f..ad234250ce 100644 --- a/tests/unit_tests/test_cpp_helpers.py +++ b/tests/unit_tests/test_cpp_helpers.py @@ -3,7 +3,6 @@ from mock import Mock from esphome import cpp_helpers as ch from esphome import const -from esphome.cpp_generator import MockObj @pytest.mark.asyncio @@ -13,15 +12,6 @@ async def test_gpio_pin_expression__conf_is_none(monkeypatch): assert actual is None -@pytest.mark.asyncio -async def test_gpio_pin_expression__new_pin(monkeypatch): - actual = await ch.gpio_pin_expression( - {const.CONF_NUMBER: 42, const.CONF_MODE: "input", const.CONF_INVERTED: False} - ) - - assert isinstance(actual, MockObj) - - @pytest.mark.asyncio async def test_register_component(monkeypatch): var = Mock(base="foo.bar") diff --git a/tests/unit_tests/test_pins.py b/tests/unit_tests/test_pins.py deleted file mode 100644 index d2ffd5f7cd..0000000000 --- a/tests/unit_tests/test_pins.py +++ /dev/null @@ -1,346 +0,0 @@ -""" -Please Note: - -These tests cover the process of identifying information about pins, they do not -check if the definition of MCUs and pins is correct. - -""" -import logging - -import pytest - -from esphome.config_validation import Invalid -from esphome.core import EsphomeCore -from esphome import boards, pins - - -MOCK_ESP8266_BOARD_ID = "_mock_esp8266" -MOCK_ESP8266_PINS = {"X0": 16, "X1": 5, "X2": 4, "LED": 2} -MOCK_ESP8266_BOARD_ALIAS_ID = "_mock_esp8266_alias" -MOCK_ESP8266_FLASH_SIZE = boards.FLASH_SIZE_2_MB - -MOCK_ESP32_BOARD_ID = "_mock_esp32" -MOCK_ESP32_PINS = {"Y0": 12, "Y1": 8, "Y2": 3, "LED": 9, "A0": 8} -MOCK_ESP32_BOARD_ALIAS_ID = "_mock_esp32_alias" - -UNKNOWN_PLATFORM = "STM32" - - -@pytest.fixture -def mock_mcu(monkeypatch): - """ - Add a mock MCU into the lists as a stable fixture - """ - boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ID] = MOCK_ESP8266_PINS - boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ID] = MOCK_ESP8266_FLASH_SIZE - boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ALIAS_ID] = MOCK_ESP8266_BOARD_ID - boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ALIAS_ID] = MOCK_ESP8266_FLASH_SIZE - boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ID] = MOCK_ESP32_PINS - boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ALIAS_ID] = MOCK_ESP32_BOARD_ID - yield - del boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ID] - del boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ID] - del boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ALIAS_ID] - del boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ALIAS_ID] - del boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ID] - del boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ALIAS_ID] - - -@pytest.fixture -def core(monkeypatch, mock_mcu): - core = EsphomeCore() - monkeypatch.setattr(pins, "CORE", core) - return core - - -@pytest.fixture -def core_esp8266(core): - core.esp_platform = "ESP8266" - core.board = MOCK_ESP8266_BOARD_ID - return core - - -@pytest.fixture -def core_esp32(core): - core.esp_platform = "ESP32" - core.board = MOCK_ESP32_BOARD_ID - return core - - -class Test_lookup_pin: - @pytest.mark.parametrize( - "value, expected", - ( - ("X1", 5), - ("MOSI", 13), - ), - ) - def test_valid_esp8266_pin(self, core_esp8266, value, expected): - actual = pins._lookup_pin(value) - - assert actual == expected - - def test_valid_esp8266_pin_alias(self, core_esp8266): - core_esp8266.board = MOCK_ESP8266_BOARD_ALIAS_ID - - actual = pins._lookup_pin("X2") - - assert actual == 4 - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y1", 8), - ("A0", 8), - ("MOSI", 23), - ), - ) - def test_valid_esp32_pin(self, core_esp32, value, expected): - actual = pins._lookup_pin(value) - - assert actual == expected - - def test_valid_32_pin_alias(self, core_esp32): - core_esp32.board = MOCK_ESP32_BOARD_ALIAS_ID - - actual = pins._lookup_pin("Y2") - - assert actual == 3 - - def test_invalid_pin(self, core_esp8266): - with pytest.raises( - Invalid, match="Cannot resolve pin name 'X42' for board _mock_esp8266." - ): - pins._lookup_pin("X42") - - def test_unsupported_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins._lookup_pin("TX") - - -class Test_translate_pin: - @pytest.mark.parametrize( - "value, expected", - ( - (2, 2), - ("3", 3), - ("GPIO4", 4), - ("TX", 1), - ("Y0", 12), - ), - ) - def test_valid_values(self, core_esp32, value, expected): - actual = pins._translate_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", ({}, None)) - def test_invalid_values(self, core_esp32, value): - with pytest.raises(Invalid, match="This variable only supports"): - pins._translate_pin(value) - - -class Test_validate_gpio_pin: - def test_esp32_valid(self, core_esp32): - actual = pins.validate_gpio_pin("GPIO22") - - assert actual == 22 - - @pytest.mark.parametrize( - "value, match", - ( - (-1, "ESP32: Invalid pin number: -1"), - (40, "ESP32: Invalid pin number: 40"), - (6, "This pin cannot be used on ESP32s and"), - (7, "This pin cannot be used on ESP32s and"), - (8, "This pin cannot be used on ESP32s and"), - (11, "This pin cannot be used on ESP32s and"), - (20, "The pin GPIO20 is not usable on ESP32s"), - (24, "The pin GPIO24 is not usable on ESP32s"), - (28, "The pin GPIO28 is not usable on ESP32s"), - (29, "The pin GPIO29 is not usable on ESP32s"), - (30, "The pin GPIO30 is not usable on ESP32s"), - (31, "The pin GPIO31 is not usable on ESP32s"), - ), - ) - def test_esp32_invalid_pin(self, core_esp32, value, match): - with pytest.raises(Invalid, match=match): - pins.validate_gpio_pin(value) - - @pytest.mark.parametrize("value", (9, 10)) - def test_esp32_warning(self, core_esp32, caplog, value): - caplog.at_level(logging.WARNING) - pins.validate_gpio_pin(value) - - assert len(caplog.messages) == 1 - assert caplog.messages[0].endswith("flash interface in QUAD IO flash mode.") - - def test_esp8266_valid(self, core_esp8266): - actual = pins.validate_gpio_pin("GPIO12") - - assert actual == 12 - - @pytest.mark.parametrize( - "value, match", - ( - (-1, "ESP8266: Invalid pin number: -1"), - (18, "ESP8266: Invalid pin number: 18"), - (6, "This pin cannot be used on ESP8266s and"), - (7, "This pin cannot be used on ESP8266s and"), - (8, "This pin cannot be used on ESP8266s and"), - (11, "This pin cannot be used on ESP8266s and"), - ), - ) - def test_esp8266_invalid_pin(self, core_esp8266, value, match): - with pytest.raises(Invalid, match=match): - pins.validate_gpio_pin(value) - - @pytest.mark.parametrize("value", (9, 10)) - def test_esp8266_warning(self, core_esp8266, caplog, value): - caplog.at_level(logging.WARNING) - pins.validate_gpio_pin(value) - - assert len(caplog.messages) == 1 - assert caplog.messages[0].endswith("flash interface in QUAD IO flash mode.") - - def test_unknown_device(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.validate_gpio_pin("0") - - -class Test_input_pin: - @pytest.mark.parametrize("value, expected", (("X0", 16),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.input_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y0", 12), - (17, 17), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.input_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", (17,)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.input_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.input_pin(2) - - -class Test_input_pullup_pin: - @pytest.mark.parametrize("value, expected", (("X0", 16),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.input_pullup_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y0", 12), - (17, 17), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.input_pullup_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", (0,)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.input_pullup_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.input_pullup_pin(2) - - -class Test_output_pin: - @pytest.mark.parametrize("value, expected", (("X0", 16),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.output_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y0", 12), - (17, 17), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.output_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", (17,)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.output_pin(value) - - @pytest.mark.parametrize("value", range(34, 40)) - def test_invalid_esp32_values(self, core_esp32, value): - with pytest.raises(Invalid): - pins.output_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.output_pin(2) - - -class Test_analog_pin: - @pytest.mark.parametrize("value, expected", ((17, 17),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.analog_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - (32, 32), - (39, 39), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.analog_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", ("X0",)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.analog_pin(value) - - @pytest.mark.parametrize("value", ("Y0",)) - def test_invalid_esp32_values(self, core_esp32, value): - with pytest.raises(Invalid): - pins.analog_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.analog_pin(2) diff --git a/tests/unit_tests/test_wizard.py b/tests/unit_tests/test_wizard.py index 56bd5119b5..18e040b0a6 100644 --- a/tests/unit_tests/test_wizard.py +++ b/tests/unit_tests/test_wizard.py @@ -2,7 +2,7 @@ import esphome.wizard as wz import pytest -from esphome.boards import ESP8266_BOARD_PINS +from esphome.components.esp8266.boards import ESP8266_BOARD_PINS from mock import MagicMock