Thursday, May 4, 2017

Programming the MeinEnigma - Rotors



One of the unique features of the MeinEnigma is the rotatable rotors that physically resemble those of the original Enigma machine and add realism.

Rotation of the four rotors is sensed using four rotary encoders which output quadrature pulses when turned. The levels are sensed by digital inputs on the Arduino. The Arduino can determine when and how far a rotor is turned, and the direction, but not the absolute position. It can be done using interrupts so the Arduino does not have to constantly poll the encoders.

Each encoder uses two signals, so the four encoders uses a total of eight of the Arduino's I/O pins. These encoders also have a pushbutton built in to the shaft, but this feature is not used.

The encoders are low in cost, reliable, and commonly used in Arduino-based designs. They often come on a small PCB with a 4 pin header. The MeinEnigma uses a custom PCB in order to better fit close to the mainboard.

For my programming example I use a standard library that I have found works well. It supports using interrupts but does not require using them.

The MeinEnigma application uses some custom code to read the rotors and avoid the memory overhead of a larger library which has features that aren't used.

The MeinEnigma rotors are connected to Arduino digital pins 2/3, 4/5, 6/7, 10/11. Note that the polarity of the first rotor is reversed from the others.

The sample program, shown below, creates an Encoder object for each rotor. It reads each rotor position, and if it has changed, reports the new position over the serial interface. Here is some sample output:

Rotor Demonstration
Position1 = 0
Position2 = 0
Position3 = 0
Position4 = 0
Position1 = -1
Position1 = -2
Position2 = -1
Position4 = 1
Position4 = 2
Position4 = 3
Position4 = 4
Position4 = 5
Position4 = 6
Position3 = 1
Position2 = 3
Position1 = 2
Position2 = 4
Position4 = 10

By uncommenting either the line that defines ENCODER_USE_INTERRUPTS or ENCODER_DO_NOT_USE_INTERRUPTS you can configure whether to use interrupts. Either option works on the MeinEnigma.

The program could be made shorter by using some arrays and loops rather than repeating the same code four times for each rotor, but I wanted to make the example easy to understand.

/*
  MeinEnigma Example

  Demonstrates reading the rotors.

  This uses the encoder library and was adapted from the basic example
  at http://www.pjrc.com/teensy/td_libs_Encoder.html

  Jeff Tranter

*/

#define ENCODER_USE_INTERRUPTS
//#define ENCODER_DO_NOT_USE_INTERRUPTS

#include

// Change these two numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
// Avoid using pins with LEDs attached

Encoder rotor1(3, 2);
Encoder rotor2(4, 5);
Encoder rotor3(6, 7);
Encoder rotor4(10, 11);

void setup() {
  Serial.begin(9600);
  Serial.println("Rotor Demonstration");
}

long oldPosition1  = -999;
long oldPosition2  = -999;
long oldPosition3  = -999;
long oldPosition4  = -999;

void loop() {
  long newPosition1 = rotor1.read();
  long newPosition2 = rotor2.read();
  long newPosition3 = rotor3.read();
  long newPosition4 = rotor4.read();
      
  if (newPosition1 != oldPosition1) {
    oldPosition1 = newPosition1;
    Serial.print("Position1 = ");
    Serial.println(newPosition1);
  }
  if (newPosition2 != oldPosition2) {
    oldPosition2 = newPosition2;
    Serial.print("Position2 = ");
    Serial.println(newPosition2);
  }
  if (newPosition3 != oldPosition3) {
    oldPosition3 = newPosition3;
    Serial.print("Position3 = ");
    Serial.println(newPosition3);
  }
  if (newPosition4 != oldPosition4) {
    oldPosition4 = newPosition4;
    Serial.print("Position4 = ");
    Serial.println(newPosition4);
  }
  delay(20);
}

References


  1. http://www.pjrc.com/teensy/td_libs_Encoder.html


No comments: