The input code file emerged as a result of increasing complexity in emulator.py, and as a clean way to support our implementation on platforms that may or may not have GPIO inputs. It serves the following functions:
- Inform emulator.py of rising or falling edges detected on any input.
- Scan the keypad matrix to detect pressed and released keys.
- Prevent detection of duplicate key presses (debouncing).
- Avoid missing any edges (reliability).
Design Steps
Unlike most of our other code, which followed a strict incremental design approach, inputs.py was written largely using top-down design. In particular, we designed the interface to resemble pygame.events as closely as possible, which allowed emulator.py to multiplex gpio and keyboard inputs elegantly.
The code itself was relatively simple. On include, if RPi.GPIO was loaded correctly, the code runs a script that correctly initializes gpio pins, and defines callbacks as necessary. It interfaces with emulator.py through two callable functions: get_pressed and get_released, that return presses or releases detected since the last call. To do so, these functions simply poll global variables set by the callbacks, and call functions to scan the keypad matrix.
While rising and falling edge callbacks for the joystick and pushbuttons were relatively simple to implement, implementing scan_keypad took some effort. In particular, a challenging aspect was detecting released keys in addition to pressed ones.
Challenges Faced
While input code was easily the shortest single file we had to write, interfacing with real hardware made this the most unreliable and difficult part of our code. Some problems we faced are described here:
- Preventing exceptions when the code was run on a non-RPi environment. As the lack of RPi.GPIO would tradionally throw an exception, we had to wrap most of this module into a try-except block to ensure safe behaviour.
- Preventing duplicate keypresses. This traditional debouncing problem proved fairly difficult to implement, as no value of debounce_time in RPi.GPIO callbacks worked reliably. To deal with this, we used software state variables that updated on every keypress and release. With this in place, we simply ignored the keypresses that did not modify state.
- Preventing missed falling or rising edges. Missing falling or rising edges can prove fatal in an emulator, as the CPU may be led to believe that a button has been pressed for a while. To deal with this, we had to reduce debounce_time to the smallest possible value in our callback definitions. However, even this did not give us 100% reliability.