@@ -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
3745type 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 >
0 commit comments