diff --git a/esphome/owon-b35t.yaml b/esphome/owon-b35t-m5stack-core2.yaml similarity index 78% rename from esphome/owon-b35t.yaml rename to esphome/owon-b35t-m5stack-core2.yaml index ea6bc6f..d314250 100644 --- a/esphome/owon-b35t.yaml +++ b/esphome/owon-b35t-m5stack-core2.yaml @@ -1,7 +1,7 @@ substitutions: - name: "owon-b35t" - friendly_name: "OWON B35T Multimeter" - device_description: "M5Stack Core 1 BLE client for OWON B35T/B35T+ multimeter with local graphical display" + name: "owon-b35t-m5stack-core2" + friendly_name: "OWON B35T Multimeter Core2" + device_description: "M5Stack Core2 BLE client for OWON B35T/B35T+ multimeter with local graphical display" owon_mac_address: !secret owon_b35t_mac_address esphome: @@ -10,17 +10,27 @@ esphome: comment: ${device_description} min_version: 2024.6.0 includes: - - owon_b35t.h + - owon_b35t-m5stack-core2.h + on_boot: + priority: 850 + then: + - lambda: |- + owon_b35t::core2_axp192_init(id(core2_i2c)); project: - name: "custom.owon-b35t-m5stack" + name: "custom.owon-b35t-m5stack-core2" version: "1.0" esp32: - board: m5stack-core-esp32 + board: m5stack-core2 + flash_size: 16MB framework: type: esp-idf advanced: - minimum_chip_revision: "3.1" + sram1_as_iram: true + +psram: + mode: quad + speed: 80MHz logger: level: INFO @@ -44,20 +54,21 @@ wifi: ssid: "OWON B35T Fallback Hotspot" password: !secret fallback_psk -# Disabled to save RAM on the M5Stack Core 1. Re-enable temporarily if Wi-Fi recovery is needed. -# captive_portal: +captive_portal: interval: - interval: 10s then: - lambda: |- - ESP_LOGI("mem", "heap free=%u min_free=%u internal_free=%u internal_largest=%u dma_free=%u dma_largest=%u", + ESP_LOGI("mem", "heap free=%u min_free=%u internal_free=%u internal_largest=%u dma_free=%u dma_largest=%u psram_free=%u psram_largest=%u", static_cast(esp_get_free_heap_size()), static_cast(esp_get_minimum_free_heap_size()), static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), static_cast(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL)), static_cast(heap_caps_get_free_size(MALLOC_CAP_DMA)), - static_cast(heap_caps_get_largest_free_block(MALLOC_CAP_DMA))); + static_cast(heap_caps_get_largest_free_block(MALLOC_CAP_DMA)), + static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)), + static_cast(heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM))); esp32_ble_tracker: scan_parameters: @@ -75,22 +86,24 @@ ble_client: then: - lambda: |- owon_meter.on_disconnect(); - - logger.log: - level: WARN - format: "OWON BLE meter disconnected; restarting M5Stack to reclaim heap" - - delay: 1s - - lambda: |- - esp_restart(); spi: clk_pin: GPIO18 mosi_pin: GPIO23 - miso_pin: GPIO19 + +i2c: + id: core2_i2c + sda: GPIO21 + scl: GPIO22 + scan: true output: - - platform: ledc - pin: GPIO32 + - platform: template + type: float id: lcd_backlight + write_action: + - lambda: |- + owon_b35t::core2_axp192_set_backlight(state); light: - platform: monochromatic @@ -173,35 +186,38 @@ font: ] display: - - platform: ili9xxx + - platform: mipi_spi id: lcd - model: M5STACK - cs_pin: GPIO14 - dc_pin: GPIO27 - reset_pin: GPIO33 - invert_colors: true - color_palette: 8BIT - rotation: 0 + model: M5CORE2 update_interval: 500ms lambda: |- owon_meter.render(it, id(meter_font)); +touchscreen: + - platform: ft63x6 + id: touch + display: lcd + binary_sensor: - - platform: gpio + - platform: touchscreen id: button_a - pin: - number: GPIO39 - inverted: true + touchscreen_id: touch + x_min: 34 + x_max: 74 + y_min: 212 + y_max: 240 internal: true on_press: then: - lambda: |- owon_meter.previous_button(); - - platform: gpio + - platform: touchscreen id: button_b - pin: - number: GPIO38 - inverted: true + touchscreen_id: touch + x_min: 108 + x_max: 208 + y_min: 212 + y_max: 240 internal: true on_click: - min_length: 50ms @@ -233,11 +249,13 @@ binary_sensor: uint8_t press_type = (owon_meter.selected_button == 1 || owon_meter.selected_button == 5) ? 0x01 : 0x00; std::vector data = {owon_meter.selected_button, press_type}; return data; - - platform: gpio + - platform: touchscreen id: button_c - pin: - number: GPIO37 - inverted: true + touchscreen_id: touch + x_min: 242 + x_max: 282 + y_min: 212 + y_max: 240 internal: true on_press: then: @@ -278,16 +296,16 @@ sensor: text_sensor: - platform: template name: "${friendly_name} Reading" - update_interval: 2s + update_interval: 1s lambda: |- return owon_meter.reading_text(); - platform: template name: "${friendly_name} Unit" - update_interval: 2s + update_interval: 1s lambda: |- return std::string(owon_meter.scale()) + owon_meter.unit(); - platform: template name: "${friendly_name} Mode" - update_interval: 2s + update_interval: 1s lambda: |- return owon_meter.mode_text(); diff --git a/esphome/owon_b35t.h b/esphome/owon_b35t-m5stack-core2.h similarity index 87% rename from esphome/owon_b35t.h rename to esphome/owon_b35t-m5stack-core2.h index 9173dd2..95c7602 100644 --- a/esphome/owon_b35t.h +++ b/esphome/owon_b35t-m5stack-core2.h @@ -13,6 +13,7 @@ #include "esp_heap_caps.h" #include "esp_system.h" #include "esphome/core/helpers.h" +#include "esphome/components/i2c/i2c.h" #include "esphome/core/log.h" #include "esphome/components/display/display.h" @@ -22,6 +23,92 @@ using esphome::Color; using esphome::display::Display; static const char *const TAG = "owon_b35t"; +static const char *const POWER_TAG = "core2_power"; +static constexpr uint8_t AXP192_ADDR = 0x34; +static esphome::i2c::I2CDevice axp192; +static bool axp192_ready = false; + +static bool axp_write(uint8_t reg, uint8_t value) { + if (!axp192_ready) return false; + bool ok = axp192.write_byte(reg, value); + if (!ok) ESP_LOGW(POWER_TAG, "AXP192 write reg 0x%02X failed", reg); + return ok; +} + +static bool axp_read(uint8_t reg, uint8_t *value) { + if (!axp192_ready) return false; + bool ok = axp192.read_byte(reg, value); + if (!ok) ESP_LOGW(POWER_TAG, "AXP192 read reg 0x%02X failed", reg); + return ok; +} + +static void axp_update(uint8_t reg, uint8_t clear_mask, uint8_t set_mask) { + uint8_t value = 0; + if (!axp_read(reg, &value)) return; + value = (value & ~clear_mask) | set_mask; + axp_write(reg, value); +} + +static uint8_t axp_dc_voltage_data(uint16_t millivolts) { + if (millivolts < 700) millivolts = 700; + if (millivolts > 3500) millivolts = 3500; + return static_cast((millivolts - 700) / 25) & 0x7F; +} + +static uint8_t axp_ldo_voltage_data(uint16_t millivolts) { + if (millivolts < 1800) millivolts = 1800; + if (millivolts > 3300) millivolts = 3300; + return static_cast((millivolts - 1800) / 100) & 0x0F; +} + +static void core2_axp192_set_lcd_voltage(uint16_t millivolts) { + uint8_t value = 0; + axp_read(0x27, &value); + axp_write(0x27, (value & 0x80) | axp_dc_voltage_data(millivolts)); // DCDC3, LCD backlight +} + +static void core2_axp192_set_backlight(float brightness) { + if (brightness <= 0.0f) { + axp_update(0x12, 0x02, 0x00); // DCDC3 off + return; + } + if (brightness > 1.0f) brightness = 1.0f; + uint16_t millivolts = static_cast(2400 + brightness * 900); + core2_axp192_set_lcd_voltage(millivolts); + axp_update(0x12, 0x00, 0x02); // DCDC3 on +} + +static void core2_axp192_init(esphome::i2c::I2CBus *bus) { + axp192.set_i2c_bus(bus); + axp192.set_i2c_address(AXP192_ADDR); + axp192_ready = true; + ESP_LOGI(POWER_TAG, "Initializing M5Stack Core2 AXP192 LCD power"); + axp_update(0x30, 0xF9, 0x02); // Disable VBUS current limit, preserve bit 2. + axp_update(0x92, 0x07, 0x00); // GPIO1 open-drain output. + axp_update(0x93, 0x07, 0x00); // GPIO2 open-drain output. + axp_write(0x35, 0xA2); // RTC battery charging. + + uint8_t value = 0; + axp_read(0x26, &value); + axp_write(0x26, (value & 0x80) | axp_dc_voltage_data(3350)); // DCDC1 ESP32 VDD. + core2_axp192_set_lcd_voltage(2800); + + uint8_t ldo2 = axp_ldo_voltage_data(3300); + uint8_t ldo3 = axp_ldo_voltage_data(2000); + axp_write(0x28, (ldo2 << 4) | ldo3); // LDO2 LCD logic/SD, LDO3 vibrator. + axp_update(0x12, 0x00, 0x07); // Enable DCDC1, DCDC3, LDO2. + axp_write(0x82, 0xFF); // ADCs on. + axp_update(0x95, 0x8D, 0x84); // GPIO4 setup, as M5Core2 library does. + axp_write(0x36, 0x4C); // Power key timing. + + // LCD reset through AXP192 GPIO4. + axp_update(0x96, 0x02, 0x00); + delay(100); + axp_update(0x96, 0x00, 0x02); + delay(100); + + core2_axp192_set_backlight(1.0f); +} static const uint8_t ACCU_BMP[32] = { 0b00000000, 0b00000000,