Skip to content

Commit 7bf8d38

Browse files
committed
fix and improve debug mode, add signal filtering on by default
1 parent 270c65d commit 7bf8d38

8 files changed

Lines changed: 190 additions & 27 deletions

File tree

Keypad.Firmware/configuration.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
#define CONFIGURATION_ENCODER_CAPACITY 1
88
#define CONFIGURATION_DEBUG_MODE 0
99

10+
#define DEBUG_NOISE_FILTER_ENABLED 1
11+
#define DEBUG_PULLUPS_ENABLED 1
12+
#define DEBUG_CONFIRM_SAMPLES 3
13+
#define DEBUG_CONFIRM_DELAY_MS 1
14+
1015
#define HID_MAX_KEY_STEPS 1
1116

1217
#define PIN_NEO P34

Keypad.Firmware/src/debug_mode.c

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ typedef struct
1818
#define DEBUG_PIN_CAPACITY 40
1919
#define SUMMARY_INTERVAL_MS 1000UL
2020

21+
#ifndef DEBUG_NOISE_FILTER_ENABLED
22+
#define DEBUG_NOISE_FILTER_ENABLED 1
23+
#endif
24+
25+
#ifndef DEBUG_PULLUPS_ENABLED
26+
#define DEBUG_PULLUPS_ENABLED 1
27+
#endif
28+
29+
#ifndef DEBUG_CONFIRM_SAMPLES
30+
#define DEBUG_CONFIRM_SAMPLES 3
31+
#endif
32+
33+
#ifndef DEBUG_CONFIRM_DELAY_MS
34+
#define DEBUG_CONFIRM_DELAY_MS 1
35+
#endif
36+
2137
static debug_pin_entry_t debug_pins_s[DEBUG_PIN_CAPACITY];
2238
static uint8_t debug_pin_count_s = 0;
2339
static uint8_t debug_pin_state_s[DEBUG_PIN_CAPACITY];
@@ -38,6 +54,7 @@ static void debug_mode_print_summary(void);
3854
static void debug_mode_format_label(uint8_t pin, char *buffer, size_t buffer_length);
3955
static uint8_t debug_mode_is_reserved_pin(uint8_t pin);
4056
static void debug_mode_print_timestamp_prefix(const char *tag);
57+
static uint8_t debug_mode_confirm_change(uint8_t pin, uint8_t previous_state);
4158

4259
void debug_mode_setup(void)
4360
{
@@ -53,6 +70,7 @@ void debug_mode_setup(void)
5370
{
5471
uint8_t mode = debug_pins_s[i].use_pullup ? INPUT_PULLUP : INPUT;
5572
pinMode(debug_pins_s[i].pin, mode);
73+
delay(1);
5674
debug_pin_state_s[i] = digitalRead(debug_pins_s[i].pin);
5775
debug_mode_print_pin_snapshot(i, 1);
5876
}
@@ -69,12 +87,20 @@ void debug_mode_loop(void)
6987

7088
for (i = 0; i < debug_pin_count_s; ++i)
7189
{
72-
uint8_t raw = digitalRead(debug_pins_s[i].pin);
90+
uint8_t raw = (uint8_t)(digitalRead(debug_pins_s[i].pin) ? 1 : 0);
7391
if (raw != debug_pin_state_s[i])
7492
{
75-
debug_pin_state_s[i] = raw;
76-
debug_mode_print_pin_snapshot(i, 0);
77-
changed = 1;
93+
uint8_t confirmed = 1;
94+
#if DEBUG_NOISE_FILTER_ENABLED
95+
confirmed = debug_mode_confirm_change(debug_pins_s[i].pin, debug_pin_state_s[i]);
96+
#endif
97+
98+
if (confirmed)
99+
{
100+
debug_pin_state_s[i] = (uint8_t)(digitalRead(debug_pins_s[i].pin) ? 1 : 0);
101+
debug_mode_print_pin_snapshot(i, 0);
102+
changed = 1;
103+
}
78104
}
79105
}
80106

@@ -124,7 +150,7 @@ static void debug_mode_collect_unassigned_pins(void)
124150

125151
for (i = 0; i < count; ++i)
126152
{
127-
debug_mode_add_pin(candidates[i], 0, 0, 0);
153+
debug_mode_add_pin(candidates[i], DEBUG_PULLUPS_ENABLED ? 1 : 0, 0, 0);
128154
}
129155
}
130156

