Document Core2 BLE proxy power and UI logic
This commit is contained in:
+80
-17
@@ -25,12 +25,16 @@ namespace owon_b35t {
|
||||
using esphome::Color;
|
||||
using esphome::display::Display;
|
||||
|
||||
// Log tags used by ESPHome for BLE meter parsing and Core2 power-management messages.
|
||||
static const char *const TAG = "owon_b35t";
|
||||
static const char *const POWER_TAG = "core2_power";
|
||||
|
||||
// The M5Stack Core2 routes LCD/backlight power through an AXP192 PMIC on I2C.
|
||||
static constexpr uint8_t AXP192_ADDR = 0x34;
|
||||
static esphome::i2c::I2CDevice axp192;
|
||||
static bool axp192_ready = false;
|
||||
static bool axp192_ready = false; // Guard PMIC accesses until the I2C bus/address has been configured.
|
||||
|
||||
// Write one byte to an AXP192 register and log failures instead of silently losing power changes.
|
||||
static bool axp_write(uint8_t reg, uint8_t value) {
|
||||
if (!axp192_ready) return false;
|
||||
bool ok = axp192.write_byte(reg, value);
|
||||
@@ -38,6 +42,7 @@ static bool axp_write(uint8_t reg, uint8_t value) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Read one byte from an AXP192 register; callers use this before read/modify/write updates.
|
||||
static bool axp_read(uint8_t reg, uint8_t *value) {
|
||||
if (!axp192_ready) return false;
|
||||
bool ok = axp192.read_byte(reg, value);
|
||||
@@ -45,6 +50,7 @@ static bool axp_read(uint8_t reg, uint8_t *value) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Clear and set selected bits in an AXP192 register while preserving all other bits.
|
||||
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;
|
||||
@@ -52,24 +58,28 @@ static void axp_update(uint8_t reg, uint8_t clear_mask, uint8_t set_mask) {
|
||||
axp_write(reg, value);
|
||||
}
|
||||
|
||||
// Convert a desired DCDC output voltage to the 25 mV-step register encoding used by AXP192.
|
||||
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;
|
||||
}
|
||||
|
||||
// Convert a desired LDO voltage to the 100 mV-step register encoding used by AXP192.
|
||||
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;
|
||||
}
|
||||
|
||||
// Set the LCD backlight rail (DCDC3) voltage; brightness is controlled by this rail voltage.
|
||||
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
|
||||
}
|
||||
|
||||
// Map a 0.0-1.0 brightness value to the Core2 LCD backlight voltage and enable/disable DCDC3.
|
||||
static void core2_axp192_set_backlight(float brightness) {
|
||||
if (brightness <= 0.0f) {
|
||||
axp_update(0x12, 0x02, 0x00); // DCDC3 off
|
||||
@@ -81,6 +91,7 @@ static void core2_axp192_set_backlight(float brightness) {
|
||||
axp_update(0x12, 0x00, 0x02); // DCDC3 on
|
||||
}
|
||||
|
||||
// Initialize the Core2 PMIC rails needed by ESP32, LCD logic, LCD backlight, and the display reset line.
|
||||
static void core2_axp192_init(esphome::i2c::I2CBus *bus) {
|
||||
axp192.set_i2c_bus(bus);
|
||||
axp192.set_i2c_address(AXP192_ADDR);
|
||||
@@ -113,6 +124,7 @@ static void core2_axp192_init(esphome::i2c::I2CBus *bus) {
|
||||
core2_axp192_set_backlight(1.0f);
|
||||
}
|
||||
|
||||
// 16x16 monochrome status icons drawn manually into the ESPHome display framebuffer.
|
||||
static const uint8_t ACCU_BMP[32] = {
|
||||
0b00000000, 0b00000000,
|
||||
0b00000000, 0b00000000,
|
||||
@@ -229,6 +241,8 @@ static const uint8_t BUZZ_BMP[32] = {
|
||||
|
||||
class Meter {
|
||||
public:
|
||||
// Offsets and bit masks for the 14-byte "classic" OWON frame layout. B35T+ frames are
|
||||
// normalized into this same buffer so the rest of the code can use one representation.
|
||||
static constexpr uint8_t REGPLUSMINUS = 0x00;
|
||||
static constexpr uint8_t FLAGPLUS = 0b00101011;
|
||||
static constexpr uint8_t FLAGMINUS = 0b00101101;
|
||||
@@ -268,24 +282,29 @@ class Meter {
|
||||
static constexpr uint8_t FLAGUNITAMP = 0b01000000;
|
||||
static constexpr uint8_t FLAGUNITVOLT = 0b10000000;
|
||||
|
||||
bool connected{false};
|
||||
bool write_available{false};
|
||||
bool is_plus{false};
|
||||
bool low_battery{false};
|
||||
bool overload{false};
|
||||
bool has_reading{false};
|
||||
uint8_t selected_button{1};
|
||||
uint32_t last_notify_ms{0};
|
||||
// Connection and decoded meter state consumed by ESPHome sensors and the local display.
|
||||
bool connected{false}; // BLE link to the OWON meter is currently established.
|
||||
bool write_available{false}; // The writable BLE characteristic is ready for button commands.
|
||||
bool is_plus{false}; // True when the last notification used the compact B35T+ frame format.
|
||||
bool low_battery{false}; // Battery indicator decoded from the meter status bits.
|
||||
bool overload{false}; // True when the meter reports OL instead of numeric digits.
|
||||
bool has_reading{false}; // At least one valid BLE notification has been decoded.
|
||||
uint8_t selected_button{1}; // UI-selected OWON remote button command (1..6).
|
||||
uint32_t last_notify_ms{0}; // Timestamp of the last accepted meter notification.
|
||||
|
||||
// Decode a BLE notification from either supported OWON protocol variant and refresh cached values.
|
||||
bool handle_notify(const std::vector<uint8_t> &data) {
|
||||
if (data.size() > sizeof(this->raw_))
|
||||
return false;
|
||||
|
||||
// B35T+ sends a compact 6-byte binary frame; convert it into the classic 14-byte layout.
|
||||
if (data.size() == 6 && data[1] >= 0xF0) {
|
||||
memset(this->raw_, 0, sizeof(this->raw_));
|
||||
memcpy(this->raw_, data.data(), data.size());
|
||||
this->is_plus = true;
|
||||
this->parse_plus_();
|
||||
} else if (data.size() == 14 && data[12] == 0x0D && data[13] == 0x0A) {
|
||||
// Classic B35T sends an ASCII-like 14-byte frame terminated by CR/LF; it can be used directly.
|
||||
memset(this->value_, 0, sizeof(this->value_));
|
||||
memcpy(this->value_, data.data(), data.size());
|
||||
this->is_plus = false;
|
||||
@@ -294,26 +313,32 @@ class Meter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache derived states so display drawing and Home Assistant sensors do not have to re-parse frames.
|
||||
this->overload = memcmp(this->value_, OVERLOAD_FRAME, sizeof(OVERLOAD_FRAME)) == 0;
|
||||
this->display_value = this->calc_display_value_();
|
||||
this->base_value = this->calc_base_value_();
|
||||
this->display_value = this->calc_display_value_(); // Numeric value exactly as shown on the meter.
|
||||
this->base_value = this->calc_base_value_(); // Same reading converted to base SI scale.
|
||||
this->has_reading = true;
|
||||
this->last_notify_ms = millis();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called by the ESPHome BLE client once the meter connection and write characteristic are usable.
|
||||
void on_connect() {
|
||||
this->connected = true;
|
||||
this->write_available = true;
|
||||
}
|
||||
|
||||
// Reset connection flags when BLE disconnects; the last reading is kept for display/sensor continuity.
|
||||
void on_disconnect() {
|
||||
this->connected = false;
|
||||
this->write_available = false;
|
||||
}
|
||||
|
||||
// Numeric accessors used by ESPHome lambdas when publishing sensor state to Home Assistant.
|
||||
float value() const { return this->display_value; }
|
||||
float value_base() const { return this->base_value; }
|
||||
|
||||
// Boolean flag helpers keep the bit-mask details out of UI and sensor publishing code.
|
||||
bool negative() const { return (this->value_[REGPLUSMINUS] & FLAGMINUS) == FLAGMINUS; }
|
||||
bool auto_range() const { return (this->value_[REGMODE] & FLAGMODEAUTO) == FLAGMODEAUTO; }
|
||||
bool hold() const { return (this->value_[REGMODE] & FLAGMODEHOLD) == FLAGMODEHOLD; }
|
||||
@@ -325,6 +350,7 @@ class Meter {
|
||||
bool diode() const { return (this->value_[REGSCALE] & FLAGSCALEDIODE) == FLAGSCALEDIODE; }
|
||||
bool continuity() const { return (this->value_[REGSCALE] & FLAGSCALEBUZZ) == FLAGSCALEBUZZ; }
|
||||
|
||||
// Translate decoded unit bits into the text suffix shown on the display and exposed via sensors.
|
||||
const char *unit() const {
|
||||
switch (this->value_[REGUNIT]) {
|
||||
case FLAGUNITFAHR: return "°F";
|
||||
@@ -339,6 +365,7 @@ class Meter {
|
||||
}
|
||||
}
|
||||
|
||||
// Translate decoded scale bits into SI prefixes or duty-cycle percent.
|
||||
const char *scale() const {
|
||||
if ((this->value_[REGSCALE] & FLAGSCALEDUTY) == FLAGSCALEDUTY) return "%";
|
||||
if ((this->value_[REGSCALE] & FLAGSCALEMEGA) == FLAGSCALEMEGA) return "M";
|
||||
@@ -348,6 +375,7 @@ class Meter {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Build a compact human-readable list of active meter modes for Home Assistant text sensors.
|
||||
std::string mode_text() const {
|
||||
std::string out;
|
||||
if (this->dc()) out += "DC ";
|
||||
@@ -363,6 +391,7 @@ class Meter {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Format the current reading as a single string, including disconnected/waiting/overload states.
|
||||
std::string reading_text() const {
|
||||
if (!this->connected) return "Disconnected";
|
||||
if (!this->has_reading) return "Waiting for data";
|
||||
@@ -372,6 +401,7 @@ class Meter {
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Classify the reading so ESPHome can publish it to the correct measurement-specific sensor.
|
||||
enum Kind { KIND_OTHER, KIND_VOLTAGE, KIND_CURRENT, KIND_RESISTANCE, KIND_FREQUENCY, KIND_CAPACITANCE, KIND_TEMP_C, KIND_TEMP_F, KIND_DUTY };
|
||||
Kind kind() const {
|
||||
if ((this->value_[REGSCALE] & FLAGSCALEDUTY) == FLAGSCALEDUTY) return KIND_DUTY;
|
||||
@@ -387,6 +417,7 @@ class Meter {
|
||||
}
|
||||
}
|
||||
|
||||
// UI label for the currently selected remote-control command sent back to the OWON meter.
|
||||
const char *selected_button_name() const {
|
||||
static const char *const names[] = {"SELECT", "RANGE", "HLD/LIG", "REL/BT", "HZ/DUTY", "MAX/MIN"};
|
||||
uint8_t index = this->selected_button;
|
||||
@@ -395,6 +426,7 @@ class Meter {
|
||||
return names[index - 1];
|
||||
}
|
||||
|
||||
// Move the selected remote button left/right; bounds match the six commands in selected_button_name().
|
||||
void previous_button() {
|
||||
if (this->selected_button > 1) this->selected_button--;
|
||||
}
|
||||
@@ -402,6 +434,7 @@ class Meter {
|
||||
if (this->selected_button < 6) this->selected_button++;
|
||||
}
|
||||
|
||||
// Draw either the OWON multimeter page or the Atorch DL24 load page on the M5Stack display.
|
||||
void render(esphome::display::Display &it, esphome::display::BaseFont *font,
|
||||
esphome::display::BaseFont *value_font, int display_page = 0,
|
||||
bool atorch_connected = false,
|
||||
@@ -422,7 +455,9 @@ class Meter {
|
||||
if (display_page == 0) {
|
||||
// --- PAGE 1: OWON Multimeter ---
|
||||
it.fill(bg);
|
||||
bool status_active = this->connected && this->has_reading;
|
||||
bool status_active = this->connected && this->has_reading; // Only highlight decoded flags after data arrives.
|
||||
|
||||
// Top status row mirrors the physical meter annunciators: battery, BLE, range, min/max, hold, rel, diode, buzzer.
|
||||
this->draw_icon_(it, 12, 8, 16, 16, ACCU_BMP, status_active ? (this->low_battery ? red : green) : inactive);
|
||||
this->draw_icon_(it, 46, 8, 16, 16, BLE_BMP, this->connected ? blue : inactive);
|
||||
this->label_(it, font, 86, 8, "AUTO", status_active && this->auto_range() ? fg : inactive);
|
||||
@@ -436,6 +471,7 @@ class Meter {
|
||||
this->label_(it, font, 8, 104, "DC", status_active && this->dc() ? cyan : inactive);
|
||||
this->label_(it, font, 8, 124, "AC", status_active && this->ac() ? magenta : inactive);
|
||||
|
||||
// Main seven-segment area distinguishes connection, waiting-for-first-frame, overload, and normal readings.
|
||||
if (!this->connected) {
|
||||
this->draw_digits_(it, "----", false, inactive);
|
||||
it.print(160, 148, font, inactive, esphome::display::TextAlign::CENTER, "scan/connect");
|
||||
@@ -462,6 +498,8 @@ class Meter {
|
||||
|
||||
bool bargraph_active = status_active && !this->overload;
|
||||
this->draw_bargraph_(it, bargraph_active ? this->digits_from_buffer_() : 0, bargraph_active);
|
||||
|
||||
// Bottom soft-button row shows the currently selected OWON command and whether BLE writes are available.
|
||||
it.filled_rectangle(34, 212, 40, 24, this->write_available ? fg : inactive);
|
||||
it.filled_rectangle(108, 212, 100, 24, this->write_available ? fg : inactive);
|
||||
it.filled_rectangle(242, 212, 40, 24, this->write_available ? fg : inactive);
|
||||
@@ -488,6 +526,7 @@ class Meter {
|
||||
const Color temp_color = atorch_connected ? magenta : dim_accent;
|
||||
const Color header_color = atorch_connected ? cyan : dim_value;
|
||||
|
||||
// Prepare formatted metric strings before drawing so missing/NaN Atorch values display consistently.
|
||||
char voltage_text[24];
|
||||
char current_text[24];
|
||||
char power_text[24];
|
||||
@@ -508,6 +547,7 @@ class Meter {
|
||||
}
|
||||
it.print(310, 7, font, inactive, esphome::display::TextAlign::TOP_RIGHT, "DC LOAD");
|
||||
|
||||
// Four primary Atorch measurements are shown as dashboard cards; accumulated counters fit in the footer.
|
||||
this->draw_metric_card_(it, font, value_font, 10, 42, 145, 76, "VOLTAGE", voltage_text, voltage_color, panel, border, value_color);
|
||||
this->draw_metric_card_(it, font, value_font, 165, 42, 145, 76, "CURRENT", current_text, current_color, panel, border, value_color);
|
||||
this->draw_metric_card_(it, font, value_font, 10, 128, 145, 76, "POWER", power_text, power_color, panel, border, value_color);
|
||||
@@ -526,12 +566,13 @@ class Meter {
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t raw_[14]{};
|
||||
uint8_t value_[14]{};
|
||||
float display_value{NAN};
|
||||
float base_value{NAN};
|
||||
static constexpr uint8_t OVERLOAD_FRAME[5] = {0x2B, 0x3F, 0x30, 0x3A, 0x3F};
|
||||
uint8_t raw_[14]{}; // Last raw BLE payload, padded to the maximum supported OWON frame length.
|
||||
uint8_t value_[14]{}; // Normalized classic-frame view used by all accessors and display rendering.
|
||||
float display_value{NAN}; // Meter reading with its displayed prefix still applied, e.g. 12.3 kΩ -> 12.3.
|
||||
float base_value{NAN}; // Meter reading converted to base units, e.g. 12.3 kΩ -> 12300 Ω.
|
||||
static constexpr uint8_t OVERLOAD_FRAME[5] = {0x2B, 0x3F, 0x30, 0x3A, 0x3F}; // "+?0:?" OL marker.
|
||||
|
||||
// Reconstruct the four visible seven-segment digits as an integer for value calculations/bargraph scaling.
|
||||
uint16_t digits_from_buffer_() const {
|
||||
uint16_t out = 0;
|
||||
if (this->value_[REGDIG1] >= '0' && this->value_[REGDIG1] <= '9') out += (this->value_[REGDIG1] - '0') * 1000;
|
||||
@@ -541,10 +582,12 @@ class Meter {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convert the normalized frame into the floating-point number shown by the meter display.
|
||||
float calc_display_value_() const {
|
||||
if (this->overload) return NAN;
|
||||
|
||||
if (this->is_plus) {
|
||||
// B35T+ encodes decimal placement and sign/magnitude as packed little-endian words.
|
||||
uint16_t pair1 = static_cast<uint16_t>(this->raw_[0]) | (static_cast<uint16_t>(this->raw_[1]) << 8);
|
||||
uint8_t decimal = pair1 & 0x07;
|
||||
if (decimal >= 7) return NAN;
|
||||
@@ -556,6 +599,7 @@ class Meter {
|
||||
return negative ? -v : v;
|
||||
}
|
||||
|
||||
// Classic frames carry decimal placement as one of three point flags in the normalized buffer.
|
||||
uint8_t decimal = 0;
|
||||
switch (this->value_[REGPOINT] & 0x07) {
|
||||
case 0b001: decimal = 1; break;
|
||||
@@ -567,6 +611,7 @@ class Meter {
|
||||
return this->negative() ? -v : v;
|
||||
}
|
||||
|
||||
// Apply the decoded SI prefix so Home Assistant receives comparable base-unit sensor values.
|
||||
float calc_base_value_() const {
|
||||
if (std::isnan(this->display_value)) return NAN;
|
||||
if (this->value_[REGUNIT] == FLAGUNITNF) return this->display_value * 1e-9f;
|
||||
@@ -577,17 +622,20 @@ class Meter {
|
||||
return this->display_value;
|
||||
}
|
||||
|
||||
// Decode the compact B35T+ payload and synthesize the 14-byte classic representation.
|
||||
void parse_plus_() {
|
||||
memset(this->value_, 0, sizeof(this->value_));
|
||||
this->value_[5] = 0x20;
|
||||
this->value_[12] = 0x0D;
|
||||
this->value_[13] = 0x0A;
|
||||
|
||||
// First word: measurement function, SI scale, and decimal-point position.
|
||||
uint16_t pair1 = static_cast<uint16_t>(this->raw_[0]) | (static_cast<uint16_t>(this->raw_[1]) << 8);
|
||||
uint8_t function = (pair1 >> 6) & 0x0F;
|
||||
uint8_t scale = (pair1 >> 3) & 0x07;
|
||||
uint8_t decimal = pair1 & 0x07;
|
||||
|
||||
// Translate B35T+ decimal count to the classic point-flag convention used by draw_decimal_points_().
|
||||
switch (decimal) {
|
||||
case 0: this->value_[REGPOINT] = FLAGPOINT0; break;
|
||||
case 1: this->value_[REGPOINT] = FLAGPOINT3; break;
|
||||
@@ -596,6 +644,7 @@ class Meter {
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Translate the function selector into classic unit and AC/DC/mode flags.
|
||||
switch (function) {
|
||||
case 0: this->value_[REGUNIT] |= FLAGUNITVOLT; this->value_[REGMODE] |= FLAGMODEDC; break;
|
||||
case 1: this->value_[REGUNIT] |= FLAGUNITVOLT; this->value_[REGMODE] |= FLAGMODEAC; break;
|
||||
@@ -613,6 +662,7 @@ class Meter {
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Translate the B35T+ scale selector into SI-prefix flags.
|
||||
switch (scale) {
|
||||
case 2: this->value_[REGSCALE] |= FLAGSCALEMICRO; break;
|
||||
case 3: this->value_[REGSCALE] |= FLAGSCALEMILLI; break;
|
||||
@@ -621,6 +671,7 @@ class Meter {
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Second word: status flags such as hold, relative, auto-range, battery, and min/max.
|
||||
uint16_t pair2 = static_cast<uint16_t>(this->raw_[2]) | (static_cast<uint16_t>(this->raw_[3]) << 8);
|
||||
if (pair2 & (1 << 0)) this->value_[REGMODE] |= FLAGMODEHOLD;
|
||||
if (pair2 & (1 << 1)) this->value_[REGMODE] |= FLAGMODEREL;
|
||||
@@ -629,6 +680,7 @@ class Meter {
|
||||
if (pair2 & (1 << 4)) this->value_[REGMINMAX] |= FLAGMIN;
|
||||
if (pair2 & (1 << 5)) this->value_[REGMINMAX] |= FLAGMAX;
|
||||
|
||||
// Third word: signed 4-digit display value, with bit 15 indicating a negative reading.
|
||||
uint16_t pair3 = static_cast<uint16_t>(this->raw_[4]) | (static_cast<uint16_t>(this->raw_[5]) << 8);
|
||||
if (decimal < 7) {
|
||||
uint16_t digits = pair3;
|
||||
@@ -643,15 +695,18 @@ class Meter {
|
||||
this->value_[REGDIG3] = '0' + ((digits / 10) % 10);
|
||||
this->value_[REGDIG4] = '0' + (digits % 10);
|
||||
} else {
|
||||
// Decimal values 7 and above are used by this protocol to indicate overload/OL.
|
||||
memcpy(this->value_, OVERLOAD_FRAME, sizeof(OVERLOAD_FRAME));
|
||||
}
|
||||
}
|
||||
|
||||
// Return a printable digit for a display register, using a blank for non-digit status bytes.
|
||||
char digit_char_(uint8_t reg) const {
|
||||
uint8_t c = this->value_[reg];
|
||||
return (c >= '0' && c <= '9') ? static_cast<char>(c) : ' ';
|
||||
}
|
||||
|
||||
// Format Atorch metrics, using "--" for missing values while keeping units visible.
|
||||
void format_metric_(char *buffer, size_t size, float value, const char *unit, uint8_t decimals) const {
|
||||
if (!std::isfinite(value)) {
|
||||
snprintf(buffer, size, "-- %s", unit);
|
||||
@@ -662,6 +717,7 @@ class Meter {
|
||||
snprintf(buffer, size, format, value, unit);
|
||||
}
|
||||
|
||||
// Draw one Atorch dashboard tile with a colored accent, label, and centered numeric value.
|
||||
void draw_metric_card_(Display &it, esphome::display::BaseFont *label_font, esphome::display::BaseFont *value_font,
|
||||
int x, int y, int w, int h, const char *title, const char *value,
|
||||
Color accent, Color fill, Color border, Color value_color) {
|
||||
@@ -678,10 +734,12 @@ class Meter {
|
||||
it.filled_rectangle(x + 10, y + h - 11, w - 20, 1, accent);
|
||||
}
|
||||
|
||||
// Small wrapper for consistent top-left aligned labels.
|
||||
void label_(Display &it, esphome::display::BaseFont *font, int x, int y, const char *text, Color color) {
|
||||
it.print(x, y, font, color, esphome::display::TextAlign::TOP_LEFT, text);
|
||||
}
|
||||
|
||||
// Draw the large four-digit OWON readout, including a separate minus sign when needed.
|
||||
void draw_digits_(Display &it, const char *text, bool negative, Color color) {
|
||||
if (negative) this->draw_segment_(it, 8, 83, 26, 9, true, color);
|
||||
constexpr int digit_x = 40;
|
||||
@@ -694,6 +752,7 @@ class Meter {
|
||||
}
|
||||
}
|
||||
|
||||
// Draw decimal points between the large seven-segment digits according to the normalized frame flags.
|
||||
void draw_decimal_points_(Display &it, Color color) {
|
||||
uint8_t p = this->value_[REGPOINT];
|
||||
if ((p & FLAGPOINT1) == FLAGPOINT1) it.filled_rectangle(95, 135, 8, 10, color);
|
||||
@@ -701,6 +760,7 @@ class Meter {
|
||||
if ((p & FLAGPOINT3) == FLAGPOINT3) it.filled_rectangle(223, 135, 8, 10, color);
|
||||
}
|
||||
|
||||
// Draw one beveled horizontal or vertical segment used by the custom seven-segment font.
|
||||
void draw_segment_(Display &it, int x, int y, int w, int h, bool horizontal, Color color) {
|
||||
if (horizontal) {
|
||||
int cap = h / 2;
|
||||
@@ -715,6 +775,7 @@ class Meter {
|
||||
}
|
||||
}
|
||||
|
||||
// Plot a packed 1-bit bitmap icon, where each set bit becomes one colored display pixel.
|
||||
void draw_icon_(Display &it, int x, int y, int w, int h, const uint8_t *data, Color color) {
|
||||
for (int row = 0; row < h; row++) {
|
||||
for (int col = 0; col < w; col++) {
|
||||
@@ -726,6 +787,7 @@ class Meter {
|
||||
}
|
||||
}
|
||||
|
||||
// Map a digit/OL/minus character to seven segment states and draw the active segments.
|
||||
void draw_seven_segment_(Display &it, int x, int y, int w, int h, char ch, Color color) {
|
||||
bool a=false,b=false,c=false,d=false,e=false,f=false,g=false;
|
||||
switch (ch) {
|
||||
@@ -766,6 +828,7 @@ class Meter {
|
||||
if (g) this->draw_segment_(it, x + t / 2, mid_y, w - t, t, true, color);
|
||||
}
|
||||
|
||||
// Draw the OWON-style bottom bargraph by scaling raw 0..6000 display digits to a 240 px ruler.
|
||||
void draw_bargraph_(Display &it, uint16_t digits, bool active) {
|
||||
const Color fg(255, 255, 255);
|
||||
const Color inactive(80, 80, 80);
|
||||
|
||||
Reference in New Issue
Block a user