Ported the code to M5Stack Core2 (different display) and removed reboot on disconnect

This commit is contained in:
2026-05-26 19:10:30 +02:00
parent 69c2166414
commit 1b3b4870e7
2 changed files with 148 additions and 43 deletions
@@ -1,7 +1,7 @@
substitutions: substitutions:
name: "owon-b35t" name: "owon-b35t-m5stack-core2"
friendly_name: "OWON B35T Multimeter" friendly_name: "OWON B35T Multimeter Core2"
device_description: "M5Stack Core 1 BLE client for OWON B35T/B35T+ multimeter with local graphical display" device_description: "M5Stack Core2 BLE client for OWON B35T/B35T+ multimeter with local graphical display"
owon_mac_address: !secret owon_b35t_mac_address owon_mac_address: !secret owon_b35t_mac_address
esphome: esphome:
@@ -10,17 +10,27 @@ esphome:
comment: ${device_description} comment: ${device_description}
min_version: 2024.6.0 min_version: 2024.6.0
includes: includes:
- owon_b35t.h - owon_b35t-m5stack-core2.h
on_boot:
priority: 850
then:
- lambda: |-
owon_b35t::core2_axp192_init(id(core2_i2c));
project: project:
name: "custom.owon-b35t-m5stack" name: "custom.owon-b35t-m5stack-core2"
version: "1.0" version: "1.0"
esp32: esp32:
board: m5stack-core-esp32 board: m5stack-core2
flash_size: 16MB
framework: framework:
type: esp-idf type: esp-idf
advanced: advanced:
minimum_chip_revision: "3.1" sram1_as_iram: true
psram:
mode: quad
speed: 80MHz
logger: logger:
level: INFO level: INFO
@@ -44,20 +54,21 @@ wifi:
ssid: "OWON B35T Fallback Hotspot" ssid: "OWON B35T Fallback Hotspot"
password: !secret fallback_psk 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:
- interval: 10s - interval: 10s
then: then:
- lambda: |- - 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<unsigned>(esp_get_free_heap_size()), static_cast<unsigned>(esp_get_free_heap_size()),
static_cast<unsigned>(esp_get_minimum_free_heap_size()), static_cast<unsigned>(esp_get_minimum_free_heap_size()),
static_cast<unsigned>(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), static_cast<unsigned>(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)),
static_cast<unsigned>(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL)), static_cast<unsigned>(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL)),
static_cast<unsigned>(heap_caps_get_free_size(MALLOC_CAP_DMA)), static_cast<unsigned>(heap_caps_get_free_size(MALLOC_CAP_DMA)),
static_cast<unsigned>(heap_caps_get_largest_free_block(MALLOC_CAP_DMA))); static_cast<unsigned>(heap_caps_get_largest_free_block(MALLOC_CAP_DMA)),
static_cast<unsigned>(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)),
static_cast<unsigned>(heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM)));
esp32_ble_tracker: esp32_ble_tracker:
scan_parameters: scan_parameters:
@@ -75,22 +86,24 @@ ble_client:
then: then:
- lambda: |- - lambda: |-
owon_meter.on_disconnect(); owon_meter.on_disconnect();
- logger.log:
level: WARN
format: "OWON BLE meter disconnected; restarting M5Stack to reclaim heap"
- delay: 1s
- lambda: |-
esp_restart();
spi: spi:
clk_pin: GPIO18 clk_pin: GPIO18
mosi_pin: GPIO23 mosi_pin: GPIO23
miso_pin: GPIO19
i2c:
id: core2_i2c
sda: GPIO21
scl: GPIO22
scan: true
output: output:
- platform: ledc - platform: template
pin: GPIO32 type: float
id: lcd_backlight id: lcd_backlight
write_action:
- lambda: |-
owon_b35t::core2_axp192_set_backlight(state);
light: light:
- platform: monochromatic - platform: monochromatic
@@ -173,35 +186,38 @@ font:
] ]
display: display:
- platform: ili9xxx - platform: mipi_spi
id: lcd id: lcd
model: M5STACK model: M5CORE2
cs_pin: GPIO14
dc_pin: GPIO27
reset_pin: GPIO33
invert_colors: true
color_palette: 8BIT
rotation: 0
update_interval: 500ms update_interval: 500ms
lambda: |- lambda: |-
owon_meter.render(it, id(meter_font)); owon_meter.render(it, id(meter_font));
touchscreen:
- platform: ft63x6
id: touch
display: lcd
binary_sensor: binary_sensor:
- platform: gpio - platform: touchscreen
id: button_a id: button_a
pin: touchscreen_id: touch
number: GPIO39 x_min: 34
inverted: true x_max: 74
y_min: 212
y_max: 240
internal: true internal: true
on_press: on_press:
then: then:
- lambda: |- - lambda: |-
owon_meter.previous_button(); owon_meter.previous_button();
- platform: gpio - platform: touchscreen
id: button_b id: button_b
pin: touchscreen_id: touch
number: GPIO38 x_min: 108
inverted: true x_max: 208
y_min: 212
y_max: 240
internal: true internal: true
on_click: on_click:
- min_length: 50ms - 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; uint8_t press_type = (owon_meter.selected_button == 1 || owon_meter.selected_button == 5) ? 0x01 : 0x00;
std::vector<uint8_t> data = {owon_meter.selected_button, press_type}; std::vector<uint8_t> data = {owon_meter.selected_button, press_type};
return data; return data;
- platform: gpio - platform: touchscreen
id: button_c id: button_c
pin: touchscreen_id: touch
number: GPIO37 x_min: 242
inverted: true x_max: 282
y_min: 212
y_max: 240
internal: true internal: true
on_press: on_press:
then: then:
@@ -278,16 +296,16 @@ sensor:
text_sensor: text_sensor:
- platform: template - platform: template
name: "${friendly_name} Reading" name: "${friendly_name} Reading"
update_interval: 2s update_interval: 1s
lambda: |- lambda: |-
return owon_meter.reading_text(); return owon_meter.reading_text();
- platform: template - platform: template
name: "${friendly_name} Unit" name: "${friendly_name} Unit"
update_interval: 2s update_interval: 1s
lambda: |- lambda: |-
return std::string(owon_meter.scale()) + owon_meter.unit(); return std::string(owon_meter.scale()) + owon_meter.unit();
- platform: template - platform: template
name: "${friendly_name} Mode" name: "${friendly_name} Mode"
update_interval: 2s update_interval: 1s
lambda: |- lambda: |-
return owon_meter.mode_text(); return owon_meter.mode_text();
@@ -13,6 +13,7 @@
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_system.h" #include "esp_system.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/display/display.h" #include "esphome/components/display/display.h"
@@ -22,6 +23,92 @@ using esphome::Color;
using esphome::display::Display; using esphome::display::Display;
static const char *const TAG = "owon_b35t"; 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<uint8_t>((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<uint8_t>((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<uint16_t>(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] = { static const uint8_t ACCU_BMP[32] = {
0b00000000, 0b00000000, 0b00000000, 0b00000000,