@@ -278,6 +304,31 @@ static void debug_mode_print_timestamp_prefix(const char *tag)
278304
debug_serial_print_s("ms] ");
279305
}
280306

307+
static uint8_t debug_mode_confirm_change(uint8_t pin, uint8_t previous_state)
308+
{
309+
#if DEBUG_NOISE_FILTER_ENABLED
310+
const uint8_t samples = (DEBUG_CONFIRM_SAMPLES == 0U) ? 1U : DEBUG_CONFIRM_SAMPLES;
311+
uint8_t confirmations = 0;
312+
uint8_t i;
313+
314+
for (i = 0; i < samples; ++i)
315+
{
316+
uint8_t sample = (uint8_t)(digitalRead(pin) ? 1 : 0);
317+
if (sample != previous_state)
318+
{
319+
++confirmations;
320+
}
321+
delay(DEBUG_CONFIRM_DELAY_MS);
322+
}
323+
324+
return (uint8_t)(confirmations > (samples / 2U));
325+
#else
326+
(void)pin;
327+
(void)previous_state;
328+
return 1;
329+
#endif
330+
}
331+
281332
static void debug_serial_print_c(char value)
282333
{
283334
USBSerial_write(value);

Keypad.Flasher.Client/src/KeypadFlasherApp.tsx

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ type FirmwareRequestBody = {
3232
bindingProfile: BindingProfileDto | null;
3333
debug: boolean;
3434
ledConfig: LedConfigurationDto | null;
35+
debugOptions: DebugOptionsDto | null;
36+
};
37+
38+
type DebugOptionsDto = {
39+
enableNoiseFilter: boolean;
40+
enablePullups: boolean;
41+
confirmSamples: number;
42+
confirmDelayMs: number;
3543
};
3644

3745
type StatusState =
@@ -401,6 +409,9 @@ export default function KeypadFlasherApp() {
401409
const [demoMode, setDemoMode] = useState<boolean>(false);
402410
const [devMode, setDevMode] = useState<boolean>(false);
403411
const [debugFirmware, setDebugFirmware] = useState<boolean>(false);
412+
const defaultDebugOptions: DebugOptionsDto = { enableNoiseFilter: true, enablePullups: true, confirmSamples: 3, confirmDelayMs: 1 };
413+
const classicDebugOptions: DebugOptionsDto = { enableNoiseFilter: false, enablePullups: false, confirmSamples: 1, confirmDelayMs: 0 };
414+
const [debugOptions, setDebugOptions] = useState<DebugOptionsDto>(defaultDebugOptions);
404415
const [selectedProfile, setSelectedProfile] = useState<KnownDeviceProfile | null>(null);
405416
const [rememberedBootloaderId, setRememberedBootloaderId] = useState<number[] | null>(null);
406417
const [currentBindings, setCurrentBindings] = useState<BindingProfileDto | null>(null);
@@ -843,10 +854,16 @@ export default function KeypadFlasherApp() {
843854

844855
try {
845856
setStatus({ state: "compiling", detail: debugFirmware ? "Debug firmware" : selectedProfile?.name });
846-
const requestLedConfig = assertLedConfigMatchesLayout(selectedLayout, ledConfig);
847-
const payload: FirmwareRequestBody = (!selectedLayout && debugFirmware)
848-
? { layout: null, bindingProfile: null, debug: true, ledConfig: null }
849-
: { layout: selectedLayout, bindingProfile: currentBindings, debug: debugFirmware, ledConfig: requestLedConfig };
857+
const requestLedConfig = debugFirmware ? null : assertLedConfigMatchesLayout(selectedLayout, ledConfig);
858+
const sanitizedDebugOptions: DebugOptionsDto = {
859+
enableNoiseFilter: debugOptions.enableNoiseFilter,
860+
enablePullups: debugOptions.enablePullups,
861+
confirmSamples: Math.max(1, Math.min(255, Math.round(debugOptions.confirmSamples))),
862+
confirmDelayMs: Math.max(0, Math.min(255, Math.round(debugOptions.confirmDelayMs))),
863+
};
864+
const payload: FirmwareRequestBody = debugFirmware
865+
? { layout: null, bindingProfile: null, debug: true, ledConfig: null, debugOptions: sanitizedDebugOptions }
866+
: { layout: selectedLayout, bindingProfile: currentBindings, debug: false, ledConfig: requestLedConfig, debugOptions: null };
850867

851868
const resp = await fetch("flasher", {
852869
method: "POST",
@@ -884,7 +901,7 @@ export default function KeypadFlasherApp() {
884901
} catch (err) {
885902
setStatus({ state: "compileError", detail: String((err as Error).message ?? err) });
886903
}
887-
}, [assertLedConfigMatchesLayout, flashBytes, debugFirmware, selectedLayout, selectedProfile, currentBindings, ledConfig]);
904+
}, [assertLedConfigMatchesLayout, flashBytes, debugFirmware, debugOptions, selectedLayout, selectedProfile, currentBindings, ledConfig]);
888905

889906
const unsupportedDevice = connectedInfo != null && selectedProfile == null;
890907
const userButtons = selectedLayout ? selectedLayout.buttons : [];
@@ -1424,7 +1441,63 @@ export default function KeypadFlasherApp() {
14241441
<p className="muted small">
14251442
Use debug firmware to expose a USB CDC serial console for troubleshooting layouts. See the <a className="link" href="https://github.com/AmyJeanes/KeypadFlasher#adding-support-for-new-keypads" target="_blank" rel="noreferrer">adding support guide</a> for wiring notes, LED direction tips, and how to contribute new keypad profiles.
14261443
</p>
1427-
<div className="card subtle">
1444+
{debugFirmware && (
1445+
<div className="card subtle" style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
1446+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: "10px", flexWrap: "wrap" }}>
1447+
<div className="card-title" style={{ marginBottom: 0 }}>Debug firmware options</div>
1448+
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
1449+
<button className="btn" onClick={() => setDebugOptions(classicDebugOptions)}>Raw (no filtering/pull-ups)</button>
1450+
<button className="btn" onClick={() => setDebugOptions(defaultDebugOptions)}>Reset defaults</button>
1451+
</div>
1452+
</div>
1453+
<div className="muted small">
1454+
Tweak how the debug logger handles floating pins and noisy edges.
1455+
</div>
1456+
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: "10px" }}>
1457+
<label className="checkbox" title="Majority-vote a few quick samples before logging a change to filter out glitches.">
1458+
<input
1459+
type="checkbox"
1460+
checked={debugOptions.enableNoiseFilter}
1461+
onChange={(e) => setDebugOptions((prev) => ({ ...prev, enableNoiseFilter: e.target.checked }))}
1462+
/>
1463+
Enable noise filter
1464+
</label>
1465+
<label className="checkbox" title="Use INPUT_PULLUP on unassigned pins to bias floating lines high.">
1466+
<input
1467+
type="checkbox"
1468+
checked={debugOptions.enablePullups}
1469+
onChange={(e) => setDebugOptions((prev) => ({ ...prev, enablePullups: e.target.checked }))}
1470+
/>
1471+
Pull-ups on unassigned pins
1472+
</label>
1473+
<label className="inline-input">
1474+
<span className="input-label" title="How many quick samples to take when a pin flips before logging it.">Confirm samples</span>
1475+
<input
1476+
id="debug-confirm-samples"
1477+
className="text-input"
1478+
type="number"
1479+
min={1}
1480+
max={255}
1481+
value={debugOptions.confirmSamples}
1482+
onChange={(e) => setDebugOptions((prev) => ({ ...prev, confirmSamples: Number(e.target.value) || 1 }))}
1483+
/>
1484+
</label>
1485+
<label className="inline-input">
1486+
<span className="input-label" title="Delay in milliseconds between confirmation samples when the filter is on.">Confirm delay (ms)</span>
1487+
<input
1488+
id="debug-confirm-delay"
1489+
className="text-input"
1490+
type="number"
1491+
min={0}
1492+
max={255}
1493+
value={debugOptions.confirmDelayMs}
1494+
onChange={(e) => setDebugOptions((prev) => ({ ...prev, confirmDelayMs: Math.max(0, Number(e.target.value) || 0) }))}
1495+
/>
1496+
</label>
1497+
</div>
1498+
</div>
1499+
)}
1500+
<div className="card subtle" style={{ marginTop: "12px" }}>
14281501
<div className="card-title">Connected device</div>
14291502
<div>Bootloader: {connectedInfo ? connectedInfo.version : "n/a"}</div>
14301503
<div>Bootloader ID: {connectedInfo ? connectedInfo.id.join(", ") : "n/a"}</div>

