Skip to content

Commit

Permalink
Add GraphicalMeter example
Browse files Browse the repository at this point in the history
  • Loading branch information
GOB52 committed Dec 12, 2024
1 parent 36d71f9 commit 51d04ed
Show file tree
Hide file tree
Showing 7 changed files with 576 additions and 0 deletions.
13 changes: 13 additions & 0 deletions examples/UnitUnified/GraphicalMeter/GraphicalMeter.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Graphical meter example for Unit-AnyMeter
The core must be equipped with LCD
*/
#include "main/GraphicalMeter.cpp"



193 changes: 193 additions & 0 deletions examples/UnitUnified/GraphicalMeter/main/GraphicalMeter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Graphical meter example for Unit-Meter
The core must be equipped with LCD
Ameter,Vmeter,or KmeterISO unit must be connected
*/
#include <M5Unified.h>
#include <M5GFX.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedMETER.h>
#include "meter.hpp"
#include <cassert>

// ********************************************************************************
// *** Select the unit (override this source or specify in the compile options) ***
// ********************************************************************************
#if !defined(USING_VMETER) && !defined(USING_AMETER) && !defined(USING_KMETER_ISO)
#define USING_VMETER
// #define USING_AMETER
// #define USING_KMETER_ISO
#endif

namespace {
auto& lcd = M5.Display;
LGFX_Sprite strips[2];
constexpr uint32_t SPLIT_NUM{4};
int32_t strip_height{};

m5::unit::UnitUnified Units;
#if defined(USING_VMETER)
#pragma message "Using Vmeter"
m5::unit::UnitVmeter unit{};
constexpr char tag[] = "Voltage";
constexpr char val_unit[] = "V";
constexpr m5gfx::rgb565_t theme_clr{45, 136, 218};
#elif defined(USING_AMETER)
#pragma message "Using Ameter"
m5::unit::UnitAmeter unit{};
constexpr char tag[] = "Current";
constexpr char val_unit[] = "A";
constexpr m5gfx::rgb565_t theme_clr{254, 79, 147};
#elif defined(USING_KMETER_ISO)
m5::unit::UnitKmeterISO unit{};
#pragma message "Using KmeterISO"
constexpr char tag[] = "Temp";
constexpr char val_unit[] = "C";
constexpr m5gfx::rgb565_t theme_clr{241, 188, 105};
#else
#error "Choose unit"
#endif

volatile SemaphoreHandle_t _updateLock{};

void update_meter(void*)
{
for (;;) {
Units.update();
if (xSemaphoreTake(_updateLock, 1)) {
while (unit.available()) {
#if defined(USING_VMETER)
store_value(unit.voltage());
#elif defined(USING_AMETER)
store_value(unit.current());
#elif defined(USING_KMETER_ISO)
store_value(unit.temperature());
#else
#error "Choose unit"
#endif
unit.discard();
}
xSemaphoreGive(_updateLock);
}
}
}

} // namespace

void setup()
{
M5.begin();

// No LCD or display device?
if (lcd.width() == 0 || lcd.isEPD()) {
M5_LOGE("The core must be equipped with LCD");
M5.Speaker.tone(1000, 20);
while (true) {
m5::utility::delay(10000);
}
}

// The screen shall be in landscape mode
if (lcd.height() > lcd.width()) {
lcd.setRotation(1);
}

// Make strips
strip_height = (lcd.height() + SPLIT_NUM - 1) / SPLIT_NUM;
uint32_t cnt{};
for (auto&& spr : strips) {
spr.setPsram(false);
spr.setColorDepth(lcd.getColorDepth());
cnt += spr.createSprite(lcd.width(), strip_height) ? 1 : 0;
spr.setAddrWindow(0, 0, lcd.width(), strip_height);
}
assert(cnt == 2 && "Failed to create sprite");

// UnitUnified
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);

auto cfg = unit.config();
#if defined(USING_VMETER) || defined(USING_AMETER)
cfg.rate = m5::unit::ads111x::Sampling::Rate250;
#elif defined(USING_KMETER_ISO)
cfg.interval = 1000 / 250;
#else
#error "Choose unit"
#endif
unit.config(cfg);
auto ccfg = unit.component_config();
ccfg.stored_size = 250;
unit.component_config(ccfg);

