Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
393 lines (336 sloc) 9.46 KB
#include "LedControl.h"
// Pins: DIN,CLK,CS, # of Display connected
LedControl lc = LedControl(12, 11, 10, 2);
#define POT_PIN 0
#define DEBUG 1
#define INTENSITY 0
#define EYE_LEFT 0
#define EYE_RIGHT 1
#define NUM_EYES 2
#define NUM_COLS 8
#define NUM_ROWS 8
#define MARGIN_X 1
#define MARGIN_Y 1
#define PUPIL_COLS 2
#define PUPIL_ROWS 2
#define MIN_PUPIL_X 0
#define MAX_PUPIL_X NUM_COLS - PUPIL_COLS - MARGIN_X * 2
#define MIN_PUPIL_Y 0
#define MAX_PUPIL_Y NUM_ROWS - PUPIL_ROWS - MARGIN_Y * 2
#define MIN_INTENSITY 1
#define MAX_INTENSITY 15
// Templates for pupil and eyeball
const byte template_pupil = B11000000;
const byte template_eye[8] = {
B00111100,
B01111110,
B11111111,
B11111111,
B11111111,
B11111111,
B01111110,
B00111100
};
#define OP_MOVE 10
#define OP_BLINK 20
#define OP_WANDER 30
#define OP_END 99
#define WAIT_QUICK 50
#define WAIT_FAST 200
#define WAIT_MED 400
#define WAIT_SLOW 800
#define WAIT_HOLD 1600
#define WAIT_RANDOM -999
#define WANDER_WAIT 100
#define MIN_WANDER_STEPS 10
#define MAX_WANDER_STEPS 25
#define BLINK_WAIT 5
#define RANDOM_WAIT_MIN 750
#define RANDOM_WAIT_MAX 1500
struct AnimationStep {
int op_left;
int left_x;
int left_y;
int op_right;
int right_x;
int right_y;
int wait;
};
AnimationStep ANIM_STARE[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_RANDOM},
{OP_BLINK,2,2,OP_BLINK,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_SIDE_TO_SIDE[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,1,2,OP_MOVE,1,2,WAIT_QUICK},
{OP_MOVE,0,2,OP_MOVE,0,2,WAIT_HOLD},
{OP_MOVE,1,2,OP_MOVE,1,2,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,3,2,OP_MOVE,3,2,WAIT_QUICK},
{OP_MOVE,4,2,OP_MOVE,4,2,WAIT_HOLD},
{OP_MOVE,3,2,OP_MOVE,3,2,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_ROLL[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,1,2,OP_MOVE,1,2,WAIT_QUICK},
{OP_MOVE,0,2,OP_MOVE,0,2,WAIT_QUICK},
{OP_MOVE,0,1,OP_MOVE,0,1,WAIT_QUICK},
{OP_MOVE,0,0,OP_MOVE,0,0,WAIT_QUICK},
{OP_MOVE,1,0,OP_MOVE,1,0,WAIT_QUICK},
{OP_MOVE,2,0,OP_MOVE,2,0,WAIT_QUICK},
{OP_MOVE,3,0,OP_MOVE,3,0,WAIT_QUICK},
{OP_MOVE,4,0,OP_MOVE,4,0,WAIT_QUICK},
{OP_MOVE,4,1,OP_MOVE,4,1,WAIT_QUICK},
{OP_MOVE,4,2,OP_MOVE,4,2,WAIT_QUICK},
{OP_MOVE,3,2,OP_MOVE,3,2,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_HOLD},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_CROSSED[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,3,2,OP_MOVE,1,2,WAIT_QUICK},
{OP_MOVE,4,2,OP_MOVE,0,2,WAIT_HOLD},
{OP_MOVE,3,2,OP_MOVE,1,2,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_HOLD},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_DERP[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,1,2,OP_MOVE,3,2,WAIT_QUICK},
{OP_MOVE,0,2,OP_MOVE,4,2,WAIT_HOLD},
{OP_MOVE,1,2,OP_MOVE,3,2,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_HOLD},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_LOOK_NOSE[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,3,3,OP_MOVE,1,3,WAIT_QUICK},
{OP_MOVE,4,4,OP_MOVE,0,4,WAIT_HOLD},
{OP_BLINK,4,4,OP_BLINK,0,4,WAIT_RANDOM},
{OP_MOVE,3,3,OP_MOVE,1,3,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_LOOK_BROW[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,3,1,OP_MOVE,1,1,WAIT_QUICK},
{OP_MOVE,4,0,OP_MOVE,0,0,WAIT_HOLD},
{OP_BLINK,4,0,OP_BLINK,0,0,WAIT_RANDOM},
{OP_MOVE,3,1,OP_MOVE,1,1,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_WINK_LEFT[] = {
{OP_BLINK,2,2,OP_MOVE,2,2,WAIT_RANDOM},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_WINK_RIGHT[] = {
{OP_MOVE,2,2,OP_BLINK,2,2,WAIT_RANDOM},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_DOWN_LEFT[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,1,3,OP_MOVE,1,3,WAIT_QUICK},
{OP_MOVE,0,4,OP_MOVE,0,4,WAIT_HOLD},
{OP_MOVE,1,3,OP_MOVE,1,3,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_DOWN_RIGHT[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,3,3,OP_MOVE,3,3,WAIT_QUICK},
{OP_MOVE,4,4,OP_MOVE,4,4,WAIT_HOLD},
{OP_MOVE,3,3,OP_MOVE,3,3,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_UP_LEFT[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,1,1,OP_MOVE,1,1,WAIT_QUICK},
{OP_MOVE,0,0,OP_MOVE,0,0,WAIT_HOLD},
{OP_MOVE,1,1,OP_MOVE,1,1,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_UP_RIGHT[] = {
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_MOVE,3,1,OP_MOVE,3,1,WAIT_QUICK},
{OP_MOVE,4,0,OP_MOVE,4,0,WAIT_HOLD},
{OP_MOVE,3,1,OP_MOVE,3,1,WAIT_QUICK},
{OP_MOVE,2,2,OP_MOVE,2,2,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep ANIM_WANDER[] = {
{OP_WANDER,0,0,OP_WANDER,0,0,WAIT_QUICK},
{OP_END,0,0,0,0,0,0}
};
AnimationStep* animations[] = {
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_STARE,
ANIM_SIDE_TO_SIDE,
ANIM_SIDE_TO_SIDE,
ANIM_SIDE_TO_SIDE,
ANIM_SIDE_TO_SIDE,
ANIM_ROLL,
ANIM_ROLL,
ANIM_ROLL,
ANIM_ROLL,
ANIM_WANDER,
ANIM_CROSSED,
ANIM_DERP,
ANIM_LOOK_NOSE,
ANIM_LOOK_BROW,
ANIM_WINK_LEFT,
ANIM_WINK_RIGHT,
ANIM_UP_LEFT,
ANIM_UP_RIGHT,
ANIM_DOWN_LEFT,
ANIM_DOWN_RIGHT
};
// Display buffer for rendering eyes
byte eyes[NUM_EYES + 1][NUM_ROWS + 1];
// Prerendered positions of the eyeball & pupil
byte positions[MAX_PUPIL_X + 1][MAX_PUPIL_Y + 1][NUM_ROWS + 1];
void setup() {
#ifdef DEBUG
Serial.begin(9600);
while (!Serial) { }
Serial.println("STARTING UP");
#endif
prerenderPositions();
resetDisplay();
}
int pot_val;
void loop() {
pot_val = analogRead(POT_PIN);
for (int i=0; i<NUM_EYES; i++) {
lc.setIntensity(i, map(pot_val, 0, 1023, 0, 15));
}
runAnimation(animations[random(0, sizeof(animations) / sizeof(AnimationStep*))]);
}
// This is a total mess, and it shames me.
void runAnimation(struct AnimationStep* step_ptr) {
while (1) {
AnimationStep curr = *step_ptr;
if (curr.op_left == OP_END) { break; }
if (curr.op_left == OP_BLINK && curr.op_right == OP_BLINK) {
blinkEyes(1, 1, curr.left_x, curr.left_y, curr.right_x, curr.right_y);
} else if (curr.op_left == OP_WANDER && curr.op_right == OP_WANDER) {
wanderEyes();
} else {
if (curr.op_left == OP_MOVE) {
copyEyeballPosition(EYE_LEFT, curr.left_x, curr.left_y);
}
if (curr.op_right == OP_MOVE) {
copyEyeballPosition(EYE_RIGHT, curr.right_x, curr.right_y);
}
updateDisplay();
if (curr.op_left == OP_BLINK) {
blinkEyes(1, 0, curr.left_x, curr.left_y, curr.right_x, curr.right_y);
}
if (curr.op_right == OP_BLINK) {
blinkEyes(0, 1, curr.left_x, curr.left_y, curr.right_x, curr.right_y);
}
}
if (curr.wait == WAIT_RANDOM) {
delay(random(RANDOM_WAIT_MIN, RANDOM_WAIT_MAX));
} else {
delay(curr.wait);
}
step_ptr += 1;
}
}
// Blink by rapidly dropping and raising the "eyelid"
void blinkEyes(int do_left, int do_right, int left_x, int left_y, int right_x, int right_y) {
int y;
for (int idx = -NUM_ROWS; idx <= NUM_ROWS; idx++) {
y = NUM_ROWS - abs(idx);
if (do_left) {
copyEyeballPosition(EYE_LEFT, left_x, left_y);
renderEyelid(EYE_LEFT, y);
}
if (do_right) {
copyEyeballPosition(EYE_RIGHT, right_x, right_y);
renderEyelid(EYE_RIGHT, y);
}
updateDisplay();
delay(BLINK_WAIT);
}
}
// Random wandering eyes
void wanderEyes() {
int x = 2;
int y = 2;
int max_steps = random(MIN_WANDER_STEPS, MAX_WANDER_STEPS);
for (int steps = 0; steps < max_steps; steps++) {
x += random(-1, 2);
x = constrain(x, MIN_PUPIL_X, MAX_PUPIL_X);
y += random(-1, 2);
y = constrain(y, MIN_PUPIL_Y, MAX_PUPIL_Y);
copyEyeballPosition(EYE_LEFT, x, y);
copyEyeballPosition(EYE_RIGHT, x, y);
updateDisplay();
delay(WANDER_WAIT);
}
}
// Render "eyelid" in display buffer by blacking out rows
void renderEyelid(int idx, int y) {
for (int i=0; i<y; i++) {
eyes[idx][i] = 0;
}
}
// Ensure the displays are on, intense, and cleared.
void resetDisplay() {
for (int i=0; i<NUM_EYES; i++) {
lc.shutdown(i, false);
lc.setIntensity(i, INTENSITY);
lc.clearDisplay(i);
}
}
// Set the eyeball by copying a prerendered position
void copyEyeballPosition(int idx, int x, int y) {
memcpy(eyes[idx], positions[x][y], NUM_ROWS);
}
// Fill the set of prerendered positions for all x/y pairs
// Probably a gross over-optimization, but kind of hoping
// doing less math on every eye position will save battery life
void prerenderPositions() {
for (int x=0; x<=MAX_PUPIL_X; x++) {
for (int y=0; y<=MAX_PUPIL_Y; y++) {
renderPosition(x, y);
}
}
}
// Render one eyeball by copying in blank template and
// blacking out the pupil at correct location
void renderPosition(int x, int y) {
// Copy in the blank eyeball template
memcpy(positions[x][y], template_eye, NUM_ROWS);
// Use binary right shift to adjust pupil to x position
byte pupil = template_pupil >> (x + MARGIN_X);
// Use XOR to black out the pupil in the appropriate rows
int start_y = y + MARGIN_Y;
int end_y = start_y + PUPIL_ROWS;
for (int i=start_y; i<end_y; i++) {
positions[x][y][i] ^= pupil;
}
}
// Set the LED contents from the display buffers
void updateDisplay() {
for (int j=0; j<NUM_ROWS; j++) {
for (int i=0; i<NUM_EYES; i++) {
lc.setRow(i, j, eyes[i][j]);
}
}
}