next iteration on display, still some artifacting issues, and ux with buttons states and updating
This commit is contained in:
1 parent
45998b6176
commit
ae5d12b3a2
14 files changed
+860
-398
No files matched your search
@@ -305,6 +305,120 @@ const char *ha_condition_short(const char *slug)
|
||||
return (c != NULL) ? c->shrt : (slug != NULL ? slug : "--");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ trends */
|
||||
|
||||
/*
|
||||
* 20 slots, one kept per ~4 minutes, covers ~80 minutes. Feeding faster than
|
||||
* that overwrites the newest slot instead of consuming a new one, so a 60s
|
||||
* poll cadence still fills the ring at 4-minute spacing.
|
||||
*
|
||||
* Size matters here more than it looks: this array is .bss, which lives in
|
||||
* internal RAM on the S3 -- the same internal RAM the RGB panel's bounce
|
||||
* buffers come from, with only a few KB of slack (README, memory notes). A
|
||||
* naive 72-slot-per-poll ring cost 2.6KB and was enough to tip display_init
|
||||
* into "no mem for bounce buffer" on an unlucky boot. Keep this structure
|
||||
* small; if it ever needs to grow, it must move to PSRAM instead.
|
||||
*
|
||||
* Static per channel; a reboot forgets history, and the display shows no
|
||||
* arrow until enough accumulates again (see ha_data.h).
|
||||
*/
|
||||
#define TREND_SLOTS 20
|
||||
#define TREND_BUCKET_MS (4u * 60u * 1000u)
|
||||
|
||||
typedef struct {
|
||||
float value;
|
||||
uint32_t ms;
|
||||
bool used;
|
||||
} trend_sample_t;
|
||||
|
||||
static trend_sample_t trend_ring[HA_TREND_CHANNELS][TREND_SLOTS];
|
||||
static int trend_head[HA_TREND_CHANNELS]; /* slot most recently written */
|
||||
|
||||
void ha_trend_feed(int channel, float value, uint32_t now_ms)
|
||||
{
|
||||
if (channel < 0 || channel >= HA_TREND_CHANNELS) {
|
||||
return;
|
||||
}
|
||||
|
||||
trend_sample_t *newest = &trend_ring[channel][trend_head[channel]];
|
||||
|
||||
/* Inside the current bucket: refresh the newest sample in place, so
|
||||
* compute always sees the latest reading without spending a slot. */
|
||||
if (newest->used && (now_ms - newest->ms) < TREND_BUCKET_MS) {
|
||||
newest->value = value;
|
||||
return;
|
||||
}
|
||||
|
||||
const int next = newest->used ? (trend_head[channel] + 1) % TREND_SLOTS
|
||||
: trend_head[channel];
|
||||
trend_ring[channel][next] = (trend_sample_t){ value, now_ms, true };
|
||||
trend_head[channel] = next;
|
||||
}
|
||||
|
||||
ha_trend_t ha_trend_compute(int channel, float threshold_per_hour, uint32_t now_ms)
|
||||
{
|
||||
if (channel < 0 || channel >= HA_TREND_CHANNELS) {
|
||||
return HA_TREND_UNKNOWN;
|
||||
}
|
||||
|
||||
/* The newest sample is the one just fed; find the usable sample whose
|
||||
* age is closest to the window. Unsigned subtraction keeps this correct
|
||||
* across tick wrap. */
|
||||
const trend_sample_t *newest = NULL;
|
||||
const trend_sample_t *anchor = NULL;
|
||||
uint32_t best_dist = UINT32_MAX;
|
||||
|
||||
for (int i = 0; i < TREND_SLOTS; i++) {
|
||||
const trend_sample_t *s = &trend_ring[channel][i];
|
||||
if (!s->used) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t age = now_ms - s->ms;
|
||||
if (age > HA_TREND_WINDOW_MS + HA_TREND_MIN_AGE_MS) {
|
||||
continue; /* too old even for a sparse ring; ignore */
|
||||
}
|
||||
|
||||
if (newest == NULL || age < (now_ms - newest->ms)) {
|
||||
newest = s;
|
||||
}
|
||||
|
||||
if (age >= HA_TREND_MIN_AGE_MS) {
|
||||
const uint32_t dist = (age > HA_TREND_WINDOW_MS)
|
||||
? age - HA_TREND_WINDOW_MS
|
||||
: HA_TREND_WINDOW_MS - age;
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
anchor = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newest == NULL || anchor == NULL || newest == anchor) {
|
||||
return HA_TREND_UNKNOWN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale the threshold to the actual span so a 20-minute-old anchor uses
|
||||
* a proportionally smaller bar than a full hour would. Without this the
|
||||
* first hour after boot under-reports every trend.
|
||||
*/
|
||||
const uint32_t span_ms = anchor->ms == newest->ms
|
||||
? 1
|
||||
: newest->ms - anchor->ms;
|
||||
const float threshold = threshold_per_hour *
|
||||
((float)span_ms / (float)HA_TREND_WINDOW_MS);
|
||||
|
||||
const float delta = newest->value - anchor->value;
|
||||
if (delta > threshold) {
|
||||
return HA_TREND_RISING;
|
||||
}
|
||||
if (delta < -threshold) {
|
||||
return HA_TREND_FALLING;
|
||||
}
|
||||
return HA_TREND_FLAT;
|
||||
}
|
||||
|
||||
const char *ha_wind_cardinal(float degrees)
|
||||
{
|
||||
static const char *const points[16] = {
|
||||
|
||||
Reference in new issue
Block a user