One technical challenge was to minimize the cables and GPIOs to address all LEDs and buttons without additional hardware. In total, only 12 GPIO pins are needed. This is possible by cyclically reversing the GPIO lines and switching between input and output behavior. Several modules share the 8 bit bus to address D1-D8, each module is set active on with the Enable line.
To light an LED, all GPIO lines are set to output. Enable line is set to high, LED cathode line is set to low. The brightness of the LED is controlled by a very short wait() cycle.
To scan a switch, the LED cathode line is set to high, Enable line GPIO is configured as input. If the switch is open, this input is low via the 10k pulldown resistor. If it is closed, the input is high over the LED line.
I was surprised that the Arduino Due is able to do the reconfiguration really fast, so the scanning is possible with over 100 times per second in the main loop.