Keypad.Flasher.Server.Tests/ConfigurationGeneratorTests.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public void GenerateHeader_MatchesExpectedLayout()
3838
DebugMode: false,
3939
NeoPixelPin: 34,
4040
NeoPixelReversed: false,
41-
LedConfig: DefaultLedConfig(buttons));
41+
LedConfig: DefaultLedConfig(buttons),
42+
DebugOptions: DebugOptions.Default);
4243

4344
var expected = Lines(
4445
"// This file is auto-generated. Do not edit manually.",
@@ -50,6 +51,10 @@ public void GenerateHeader_MatchesExpectedLayout()
5051
"#define CONFIGURATION_BUTTON_CAPACITY 2",
5152
"#define CONFIGURATION_ENCODER_CAPACITY 0",
5253
"#define CONFIGURATION_DEBUG_MODE 0",
54+
"#define DEBUG_NOISE_FILTER_ENABLED 1",
55+
"#define DEBUG_PULLUPS_ENABLED 1",
56+
"#define DEBUG_CONFIRM_SAMPLES 3",
57+
"#define DEBUG_CONFIRM_DELAY_MS 1",
5358
"#define HID_MAX_KEY_STEPS 1",
5459
string.Empty,
5560
"#define PIN_NEO P34",
@@ -89,7 +94,8 @@ public void GenerateHeader_WithMultipleLedIndices_ComputesNeoCountFromBindings()
8994
DebugMode: false,
9095
NeoPixelPin: 34,
9196
NeoPixelReversed: false,
92-
LedConfig: DefaultLedConfig(buttons));
97+
LedConfig: DefaultLedConfig(buttons),
98+
DebugOptions: DebugOptions.Default);
9399

