-
Notifications
You must be signed in to change notification settings - Fork 66
/
Copy pathcombobreaker.ino
381 lines (310 loc) · 7.97 KB
/
combobreaker.ino
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
/*
C-C-C-C-Combo Breaker
by samy kamkar
http://samy.pl/combobreaker
A motorized, battery powered, 3D printed,
Arduino-based combination lock cracking device.
current hardware:
Stepper Motor (dial): 28STH32-0674B (3.8V 0.67A stepper)
Rotary Encoder: HKT22-3531 optical rotary encoder (1200steps)
Servo Motor (shackle): Batan S1213 (with analog feedback)
Stepper Driver: Allegro A3967 or Allegro A4988 (EasyDriver, 750mA, 1/8th step)
Microcontroller: Arduino Nano ATmega328P @ 5V
Stepper/Arduino Power: 3S LiPo battery (12V power supply also works)
Servo Power: L7805 Voltage Regulator
*/
// Servo requirements: at least 6.5kg/cm of torque
// TODO: CW could be autodetected by the rotary encoder
// Depending on your stepper wiring, you may need to change CW to true or false
#define CW true // clockwise
#define CCW !CW // counter-clockwise
// Servo details (lifts the shackle)
#include <Servo.h>
#include <Encoder.h>
#define SERVO_DEFAULT 30
#define SERVO_MIN 0
#define SERVO_MAX 200
#define SERVO_PIN 9
#define SERVO_FEEDBACK_PIN A0
#define FB_DIFF 10
#define FB_TOLERANCE 3.5
#define FB_DELAY 500
// Encoder details (pins)
#define ENCODER1 2 // 5
#define ENCODER2 3 // 6
#define ENCODER_STEPS 1200 // 1200 per rotation
// Stepper driver details (controls the dial)
#define DIR_PIN 4
#define STEP_PIN 5
#define STEPS 200
#define MICROSTEPS 8 // was 8 on easydriver
// fastest (us) we can get stepper moving without skipping
// #define MIN_DELAY (1600 / MICROSTEPS) // was 200
#define MIN_DELAY 400
// Number of digits on a Master combo lock
#define DIGITS 40
// C's % is remainder instead of modulo
#define mod(a, b) ((a % b + b) % b)
// For debug
#define BAUDRATE 9600
// this means we can rotate *consistently* 3.125 times per second (187.5rpm):
// 1sec in us / min_delay / (steps * microsteps)
// 1_000_000 / 200 / (200 * 8) = 3.125
int current_digit = 0;
Servo shackle;
Encoder myEnc(ENCODER1, ENCODER2);
//int servo_pos = 0;
long oldPosition;
int mini, maxi, minFeedback, maxFeedback;
void setup()
{
Serial.begin(BAUDRATE);
pinMode(DIR_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
pinMode(SERVO_FEEDBACK_PIN, INPUT);
shackle.attach(SERVO_PIN);
oldPosition = myEnc.read();
calibrateServo();
}
void loop()
{
brute(-1, -1, -1);
for (int i = 0; i < ceil(DIGITS/3); i++)
{
// turn to new position
spinto(0, i * 3, CCW);
// hold shackle high to find left and right
shackleHigh();
spinto(0, (i-1)*3, CW);
// delay(100);
Serial.print("spin low ");
Serial.print(oldPosition);
Serial.print(" ");
spinto(0, (i+1)*3, CCW);
// delay(100);
Serial.print(oldPosition);
Serial.println("");
// delay(100);
spinto(0, i * 3, CCW);
shackleMid();
delay(2000);
}
// spinto(1, 0, CW);
// brute(16, -1, 16);
// pin(0, 0, 0);
// openShackle();
}
// pulls the shackle up
// NOTE: if you do this, you should immediately
// detect if the servo actually moved up, and if not,
// return back to shackleMid(), otherwise you'll be
// putting stress on the motor and 3d printed case
void shackleHigh()
{
shackle.write(maxi + (FB_DIFF*(FB_TOLERANCE-1)));
}
// puts the shackle in middle position
void shackleMid()
{
shackle.write(maxi);
}
// attempts to open the shackle *safely*
boolean openShackle()
{
int fb;
boolean ret = false;
shackle.write(maxi + (FB_DIFF*FB_TOLERANCE*1.5));
delay(300);
fb = analogRead(SERVO_FEEDBACK_PIN);
if (fb - maxFeedback > FB_DIFF*FB_TOLERANCE)
{
Serial.println("OPENED!");
ret = true;
}
else
{
ret = false;
Serial.println("not opened :(");
}
shackleMid();
return ret;
}
// detect the middle and high positions of the servo
void calibrateServo()
{
int fb;
int i = SERVO_DEFAULT;
shackle.write(i);
delay(500);
fb = analogRead(SERVO_FEEDBACK_PIN);
Serial.println(fb);
for (i -= FB_DIFF; i > SERVO_MIN; i -= FB_DIFF)
{
Serial.print("i=");
Serial.print(i);
shackle.write(i);
mini = i;
delay(FB_DELAY);
minFeedback = analogRead(SERVO_FEEDBACK_PIN);
Serial.print(" fb=");
Serial.println(minFeedback);
// if difference is less than 10, we can't move!
if (fb - minFeedback < FB_DIFF)
{
mini = i + (FB_DIFF*FB_TOLERANCE);
Serial.println("crap!");
break;
}
fb = minFeedback;
}
for (i+=FB_DIFF*2; i <= SERVO_MAX; i += FB_DIFF)
{
Serial.print("i=");
Serial.print(i);
shackle.write(i);
maxi = i;
delay(FB_DELAY);
maxFeedback = analogRead(SERVO_FEEDBACK_PIN);
Serial.print(" fb=");
Serial.println(maxFeedback);
// if difference is less than 10, we can't move!
if (maxFeedback - fb < FB_DIFF)
{
maxi = i - (FB_DIFF*FB_TOLERANCE);
Serial.print("crap!! changing to ");
Serial.println(maxi);
shackleMid();
break;
}
fb = maxFeedback;
}
Serial.print("mini=");
Serial.print(mini);
Serial.print(" maxi=");
Serial.print(maxi);
Serial.println("");
}
// brute force pin numbers based off of the
// mathematical congruencies of the lock
void brute(int pin1, int pin2, int pin3)
{
int si = pin1, sj = pin2, sk = pin3;
if (pin1 == -1)
si = pin2 >= 0 ?
(pin2 + 2) % 4 :
pin3 >= 0 ?
pin3 % 4 : 0;
if (pin2 == -1)
sj = pin1 >= 0 ?
(pin1 + 2) % 4 :
pin3 >= 0 ?
(pin3 + 2) % 4 : 0;
if (pin3 == -1)
sk = pin1 >= 0 ?
pin1 % 4 :
pin2 >= 0 ?
(pin2 + 2) % 4 : 0;
Serial.println(si);
Serial.println(sj);
Serial.println(sk);
Serial.println("go");
for (int i = si; (pin1 == i || pin1 == -1) && i < DIGITS; i += 4)
for (int j = sj; (pin2 == j || pin2 == -1) && j < DIGITS; j += 4)
for (int k = sk; (pin3 == k || pin3 == -1) && k < DIGITS; k += 4)
{
Serial.print("testing: ");
Serial.print(i);
Serial.print(" ");
Serial.print(j);
Serial.print(" ");
Serial.print(k);
Serial.println();
pin(i, j, k);
if (openShackle())
return;
delay(100);
}
}
// enter a full combination through the dial
void pin(int pin1, int pin2, int pin3)
{
// clockwise 3 times, then to first number
spinto(3, pin1, CW);
// counterclockwise once, then to second
spinto(1, pin2, CCW);
// clockwise to third number
spinto(0, pin3, CW);
}
// spin to a specific digit,
// possibly with some additional rotations
void spinto(int spins, int digit, boolean cw)
{
digitalWrite(DIR_PIN, cw ? HIGH : LOW);
// spin some amount all the way?
for (int i = 0; i < spins; i++)
{
step(STEPS);
// delay(1000);
}
// track where we are for next time (get set later)
int tmp = digit;
Serial.print("going to ");
Serial.print(digit);
Serial.print(" cur=");
Serial.print(current_digit);
if (cw == CW)
digit = (DIGITS - digit) + current_digit;
else
digit = digit - current_digit;
digit = mod(digit, DIGITS);
// track where we are for next time
current_digit = tmp;
Serial.print(" moving ");
Serial.println(digit);
step(digit * (STEPS / DIGITS));
// delay(1000);
}
/*
void rotate(int digits, boolean cw)
{
rotate(0, digits, cw);
}
void rotate(int spins, int digits, boolean cw)
{
digitalWrite(DIR_PIN, cw ? HIGH : LOW);
// spin some amount all the way?
for (int i = 0; i < spins; i++)
{
step(STEPS);
// delay(1000);
}
step(digits * (STEPS / DIGITS));
// delay(1000);
}
*/
// step to location
// TODO: return encoder feedback
void step(int steps)
{
long newPosition;
for (int i = 0; i < steps * MICROSTEPS; i++)
{
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(MIN_DELAY);
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(MIN_DELAY);
}
// get encoder position
newPosition = myEnc.read();
if (newPosition != oldPosition)
{
// oldPosition = newPosition;
oldPosition = mod(newPosition, ENCODER_STEPS);//XXX
Serial.print("newpos=");
Serial.print(newPosition);
Serial.print(" actual=");
Serial.print((int)(oldPosition / DIGITS));
Serial.print(" pos=");
Serial.println(oldPosition);
}
}