Introduction: Arduino Enigma Machine
Enjoy working with Arduino electronics? Want to make a machine that can encrypt and decrypt secret messages? This is the instructable for you.
:)
Step 1: Materials
- an Arduino UNO
- USB cable A/B (this connects to the Arduino)
- A large breadboard
- Male-to-male jumper cables (our setup uses 12)
- 4 Female-to-male jumper cables
- Flat wires (these make things a bit neater)
- A female-to-female cable
- A potentiometer (1kΩ)
- Four resistors (approximately 1 kΩ each)
- A pair of tweezers
- 16x2 liquid crystal display (LCD)
- LCD I2C interface adapter
Step 2: Wiring Up the LCD
Liquid crystal displays (or LCDs) usually communicate with an external microprocessor (in this case an Arduino UNO) using a series of 8 pins, and other necessary pins such as ground and power. The Arduino sends 8 bits (a bit is a single 0 or 1) across the 8 pins, which the LCD then interprets and presents on the screen. However, using the I2C bus, the number of necessary pins drops to 4.
To connect the LCD to the Arduino, use 4 female-to-male jumper cables. Connect the GND pin on the I2C interface to the GND pin on the Arduino (the right GND pin to use is labeled 'POWER'). Connect the VCC pin to the 5V pin on the Arduino (right next to the GND pin). Then connect SDA and SCL to A4 and A5 respectively. Lastly, use either a cap or a female-to-female jumper cable to connect the last two pins together.
Step 3: Wiring Up the Breadboard
Using male-to-male jumper cables, connect the negative power rail on the breadboard to the GND pin on the Arduino, then connect pin 13 to the positive power rail (pin 13 is set up in the code to be +5V).
Take 4 1kΩ resistors and connect them between the negative power rail and individual rows on the breadboard. Each resistor must have two spaces between them.
Connect a push button to each resistor, and +5V from the positive power rail on the other side of each button. Then connect 4 jumper cables from pins 2, 3, 4, and 5 to each resistor (preferably in order).
Lastly, take a potentiometer and connect one end to the negative power rail, and another end to pin 12 using a jumper cable. Pin 12 is also set to be +5V in the code, but the voltage sources for the buttons and potentiometer must remain separate so the buttons do not affect its readings. Then, connect a jumper cable between A0 and the middle pin of the potentiometer.
Given everything is in place and the connections are solid, you're finished!
Step 4: Putting These Together
The wiring for the LCD and breadboard should now look something like this.
Step 5: Coding the Enigma
Libraries
For this code to work, two libraries need to be installed. They can be found attached to this instructable. In your 'Arduino' folder (which has various folders and the arduino.exe file), open the 'libraries' folder, and insert the 'Wire' library and 'LiquidCrystal' library.
Tools
To upload the code to your Arduino, you need to make sure the Arduino IDE is configured correctly. Go to 'Tools', and click 'Port'. Then Select the port your Arduino is connected to. Make sure the USB A/B cable is connecting the Arduino and your computer too. Just above 'Port' (in 'Tools'), click 'Board', and if not already selected, choose 'Arduino/Genuino Uno'.
Below is the code required for the project. We have explained it section by section.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>int pot = A0; int selectB = 2; int clearB = 3; int setRotorB = 4; int backB = 5;
int potIn; int counter = 0; char alphaValues[26] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; int rotor[3][26] = { { 5, 20, 19, 8, 7, 0, 21, 4, 3, 23, 11, 10, 25, 24, 15, 14, 22, 18, 17, 2, 1, 6, 16, 9, 13, 12 }, { 20, 8, 11, 18, 12, 22, 23, 21, 1, 24, 17, 2, 4, 15, 25, 13, 19, 10, 3, 16, 0, 7, 5, 6, 9, 14 }, { 9, 2, 1, 17, 22, 24, 16, 21, 15, 0, 23, 20, 14, 19, 12, 8, 6, 3, 25, 13, 11, 7, 4, 10, 5, 18 } }; int reflector[26] = { 9, 11, 8, 22, 14, 7, 21, 5, 2, 0, 20, 1, 17, 16, 4, 25, 13, 12, 23, 24, 10, 6, 3, 18, 19, 15 }; int rotorPos[3] = { 0, 0, 0 };
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
This first section includes the necessary libraries for this code to work, and sets up some basic variables that give names to certain pins, so the code is easier to read later. The potIn will later be used to store the value the potentiometer returns to the Arduino. The counter tell the LCD where to print letters on the screen. The alphaValues[] array simply gives numbers to each letter of the alphabet, so letters can be converted to numbers and vice versa. The rotor[] array and reflector[] array become important in the enigma() function, which does the actual encrypting and decrypting. If you want a more detailed look into how an enigma machine works, there are many useful resources online. The rotorPos[] array stores the state of each rotor, and how many times they have rotated. The last line initialises the LCD for use later.
void setup() {
pinMode(13, OUTPUT); digitalWrite(13, HIGH); pinMode(12, OUTPUT); digitalWrite(12, HIGH);pinMode(selectB, INPUT); pinMode(pot, INPUT); pinMode(clearB, INPUT); pinMode(setRotorB, INPUT); pinMode(backB, INPUT); lcd.begin(16, 2); lcd.backlight(); }
This section tells the Arduino what specific pins are, and generally sets things up for the following code. Pins 13 and 12 are set to be high at all times, so they can act as +5V voltage providers. The pins related to the four buttons and potentiometer are set to be input pins. The Arduino begins sending information to the LCD, and the backlight is turned on.
void clearScrn() {
lcd.clear(); counter = 0; }void printRotors() { for (int i = 2; i >= 0; i--) { lcd.setCursor(14 - 3 * i, 0); if (rotorPos[i] < 10) {lcd.print("0");} lcd.print(rotorPos[i]); lcd.print("/"); } }
void setRotorPos() { clearScrn(); printRotors(); for (int i = 2; i >= 0; i--) { //This loop updates continually until selectB is pressed while (digitalRead(selectB) != HIGH) { potIn = (int)floor((analogRead(pot)*26)/1024); //Updates potIn lcd.setCursor(14 - 3 * i, 0); //Setting cursor position based on which rotor is being editted if (potIn < 10) {lcd.print("0");} // lcd.print(potIn); //Displaying potIn delay(10); } rotorPos[i] = potIn; //Ensuring nothing happens while the button is held, things take place when the button is released again while (digitalRead(selectB) != LOW) { delay(100); } } }
These functions set up certain processes which will be useful later. clearScrn() simply clears the screen and returns the cursor to 0, so new letters will appear at the left of the screen. printRotors() gives a visual display in the top right of the LCD of the rotor positions. This is essential to know when decrypting enigma messages. setRotorPos() lets the user set the position of each rotor, which is again important in decrypting messages
int enigma(int inLetterVal) {
//Forward run through rotors for (int i = 0; i <= 2; i++) { inLetterVal += rotorPos[i]; //Applying whatever setting the rotor is on (this is reversed at the end, it just sends it down a new path) while (inLetterVal >= 26) inLetterVal -= 26; //Looping back from 26 to 0 inLetterVal = rotor[i][inLetterVal]; } //Reflector inLetterVal = reflector[inLetterVal]; //Coming back through rotors for (int i = 2; i >= 0; i--) { inLetterVal = rotor[i][inLetterVal]; inLetterVal -= rotorPos[i]; //Reversing rotor click effect on letter value while (inLetterVal < 0) inLetterVal += 26; //Looping back from 0 to 26 }//Clicking rotors rotorPos[0] += 1; if (rotorPos[0] >= 26) { rotorPos[1] += 1; rotorPos[0] = 0; if (rotorPos[1] >= 26) { rotorPos[2] += 1; rotorPos[1] = 0; if (rotorPos[2] >= 26) { rotorPos[2] = 0; } } } return inLetterVal; }
The enigma() function does the heavy-lifting of making this machine work. All the other functions simply relate to making the LCD work or ensuring all the components communicate and work correctly. This function uses the rotor[] array and rotorPos[] array to decide how letters should be encrypted. The final section updates the rotor positions, so that every time the function is called, the rotors click appropriately.
void loop() {
//Prints Rotor Positions printRotors();//Letter Selection potIn = (int)floor((analogRead(pot)*26)/1024); lcd.setCursor(0, 0); lcd.print(alphaValues[potIn]); //Showing currently selected letter lcd.print(" "); // if (potIn < 10) {lcd.print("0");} //Showing selected letter number lcd.print(potIn); //
//Checks if select button pushed if (digitalRead(selectB) == HIGH && counter < 16) { lcd.setCursor(counter, 1); lcd.print(alphaValues[enigma(potIn)]); counter++; while (digitalRead(selectB) != LOW) { delay(100); } }
//Check if clear button pushed if (digitalRead(clearB) == HIGH) { clearScrn(); while (digitalRead(clearB) != LOW) { delay(100); } }
//Check if set rotor button pushed if (digitalRead(setRotorB) == HIGH) { setRotorPos(); while (digitalRead(setRotorB) != LOW) { delay(100); } }
if (digitalRead(backB) == HIGH && counter >= 1) { counter--; lcd.setCursor(counter, 1); lcd.print(" "); //Clicking rotors backwards rotorPos[0] -= 1; if (rotorPos[0] < 0) { rotorPos[1] -= 1; rotorPos[0] = 25; if (rotorPos[1] < 0) { rotorPos[2] -= 1; rotorPos[1] = 25; if (rotorPos[2] < 0) { rotorPos[2] = 25; } } } while (digitalRead(backB) != LOW) { delay(100); } } }
This is the main loop of the function, and is repeated indefinitely while the Arduino remains powered. The first job is to print the rotors in the top right corner. Then, the potentiometer is used to determine which letter is being selected. The following if statements are event handlers that check if a button has been pushed. If a button is pushed, its corresponding code is run. Then, the while loops make sure nothing else happens until the button is released. This lowers the likelihood of the machine accidentally breaking or doing unexpected things from multiple inputs at once.
Attachments
Step 6: Implementing the Code
Make sure the Arduino is connected to your computer with the USB A/B cable, and click the button in the top left that says 'upload'. Then, your machine should come to life!
Step 7: Using Your Enigma
Push Buttons:
- Select (S): Enters whichever letter or number is currently on screen.
- Clear (C): Clears the screen, without changing the rotor positions.
- Rotor (R): Changes the rotor positions in order from left to right, also clears the screen.
- Backspace (B): Deletes the last letter inputted and unclicks the rotor.