94100
var result = Generator.GenerateHeader(configuration);
95101

@@ -116,7 +122,8 @@ public void GenerateHeader_WithCustomNeoPixelPin_EmitsPin()
116122
DebugMode: false,
117123
NeoPixelPin: 31,
118124
NeoPixelReversed: false,
119-
LedConfig: DefaultLedConfig(buttons));
125+
LedConfig: DefaultLedConfig(buttons),
126+
DebugOptions: DebugOptions.Default);
120127

121128
var result = Generator.GenerateHeader(configuration);
122129

@@ -143,7 +150,8 @@ public void GenerateHeader_WithReversedNeoPixels_EmitsFlag()
143150
DebugMode: false,
144151
NeoPixelPin: 31,
145152
NeoPixelReversed: true,
146-
LedConfig: DefaultLedConfig(buttons));
153+
LedConfig: DefaultLedConfig(buttons),
154+
DebugOptions: DebugOptions.Default);
147155

148156
var result = Generator.GenerateHeader(configuration);
149157

@@ -170,7 +178,8 @@ public void GenerateHeader_WithNoAssignedLedIndices_SetsNeoCountToZero()
170178
DebugMode: false,
171179
NeoPixelPin: 34,
172180
NeoPixelReversed: false,
173-
LedConfig: DefaultLedConfig(buttons));
181+
LedConfig: DefaultLedConfig(buttons),
182+
DebugOptions: DebugOptions.Default);
174183

175184
var result = Generator.GenerateHeader(configuration);
176185

@@ -190,7 +199,8 @@ public void GenerateHeader_WithDebugMode_EmitsFlag()
190199
DebugMode: true,
191200
NeoPixelPin: 34,
192201
NeoPixelReversed: false,
193-
LedConfig: DefaultLedConfig(buttons));
202+
LedConfig: DefaultLedConfig(buttons),
203+
DebugOptions: DebugOptions.Default);
194204

195205
var result = Generator.GenerateHeader(configuration);
196206

