-
Notifications
You must be signed in to change notification settings - Fork 1
/
eyes.ino
299 lines (255 loc) · 7.83 KB
/
eyes.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
// import required libraries
#include <SPI.h>
#include <Servo.h>
// Debugging
#define DEBUG_QUALITY 0 // Print the quality of the camera if set to 1
#define DEBUG_XY 0 // Print the x/y coordinates if set to 1
// Left/right eye seen as the zuck himself (left eye in code = right eye if you stand in front of it)
Servo srvRx; // pin 2 - right eye swing
Servo srvRy; // pin 3 - right eye tilt
Servo srvLx; // pin 7 - left eye swing
Servo srvLy; // pin 6 - left eye tilt
Servo srvShut; // pin 5 - eyelids open/close
// Starting positions of the servo's
int posX = 90; // swing middle position
int posY = 90; // tilt middle position
int posS = 90; // shut start position
// Boundaries
// Without boundaries the servo's could move too far and break the eyes
// Or move too far and break the metal wire
short blinkMax = 105; // boundaries for eyelids
short blinkMin = 165; // boundaries for eyelids
short yMin = 39; // eye max up position
short yMax = 153; // eye max down position
short xMin = 60; // eye max right position
short xMax = 127; // eye max left position
short posYrev = 90; // reversed eye tilt position
// Other variables
short loopDelay = 10; // Slow down the loop with 10 ms
// Loop time for blinking
unsigned long blinkTime = 2000;
// No movement detected => rest = true => eyelids closed
unsigned long lastTrack = 0; // keeps track of when the last time movement is detected
bool rest = true; // keeps track of whether the eyes are in rest position (eyelids closed) or not
// Camera pins
#define PIN_MISO 12
#define PIN_MOSI 11
#define PIN_SCK 13
#define PIN_MOUSECAM_RESET 9
#define PIN_MOUSECAM_CS 10
// Amount of pixels on the camera
#define ADNS3080_PIXELS_X 30
#define ADNS3080_PIXELS_Y 30
// Camera Configuration
#define ADNS3080_PRODUCT_ID 0x00
#define ADNS3080_REVISION_ID 0x01
#define ADNS3080_MOTION 0x02
#define ADNS3080_DELTA_X 0x03
#define ADNS3080_DELTA_Y 0x04
#define ADNS3080_SQUAL 0x05
#define ADNS3080_PIXEL_SUM 0x06
#define ADNS3080_MAXIMUM_PIXEL 0x07
#define ADNS3080_CONFIGURATION_BITS 0x0a
#define ADNS3080_EXTENDED_CONFIG 0x0b
#define ADNS3080_DATA_OUT_LOWER 0x0c
#define ADNS3080_DATA_OUT_UPPER 0x0d
#define ADNS3080_SHUTTER_LOWER 0x0e
#define ADNS3080_SHUTTER_UPPER 0x0f
#define ADNS3080_FRAME_PERIOD_LOWER 0x10
#define ADNS3080_FRAME_PERIOD_UPPER 0x11
#define ADNS3080_MOTION_CLEAR 0x12
#define ADNS3080_FRAME_CAPTURE 0x13
#define ADNS3080_SROM_ENABLE 0x14
#define ADNS3080_FRAME_PERIOD_MAX_BOUND_LOWER 0x19
#define ADNS3080_FRAME_PERIOD_MAX_BOUND_UPPER 0x1a
#define ADNS3080_FRAME_PERIOD_MIN_BOUND_LOWER 0x1b
#define ADNS3080_FRAME_PERIOD_MIN_BOUND_UPPER 0x1c
#define ADNS3080_SHUTTER_MAX_BOUND_LOWER 0x1e
#define ADNS3080_SHUTTER_MAX_BOUND_UPPER 0x1e
#define ADNS3080_SROM_ID 0x1f
#define ADNS3080_OBSERVATION 0x3d
#define ADNS3080_INVERSE_PRODUCT_ID 0x3f
#define ADNS3080_PIXEL_BURST 0x40
#define ADNS3080_MOTION_BURST 0x50
#define ADNS3080_SROM_LOAD 0x60
#define ADNS3080_PRODUCT_ID_VAL 23
// Reset the camera
void mousecam_reset()
{
digitalWrite(PIN_MOUSECAM_RESET, HIGH);
delay(1); // reset pulse >10us
digitalWrite(PIN_MOUSECAM_RESET, LOW);
delay(35); // 35ms from reset to functional
}
// Initialize/setup the camera
int mousecam_init()
{
pinMode(PIN_MOUSECAM_RESET, OUTPUT);
pinMode(PIN_MOUSECAM_CS, OUTPUT);
digitalWrite(PIN_MOUSECAM_CS, HIGH);
// Reset the camera
mousecam_reset();
// Make sure the camera is correctly connected by fetching the PID
digitalWrite(PIN_MOUSECAM_CS, LOW);
SPI.transfer(ADNS3080_PRODUCT_ID);
delayMicroseconds(75);
int pid = SPI.transfer(0xff);
digitalWrite(PIN_MOUSECAM_CS, HIGH);
delayMicroseconds(1);
// Make sure the product id matches
if (pid != ADNS3080_PRODUCT_ID_VAL) {
Serial.println("PID Changed:");
Serial.println(pid);
return -1;
}
// Turn on sensitive mode
digitalWrite(PIN_MOUSECAM_CS, LOW);
SPI.transfer(ADNS3080_CONFIGURATION_BITS | 0x80);
SPI.transfer(0x19);
digitalWrite(PIN_MOUSECAM_CS, HIGH);
delayMicroseconds(50);
return 0;
}
// Related to fetching data from the camera
struct MD
{
byte motion;
char dx, dy;
byte squal;
word shutter;
byte max_pix;
};
void mousecam_read_motion(struct MD *p)
{
digitalWrite(PIN_MOUSECAM_CS, LOW);
SPI.transfer(ADNS3080_MOTION_BURST);
delayMicroseconds(75);
p->motion = SPI.transfer(0xff);
p->dx = SPI.transfer(0xff);
p->dy = SPI.transfer(0xff);
p->squal = SPI.transfer(0xff);
p->shutter = SPI.transfer(0xff) << 8;
p->shutter |= SPI.transfer(0xff);
p->max_pix = SPI.transfer(0xff);
digitalWrite(PIN_MOUSECAM_CS, HIGH);
delayMicroseconds(5);
}
void setup()
{
// Start serial connection on 57600
Serial.begin(57600);
// Set camera pins as input/output
pinMode(PIN_MISO, INPUT);
pinMode(PIN_MOSI, OUTPUT);
pinMode(PIN_SCK, OUTPUT);
// Configure serial connection with camera
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV32);
SPI.setDataMode(SPI_MODE3);
SPI.setBitOrder(MSBFIRST);
// Make sure the camera is setup correctly
if (mousecam_init() == -1)
{
Serial.println("Mouse cam failed to init");
while (1);
}
// Set pins for servo's
srvRx.attach(2);
srvRy.attach(3);
srvLx.attach(7);
srvLy.attach(6);
srvShut.attach(5);
// Set servo's to start position
srvRx.write(posX);
srvLx.write(posX);
srvRy.write(posY);
srvLy.write(posY);
srvShut.write(blinkMax);
}
void loop()
{
// Slow down the loop
delay(loopDelay);
// Blinking mechanism
if (!rest) {
blinkTime = blinkTime - loopDelay;
// When "blinkTime" is between 500 and 100 the eyelids will close and open
if (blinkTime <= 100) { // Open eyelids
srvShut.write(blinkMin);
blinkTime = random(2, 12) * 500;
} else if (blinkTime <= 500) { // Close eyelids
srvShut.write(blinkMax);
}
}
// Fetch the movement from the camera
MD md;
mousecam_read_motion(&md);
// Quality debugging
if (DEBUG_QUALITY != 0) {
Serial.println(md.squal);
}
// If movement in x direction is detected
if ((int)md.dx != 0) {
// Make sure eyes are open when movement is detected (not in rest)
if (rest) {
srvShut.write(blinkMin);
rest = false;
}
// Calculate new servo position
posX -= (int)md.dx;
if (posX > xMax) {
posX = xMax;
} else if (posX < xMin) {
posX = xMin;
}
// Set eyes to new x position (swing)
srvRx.write(180 - posX);
srvLx.write(180 - posX);
// Print x coordinate
if (DEBUG_XY != 0) {
Serial.print("X: ");
Serial.println(posX);
}
// Update when last movement is detected
lastTrack = millis();
}
// If movement in y direction is detected
if ((int)md.dy != 0) {
// Make sure eyes are open when movement is detected (not in rest)
if (rest) {
srvShut.write(blinkMin);
rest = false;
}
// Calculate new servo position
posY -= (int)md.dy;
if (posY > yMax) {
posY = yMax;
} else if (posY < yMin) {
posY = yMin;
}
// Set eyes to new y position (tilt)
// Calculate reversed position (the left servo is rotated 180 degrees)
posYrev = map(posY, yMin, yMax, yMax, yMin);
srvRy.write(posY);
srvLy.write(posYrev);
// Print y coordinate
if (DEBUG_XY != 0) {
Serial.print("Y: ");
Serial.println(posY);
}
// Update when last movement is detected
lastTrack = millis();
}
// When no movement detected for 3 seconds, go to "rest" position (eyelids closed)
if (millis() - lastTrack >= 3000) {
rest = true;
posX = 90; // swing
posY = 90; // tilt
posS = 90; // shut
srvRx.write(posX);
srvRy.write(posY);
srvLx.write(posX);
srvLy.write(posY);
srvShut.write(blinkMax);
}
}