ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
hw_health.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file hw_health.cpp
6 * @brief Hardware-health diagnostics (see hw_health.h).
7 */
8
10#include "shared_primitives/strbuf.h" // pc_sb frame builder
11
12#if PC_ENABLE_HW_HEALTH
13
14#include <string.h>
15
16void pc_hwhealth_rail_init(HwRailMonitor *m, uint32_t nominal_mv, uint32_t warn_mv, uint32_t crit_mv)
17{
18 if (!m)
19 {
20 return;
21 }
22 m->nominal_mv = nominal_mv;
23 m->warn_mv = warn_mv;
24 m->crit_mv = crit_mv;
25 m->min_mv = nominal_mv;
26 m->sag_events = 0;
27 m->brownout_events = 0;
28}
29
30HwRailVerdict pc_hwhealth_rail_sample(HwRailMonitor *m, uint32_t mv)
31{
32 if (!m)
33 {
34 return HwRailVerdict::HW_RAIL_OK;
35 }
36 if (mv < m->min_mv)
37 {
38 m->min_mv = mv;
39 }
40 if (mv < m->crit_mv)
41 {
42 m->brownout_events++;
43 return HwRailVerdict::HW_RAIL_BROWNOUT;
44 }
45 if (mv < m->warn_mv)
46 {
47 m->sag_events++;
48 return HwRailVerdict::HW_RAIL_SAG;
49 }
50 return HwRailVerdict::HW_RAIL_OK;
51}
52
53size_t pc_hwhealth_rail_json(const HwRailMonitor *m, char *out, size_t cap)
54{
55 if (!m || !out || cap == 0)
56 {
57 return 0;
58 }
59 pc_sb b = {out, cap, 0, true};
60 pc_sb_put(&b, "{\"nominal_mv\":");
61 pc_sb_u32(&b, m->nominal_mv);
62 pc_sb_put(&b, ",\"min_mv\":");
63 pc_sb_u32(&b, m->min_mv);
64 pc_sb_put(&b, ",\"sag\":");
65 pc_sb_u32(&b, m->sag_events);
66 pc_sb_put(&b, ",\"brownout\":");
67 pc_sb_u32(&b, m->brownout_events);
68 pc_sb_put(&b, "}");
69 if (!b.ok)
70 {
71 return 0;
72 }
73 out[b.len] = '\0';
74 return b.len;
75}
76
77void pc_hwhealth_spi_init(HwSpiBackoff *s, uint32_t start_hz, uint32_t min_hz, uint32_t max_hz, uint16_t fail_trip,
78 uint16_t ok_trip)
79{
80 if (!s)
81 {
82 return;
83 }
84 s->min_hz = min_hz;
85 s->max_hz = max_hz;
86 if (start_hz < min_hz)
87 {
88 s->hz = min_hz;
89 }
90 else if (start_hz > max_hz)
91 {
92 s->hz = max_hz;
93 }
94 else
95 {
96 s->hz = start_hz;
97 }
98 s->fail_streak = 0;
99 s->ok_streak = 0;
100 s->fail_trip = fail_trip ? fail_trip : 1;
101 s->ok_trip = ok_trip ? ok_trip : 1;
102}
103
104uint32_t pc_hwhealth_spi_result(HwSpiBackoff *s, bool crc_ok)
105{
106 if (!s)
107 {
108 return 0;
109 }
110 if (crc_ok)
111 {
112 s->fail_streak = 0;
113 if (++s->ok_streak >= s->ok_trip)
114 {
115 s->ok_streak = 0;
116 uint32_t up = s->hz << 1;
117 if (up < s->hz || up > s->max_hz) // overflow or past ceiling
118 {
119 up = s->max_hz;
120 }
121 s->hz = up;
122 }
123 }
124 else
125 {
126 s->ok_streak = 0;
127 if (++s->fail_streak >= s->fail_trip)
128 {
129 s->fail_streak = 0;
130 uint32_t down = s->hz >> 1;
131 if (down < s->min_hz)
132 {
133 down = s->min_hz;
134 }
135 s->hz = down;
136 }
137 }
138 return s->hz;
139}
140
141HwGpioVerdict pc_hwhealth_gpio_short(bool driven_high, bool read_high)
142{
143 if (driven_high && !read_high)
144 {
145 return HwGpioVerdict::HW_GPIO_SHORT_GND;
146 }
147 if (!driven_high && read_high)
148 {
149 return HwGpioVerdict::HW_GPIO_SHORT_VCC;
150 }
151 return HwGpioVerdict::HW_GPIO_OK;
152}
153
154HwCapVerdict pc_hwhealth_cap_leak(uint32_t measured_ms, uint32_t expected_ms, uint8_t tol_pct)
155{
156 if (expected_ms == 0)
157 {
158 return HwCapVerdict::HW_CAP_OK;
159 }
160 // Tolerance band around expected, computed in 64-bit to avoid overflow.
161 uint64_t band = (uint64_t)expected_ms * tol_pct / 100;
162 uint64_t lo = (uint64_t)expected_ms > band ? (uint64_t)expected_ms - band : 0;
163 uint64_t hi = (uint64_t)expected_ms + band;
164 if (measured_ms < lo)
165 {
166 return HwCapVerdict::HW_CAP_LEAK; // discharges too fast
167 }
168 if (measured_ms > hi)
169 {
170 return HwCapVerdict::HW_CAP_HIGH_ESR; // discharges too slow
171 }
172 return HwCapVerdict::HW_CAP_OK;
173}
174
175#endif // PC_ENABLE_HW_HEALTH
Hardware-health diagnostics: rail droop, SPI CRC backoff, GPIO short, cap leakage (PC_ENABLE_HW_HEALT...
Bounded no-heap string builder that fails closed on overflow (one shared copy).
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Definition strbuf.h:60
void pc_sb_u32(pc_sb *b, uint32_t v)
Append v as decimal (no leading zeros; "0" for zero).
Definition strbuf.h:303
Bump-append target; ok latches false once an append would overflow cap.
Definition strbuf.h:30
size_t len
Definition strbuf.h:33
bool ok
Definition strbuf.h:34