@@ -222,7 +232,8 @@ public void GenerateSource_WithDebugMode_WritesInactiveBindings()
222232
DebugMode: true,
223233
NeoPixelPin: 34,
224234
NeoPixelReversed: false,
225-
LedConfig: DefaultLedConfig(buttons));
235+
LedConfig: DefaultLedConfig(buttons),
236+
DebugOptions: DebugOptions.Default);
226237

227238
var result = Generator.GenerateSource(configuration);
228239

@@ -271,7 +282,8 @@ public void GenerateSource_WithFourButtons_WritesExpectedConfiguration()
271282
DebugMode: false,
272283
NeoPixelPin: 34,
273284
NeoPixelReversed: false,
274-
LedConfig: DefaultLedConfig(buttons));
285+
LedConfig: DefaultLedConfig(buttons),
286+
DebugOptions: DebugOptions.Default);
275287

276288
var expected = ReadExpected("generate_source_4_buttons.c");
277289

@@ -307,7 +319,8 @@ public void GenerateSource_WithTwoButtons_WritesExpectedConfiguration()
307319
DebugMode: false,
308320
NeoPixelPin: -1,
309321
NeoPixelReversed: false,
310-
LedConfig: DefaultLedConfig(buttons));
322+
LedConfig: DefaultLedConfig(buttons),
323+
DebugOptions: DebugOptions.Default);
311324

312325
var expected = ReadExpected("generate_source_2_buttons.c");
313326

@@ -399,7 +412,8 @@ public void GenerateSource_WithTenButtons_WritesExpectedConfiguration()
399412
DebugMode: false,
400413
NeoPixelPin: -1,
401414
NeoPixelReversed: false,
402-
LedConfig: DefaultLedConfig(buttons));
415+
LedConfig: DefaultLedConfig(buttons),
416+
DebugOptions: DebugOptions.Default);
403417

404418
var expected = ReadExpected("generate_source_10_buttons.c");
405419

@@ -458,7 +472,8 @@ public void GenerateSource_WithThreeButtonsAndEncoder_WritesExpectedConfiguratio
458472
DebugMode: false,
459473
NeoPixelPin: 34,
460474
NeoPixelReversed: false,
461-
LedConfig: DefaultLedConfig(buttons));
475+
LedConfig: DefaultLedConfig(buttons),
476+
DebugOptions: DebugOptions.Default);
462477

463478
var expected = ReadExpected("generate_source_3_buttons_1_encoder.c");
464479

Keypad.Flasher.Server/Configuration/ConfigurationBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public static ConfigurationDefinition FromLayout(DeviceLayout layout, BindingPro
2727
DebugMode: debugMode,
2828
NeoPixelPin: layout.NeoPixelPin,
2929
NeoPixelReversed: layout.NeoPixelReversed,
30-
LedConfig: ledConfiguration);
30+
LedConfig: ledConfiguration,
31+
DebugOptions: DebugOptions.Default);
3132
}
3233

3334
private static List<ButtonBinding> BuildButtons(

Keypad.Flasher.Server/Configuration/ConfigurationGenerator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ public string GenerateHeader(ConfigurationDefinition configuration)
1919
sb.AppendLine($"#define CONFIGURATION_BUTTON_CAPACITY {configuration.Buttons.Count}");
2020
sb.AppendLine($"#define CONFIGURATION_ENCODER_CAPACITY {configuration.Encoders.Count}");
2121
sb.AppendLine($"#define CONFIGURATION_DEBUG_MODE {ToCInteger(configuration.DebugMode)}");
22+
sb.AppendLine($"#define DEBUG_NOISE_FILTER_ENABLED {ToCInteger(configuration.DebugOptions.EnableNoiseFilter)}");
23+
sb.AppendLine($"#define DEBUG_PULLUPS_ENABLED {ToCInteger(configuration.DebugOptions.EnablePullups)}");
24+
sb.AppendLine($"#define DEBUG_CONFIRM_SAMPLES {configuration.DebugOptions.ConfirmSamples}");
25+
sb.AppendLine($"#define DEBUG_CONFIRM_DELAY_MS {configuration.DebugOptions.ConfirmDelayMs}");
2226
sb.AppendLine($"#define HID_MAX_KEY_STEPS {maxKeySteps}");
2327
sb.AppendLine();
2428
if (neoPixelCount > 0)

0 commit comments

Comments
 (0)