Wire.begin(pin_num_sda, pin_num_scl, 400000U);
if (!Units.add(unit, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}

M5_LOGI("M5UnitUnified has been begun");
M5_LOGI("%s", Units.debugInfo().c_str());

initialize_meter(lcd.width(), lcd.height(), lcd.width(), theme_clr);

lcd.setAddrWindow(0, 0, lcd.width(), lcd.height());
lcd.startWrite();

_updateLock = xSemaphoreCreateBinary();
xSemaphoreGive(_updateLock);
xTaskCreateUniversal(update_meter, "meter", 8192, nullptr, 2, nullptr, APP_CPU_NUM);
}

void loop()
{
#if 0
static uint32_t fpsCnt{}, fps{};
static unsigned long start_at{m5::utility::millis()};

auto now = m5::utility::millis();
if (now >= start_at + 1000) {
fps = fpsCnt;
M5_LOGW("loop FPS:%u", fps);
fpsCnt = 0;
start_at = now;
}
#endif

M5.update();

// Draw strips with DMA transfer
while (lcd.dmaBusy()) {
m5::utility::delay(1);
}

do {
if (xSemaphoreTake(_updateLock, 0)) {
//++fpsCnt;

static uint32_t current{};
int32_t offset{};
uint32_t cnt{SPLIT_NUM};
while (cnt--) {
auto& spr = strips[current];
spr.clear();
draw_meter(spr, offset, tag, val_unit);
spr.pushSprite(&lcd, 0, -offset);

current ^= 1;
offset -= strip_height;
}
xSemaphoreGive(_updateLock);
}
} while (!lcd.dmaBusy());
}
83 changes: 83 additions & 0 deletions examples/UnitUnified/GraphicalMeter/main/meter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "meter.hpp"
#include "ui_plotter.hpp"
#include <M5Utility.hpp>
#include <M5Unified.h>

class EMA {
public:
explicit EMA(float factor = 0.92f) : _alpha(factor)
{
}

inline void clear()
{
_ema_value = std::numeric_limits<float>::quiet_NaN();
}

inline float update(float new_value)
{
if (!std::isnan(_ema_value)) {
_ema_value = _alpha * new_value + (1.0f - _alpha) * _ema_value;
} else {
_ema_value = new_value;
}
return _ema_value;
}

private:
float _alpha{0.92f}, _ema_value{std::numeric_limits<float>::quiet_NaN()};
};

namespace {
m5::ui::Plotter* plotter{};
EMA ema{};
m5gfx::rgb565_t theme_color{};
} // namespace

void initialize_meter(const uint32_t swid, const uint32_t shgt, const uint32_t elements,
const m5gfx::rgb565_t theme_clr)
{
delete plotter;

auto hgt = shgt - 32;
plotter = new m5::ui::Plotter(nullptr, elements, swid, hgt, 100);
plotter->setGaugeTextDatum(textdatum_t::top_right);
plotter->setLineColor(theme_clr);
ema.clear();
theme_color = theme_clr;
}

void store_value(const float val)
{
plotter->push_back(ema.update(val));
// plotter->push_back(val);
}

void draw_meter(LGFX_Sprite& spr, const int32_t offset, const char* tag, const char* unit)
{
plotter->push(&spr, 0, offset);

int32_t x{}, y{plotter->height() + offset};
int32_t wid{spr.width()}, hgt{32};
auto f = spr.getFont();
auto td = spr.getTextDatum();

spr.fillRect(x, y, wid, 4, theme_color);

spr.setFont(wid >= 240 ? &fonts::FreeSansBold12pt7b : &fonts::FreeSansBold9pt7b);

spr.setTextDatum(textdatum_t::middle_left);
spr.drawString(tag, x, y + 32 / 2);

auto s = m5::utility::formatString("%.2f", 0.01f * plotter->latest());
spr.setTextDatum(textdatum_t::middle_right);
spr.drawString(s.c_str(), x + wid, y + 32 / 2);

spr.setTextDatum(td);
spr.setFont(f);
}
16 changes: 16 additions & 0 deletions examples/UnitUnified/GraphicalMeter/main/meter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#ifndef M5_UNIT_METER_EXAMPLE_GRAPHICAL_METER_METER_HPP
#define M5_UNIT_METER_EXAMPLE_GRAPHICAL_METER_METER_HPP

#include <M5GFX.h>

void initialize_meter(const uint32_t swid, const uint32_t shgt, const uint32_t elements,
const m5gfx::rgb565_t theme_clr);
void store_value(const float val);
void draw_meter(LGFX_Sprite& spr, const int32_t offset, const char* tag = "", const char* unit = "");

#endif
Loading

0 comments on commit 51d04ed

Please sign in to comment.