This project is an Arduino‑based punched card reader that optically senses holes in punched cards and streams the decoded information via serial communication. It is intended to illustrate operating-system and low‑level I/O concepts by recreating the characteristics of historical slow peripheral devices.
- Yi Lyo
- Patrick McCann
- Alexander Thaep
- Zhiwen "Michael" Zheng
Punched Card Diameters
-> Length: 18.7325cm
-> Width: 8.255cm
-> Bits: 12 * 80 = 960b
Punched Card Reader:
-> Read start detected with an LED indicating the presence of a card
-> Notify card read start over serial
-> Starts with all LEDs off
-> Sampling Branch @ 1kHz:
-> Samples with LEDs off
-> Samples with LEDs on
-> Difference above a threshold comprises a sample of either 1 or 0
-> Reads 12 bits no more than 80 times and emits at most 16-bit words over serial
-> Read end detected with an LED indicating the absence of a card
-> Notify card read end over serial
We have created some preliminary CAD prototypes just to virtually explore the physical design of our system. These files, in .obj, .stl, and .f3z formats, can be found in the respective folders according to their versions.
.
├── arduino/ # Arduino files for various sketches.
│ ├── PhotodiodeReadTest/ # Arduino sketch directory for testing photodiode readings.
│ └── punchedCardReader/ # Arduino sketch directory for the punched card reader ("The Implementation").
│ └── PunchcardReaderDraft/ # Early draft of the punched card reader Arduino sketch.
├── cad/ # CAD files for prototype designs.
│ └── v1/ # V1 prototype CAD files.
│ └── v2/ # V2 prototype CAD files.
├── dafny/v1 # Dafny verification files.
├── punchcard-generator/ # Punched card generator utility.
├── serial-reader/ # Serial reader utility for reading data from the punched card reader.
└── sim/ # Simulation files.
└── test-cards/ # Sample punched card files for testing the simulation.
To clone the repository, use the following command:
git clone https://github.com/zzmic/punched-card-reader.gitThe easiest way to set up the environment for compiling and uploading the Arduino sketches is to use the Arduino IDE.
- Install the Arduino IDE on a local machine.
- Connect the Arduino® UNO R4 WiFi board to the local machine via a cable.
- Open the Arduino IDE.
- In the Arduino IDE, navigate to
Tools>Board>Board Manager..., search forArduino UNO R4 WiFi, and install the board core. - Navigate to
File>Open..., and open thearduino/punchedCardReader/punchedCardReader.inofile from the cloned repository. - In the Arduino IDE, navigate to
Tools>Port, and select the port corresponding to the connected Arduino® UNO R4 WiFi board. - Set the baud rate of the serial monitor to
115200by navigating toTools>Serial Monitorand selecting115200from the dropdown menu at the top right of the serial monitor window. - Click the
Uploadbutton (right arrow icon) in the Arduino IDE to compile and upload the sketch to the Arduino board.
Unit tests for the punched card reader are located in arduino/punchedCardReader/unitTests.h and arduino/punchedCardReader/unitTests.ino.
To run these tests, first enable unit testing and testing modes by uncommenting the #define UNIT_TESTING macro in arduino/punchedCardReader/punchedCardReader.ino.
After doing so, compiling and uploading the sketch to the Arduino board will automatically execute the runUnitTests() function within the setup() phase.
The test results, showing pass/fail status for the photodiode driver, card processor, and stream processor modules, are displayed on the serial monitor, with a clean run ending with the messages All photodiode driver unit tests passed :), All card processor unit tests passed :), and All Hollerith unit tests passed :).
The testing framework uses mock functions defined in arduino/punchedCardReader/testUtils.ino, such as evenLEDsOn and sendPunchReading, which are activated by the #define TESTING macro.
These mocks intercept hardware calls to set state flags (e.g., evenLEDsOnCalled), enabling the unit tests to verify that the correct hardware control logic runs without requiring physical interaction, though the board must be connected and the sketch uploaded.
Integration tests for the punched card reader can be found at arduino/punchedCardReader/softwareIntegrationTests.h and arduino/punchedCardReader/softwareIntegrationTests.ino.
To run the integration tests, first enable integration testing and testing modes by uncommenting the #define SOFTWARE_INTEGRATION_TESTING macro in arduino/punchedCardReader/punchedCardReader.ino.
After doing so, compiling and uploading the sketch to the Arduino board drives the timer-based test harness that feeds a scripted sequence of sensor readings (simulating the card text };\n) into the system at each time step.
The harness invokes checkMessages() on each time step to compare the expected LED control, punch-reading generation, column/byte transmission, and card-end signaling against the actual mocked calls.
Results are printed to the serial monitor; any mismatch includes the expected and actual values, while a clean run ends with the message finished software integration test (if only '};\n' was printed out, it passed).
The integration tests reuse the mock interfaces in arduino/punchedCardReader/testUtils.ino, such as evenLEDsOn and sendPunchReading, allowing the tests to validate the full sensing-to-streaming pipeline without requiring physical interaction, though the board must be connected and the sketch uploaded.
Alternatively, to compile and upload Arduino sketches to the Arduino® UNO R4 WiFi board, ensure that Arduino CLI is installed and available in the system's PATH and that the Arduino® UNO R4 WiFi board core is installed. More information about interacting with Arduino CLI can be found in its official documentation.
The following make commands (make help for help) built on arduino-cli can be used.
- To list connected Arduino boards:
Make sure to record the port of the connected Arduino® UNO R4 WiFi board (e.g.,
make arduino-board-list
/dev/ttyACM0on Linux,COM3on Windows, or/dev/cu.usbmodemXXXXon macOS) for use with theuploadcommands. - To update the local cache of available platforms and libraries:
make arduino-core-update-index
- To install the core for the Arduino® UNO R4 WiFi board:
make arduino-core-install
- To list installed/available cores:
make arduino-core-list
- To compile the Arduino sketch specified in FILE:
make arduino-compile FILE=<path-to-sketch>
- To compile and upload the compiled Arduino sketch specified in FILE to the Arduino board at PORT:
make arduino-compile-and-upload FILE=<path-to-sketch> PORT=<serial-port>
Before building the simulation, ensure that a compatible GCC compiler that supports C++14 or later is installed and available in the system's PATH.
The simulation of the early design of the punched card reader is implemented in C++ and can be built and run using the provided Makefile (make help for help).
- Run
make sim-buildto build the simulation. - Run
./sim/bin/mainto start the simulation in interactive mode (specify the--binary-modeflag for binary output mode). - "Insert" a card file containing an 80 (column) * 12 (row) grid, where each entry represents a punch (any character other than
.and whitespace) or no punch (.or whitespace). Sample card files are available in thesim/test-cards/directory. - To exit the simulation, type
donewhen prompted for the next card file path. Otherwise, more card files can be input to continue the simulation. - Alternatively, do
make sim-test(ormake sim-test-binary) to run the simulation on ALL the test cards in thesim/test-cards/directory.
The Dafny files in the dafny/v1/ directory aim to formally verify, at the algorithmic level, the correctness of the logic of core components based on the early design of the punched card reader using the Dafny programming and verification language.
The Dafny implementation does not directly interact with the Arduino hardware or the Arduino framework; instead, it focuses solely on verifying the algorithms and data structures used in the punched card reader's software design.
Due to time constraints, the Dafny programs might not comprehensively cover the up-to-date punched card reader's functionality (i.e., we do not claim full verification of the entire system), but they still serve as a useful tool for spotting potential issues we might encounter during system design, specification, and implementation.
To verify the Dafny programs, ensure that the Dafny binary build or the VSCode extension for Dafny (Dafny VSCode) is installed. If using the command line, run the following command from the project's root directory:
dafny verify dafny/v1/*.dfy