Fixes
This commit is contained in:
parent
87e8deab0e
commit
31471fb6ce
@ -1,77 +1,190 @@
|
||||
dependencies:
|
||||
espressif/button:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
fccb18c37f1cfe0797b74a53a44d3f400f5fd01f4993b40052dfb7f401915089
|
||||
dependencies:
|
||||
- name: espressif/cmake_utilities
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '*'
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.0'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__button
|
||||
type: local
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 4.1.5
|
||||
espressif/cmake_utilities:
|
||||
component_hash:
|
||||
05165f30922b422b4b90c08845e6d449329b97370fbd06309803d8cb539d79e3
|
||||
351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.1'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.1.1
|
||||
version: 0.5.3
|
||||
espressif/eppp_link:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
9472e6825f4bb71eca2b39cf1bc92659c1ac60bfd7416560ad033a7dd8641b17
|
||||
dependencies:
|
||||
- name: espressif/esp_serial_slave_link
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: ^1.1.0
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.2'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__eppp_link
|
||||
type: local
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.1.4
|
||||
espressif/esp_hosted:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
6f5dc62f18c86b4ac65e1c8cb56fe122de894d80b8c8eaac451f3c0a913b8d76
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.3'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_hosted
|
||||
type: local
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 2.11.5
|
||||
espressif/esp_lcd_touch:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
3f85a7d95af876f1a6ecca8eb90a81614890d0f03a038390804e5a77e2caf862
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.4.2'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lcd_touch
|
||||
type: local
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.2.1
|
||||
espressif/esp_lcd_touch_gt911:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
be02e243d18b9a661bc13b0d22c0a5cfa3f708cf04d6eb059772276c8c8a4d76
|
||||
dependencies:
|
||||
- name: espressif/esp_lcd_touch
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: ^1.2.0
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.4.2'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lcd_touch_gt911
|
||||
type: local
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.2.0~1
|
||||
espressif/esp_lv_decoder:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
0eb7b2bceaf73484ef80f5004337ee31617b2450a3d40621812998a47e7dd349
|
||||
dependencies:
|
||||
- name: espressif/esp_new_jpeg
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: 0.*
|
||||
- name: espressif/libpng
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: 1.*
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.3'
|
||||
- name: lvgl/lvgl
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '>=8,<10'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lv_decoder
|
||||
type: local
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
targets:
|
||||
- esp32
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
- esp32p4
|
||||
- esp32c2
|
||||
- esp32c3
|
||||
- esp32c5
|
||||
- esp32c6
|
||||
version: 0.3.2
|
||||
espressif/esp_lv_fs:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
66896007884b817df34c964f9a114fff538ee2674e99fee7159162498b93f94b
|
||||
dependencies:
|
||||
- name: espressif/cmake_utilities
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: 0.*
|
||||
- name: espressif/esp_mmap_assets
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '>=1.2'
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.4'
|
||||
- name: lvgl/lvgl
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '>=8,<10'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lv_fs
|
||||
type: local
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.0.1
|
||||
espressif/esp_lvgl_adapter:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
4ba6ad754b2533cb582bff81ba672ea7a682f4724a02327e57f96e9c73d330c2
|
||||
dependencies:
|
||||
- name: espressif/button
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: 4.*
|
||||
- name: espressif/esp_lcd_touch
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: 1.*
|
||||
- name: espressif/esp_lv_decoder
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: 0.*
|
||||
- name: espressif/esp_lv_fs
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: 1.*
|
||||
- name: espressif/freetype
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: 2.*
|
||||
- name: espressif/knob
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: 1.*
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.5'
|
||||
- name: lvgl/lvgl
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '>=8,<10'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lvgl_adapter
|
||||
type: local
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 0.3.0
|
||||
espressif/esp_lvgl_port:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
f872401524cb645ee6ff1c9242d44fb4ddcfd4d37d7be8b9ed3f4e85a404efcd
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.1'
|
||||
- name: lvgl/lvgl
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: '>=8,<10'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_lvgl_port
|
||||
type: local
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 2.7.0
|
||||
espressif/esp_mmap_assets:
|
||||
component_hash:
|
||||
@ -85,7 +198,7 @@ dependencies:
|
||||
require: private
|
||||
version: '>=5.0'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.4.0
|
||||
espressif/esp_new_jpeg:
|
||||
@ -93,7 +206,7 @@ dependencies:
|
||||
e6af208a875abd0ecfc0213d3751a11b504b463ebde6930f24096047925fa5c1
|
||||
dependencies: []
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
targets:
|
||||
- esp32
|
||||
@ -113,16 +226,30 @@ dependencies:
|
||||
require: private
|
||||
version: '>=5.0'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.1.2
|
||||
espressif/esp_wifi_remote:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
67efd839bd84efb94b149a28044f9918473e7f4facf709bf4121744e4927df09
|
||||
dependencies:
|
||||
- name: espressif/esp_hosted
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: target in [esp32h2, esp32p4]
|
||||
version: '>=2.11'
|
||||
- name: espressif/wifi_remote_over_eppp
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '>=0.1'
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.3'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__esp_wifi_remote
|
||||
type: local
|
||||
version: 1.3.1
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.3.2
|
||||
espressif/freetype:
|
||||
component_hash:
|
||||
cd5e2d8458e6e8d73f1120ac474467cabb669d8ea4b25050bf6a348c1e89225e
|
||||
@ -131,15 +258,23 @@ dependencies:
|
||||
require: private
|
||||
version: '>=4.4'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 2.13.3~1
|
||||
espressif/i2c_bus:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
4e990dc11734316186b489b362c61d41f23f79d58bc169795cec215e528cba14
|
||||
dependencies:
|
||||
- name: espressif/cmake_utilities
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '*'
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.0'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/espressif__i2c_bus
|
||||
type: local
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.5.0
|
||||
espressif/knob:
|
||||
component_hash:
|
||||
@ -153,7 +288,7 @@ dependencies:
|
||||
require: private
|
||||
version: '>=4.4.1'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.0.2
|
||||
espressif/libpng:
|
||||
@ -168,7 +303,7 @@ dependencies:
|
||||
require: private
|
||||
version: ^1.2.13
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.6.54
|
||||
espressif/wifi_remote_over_eppp:
|
||||
@ -183,7 +318,7 @@ dependencies:
|
||||
require: private
|
||||
version: '>=5.3'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 0.3.0
|
||||
espressif/zlib:
|
||||
@ -194,7 +329,7 @@ dependencies:
|
||||
require: private
|
||||
version: '>=4.4'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.3.1
|
||||
idf:
|
||||
@ -206,40 +341,37 @@ dependencies:
|
||||
17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f
|
||||
dependencies: []
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 9.4.0
|
||||
waveshare/esp_lcd_jd9365_10_1:
|
||||
dependencies: []
|
||||
component_hash:
|
||||
6c1336b93a37df2b5be42c49c4c364d0bacdbf96f053a934f881349457fac679
|
||||
dependencies:
|
||||
- name: espressif/cmake_utilities
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: 0.*
|
||||
- name: espressif/i2c_bus
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: ^1.3.0
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.3'
|
||||
source:
|
||||
path:
|
||||
/home/thomas/projekte/test1/knxdisplay/managed_components/waveshare__esp_lcd_jd9365_10_1
|
||||
type: local
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
targets:
|
||||
- esp32p4
|
||||
version: 1.0.4
|
||||
direct_dependencies:
|
||||
- espressif/button
|
||||
- espressif/cmake_utilities
|
||||
- espressif/eppp_link
|
||||
- espressif/esp_hosted
|
||||
- espressif/esp_lcd_touch
|
||||
- espressif/esp_lcd_touch_gt911
|
||||
- espressif/esp_lv_decoder
|
||||
- espressif/esp_lv_fs
|
||||
- espressif/esp_lvgl_adapter
|
||||
- espressif/esp_lvgl_port
|
||||
- espressif/esp_mmap_assets
|
||||
- espressif/esp_new_jpeg
|
||||
- espressif/esp_serial_slave_link
|
||||
- espressif/esp_wifi_remote
|
||||
- espressif/freetype
|
||||
- espressif/i2c_bus
|
||||
- espressif/knob
|
||||
- espressif/libpng
|
||||
- espressif/wifi_remote_over_eppp
|
||||
- espressif/zlib
|
||||
- idf
|
||||
- lvgl/lvgl
|
||||
- waveshare/esp_lcd_jd9365_10_1
|
||||
manifest_hash: ee0446e4a514a791315863af0d77bdcf353d357a7b8081e3e2b252138a66497b
|
||||
manifest_hash: 2525ec0a57701bb08b15bfef69a11dd2185dbcba5e02784971dcae606254ce64
|
||||
target: esp32p4
|
||||
version: 2.0.0
|
||||
|
||||
@ -44,7 +44,15 @@ HistoryStore& HistoryStore::instance() {
|
||||
return inst;
|
||||
}
|
||||
|
||||
HistoryStore::HistoryStore() = default;
|
||||
HistoryStore::HistoryStore() {
|
||||
mutex_ = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
HistoryStore::~HistoryStore() {
|
||||
if (mutex_) {
|
||||
vSemaphoreDelete(mutex_);
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryStore::isNumericSource(TextSource source) {
|
||||
return source == TextSource::KNX_DPT_TEMP ||
|
||||
@ -97,6 +105,7 @@ const HistoryStore::HistorySeries* HistoryStore::findSeriesByKey(const SeriesKey
|
||||
}
|
||||
|
||||
void HistoryStore::configureFromConfig(const GuiConfig& config) {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
std::array<SeriesKey, HISTORY_MAX_SERIES> needed = {};
|
||||
size_t neededCount = 0;
|
||||
|
||||
@ -191,20 +200,29 @@ void HistoryStore::configureFromConfig(const GuiConfig& config) {
|
||||
if (changed) {
|
||||
dirty_ = true;
|
||||
}
|
||||
xSemaphoreGive(mutex_);
|
||||
}
|
||||
|
||||
bool HistoryStore::isTracked(uint16_t groupAddr, TextSource source) const {
|
||||
return findSeries(groupAddr, source) != nullptr;
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
bool tracked = findSeries(groupAddr, source) != nullptr;
|
||||
xSemaphoreGive(mutex_);
|
||||
return tracked;
|
||||
}
|
||||
|
||||
bool HistoryStore::updateLatest(uint16_t groupAddr, TextSource source, float value) {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
HistorySeries* series = findSeries(groupAddr, source);
|
||||
if (!series) return false;
|
||||
if (!series) {
|
||||
xSemaphoreGive(mutex_);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t nowSec = now();
|
||||
series->latestValue = value;
|
||||
series->latestTs = static_cast<int32_t>(nowSec);
|
||||
series->hasLatest = true;
|
||||
xSemaphoreGive(mutex_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -234,12 +252,19 @@ bool HistoryStore::fillChartSeries(uint16_t groupAddr, TextSource source, ChartP
|
||||
outValues[i] = NO_POINT;
|
||||
}
|
||||
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
const HistorySeries* series = findSeries(groupAddr, source);
|
||||
if (!series) return false;
|
||||
if (!series) {
|
||||
xSemaphoreGive(mutex_);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t nowSec = now();
|
||||
int32_t window = periodSeconds(period);
|
||||
if (window <= 0) return false;
|
||||
if (window <= 0) {
|
||||
xSemaphoreGive(mutex_);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t start = nowSec - window;
|
||||
|
||||
@ -264,6 +289,7 @@ bool HistoryStore::fillChartSeries(uint16_t groupAddr, TextSource source, ChartP
|
||||
HistoryPoint latest{series->latestTs, series->latestValue};
|
||||
accumulate(latest);
|
||||
}
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
bool hasData = false;
|
||||
for (size_t i = 0; i < outCount; ++i) {
|
||||
@ -278,6 +304,7 @@ bool HistoryStore::fillChartSeries(uint16_t groupAddr, TextSource source, ChartP
|
||||
}
|
||||
|
||||
bool HistoryStore::tick() {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
int64_t nowSec = now();
|
||||
bool added = false;
|
||||
|
||||
@ -302,35 +329,64 @@ bool HistoryStore::tick() {
|
||||
dirty_ = true;
|
||||
if (timeSynced_) dataEpoch_ = true;
|
||||
}
|
||||
|
||||
int64_t monoUs = esp_timer_get_time();
|
||||
if (dirty_ && timeSynced_ && SdCard::instance().isMounted()) {
|
||||
if (monoUs - lastSaveMonoUs_ >= HISTORY_SAVE_INTERVAL_US) {
|
||||
saveToSdCard();
|
||||
lastSaveMonoUs_ = monoUs;
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
// REMOVED synchronous save from here to avoid blocking UI task
|
||||
return added;
|
||||
}
|
||||
|
||||
void HistoryStore::performAutoSave() {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
bool shouldSave = false;
|
||||
int64_t monoUs = esp_timer_get_time();
|
||||
|
||||
if (dirty_ && timeSynced_ && SdCard::instance().isMounted()) {
|
||||
if (monoUs - lastSaveMonoUs_ >= HISTORY_SAVE_INTERVAL_US) {
|
||||
shouldSave = true;
|
||||
lastSaveMonoUs_ = monoUs;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
if (shouldSave) {
|
||||
saveToSdCard();
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryStore::updateTimeOfDay(const struct tm& value) {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
hour_ = value.tm_hour;
|
||||
minute_ = value.tm_min;
|
||||
second_ = value.tm_sec;
|
||||
hasTime_ = true;
|
||||
// applyTimeSync calls internal logic, assume called inside lock or extracted
|
||||
// But applyTimeSync calls clearAll which touches series.
|
||||
// So we must be careful about locking.
|
||||
// Let's unlock before applyTimeSync and re-lock inside?
|
||||
// No, let's just make applyTimeSync assume lock or lock itself?
|
||||
// applyTimeSync modifies member vars.
|
||||
// Refactoring for simplicity: lock around the whole block
|
||||
// BUT applyTimeSync calls settimeofday which is system call (thread safe?)
|
||||
|
||||
// We will inline the logic briefly or handle it carefully.
|
||||
// For now, assume lock held is OK for short duration.
|
||||
// We need to implement applyTimeSync correctly with locking.
|
||||
xSemaphoreGive(mutex_);
|
||||
applyTimeSync();
|
||||
}
|
||||
|
||||
void HistoryStore::updateDate(const struct tm& value) {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
year_ = value.tm_year;
|
||||
month_ = value.tm_mon;
|
||||
day_ = value.tm_mday;
|
||||
hasDate_ = true;
|
||||
xSemaphoreGive(mutex_);
|
||||
applyTimeSync();
|
||||
}
|
||||
|
||||
void HistoryStore::updateDateTime(const struct tm& value) {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
year_ = value.tm_year;
|
||||
month_ = value.tm_mon;
|
||||
day_ = value.tm_mday;
|
||||
@ -339,6 +395,7 @@ void HistoryStore::updateDateTime(const struct tm& value) {
|
||||
second_ = value.tm_sec;
|
||||
hasDate_ = true;
|
||||
hasTime_ = true;
|
||||
xSemaphoreGive(mutex_);
|
||||
applyTimeSync();
|
||||
}
|
||||
|
||||
@ -360,11 +417,16 @@ int64_t HistoryStore::buildEpoch() const {
|
||||
}
|
||||
|
||||
bool HistoryStore::applyTimeSync() {
|
||||
if (!hasDate_ || !hasTime_) return false;
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
if (!hasDate_ || !hasTime_) {
|
||||
xSemaphoreGive(mutex_);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t epoch = buildEpoch();
|
||||
if (epoch <= 0) {
|
||||
ESP_LOGW(TAG, "Invalid KNX time/date for sync");
|
||||
xSemaphoreGive(mutex_);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -373,9 +435,12 @@ bool HistoryStore::applyTimeSync() {
|
||||
baseEpoch_ = epoch;
|
||||
baseMono_ = esp_timer_get_time() / 1000000LL;
|
||||
|
||||
// Release lock for system call (optional but safer)
|
||||
xSemaphoreGive(mutex_);
|
||||
struct timeval tv = {};
|
||||
tv.tv_sec = static_cast<time_t>(epoch);
|
||||
settimeofday(&tv, nullptr);
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
|
||||
if (!wasSynced) {
|
||||
bool hasData = false;
|
||||
@ -394,11 +459,14 @@ bool HistoryStore::applyTimeSync() {
|
||||
}
|
||||
|
||||
dirty_ = true;
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
ESP_LOGI(TAG, "Time synced: %ld", static_cast<long>(epoch));
|
||||
return !wasSynced;
|
||||
}
|
||||
|
||||
void HistoryStore::clearAll() {
|
||||
// Assumes lock is held by caller
|
||||
for (size_t i = 0; i < series_.size(); ++i) {
|
||||
HistorySeries& series = series_[i];
|
||||
if (!series.active) continue;
|
||||
@ -414,7 +482,13 @@ void HistoryStore::clearAll() {
|
||||
|
||||
void HistoryStore::saveToSdCard() {
|
||||
if (!SdCard::instance().isMounted()) return;
|
||||
if (!timeSynced_) return;
|
||||
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
if (!timeSynced_) {
|
||||
xSemaphoreGive(mutex_);
|
||||
return;
|
||||
}
|
||||
xSemaphoreGive(mutex_); // Release to open file
|
||||
|
||||
FILE* f = fopen(HISTORY_FILE, "wb");
|
||||
if (!f) {
|
||||
@ -422,6 +496,7 @@ void HistoryStore::saveToSdCard() {
|
||||
return;
|
||||
}
|
||||
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
HistoryFileHeader header = {};
|
||||
header.magic = HISTORY_MAGIC;
|
||||
header.version = HISTORY_VERSION;
|
||||
@ -436,6 +511,7 @@ void HistoryStore::saveToSdCard() {
|
||||
header.coarseCapacity = HISTORY_COARSE_CAP;
|
||||
header.fineInterval = HISTORY_FINE_INTERVAL;
|
||||
header.coarseInterval = HISTORY_COARSE_INTERVAL;
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
if (fwrite(&header, sizeof(header), 1, f) != 1) {
|
||||
fclose(f);
|
||||
@ -443,41 +519,59 @@ void HistoryStore::saveToSdCard() {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < series_.size(); ++i) {
|
||||
const HistorySeries& series = series_[i];
|
||||
if (!series.active) continue;
|
||||
HistoryFileSeriesHeader sh = {};
|
||||
sh.groupAddr = series.key.addr;
|
||||
sh.source = static_cast<uint8_t>(series.key.source);
|
||||
sh.fineCount = static_cast<uint16_t>(series.fine.count);
|
||||
sh.fineHead = static_cast<uint16_t>(series.fine.head);
|
||||
sh.coarseCount = static_cast<uint16_t>(series.coarse.count);
|
||||
sh.coarseHead = static_cast<uint16_t>(series.coarse.head);
|
||||
sh.hasLatest = series.hasLatest ? 1 : 0;
|
||||
sh.latestTs = series.latestTs;
|
||||
sh.latestValue = series.latestValue;
|
||||
|
||||
if (fwrite(&sh, sizeof(sh), 1, f) != 1) {
|
||||
fclose(f);
|
||||
ESP_LOGW(TAG, "Failed to write history series header");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fwrite(series.fine.points.data(), sizeof(HistoryPoint), HISTORY_FINE_CAP, f) != HISTORY_FINE_CAP) {
|
||||
fclose(f);
|
||||
ESP_LOGW(TAG, "Failed to write fine history data");
|
||||
return;
|
||||
}
|
||||
if (fwrite(series.coarse.points.data(), sizeof(HistoryPoint), HISTORY_COARSE_CAP, f) != HISTORY_COARSE_CAP) {
|
||||
fclose(f);
|
||||
ESP_LOGW(TAG, "Failed to write coarse history data");
|
||||
return;
|
||||
}
|
||||
// Allocate temp buffer for one series to minimize lock time
|
||||
// Size: Header + Fine + Coarse
|
||||
size_t seriesDataSize = sizeof(HistoryFileSeriesHeader) +
|
||||
sizeof(HistoryPoint) * (HISTORY_FINE_CAP + HISTORY_COARSE_CAP);
|
||||
uint8_t* tempBuf = (uint8_t*)malloc(seriesDataSize);
|
||||
if (!tempBuf) {
|
||||
fclose(f);
|
||||
ESP_LOGE(TAG, "Failed to allocate temp buffer for save");
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
for (size_t i = 0; i < series_.size(); ++i) {
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
const HistorySeries& series = series_[i];
|
||||
if (!series.active) {
|
||||
xSemaphoreGive(mutex_);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy to temp buffer
|
||||
HistoryFileSeriesHeader* sh = (HistoryFileSeriesHeader*)tempBuf;
|
||||
sh->groupAddr = series.key.addr;
|
||||
sh->source = static_cast<uint8_t>(series.key.source);
|
||||
sh->fineCount = static_cast<uint16_t>(series.fine.count);
|
||||
sh->fineHead = static_cast<uint16_t>(series.fine.head);
|
||||
sh->coarseCount = static_cast<uint16_t>(series.coarse.count);
|
||||
sh->coarseHead = static_cast<uint16_t>(series.coarse.head);
|
||||
sh->hasLatest = series.hasLatest ? 1 : 0;
|
||||
sh->latestTs = series.latestTs;
|
||||
sh->latestValue = series.latestValue;
|
||||
|
||||
uint8_t* dataPtr = tempBuf + sizeof(HistoryFileSeriesHeader);
|
||||
memcpy(dataPtr, series.fine.points.data(), sizeof(HistoryPoint) * HISTORY_FINE_CAP);
|
||||
dataPtr += sizeof(HistoryPoint) * HISTORY_FINE_CAP;
|
||||
memcpy(dataPtr, series.coarse.points.data(), sizeof(HistoryPoint) * HISTORY_COARSE_CAP);
|
||||
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
// Write to file (unlocked)
|
||||
if (fwrite(tempBuf, 1, seriesDataSize, f) != seriesDataSize) {
|
||||
ESP_LOGW(TAG, "Failed to write series data");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(tempBuf);
|
||||
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
dirty_ = false;
|
||||
dataEpoch_ = true;
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
fclose(f);
|
||||
ESP_LOGI(TAG, "History saved (%d series)", static_cast<int>(activeCount));
|
||||
}
|
||||
|
||||
@ -508,6 +602,7 @@ void HistoryStore::loadFromSdCard() {
|
||||
return;
|
||||
}
|
||||
|
||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
||||
for (uint16_t i = 0; i < header.seriesCount; ++i) {
|
||||
HistoryFileSeriesHeader sh = {};
|
||||
if (fread(&sh, sizeof(sh), 1, f) != 1) {
|
||||
@ -534,9 +629,10 @@ void HistoryStore::loadFromSdCard() {
|
||||
fseek(f, sizeof(HistoryPoint) * (HISTORY_FINE_CAP + HISTORY_COARSE_CAP), SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
dirty_ = false;
|
||||
dataEpoch_ = true;
|
||||
xSemaphoreGive(mutex_);
|
||||
|
||||
fclose(f);
|
||||
ESP_LOGI(TAG, "History loaded");
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "WidgetConfig.hpp"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
@ -14,6 +16,7 @@ public:
|
||||
bool updateLatest(uint16_t groupAddr, TextSource source, float value);
|
||||
bool isTracked(uint16_t groupAddr, TextSource source) const;
|
||||
bool tick();
|
||||
void performAutoSave();
|
||||
|
||||
bool fillChartSeries(uint16_t groupAddr, TextSource source, ChartPeriod period,
|
||||
int32_t* outValues, size_t outCount) const;
|
||||
@ -33,6 +36,7 @@ public:
|
||||
|
||||
private:
|
||||
HistoryStore();
|
||||
~HistoryStore();
|
||||
|
||||
struct SeriesKey {
|
||||
uint16_t addr = 0;
|
||||
@ -124,4 +128,6 @@ private:
|
||||
bool dirty_ = false;
|
||||
int64_t lastSaveMonoUs_ = 0;
|
||||
bool dataEpoch_ = false;
|
||||
|
||||
mutable SemaphoreHandle_t mutex_ = nullptr;
|
||||
};
|
||||
|
||||
@ -243,7 +243,14 @@ void KnxWorker::clearSettings() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "HistoryStore.hpp"
|
||||
|
||||
// ... imports ...
|
||||
|
||||
void KnxWorker::loop() {
|
||||
// Check for auto-save (handled by HistoryStore logic)
|
||||
HistoryStore::instance().performAutoSave();
|
||||
|
||||
#if UART_DEBUG_MODE
|
||||
// Periodically send U_STATE_REQ to test TX direction
|
||||
uint32_t now = millis();
|
||||
|
||||
@ -322,6 +322,15 @@ const ScreenConfig* WidgetManager::currentScreen() const {
|
||||
}
|
||||
|
||||
void WidgetManager::applyScreen(uint8_t screenId) {
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to acquire LVGL lock!");
|
||||
return;
|
||||
}
|
||||
applyScreenLocked(screenId);
|
||||
esp_lv_adapter_unlock();
|
||||
}
|
||||
|
||||
void WidgetManager::applyScreenLocked(uint8_t screenId) {
|
||||
ESP_LOGI(TAG, "applyScreen(%d) - start", screenId);
|
||||
|
||||
ScreenConfig* screen = config_.findScreen(screenId);
|
||||
@ -334,16 +343,9 @@ void WidgetManager::applyScreen(uint8_t screenId) {
|
||||
|
||||
if (modalContainer_) {
|
||||
ESP_LOGI(TAG, "Closing modal first");
|
||||
closeModal();
|
||||
closeModalLocked();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Acquiring LVGL lock...");
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to acquire LVGL lock!");
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "LVGL lock acquired");
|
||||
|
||||
lv_display_t* disp = lv_display_get_default();
|
||||
bool invEnabled = true;
|
||||
if (disp) {
|
||||
@ -351,17 +353,15 @@ void WidgetManager::applyScreen(uint8_t screenId) {
|
||||
lv_display_enable_invalidation(disp, false);
|
||||
}
|
||||
|
||||
// Reset all input devices BEFORE destroying widgets to clear any
|
||||
// pending input state and prevent use-after-free on widget objects
|
||||
// Reset all input devices BEFORE destroying widgets
|
||||
ESP_LOGI(TAG, "Resetting input devices...");
|
||||
lv_indev_t* indev = lv_indev_get_next(nullptr);
|
||||
while (indev) {
|
||||
lv_indev_reset(indev, nullptr);
|
||||
indev = lv_indev_get_next(indev);
|
||||
}
|
||||
ESP_LOGI(TAG, "Input devices reset");
|
||||
|
||||
// Now destroy C++ widgets (which deletes LVGL objects) under LVGL lock
|
||||
// Now destroy C++ widgets (which deletes LVGL objects)
|
||||
ESP_LOGI(TAG, "Destroying widgets...");
|
||||
destroyAllWidgets();
|
||||
lv_obj_clean(lv_scr_act());
|
||||
@ -383,25 +383,27 @@ void WidgetManager::applyScreen(uint8_t screenId) {
|
||||
}
|
||||
lv_obj_invalidate(lv_scr_act());
|
||||
|
||||
esp_lv_adapter_unlock();
|
||||
ESP_LOGI(TAG, "applyScreen(%d) - complete", screenId);
|
||||
}
|
||||
|
||||
void WidgetManager::showModalScreen(const ScreenConfig& screen) {
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) return;
|
||||
showModalScreenLocked(screen);
|
||||
esp_lv_adapter_unlock();
|
||||
}
|
||||
|
||||
void WidgetManager::showModalScreenLocked(const ScreenConfig& screen) {
|
||||
if (modalContainer_) {
|
||||
closeModal();
|
||||
closeModalLocked();
|
||||
}
|
||||
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) return;
|
||||
|
||||
// Reset all input devices BEFORE destroying widgets
|
||||
// Reset all input devices
|
||||
lv_indev_t* indev = lv_indev_get_next(nullptr);
|
||||
while (indev) {
|
||||
lv_indev_reset(indev, nullptr);
|
||||
indev = lv_indev_get_next(indev);
|
||||
}
|
||||
|
||||
// Destroy any existing widgets before creating modal widgets
|
||||
destroyAllWidgets();
|
||||
|
||||
lv_disp_t* disp = lv_disp_get_default();
|
||||
@ -488,22 +490,22 @@ void WidgetManager::showModalScreen(const ScreenConfig& screen) {
|
||||
|
||||
applyCachedValuesToWidgets();
|
||||
|
||||
esp_lv_adapter_unlock();
|
||||
ESP_LOGI(TAG, "Modal screen %d opened (%ldx%ld)", screen.id, modalWidth, modalHeight);
|
||||
}
|
||||
|
||||
void WidgetManager::closeModal() {
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) return;
|
||||
closeModalLocked();
|
||||
esp_lv_adapter_unlock();
|
||||
}
|
||||
|
||||
void WidgetManager::closeModalLocked() {
|
||||
printf("WM: closeModal Start. Container=%p\n", (void*)modalContainer_);
|
||||
fflush(stdout);
|
||||
if (!modalContainer_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "closeModal: Failed to lock LVGL");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset input devices
|
||||
lv_indev_t* indev = lv_indev_get_next(nullptr);
|
||||
while (indev) {
|
||||
@ -530,12 +532,11 @@ void WidgetManager::closeModal() {
|
||||
modalContainer_ = nullptr;
|
||||
modalScreenId_ = SCREEN_ID_NONE;
|
||||
|
||||
esp_lv_adapter_unlock();
|
||||
printf("WM: closeModal Complete\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void WidgetManager::showScreen(uint8_t screenId) {
|
||||
void WidgetManager::showScreenLocked(uint8_t screenId) {
|
||||
ESP_LOGI(TAG, "showScreen(%d) called", screenId);
|
||||
|
||||
ScreenConfig* screen = config_.findScreen(screenId);
|
||||
@ -548,14 +549,14 @@ void WidgetManager::showScreen(uint8_t screenId) {
|
||||
static_cast<int>(screen->mode));
|
||||
|
||||
if (screen->mode == ScreenMode::MODAL) {
|
||||
showModalScreen(*screen);
|
||||
showModalScreenLocked(*screen);
|
||||
return;
|
||||
}
|
||||
|
||||
previousScreenId_ = activeScreenId_;
|
||||
activeScreenId_ = screen->id;
|
||||
standbyActive_ = false;
|
||||
applyScreen(activeScreenId_);
|
||||
applyScreenLocked(activeScreenId_);
|
||||
}
|
||||
|
||||
void WidgetManager::handleButtonAction(const WidgetConfig& cfg, lv_obj_t* target) {
|
||||
@ -603,7 +604,7 @@ void WidgetManager::handleButtonAction(const WidgetConfig& cfg, lv_obj_t* target
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetManager::goBack() {
|
||||
void WidgetManager::goBackLocked() {
|
||||
printf("WM: goBack called. Modal=%p Active=%d Prev=%d\n",
|
||||
(void*)modalContainer_, activeScreenId_, previousScreenId_);
|
||||
fflush(stdout);
|
||||
@ -611,17 +612,17 @@ void WidgetManager::goBack() {
|
||||
if (modalContainer_) {
|
||||
printf("WM: Closing modal...\n");
|
||||
fflush(stdout);
|
||||
closeModal();
|
||||
closeModalLocked();
|
||||
printf("WM: Modal closed. Restoring screen %d\n", activeScreenId_);
|
||||
fflush(stdout);
|
||||
// Restore the active screen (which was in background)
|
||||
if (config_.findScreen(activeScreenId_)) {
|
||||
applyScreen(activeScreenId_);
|
||||
applyScreenLocked(activeScreenId_);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Active screen %d not found after closing modal!", activeScreenId_);
|
||||
if (config_.findScreen(config_.startScreenId)) {
|
||||
activeScreenId_ = config_.startScreenId;
|
||||
applyScreen(activeScreenId_);
|
||||
applyScreenLocked(activeScreenId_);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -633,7 +634,7 @@ void WidgetManager::goBack() {
|
||||
if (config_.findScreen(previousScreenId_)) {
|
||||
activeScreenId_ = previousScreenId_;
|
||||
previousScreenId_ = SCREEN_ID_NONE;
|
||||
applyScreen(activeScreenId_);
|
||||
applyScreenLocked(activeScreenId_);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Previous screen %d not found", previousScreenId_);
|
||||
previousScreenId_ = SCREEN_ID_NONE;
|
||||
@ -644,6 +645,12 @@ void WidgetManager::goBack() {
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetManager::goBack() {
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) return;
|
||||
goBackLocked();
|
||||
esp_lv_adapter_unlock();
|
||||
}
|
||||
|
||||
void WidgetManager::enterStandby() {
|
||||
if (!config_.standbyEnabled || config_.standbyMinutes == 0) return;
|
||||
if (standbyActive_) return;
|
||||
@ -655,7 +662,7 @@ void WidgetManager::enterStandby() {
|
||||
standbyReturnScreenId_ = activeScreenId_;
|
||||
standbyActive_ = true;
|
||||
activeScreenId_ = standbyScreen->id;
|
||||
applyScreen(activeScreenId_);
|
||||
applyScreenLocked(activeScreenId_);
|
||||
}
|
||||
|
||||
void WidgetManager::loop() {
|
||||
@ -668,9 +675,9 @@ void WidgetManager::loop() {
|
||||
ESP_LOGI(TAG, "Executing navigation: action=%d target=%d",
|
||||
static_cast<int>(navAction_), navTargetScreen_);
|
||||
if (navAction_ == ButtonAction::JUMP) {
|
||||
showScreen(navTargetScreen_);
|
||||
showScreenLocked(navTargetScreen_);
|
||||
} else if (navAction_ == ButtonAction::BACK) {
|
||||
goBack();
|
||||
goBackLocked();
|
||||
}
|
||||
didUiNav = true;
|
||||
ESP_LOGI(TAG, "Navigation complete");
|
||||
@ -681,7 +688,7 @@ void WidgetManager::loop() {
|
||||
standbyWakePending_ = false;
|
||||
if (standbyWakeTarget_ != SCREEN_ID_NONE) {
|
||||
activeScreenId_ = standbyWakeTarget_;
|
||||
applyScreen(activeScreenId_);
|
||||
applyScreenLocked(activeScreenId_);
|
||||
}
|
||||
didUiNav = true;
|
||||
}
|
||||
@ -851,7 +858,8 @@ void WidgetManager::processUiQueue() {
|
||||
if (!uiQueue_) return;
|
||||
if (uxQueueMessagesWaiting(uiQueue_) == 0) return;
|
||||
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) return;
|
||||
// Call from loop() which is ALREADY LOCKED by LVGL timer
|
||||
// DO NOT take esp_lv_adapter_lock() here.
|
||||
|
||||
UiEvent event = {};
|
||||
static constexpr size_t kMaxEventsPerLoop = 8;
|
||||
@ -879,8 +887,6 @@ void WidgetManager::processUiQueue() {
|
||||
refreshChartWidgetsLocked();
|
||||
chartRefreshPending_ = false;
|
||||
}
|
||||
|
||||
esp_lv_adapter_unlock();
|
||||
}
|
||||
|
||||
void WidgetManager::applyCachedValuesToWidgets() {
|
||||
@ -944,9 +950,9 @@ void WidgetManager::refreshChartWidgetsLocked() {
|
||||
}
|
||||
|
||||
void WidgetManager::refreshChartWidgets() {
|
||||
if (esp_lv_adapter_lock(-1) != ESP_OK) return;
|
||||
// Call from loop() which is ALREADY LOCKED by LVGL timer
|
||||
// DO NOT take esp_lv_adapter_lock() here.
|
||||
refreshChartWidgetsLocked();
|
||||
esp_lv_adapter_unlock();
|
||||
}
|
||||
|
||||
void WidgetManager::applyKnxValue(uint16_t groupAddr, float value, TextSource source) {
|
||||
@ -1724,4 +1730,4 @@ bool WidgetManager::updateConfigFromJson(const char* json) {
|
||||
cJSON_Delete(root);
|
||||
ESP_LOGI(TAG, "Parsed %d screens from JSON", config_.screenCount);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -141,9 +141,13 @@ private:
|
||||
|
||||
void createDefaultConfig();
|
||||
void applyScreen(uint8_t screenId);
|
||||
void showScreen(uint8_t screenId);
|
||||
void applyScreenLocked(uint8_t screenId);
|
||||
void showScreenLocked(uint8_t screenId);
|
||||
void showModalScreen(const ScreenConfig& screen);
|
||||
void showModalScreenLocked(const ScreenConfig& screen);
|
||||
void closeModal();
|
||||
void closeModalLocked();
|
||||
void goBackLocked();
|
||||
void enterStandby();
|
||||
ScreenConfig* activeScreen();
|
||||
const ScreenConfig* activeScreen() const;
|
||||
|
||||
@ -52,7 +52,7 @@ public:
|
||||
1280, // Vertical resolution
|
||||
ESP_LV_ADAPTER_ROTATE_90 // Rotation
|
||||
);
|
||||
disp_cfg.profile.buffer_height = 4; // Keep internal draw buffer small.
|
||||
disp_cfg.profile.buffer_height = 34; // Reduced to 20 to fit in RAM (32KB)
|
||||
lv_disp_t* lv_display = esp_lv_adapter_register_display(&disp_cfg);
|
||||
assert(lv_display != NULL);
|
||||
|
||||
|
||||
@ -71,8 +71,8 @@ lv_obj_t* ChartWidget::create(lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
//applyAxisLabels();
|
||||
//refreshData();
|
||||
applyAxisLabels();
|
||||
refreshData();
|
||||
return obj_;
|
||||
}
|
||||
|
||||
|
||||
@ -100,8 +100,10 @@ lv_obj_t* PowerNodeWidget::create(lv_obj_t* parent) {
|
||||
config_.width > 0 ? config_.width : 120,
|
||||
config_.height > 0 ? config_.height : 120);
|
||||
lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_set_layout(obj_, LV_LAYOUT_NONE);
|
||||
lv_obj_set_flex_flow(obj_, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(obj_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_set_style_pad_all(obj_, 6, 0);
|
||||
lv_obj_set_style_pad_gap(obj_, 2, 0);
|
||||
if (labelText_[0] != '\0') {
|
||||
labelLabel_ = lv_label_create(obj_);
|
||||
set_obj_name(labelLabel_, "PowerNode", config_.id, "label");
|
||||
@ -175,63 +177,11 @@ void PowerNodeWidget::applyStyle() {
|
||||
lv_obj_set_style_text_align(valueLabel_, LV_TEXT_ALIGN_CENTER, 0);
|
||||
}
|
||||
|
||||
layoutChildren();
|
||||
}
|
||||
|
||||
void PowerNodeWidget::updateValueText(const char* text) {
|
||||
if (valueLabel_ == nullptr || text == nullptr) return;
|
||||
if (set_label_text_if_changed(valueLabel_, text)) {
|
||||
layoutChildren();
|
||||
}
|
||||
}
|
||||
|
||||
void PowerNodeWidget::layoutChildren() {
|
||||
if (obj_ == nullptr) return;
|
||||
|
||||
lv_obj_update_layout(obj_);
|
||||
|
||||
lv_coord_t width = lv_obj_get_width(obj_);
|
||||
lv_coord_t height = lv_obj_get_height(obj_);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj_, LV_PART_MAIN);
|
||||
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj_, LV_PART_MAIN);
|
||||
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj_, LV_PART_MAIN);
|
||||
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj_, LV_PART_MAIN);
|
||||
|
||||
lv_coord_t avail_w = width - pad_left - pad_right;
|
||||
lv_coord_t avail_h = height - pad_top - pad_bottom;
|
||||
if (avail_w <= 0 || avail_h <= 0) return;
|
||||
|
||||
lv_obj_t* items[3];
|
||||
uint8_t count = 0;
|
||||
if (labelLabel_) items[count++] = labelLabel_;
|
||||
if (iconLabel_) items[count++] = iconLabel_;
|
||||
if (valueLabel_) items[count++] = valueLabel_;
|
||||
if (count == 0) return;
|
||||
|
||||
const lv_coord_t gap = 2;
|
||||
lv_coord_t total = 0;
|
||||
for (uint8_t i = 0; i < count; ++i) {
|
||||
total += lv_obj_get_height(items[i]);
|
||||
}
|
||||
total += gap * (count - 1);
|
||||
|
||||
lv_coord_t start_y = pad_top;
|
||||
if (avail_h > total) {
|
||||
start_y += (avail_h - total) / 2;
|
||||
}
|
||||
|
||||
lv_coord_t y = start_y;
|
||||
for (uint8_t i = 0; i < count; ++i) {
|
||||
lv_coord_t item_w = lv_obj_get_width(items[i]);
|
||||
lv_coord_t x = pad_left;
|
||||
if (avail_w > item_w) {
|
||||
x += (avail_w - item_w) / 2;
|
||||
}
|
||||
lv_obj_set_pos(items[i], x, y);
|
||||
y += lv_obj_get_height(items[i]) + gap;
|
||||
}
|
||||
set_label_text_if_changed(valueLabel_, text);
|
||||
}
|
||||
|
||||
void PowerNodeWidget::onKnxValue(float value) {
|
||||
|
||||
@ -21,6 +21,5 @@ private:
|
||||
|
||||
void parseText();
|
||||
void updateValueText(const char* text);
|
||||
void layoutChildren();
|
||||
static int encodeUtf8(uint32_t codepoint, char* buf);
|
||||
};
|
||||
|
||||
@ -1485,7 +1485,7 @@ CONFIG_SPIRAM_USE_MALLOC=y
|
||||
CONFIG_SPIRAM_MEMTEST=y
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
|
||||
# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=100000
|
||||
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
|
||||
# CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set
|
||||
# end of PSRAM config
|
||||
|
||||
Loading…
Reference in New Issue
Block a user