自己打造一台恩尼格码密码机

按:这个作品是2013年无线电单片机竞赛的亚军。感谢所有支持这个作品的你们!

在对称加密学当中,恩尼格码机绝对是承前启后的存在。它将密码学研究从以前的语言文字学中心完全转移到了数学身上。在这里牵涉的密码并不是我们平时邮箱、银行帐号那种狭义概念,那种顶多叫做口令。这里说的密码就是通过某种转换规律方式,把一篇文章变得面目全非,非常人能阅读,以达到保密效果。这篇文章适于电脑控、军事控、历史控、数学控阅读,请做好烧脑准备。

另:我们的“开源恩尼格玛计划”在Kickstarter已经结束众筹。如有兴趣请浏览http://www.stgeotronics.com。感谢大家支持!

这是我们的初号机。以下教程将手把手教你如何完美山寨史上著名的德国恩尼格玛密码机(以下称哑谜机,不清楚历史的可以到维基、百度等地方脑补一下)。这个基于Arduino的开源程序能够加解密任何哑谜机M4型(海军型)的信息。

这个第一台全功能开源完美哑谜机复制品是根据sketchsk3tch写的《Kid’s Game to Arduino Enigma Machine》(从儿童玩具到Arduino恩尼格玛机)所作。

采用多路复用LED电路,仅用38个针脚的115个发光二极管和4个针脚的36个按键所连接的整个电路,全靠在键盘回路里准确放置的电阻以及P型号晶体管得以实现。要不然,4个16段显示器,以及每个按键上的LED将大幅增加所需针脚总量,即使用了Arduino Mega板但如果没用上述两个方法也不能如此简洁。 面对电路的超额需求,我们在http://www.stgeotronics.com设计了专用的PCB板。直接跳到第10步和以后的步骤可以找到更多信息。同时,我们以测试过的完整电子组装套装发布。

第一步:面包板上的论证


在开始制作电子哑谜机之前,我们先要确保能驱动16段LED显示。如果能的话,我们就能做接下来的所有步骤,除了数学上的问题,一切都是浮云。

第二步:万事具备

你所需要的是:

  • 1个Arduino Mega 2560板
  • 26个字母按键
  • 26个1/4英寸单通道母接口
  • 10个1/4英寸单通道公接口
  • 36个机械按钮
  • 1个单刀三掷开关
  • 4个16段橙色LED显示
  • 4个注塑2升汽水瓶罩子
  • 1个胶合板盒子
  • 一个铰链
  • 一个半榫接锁
  • 一个接线盘
  • 38个470欧电阻
  • 40个1千欧电阻
  • 7个IRF9Z24N P型晶体管
  • 1块金属片
  • 以及喷漆。

可选项:

电池盒,充电电池,充电器/充电接头。

我们真要做的时候,是不会用1/4英寸接口的。它们太大的体积几乎要超过整个恩尼格玛。香蕉插头体积较小,而且比原版德国哑谜机结合得更紧密些。

第三步:布置零件






6*8寸无线电面包版是最合适放置所有元件的,既不多余也不拥挤,而且和哑谜机盒子内部完美吻合。

最初我们将面包等分三块区域,但很快意识到如此一来,电子版哑谜机将比原版机械哑谜机长。于是我们将所有零件缩放到正好够占用的空间。

每个元件位置就绪,下一步就是焊接。

第四步:我焊,我焊,我焊焊焊……






好吧,在单一作品身上,我从没焊接如此多次。16段显示的18个针脚,还有26个字母键乘以每个4个脚,外加26个键盘灯,一些其他LED,一个三掷开关,真乃“成吉思焊”。

当初我们的决定是使这些16段LED显示看起来像老式电子管的感觉,增加了不少焊点,“巨焊”!

Arduino Mega板上针脚的分配: 17段:

针脚 线色 Duino针脚
a 2 24
b 1 22
c 16 白蓝 25
d 13 绿 31
e 9 白棕 38
f 8 36
g 6 绿 32
h 5 白橙 30
k 4 28
m 3 白蓝 26
n 17 23
p 15 27
r 12 白橙 33
s 11 35
t 7 白绿 34
u 14 白橙 29
dp 10 白棕 37
阳极1 18 39
阳极2 18 41
阳极3 18 43
阳极4 18 45

LED:

1 40
2 42
3 44
4 46
5 48

灯:

QAP 10
WSY 9
EDX 8
RFC 7
TGV 6
ZHB 5
UJN 4
IKM 3
OL 2
阳极1 (第一排) 11
阳极2 (第二排) 12
阳极3 (第三排) 13

功能键: A0 键盘:

第一排 A1
第二排 A2
第三排 A3

第五步:门面工夫——做个盒子钻出面板




在原版M4型木盒内得到确定位置数据后,我们买了一块胶合板,将它切块,然后砌盒子。

我们从旧服务器机架上卸了一块钢板,厚度正合需要。将模具(上面早已画好每个按键和灯位,并切好了洞洞)盖在钢板上,然后用记号笔画出需要切出的洞洞。

接着,我们用喷漆把它涂黑,就像真的哑谜机那样。

第六步:组装测试







首先把金属板在面包版上永久固定,确保所有按键正常工作,所有LED都能发光。

接着就是把这一大坨东东装入木盒,确保没有空隙位置。

第七步:软件啊,日完软啊!


在组装硬件过程中,我们也写了个小型Arduino程序框架,用以测试特定几个需要关注的部分:

用来测试每个按键信号能准确读取,还有测试10个功能按键的代码。

