Summary of Validating Keypad Commands with a State Machine: the XX@HH:MM# Protocol
This article explains how to validate 16-button keypad commands using state machine logic. It details a protocol (XX@HH:MM#) where specific keys map to characters like '@', ':', and '#'. The solution involves breaking the command into distinct states (e.g., VALUE1, HOUR1), defining valid ranges for time and values, and implementing transitions via a switch statement to ensure accurate input processing before execution.
Parts used in Keypad Input Validation Project:
- 16-button keypad
- 4 x 4 keypad matrix
- Switch statement
- State machine logic
- Command protocol (XX@HH:MM#)
- Alpha-Numeric key mappings
- Value range definitions
- State diagram
- Spreadsheet or table
The command format and its key mapping
The reference example is the protocol XX@HH:MM#, published by the State Machine Programming blog for a 4 x 4, 16-button keypad. In that description XX “is a value from 1-99” and HH:MM “is a time format (24 hr clock or military time)”. The keypad mapping sends A to @, C to Clear, * to the colon and # to execute, with B and D unused. Validation therefore cannot wait for a newline: each keypress either advances a parser position or is rejected, and the parser position is what distinguishes a first value digit from a first hour digit.
One state per character position
The blog treats each character of the command as a state. It names VALUE1, VALUE2, ATSIGN, HOUR1, HOUR2, COLON, MIN1, MIN2 and EXECUTE, plus an INITIAL state before any key is accepted, and introduces the pattern with the sentence “The easiest way is to consider each character in the command as a state.” The same write-up recommends a switch statement and asserts that in the author’s opinion “it is the only way to implement serial input commands”.
That last point is a preference, not a property of the problem. The same state graph can be encoded as a transition table indexed by state and key, as an array of function pointers, or as a chain of comparisons; on a small AVR the choice trades code size against table size and readability. What determines correctness is the graph, not the construct that implements it.
A transition table as a proposed specification
The table below is a specification proposed for this analysis, built on the state names listed above. It is not code that the original author published or tested. Keys not listed for a state are errors.
| State | Key | Next state | Action or note |
|---|---|---|---|
| INITIAL | 0-9 | VALUE1 | first value digit |
| VALUE1 | 0-9 | VALUE2 | second value digit |
| VALUE1 | @ | ATSIGN | one-digit value, as in 9@1:26# |
| VALUE2 | @ | ATSIGN | two-digit value, as in 45@12:45# |
| ATSIGN | 0-9 | HOUR1 | first hour digit |
| HOUR1 | 0-9 | HOUR2 | second hour digit |
| HOUR1 | : | COLON | one-digit hour, as in 9@1:26# |
| HOUR2 | : | COLON | check hour 0-23 here |
| COLON | 0-9 | MIN1 | first minute digit |
| MIN1 | 0-9 | MIN2 | second minute digit |
| MIN2 | # | EXECUTE | check minutes, dispatch, reset |
| any | C | INITIAL | discard buffer |
| any | other | unchanged | error indication, no dispatch |
Correcting the hour range to 00..23
The blog lists “HH has a range of 0-24 but 00-09 is also valid”. A 24-hour clock labels its hours 00 through 23, so 24:10 is not a time of day; a general 0-24 hour range admits input that cannot be displayed or compared meaningfully. The corrected range is 00..23, with the two-digit form checked at the COLON transition. If a project needs an end-of-day marker, 24:00 should be one explicit special case with its own rule rather than the top of an open-ended hour range.
Leading zeros also have to be decided rather than assumed. The source accepts them, noting that “XX has a range 1 to 99 but 01-09 is also valid” and that “MM has a range of 00-59”. For this specification: the value field accepts one or two digits, 1..99, where 01..09 equals 1..9; the hour accepts one or two digits, 0..23, where 00..09 equals 0..9; minutes are fixed at exactly two digits, 00..59. Fixing minutes at two digits stops 5 from meaning either 5 or 50 and keeps the command length constant.
A bounded nine-byte buffer
CALCULATIONS: the longest accepted command is 99@23:59#, which is 2 + 1 + 2 + 1 + 2 + 1 = 9 bytes. A fixed buffer of nine bytes plus a length counter is therefore sufficient, and the maximum is a property of the grammar rather than of any particular keypad routine. Keys that would exceed the count are rejected like any other invalid key instead of overwriting earlier input, and no dynamic allocation is required. The bound also caps the work done per keypress, which matters on the smaller AVR parts.
Clear, error and execute transitions
Three transitions need explicit definitions, and leaving them implicit is where parsers of this kind usually fail. Clear, mapped to C, discards the buffer and returns to INITIAL from any state; the blog’s switch example includes a CLEAR case that runs clearAll() and assigns state = INITIAL. An error is any key not listed for the current state, and the blog’s table falls back to a previous state with a “Display Format Msg” action in several rows. Whether the parser holds its position or resets on error is a design decision that should be written down, because holding lets an operator correct one mistyped digit while resetting forces a full retype.
Execute, mapped to #, must be accepted in exactly one state: after the second minute digit. In every earlier state that same key is an ordinary error. This is what prevents dispatch of partial input, since no prefix of XX@HH:MM is ever handed to the application; the application receives either a complete, range-checked command or nothing. After dispatch the parser returns to INITIAL with an empty buffer so the next command starts clean.
Two related firmware concerns sit outside this analysis but belong in any implementation: key debounce and keypad scan rate. A parser that is correct in the abstract still needs one stable event per physical press, and a scanning routine that emits two events for one press will look like a doubled digit. Readers working on the input side of small AVR builds may find the 125 kHz RFID reader based on ATtiny13 relevant for its serial data handling, and those adding operator feedback can compare approaches in Salvaging Liquid Crystal Displays (LCDs).
Counting the accepted command space
CALCULATIONS: with 99 values, 24 hours and 60 minutes, the number of distinct valid commands is 99 x 24 x 60 = 142,560. That figure counts value and time pairs, not keystroke sequences: 9@1:26# and 09@01:26# are different strings describing the same pair, so the keystroke count is larger. The number is useful for sizing a test table and for reasoning about how much input space a validator must reject, not as a statement about throughput or timing on hardware.
What this analysis does not establish
This is a design description. No firmware was written, compiled or run, and no keypad was scanned; nothing here is a claim about the original author’s code, which the blog presents as an approach rather than a released implementation. The transition table is a new specification, and the two arithmetic results are derivations from stated ranges. Timing, debounce behaviour, display driver details, and the memory cost of a table-driven parser versus a switch-based one all require measurement on a real build.
-
How can I solve the problem of validating commands as each character is typed?
Use state machine logic or programming to break complex problems into manageable states and transitions. -
What is the easiest way to implement a state machine for serial input commands?
The easiest way is to use a switch statement. -
Which key on the keypad executes the command?
The pound sign (#) key is used to execute, accept, or enter the command. -
What does the colon symbol represent in the alpha-numeric key mapping?
The asterisk (*) key maps to the colon (:). -
What are the suggested state names for the command XX@HH:MM#?
Suggested states include VALUE1, VALUE2, ATSIGN, HOUR1, HOUR2, COLON, MIN1, MIN2, EXECUTE, and INITIAL. -
What is the valid range for the hour value in the protocol?
The hour has a range of 0 to 24, and formats like 00-09 are also valid. -
Can the first digit of the value be zero in the command protocol?
Yes, 01-09 is considered valid for the value range. -
What is the recommended tool to create a state diagram?
Start with a table or spreadsheet listing all the states entered.