Enigma_POST(上电自检)确保在每种模式下所有键盘等都能准确亮起,在每种模式下每个LED信号都能传送。我们对原本面包板上的代码做了修正,确保4个16段LED显示的每个部件无懈可击。

但,即使所有手上的程序片段都说明机器状态完好,重现M4海军型哑谜机加解密功能,数学方面居功至伟。

所有Arduino程序片段在我们刚刚建好的云端都能找到。

以下是Enigma_POST程序片段(上电自检):

/* Enigma Development Code to Test each of the 4 Nixies, the 5 LEDs,
   then Turn On each Lamp in sequence.
   Written by Marc Tessier & James Sanderson 9/8/13
*/

// Define the 16-Segments Pins
int segment[17] = {24,22,25,31,38,36,32,30,28,26,23,27,33,35,34,29,37};
int anode[4] = {39,41,43,45};

// Define the 9 lamps Pins
int lamp[9] = {10,9,8,7,6,5,4,3,2};
int lanode[3] = {11,12,13};

//              LTP587P Segments: A,B,C,D,E,F,G,H,K,M,N,P,R,S,T,U,dp              
boolean segmentvals[39][17] = { { 0,0,0,0,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = A
                                { 0,0,0,0,0,0,1,1,1,0,1,0,1,0,1,1,1 },  // = B
                                { 0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = C
                                { 0,0,0,0,0,0,1,1,1,0,1,1,1,0,1,1,1 },  // = D
                                { 0,0,1,1,0,0,0,0,1,1,1,0,1,1,1,0,1 },  // = E
                                { 0,0,1,1,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = F
                                { 0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,1,1 },  // = G
                                { 1,1,0,0,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = H
                                { 0,0,1,1,0,0,1,1,1,0,1,1,1,0,1,1,1 },  // = I
                                { 1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 },  // = J
                                { 1,1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,1 },  // = K
                                { 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = L
                                { 1,1,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1 },  // = M
                                { 1,1,0,0,1,1,0,0,0,1,1,1,0,1,1,1,1 },  // = N
                                { 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = O
                                { 0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = P
                                { 0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1 },  // = Q
                                { 0,0,0,1,1,1,0,0,1,1,1,0,0,1,1,0,1 },  // = R
                                { 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,1 },  // = S
                                { 0,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1 },  // = T
                                { 1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = U
                                { 1,1,1,1,1,1,0,0,1,1,0,1,1,1,0,1,1 },  // = V
                                { 1,1,0,0,1,1,0,0,1,1,1,1,0,1,0,1,1 },  // = W
                                { 1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1 },  // = X
                                { 1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1 },  // = Y
                                { 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1 },  // = Z
                                { 0,0,0,0,0,0,0,0,1,1,0,1,1,1,0,1,1 },  // = 0
                                { 1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1 },  // = 1
                                { 0,0,0,1,0,0,0,1,1,1,1,0,1,1,1,0,1 },  // = 2
                                { 0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1 },  // = 3
                                { 1,1,0,0,1,1,1,0,1,1,1,0,1,1,1,0,1 },  // = 4
                                { 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,1 },  // = 5
                                { 0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,0,1 },  // = 6
                                { 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1 },  // = 7
                                { 0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,0,1 },  // = 8
                                { 0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1 },  // = 9
                                { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },  // = Space
                                { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },  // = Full Lit
                                { 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1 }   // = SS
                              };
//              LTP587P Segments: A,B,C,D,E,F,G,H,K,M,N,P,R,S,T,U,dp
        
boolean lampvals[9][9] =   { { 0,1,1,1,1,1,1,1,1 },  // = Q or A or P
                             { 1,0,1,1,1,1,1,1,1 },  // = W or S or Y
                             { 1,1,0,1,1,1,1,1,1 },  // = E or D or X
                             { 1,1,1,0,1,1,1,1,1 },  // = R or F or C
                             { 1,1,1,1,0,1,1,1,1 },  // = T or G or V
                             { 1,1,1,1,1,0,1,1,1 },  // = Z or H or B
                             { 1,1,1,1,1,1,0,1,1 },  // = U or J or N
                             { 1,1,1,1,1,1,1,0,1 },  // = I or K or M
                             { 1,1,1,1,1,1,1,1,0 }   // = O or L
                            };

int value_row1 = 0;
int value_row2 = 0;
int value_row3 = 0;
char key = 91;

int led1 = 40;
int led2 = 42;
int led3 = 44;
int led4 = 46;
int led5 = 48;
int wait = 100;



void setup() {
  for (int index = 0 ; index <= 3; index  ) {
    pinMode (anode[index], OUTPUT);
    digitalWrite (anode[index], 1);
  }
  for (int index = 0 ; index <= 16; index  ) {
    pinMode (segment[index], OUTPUT);
    digitalWrite (segment[index], 1);
  } 
  // initialize the digital pins as an output.
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
  pinMode(led5, OUTPUT);
  for (int index = 0 ; index <= 2; index  ) {
    pinMode (lanode[index], OUTPUT);
    digitalWrite (lanode[index], 1);
  }
  for (int index = 0 ; index <= 8; index  ) {
    pinMode (lamp[index], OUTPUT);
    digitalWrite (lamp[index], 1);
  }
}

void loop() {
   sixteenSegWrite(0, 38);
   sixteenSegWrite(1, 38);
   sixteenSegWrite(2, 38);
   sixteenSegWrite(3, 38);
  
  digitalWrite(led1, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200);               // wait for a second
  digitalWrite(led1, LOW);    // turn the LED off by making the voltage LOW
  delay(wait);               // wait for a second
  digitalWrite(led2, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200);               // wait for a second
  digitalWrite(led2, LOW);    // turn the LED off by making the voltage LOW
  delay(wait);               // wait for a second
  digitalWrite(led3, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200);               // wait for a second
  digitalWrite(led3, LOW);    // turn the LED off by making the voltage LOW
  delay(wait);               // wait for a second
  digitalWrite(led4, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200);               // wait for a second
  digitalWrite(led4, LOW);    // turn the LED off by making the voltage LOW
  delay(wait);               // wait for a second
  digitalWrite(led5, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200);               // wait for a second
  digitalWrite(led5, LOW);    // turn the LED off by making the voltage LOW
  delay(wait);               // wait for a second

for (int index = 0 ; index <= 2; index  ) {
   digitalWrite (lanode[index], 0);
   for (int mychar = 0; mychar < 9; mychar  ) {
      for (int sindex = 0; sindex < 9; sindex  ) {
         digitalWrite(lamp[sindex], lampvals[mychar][sindex]);
         delay (30);
      }
  }
  digitalWrite (lanode[index], 1);
}
}

void sixteenSegWrite(int digit, int character) {
  digitalWrite(anode[digit],0);
  for (int index = 0; index < 17; index  ) {
  digitalWrite(segment[index], segmentvals[character][index]);
  }
}

第八步:再多一些软件!









首先,我们写了个函数,给每个哑谜机工作模式用。

在模式0、默认模式,哑谜机仅仅是一台普通打字机,以跑马灯方式显示它的型号。

模式1下,允许用户从八个转子中选取三个,两个反射器中选择一个进行使用。

模式2下,允许用户排列转子次序。

模式3用于自定义转子初始字母排列。

选择模式4,用户最多可以使用接线板上10对交换字母排列。

模式5是运行模式,此时哑谜机能加解密任何从键盘录入的信息。

以下是整个哑谜机工作流程完整程序片段:

/* S&T GeoTronics Enigma Code. This Arduino Mega custom shield is programmed to replicate
   exactly the behavior of a true German M4 Enigma machine.
   It uses 4 16-Segment units, 5 LEDs, 26 Lamps setup as keyboard, 26 keyboard buttons
   & 10 Function keys. The 115 light emitting diodes are charlie-plexed to minimize the
   amount of pins needed down to 38 and all 36 pushbuttons keys are sharing a total of 4 pins.
   Designed, assembled & programmed by Marc Tessier & James Sanderson 9/20/13
*/
// Define the variables
unsigned long time = millis();
unsigned long otime = time;
int inpin[4] = {A0, A1, A2, A3};
int inval[4] = {0, 0, 0, 0};
int keyval = 100;
boolean windex = 0;
boolean windex1 = 0;
boolean windex2 = 0;
int lampval = 100;
int procesval = 0;
int procesvala = 0;
int mode = 0;
unsigned long mtime;
int mdex =0;

// Define each Nixie character
int dig1 = 37;
int dig2 = 37;
int dig3 = 37;
int dig4 = 37;

int data[36] = {36,36,36,36,18,39,19,36,6,4,14,19,17,14,13,8,2,18,36,4,13,8,6,12,0,36,12,0,17,10,36,30,36,36,36,36} ;

// Define the 16-Segments Pins as 2 Arrays
int segment[17] = {24,22,25,31,38,36,32,30,28,26,23,27,33,35,34,29,37}; //cathode array
int anode[4] = {39,41,43,45}; //annode array commin annode

// Define the 26 Lamps as a 2D Array
int lamparray[26] [2] = {{12,10},{13,5},{13,7},{12,8},{11,8},{12,7,},{12,6},{12,5},{11,3},{12,4},
                         {12,3},{13,2},{13,3},{13,4},{11,2},{13,10},{11,10},{11,7},{12,9},{11,6},
                         {11,4},{13,6},{11,9},{13,8},{13,9},{11,5}};

//  Define the 12 Lamp Pins for initialization
int lamppin[12] = {2,3,4,5,6,7,8,9,10,11,12,13}; //2 to 10 cathode, 11 to 13 common annode

// Define each LTP587P Segments: A,B,C,D,E,F,G,H,K,M,N,P,R,S,T,U,dp              
boolean segmentvals[40][17] = { { 0,0,0,0,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = A 0
                                { 0,0,0,0,0,0,1,1,1,0,1,0,1,0,1,1,1 },  // = B 1
                                { 0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = C 2
                                { 0,0,0,0,0,0,1,1,1,0,1,1,1,0,1,1,1 },  // = D 3
                                { 0,0,1,1,0,0,0,0,1,1,1,0,1,1,1,0,1 },  // = E 4
                                { 0,0,1,1,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = F 5
                                { 0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,1,1 },  // = G 6
                                { 1,1,0,0,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = H 7
                                { 0,0,1,1,0,0,1,1,1,0,1,1,1,0,1,1,1 },  // = I 8
                                { 1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 },  // = J 9
                                { 1,1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,1 },  // = K 10
                                { 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = L 11
                                { 1,1,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1 },  // = M 12
                                { 1,1,0,0,1,1,0,0,0,1,1,1,0,1,1,1,1 },  // = N 13
                                { 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = O 14
                                { 0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,1 },  // = P 15
                                { 0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1 },  // = Q 16
                                { 0,0,0,1,1,1,0,0,1,1,1,0,0,1,1,0,1 },  // = R 17
                                { 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,1 },  // = S 18
                                { 0,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1 },  // = T 19
                                { 1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 },  // = U 20
                                { 1,1,1,1,1,1,0,0,1,1,0,1,1,1,0,1,1 },  // = V 21
                                { 1,1,0,0,1,1,0,0,1,1,1,1,0,1,0,1,1 },  // = W 22
                                { 1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1 },  // = X 23
                                { 1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1 },  // = Y 24
                                { 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1 },  // = Z 25
                                { 0,0,0,0,0,0,0,0,1,1,0,1,1,1,0,1,1 },  // = 0 26
                                { 1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1 },  // = 1 27
                                { 0,0,0,1,0,0,0,1,1,1,1,0,1,1,1,0,1 },  // = 2 28
                                { 0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1 },  // = 3 29
                                { 1,1,0,0,1,1,1,0,1,1,1,0,1,1,1,0,1 },  // = 4 30
                                { 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,1 },  // = 5 31
                                { 0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,0,1 },  // = 6 32
                                { 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1 },  // = 7 33
                                { 0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,0,1 },  // = 8 34
                                { 0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1 },  // = 9 35
                                { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },  // = Space 36
                                { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },  // = Full Lit 37
                                { 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1 },  // = SS 38
                                { 0,1,1,1,0,0,0,1,0,0,1,1,0,1,1,0,1} }; // = & 39
//              LTP587P Segments: A,B,C,D,E,F,G,H,K,M,N,P,R,S,T,U,dp

// Define the 5 Mode LEDs
int led1 = 40;
int led2 = 42;
int led3 = 44;
int led4 = 46;
int led5 = 48;

                                      //4,10,12, 5,11, 6, 3,16,21,25,13,19,14,22,24, 7,23,20,18,15, 0, 8, 1,17, 2, 9
//Define the rotor values                 A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q
static const int rotorvals[12][78] = { {  4,10,12, 5,11, 6, 3,16,21,25,13,19,14,22,24, 7,123,20,18,15, 0, 8, 1,17, 2, 9,
                                         4,10,12, 5,11, 6, 3,16,21,25,13,19,14,22,24, 7,123,20,18,15, 0, 8, 1,17, 2, 9,
                                         4,10,12, 5,11, 6, 3,16,21,25,13,19,14,22,24, 7,123,20,18,15, 0, 8, 1,17, 2, 9 },  // wheel 1

                                       {  0, 9, 3,10,118, 8,17,20,23, 1,11, 7,22,19,12, 2,16, 6,25,13,15,24, 5,21,14, 4,
                                          0, 9, 3,10,118, 8,17,20,23, 1,11, 7,22,19,12, 2,16, 6,25,13,15,24, 5,21,14, 4,
                                          0, 9, 3,10,118, 8,17,20,23, 1,11, 7,22,19,12, 2,16, 6,25,13,15,24, 5,21,14, 4 },  // wheel 2
                                        
                                       {  1, 3, 5, 7, 9,11, 2,15,17,19,23,21,25,13,24, 4, 8,22, 6, 0,10,112,20,18,16,14,
                                          1, 3, 5, 7, 9,11, 2,15,17,19,23,21,25,13,24, 4, 8,22, 6, 0,10,112,20,18,16,14,
                                          1, 3, 5, 7, 9,11, 2,15,17,19,23,21,25,13,24, 4, 8,22, 6, 0,10,112,20,18,16,14 },  // wheel 3
                                        
                                       {  4,18,14,21,15,25, 9, 0,24,116,20, 8,17, 7,23,11,13, 5,19, 6,10, 3, 2,12,22, 1,
                                          4,18,14,21,15,25, 9, 0,24,116,20, 8,17, 7,23,11,13, 5,19, 6,10, 3, 2,12,22, 1,
                                          4,18,14,21,15,25, 9, 0,24,116,20, 8,17, 7,23,11,13, 5,19, 6,10, 3, 2,12,22, 1 },  // wheel 4
                                          
                                       { 21,25, 1,17, 6, 8,19,24,20,15,18, 3,13, 7,11,23, 0,22,12, 9,16,14, 5, 4, 2,110,
                                         21,25, 1,17, 6, 8,19,24,20,15,18, 3,13, 7,11,23, 0,22,12, 9,16,14, 5, 4, 2,110,
                                         21,25, 1,17, 6, 8,19,24,20,15,18, 3,13, 7,11,23, 0,22,12, 9,16,14, 5, 4, 2,110  },  // wheel 5
                                        
                                       {  9,15, 6,21,14,20,12, 5,24,16, 1, 4,113, 7,25,17, 3,10, 0,18,23,11, 8, 2,19,122,
                                          9,15, 6,21,14,20,12, 5,24,16, 1, 4,113, 7,25,17, 3,10, 0,18,23,11, 8, 2,19,122,
                                          9,15, 6,21,14,20,12, 5,24,16, 1, 4,113, 7,25,17, 3,10, 0,18,23,11, 8, 2,19,122 },  // wheel 6
                                         
                                       { 13,25, 9, 7, 6,17, 2,23,12,24,18,22,101,14,20, 5, 0, 8,21,11,15, 4,10,16, 3,119,
                                         13,25, 9, 7, 6,17, 2,23,12,24,18,22,101,14,20, 5, 0, 8,21,11,15, 4,10,16, 3,119,
                                         13,25, 9, 7, 6,17, 2,23,12,24,18,22,101,14,20, 5, 0, 8,21,11,15, 4,10,16, 3,119 },  // wheel 7
                                        
                                       { 5,10,16, 7,19,11,23,14, 2, 1, 9,18,115, 3,25,17, 0,12, 4,22,13, 8,20,24, 6,121,
                                         5,10,16, 7,19,11,23,14, 2, 1, 9,18,115, 3,25,17, 0,12, 4,22,13, 8,20,24, 6,121,
                                         5,10,16, 7,19,11,23,14, 2, 1, 9,18,115, 3,25,17, 0,12, 4,22,13, 8,20,24, 6,121 },    // wheel 8
                                        
                                       { 11,4,24,9,21,2,13,8,23,22,15,1,16,12,3,17,19,0,10,25,6,5,20,7,14,18,
                                         11,4,24,9,21,2,13,8,23,22,15,1,16,12,3,17,19,0,10,25,6,5,20,7,14,18,
                                         11,4,24,9,21,2,13,8,23,22,15,1,16,12,3,17,19,0,10,25,6,5,20,7,14,18 },    // Beta
                                        
                                       { 5,18,14,10,0,13,20,4,17,7,12,1,19,8,24,2,22,11,16,15,25,23,21,6,9,3,
                                         5,18,14,10,0,13,20,4,17,7,12,1,19,8,24,2,22,11,16,15,25,23,21,6,9,3,
                                         5,18,14,10,0,13,20,4,17,7,12,1,19,8,24,2,22,11,16,15,25,23,21,6,9,3 },    // Gamma
                                        
                                       { 4,13,10,16,0,20,24,22,9,8,2,14,15,1,11,12,3,23,25,21,5,19,7,17,6,18,
                                         4,13,10,16,0,20,24,22,9,8,2,14,15,1,11,12,3,23,25,21,5,19,7,17,6,18,
                                         4,13,10,16,0,20,24,22,9,8,2,14,15,1,11,12,3,23,25,21,5,19,7,17,6,18},    // = UKW-B
                                        
                                       { 17,3,14,1,9,13,19,10,21,4,7,12,11,5,2,22,25,0,23,6,24,8,15,18,20,16,
                                         17,3,14,1,9,13,19,10,21,4,7,12,11,5,2,22,25,0,23,6,24,8,15,18,20,16,
                                         17,3,14,1,9,13,19,10,21,4,7,12,11,5,2,22,25,0,23,6,24,8,15,18,20,16 }    // = UKW-C
                                        };
                                       
                                   
static const int rotorvali[10][78] = { { 20,22,24, 6, 0, 3, 5,15,21,25, 1, 4, 2,10,12,19, 7,23,18,11,17, 8,13,16,14, 9,
                                         20,22,24, 6, 0, 3, 5,15,21,25, 1, 4, 2,10,12,19, 7,23,18,11,17, 8,13,16,14, 9,
                                         20,22,24, 6, 0, 3, 5,15,21,25, 1, 4, 2,10,12,19, 7,23,18,11,17, 8,13,16,14, 9  },  //wheel 1 i
//                                  
                                      {  0, 9,15, 2,25,22,17,11, 5, 1, 3,10,14,19,24,20,16, 6, 4,13, 7,23,12, 8,21,18,
                                         0, 9,15, 2,25,22,17,11, 5, 1, 3,10,14,19,24,20,16, 6, 4,13, 7,23,12, 8,21,18,
                                         0, 9,15, 2,25,22,17,11, 5, 1, 3,10,14,19,24,20,16, 6, 4,13, 7,23,12, 8,21,18  },  //wheel 2 i
                                       
                                      { 19, 0, 6, 1,15, 2,18, 3,16, 4,20, 5,21,13,25, 7,24, 8,23, 9,22,11,17,10,14,12,
                                        19, 0, 6, 1,15, 2,18, 3,16, 4,20, 5,21,13,25, 7,24, 8,23, 9,22,11,17,10,14,12,
                                        19, 0, 6, 1,15, 2,18, 3,16, 4,20, 5,21,13,25, 7,24, 8,23, 9,22,11,17,10,14,12 },  //wheel 3 i
                                       
                                       
                                       { 7,25,22,21, 0,17,19,13,11, 6,20,15,23,16, 2, 4, 9,12, 1,18,10, 3,24,14, 8, 5,
                                         7,25,22,21, 0,17,19,13,11, 6,20,15,23,16, 2, 4, 9,12, 1,18,10, 3,24,14, 8, 5,
                                         7,25,22,21, 0,17,19,13,11, 6,20,15,23,16, 2, 4, 9,12, 1,18,10, 3,24,14, 8, 5 },  //wheel 4 i
                                        
                                       { 16, 2,24,11,23,22, 4,13, 5,19,25,14,18,12,21, 9,20, 3,10, 6, 8, 0,17,15, 7, 1,
                                         16, 2,24,11,23,22, 4,13, 5,19,25,14,18,12,21, 9,20, 3,10, 6, 8, 0,17,15, 7, 1,
                                         16, 2,24,11,23,22, 4,13, 5,19,25,14,18,12,21, 9,20, 3,10, 6, 8, 0,17,15, 7, 1 },  //wheel 5 i
                                        
                                       { 18,10,23,16,11, 7, 2,13,22, 0,17,21,06,12, 4, 1, 9,15,19,24, 5, 3, 25,20, 8,14,
                                         18,10,23,16,11, 7, 2,13,22, 0,17,21,06,12, 4, 1, 9,15,19,24, 5, 3, 25,20, 8,14,
                                         18,10,23,16,11, 7, 2,13,22, 0,17,21,06,12, 4, 1, 9,15,19,24, 5, 3, 25,20, 8,14 },  //wheel 6 i
                                    
                                       { 16,12,6,24,21,15,4,3,17,2,22,19,8,0,13,20,23,5,10,25,14,18,11,7,9,1,
                                         16,12,6,24,21,15,4,3,17,2,22,19,8,0,13,20,23,5,10,25,14,18,11,7,9,1,
                                         16,12,6,24,21,15,4,3,17,2,22,19,8,0,13,20,23,5,10,25,14,18,11,7,9,1 },  //wheel 7 i
                                       
                                       { 16,9,8,13,18,0,24,3,21,10,1,5,17,20,7,12,2,15,11,4,22,25,19,6,23,14,
                                         16,9,8,13,18,0,24,3,21,10,1,5,17,20,7,12,2,15,11,4,22,25,19,6,23,14,
                                         16,9,8,13,18,0,24,3,21,10,1,5,17,20,7,12,2,15,11,4,22,25,19,6,23,14 },   //wheel 8 i
                                        
                                       { 17,11,5,14,1,21,20,23,7,3,18,0,13,6,24,10,12,15,25,16,22,4,9,8,2,19,
                                         17,11,5,14,1,21,20,23,7,3,18,0,13,6,24,10,12,15,25,16,22,4,9,8,2,19,
                                         17,11,5,14,1,21,20,23,7,3,18,0,13,6,24,10,12,15,25,16,22,4,9,8,2,19 },   //Beta i
                                        
                                       { 4,11,15,25,7,0,23,9,13,24,3,17,10,5,2,19,18,8,1,12,6,22,16,21,14,20,
                                         4,11,15,25,7,0,23,9,13,24,3,17,10,5,2,19,18,8,1,12,6,22,16,21,14,20,
                                         4,11,15,25,7,0,23,9,13,24,3,17,10,5,2,19,18,8,1,12,6,22,16,21,14,20 } }; //Gamma i
                                                                               
                                       
// Define a 2D Array for keeping the wheel locations & positions
int wheel[3][3] = {{26,0,0},{26,0,0},{26,0,0}};
int reflect[2] = {1,0};

// Define Array for plugbord values 25 x2 position 0 holds use position 1 holds value int plugu holds the total nomber of plugs used (10 max)
int plugval [2][26] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                       {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25}};
int pluguse = 0; int paindex = 0; int pbindex = 1;


void setup() {
// Initialize all 38 LED pins as Output 
  for (int index = 0; index <= 11; index  ) {
    pinMode(lamppin[index], OUTPUT);
    digitalWrite(lamppin[index],1);
  }
  for (int index = 0 ; index <= 3; index  ) {
    pinMode (anode[index], OUTPUT);
    digitalWrite (anode[index], 1);
  }
  for (int index = 0 ; index <= 16; index  ) {
    pinMode (segment[index], OUTPUT);
    digitalWrite (segment[index], 1);
  } 
  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
  pinMode(led3,OUTPUT);
  pinMode(led4,OUTPUT);
  pinMode(led5,OUTPUT);
 
// Serial.begin(9600);
// Initialize all 4 pusbutton pins as Input   
  for (int index = 0; index  otime   500) {keyval = readkbde();}
  if((keyval == 45) && (windex ==1)) {modeselect();}
// The whole Enigma machine operation revolves around which Operating Mode is current 
  if(mode == 0) {mode0();}
  else if(mode == 1) {mode1();}
  else if(mode == 2) {mode2();}
  else if(mode == 3) {mode3();}
  else if(mode == 4) {mode4();}
  else if(mode == 5) {mode5();}
  else {    }

//Serial.println(keyval); // for debuging prints keybord value to serial monitor// for run or del

}

// This function takes care of figuring out which key has been pressed & returns a unique Integer
int readkbde() {
int kval = 100;
for (int index = 0; index  925) && (inval[1] > 828) && (inval[2] > 730) && (inval[3] > 828))  {kval = 100;} // no key press
else if((inval[0]  915)) {kval = 49;} //up arrow 4
else if((inval[0]  903)) {kval = 48;} //up arrow 3
else if((inval[0]  887)) {kval = 47;} //up arrow 2
else if((inval[0]  865)) {kval = 46;} //up arrow 1
else if((inval[0]  836)) {kval = 45;} //mode
else if((inval[0]  793)) {kval = 44;} //enter
else if((inval[0]  724)) {kval = 43;}
else if((inval[0]  594)) {kval = 42;}
else if((inval[0]  260)) {kval = 41;}
else if(inval[0] < 259 ) {kval = 40;}
else if((inval[1]  807)) {kval = 14;}
else if((inval[1]  781)) {kval = 8;}
else if((inval[1]  749)) {kval = 20;}
else if((inval[1]  706)) {kval = 25;}
else if((inval[1]  647)) {kval = 19;}
else if((inval[1]  555)) {kval = 17;}
else if((inval[1]  418)) {kval = 4;}
else if((inval[1]  169)) {kval = 22;}
else if(inval[1] < 168 ) {kval = 16;}
else if((inval[2]  699)) {kval = 10;}
else if((inval[2]  660)) {kval = 9;}
else if((inval[2]  611)) {kval = 7;}
else if((inval[2]  547)) {kval = 6;}
else if((inval[2]  455)) {kval = 5;}
else if((inval[2]  331)) {kval = 3;}
else if((inval[2]  127)) {kval = 18;}
else if(inval[2] < 126 ) {kval = 0;}
else if((inval[3]  807)) {kval = 11;}
else if((inval[3]  781)) {kval = 12;}
else if((inval[3]  749)) {kval = 13;}
else if((inval[3]  706)) {kval = 1;}
else if((inval[3]  647)) {kval = 21;}
else if((inval[3]  555)) {kval = 2;}
else if((inval[3]  418)) {kval = 23;}
else if((inval[3]  169)) {kval = 24;}
else if(inval[3] < 169 ) {kval = 15;}
else {kval = 100;}
if(kval = 0) && (kval =6) {mode = 0;}
  windex = 0;
}

// Default Mode: Enigma is a Typewriter
void mode0() {
  if((keyval >= 0) && (keyval  2) {reflect[0] = 1;}  windex = 0;}}
    if(windex == 1) {if(keyval == 47) { for(index = wheel[2][0];(index == wheel[1][0]) || (index == wheel[0][0]) || (index == wheel[2][0]);
                        index  ) {if(index > 33) {index = 26;} }wheel[2][0] = index;  windex = 0;}}
    if(windex == 1) {if(keyval == 48) { for(index = wheel[1][0];(index == wheel[2][0]) || (index == wheel[0][0]) || (index == wheel[1][0]);
                        index  ) {if(index > 33) {index = 26;} }wheel[1][0] = index;  windex = 0;}}
    if(windex == 1) {if(keyval == 49) { for(index = wheel[0][0];(index == wheel[2][0]) || (index == wheel[1][0]) || (index == wheel[0][0]);
                        index  ) {if(index > 33) {index = 26;} }wheel[0][0] = index;  windex = 0;}}
    if(windex == 1) {if(keyval == 42) { for(index = wheel[2][0];(index == wheel[1][0]) || (index == wheel[0][0]) || (index == wheel[2][0]);
                        index--) {if(index < 28) {index = 35;} }wheel[2][0] = index;  windex = 0;}}
    if(windex == 1) {if(keyval == 41) { for(index = wheel[1][0];(index == wheel[2][0]) || (index == wheel[0][0]) || (index == wheel[1][0]);
                        index--) {if(index < 28) {index = 35;} }wheel[1][0] = index;  windex = 0;}}
    if(windex == 1) {if(keyval == 40) { for(index = wheel[0][0];(index == wheel[2][0]) || (index == wheel[1][0]) || (index == wheel[0][0]);
                        index--) {if(index  25) {wheel[2][1] = 0;}}
      if(keyval == 48) {wheel[1][1]  ; if(wheel[1][1] > 25) {wheel[1][1] = 0;}}
      if(keyval == 49) {wheel[0][1]  ; if(wheel[0][1] > 25) {wheel[0][1] = 0;}}
      if(keyval == 42) {wheel[2][1]--; if(wheel[2][1] < 0) {wheel[2][1] = 25;}}
      if(keyval == 41) {wheel[1][1]--; if(wheel[1][1] < 0) {wheel[1][1] = 25;}}
      if(keyval == 40) {wheel[0][1]--; if(wheel[0][1]  25) {reflect[1] = 0;}}
      if(keyval == 47) {wheel[2][2]  ; if(wheel[2][2] > 25) {wheel[2][2] = 0;}}
      if(keyval == 48) {wheel[1][2]  ; if(wheel[1][2] > 25) {wheel[1][2] = 0;}}
      if(keyval == 49) {wheel[0][2]  ; if(wheel[0][2] > 25) {wheel[0][2] = 0;}}
      if(keyval == 43) {reflect[1]--; if(reflect[1] < 0) {reflect[1] = 25;}}
      if(keyval == 42) {wheel[2][2]--; if(wheel[2][2] < 0) {wheel[2][2] = 25;}}
      if(keyval == 41) {wheel[1][2]--; if(wheel[1][2] < 0) {wheel[1][2] = 25;}}
      if(keyval == 40) {wheel[0][2]--; if(wheel[0][2] < 0) {wheel[0][2] = 25;}}
      windex = 0; }
   
    dig2 = wheel[2][2];  dig3 = wheel[1][2]; dig4 = wheel[0][2]; dig1 = reflect[1];
    nixisend();
    dig1 =37; dig2 = 37; dig3 = 37; dig4 = 37;
    digitalWrite(led3, LOW);
  }

// Define the Plugboard pairs 
  void mode4() {
    int index = 0;
    digitalWrite(led4, HIGH);
    if(pluguse  24) {index = -1;}} paindex = index;}
    if(plugval[0][pbindex] == 1) {for(index = pbindex;(index == pbindex) || (index == paindex) || (plugval[0][index] == 1); index  ) {if(index > 24) {index = -1;}} pbindex = index;}
    if(windex == 1)  {
    if(keyval == 46) { for(index = paindex;(index == paindex) || (index == pbindex) || (plugval[0][index] == 1); index  ) {if(index > 24) {index = -1;}} paindex = index; windex = 0; }
    if(keyval == 43) { for(index = paindex;(index == paindex) || (index == pbindex) || (plugval[0][index] == 1); index--) {if(index  24) {index = -1;}} pbindex = index; windex = 0; }
    if(keyval == 40) { for(index = pbindex;(index == pbindex) || (index == paindex) || (plugval[0][index] == 1); index--) {if(index = 0) && (keyval <= 25)) {  if(windex == 1){procesvala = keyval;  indexwheels();}}
    windex = 0;
    procesval = procesvala;
    procesval = plugval[1][procesval];
   // Serial.print (procesval); Serial.print("   ");
   
    pv = (procesval   (wheel[0][2] - wheel[0][1]));
    if(pv = 100) {procesval = procesval - 100;}
    procesval = (procesval - (wheel[0][2] - wheel[0][1]));
    if(procesval  25) {procesval = procesval - 26;}
  //  Serial.print (procesval); Serial.print("   ");
   
    pv = (procesval   (wheel[1][2] - wheel[1][1]));
    if(pv = 100) {procesval = procesval - 100;}
    procesval = (procesval - (wheel[1][2] - wheel[1][1]));
    if(procesval  25) {procesval = procesval - 26;}
   // Serial.print (procesval); Serial.print("   ");
   
    pv = (procesval   (wheel[2][2] - wheel[2][1]));
    if(pv = 100) {procesval = procesval - 100;}
    procesval = (procesval - (wheel[2][2] - wheel[2][1]));
    if(procesval  25) {procesval = procesval - 26;}
   // Serial.print (procesval); Serial.print("   ");
  
    pv = (procesval );
    procesval = rotorvals[reflect[0]   7][pv];
    if(procesval >= 100) {procesval = procesval - 100;}
    if(procesval  25) {procesval = procesval - 26;}
    //Serial.print (procesval); Serial.print("   ");
  
    procesval = rotorvals[reflect[0]   9][procesval];
    //Serial.print (procesval); Serial.print("   ");
  
    pv = (procesval  26);
    procesval = rotorvali[reflect[0]   7][pv];
    if(procesval >= 100) {procesval = procesval - 100;}
    if(procesval  25) {procesval = procesval - 26;}
  // Serial.print (procesval); Serial.print("   ");
 
    pv = (procesval   (wheel[2][2] - wheel[2][1]));
    if(pv = 100) {procesval = procesval - 100;}
    procesval = (procesval - (wheel[2][2] - wheel[2][1]));
    if(procesval  25) {procesval = procesval - 26;}
   //Serial.print (procesval); Serial.print("   ");
 
    pv = (procesval   (wheel[1][2] - wheel[1][1]));
    if(pv = 100) {procesval = procesval - 100;}
    procesval = (procesval - (wheel[1][2] - wheel[1][1]));
    if(procesval  25) {procesval = procesval - 26;}
   //Serial.print (procesval); Serial.print("   ");
  
    pv = (procesval   (wheel[0][2] - wheel[0][1]));
    if(pv = 100) {procesval = procesval - 100;}
    procesval = (procesval - (wheel[0][2] - wheel[0][1]));
    if(procesval  25) {procesval = procesval - 26;}
  
   // Serial.print (procesval); Serial.print("   ");
  
    procesval = plugval[1][procesval];
  
    lampval = procesval;
    //Serial.println(lampval);
    dig2 = wheel[2][2];  dig3 = wheel[1][2]; dig4 = wheel[0][2]; dig1 = reflect[1];
    lampita();
    nixisend();
    lampitb();
    dig1 =37; dig2 = 37; dig3 = 37; dig4 = 37;
    digitalWrite(led5, LOW);
  }

// Helper Function to light the proper key
void lampita() {
   digitalWrite(lamparray[lampval][0],0);
   digitalWrite(lamparray[lampval][1],0);
}
void lampitb(){
   digitalWrite(lamparray[lampval][0],1);
   digitalWrite(lamparray[lampval][1],1); 
}

// Help Function to illuminate "Nixies"
void nixisend() {
sixteenSegWrite(0, dig1);
sixteenSegWrite(1, dig2);
sixteenSegWrite(2, dig3);
sixteenSegWrite(3, dig4);
}

void marquee() {
 
time = millis();
if( mtime = 31) {mdex = 0;}
nixisend();

}
// Function that actually turns on each of 17 appropriate segments on each "Nixie"
void sixteenSegWrite(int digit, int character) {
  digitalWrite(anode[digit],0);
  for (int index = 0; index < 17; index  ) {
  digitalWrite(segment[index], segmentvals[character][index]);
  }
  delay(7);
  for (int index =0; index = 100) {windex1 = 1;}
    wheel[0][2]  ; if(wheel[0][2] > 25) {wheel[0][2] = 0;}
    windex = 0;
    if(windex1 == 1){
    if(rotorvals[wheel[1][0]-27][wheel[1][2]] >= 100) {windex2 = 1;}
    wheel[1][2]  ; if(wheel[1][2] > 25) {wheel[1][2] = 0;}}
    windex1 = 0;
    if(windex2 == 1){
    wheel[2][2]  ; if(wheel[2][2] > 25) {wheel[2][2] = 0;}
      windex2 = 0; }
  }

如果有足够利润,我们将研发印刷电路板,装载更容易组装的全功能哑谜机复制品。请登陆http://www.stgeotronics.com查询商品上架与否、价格、下单或预订吧。电路图已有,印刷电路已进入开发阶段,即将内测。

第九步:电路图


徇众要求,电路图两份在此。

第一个是仿电子管(4个16段显示单元)如何布线,用于显示转子在哑谜机上的输出信号。同时,它们也用于每种调试模式,反馈用户机器设定信息。

第二幅电路图显示26个字母按键及10个功能键、26个键盘灯和5个LED是如何布线的。

所有LED电阻都是470欧,而开关电阻则都是1千欧。 印刷电路设计档仍在修正中。 希望您享受我们第一份制作教程,感谢您抽出宝贵时间阅读!

第十步:PCB样机版


徇众要求,我们设计及定制了一些印刷电路板。

它们终于面世了,如此清纯可爱!我们忙于组装,并测试其中一块样品,确保它在功能上与外观一样完美无暇。更重要的是,能和那台原型测试机一样的功能。 订购回来的底板几乎完美,只需一点引脚线去修补设计瑕疵。而这些瑕疵对功能没有影响,修理它们是小菜一碟。 有了这些引脚,你能更容易制作自己的哑谜复刻机,比起教程里的布线方便多了。我们在此很高兴宣布,测试完成,新型板一样给力! 下单订购请登陆http://www.stgeotronics.com,已发货热卖中。组装图片请见第十一步。 感谢大家的不懈支持和宝贵反馈意见!

第十一步:组装完成的作品






花了一晚上组装完成,如上图所示。 感谢您的支持与反馈!
via 作者:ST-Geotronics(团体) 译者:Kalimov

坐沙发

发表评论

你的邮件地址不会公开


*