Last active
October 15, 2016 14:52
-
-
Save GreenMoonArt/3aaf5d6440a3a255db16ac56bb7f7018 to your computer and use it in GitHub Desktop.
RoboFace - modified from Adafruit_LED_Backpack Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Edited by @GreenMoonArt for TechShop DC's Halloween 2016 | |
/* | |
Project based on: | |
https://learn.adafruit.com/animating-multiple-led-backpacks/ideas?view=all | |
Libraries and IDE board support: | |
Adafruit_LED_Backpack | |
https://github.com/adafruit/Adafruit_LED_Backpack | |
Adafruit-GFX-Library | |
https://github.com/adafruit/Adafruit-GFX-Library | |
Adafruit Board Support package | |
Instructions on this page: | |
https://learn.adafruit.com/adafruit-arduino-ide-setup/arduino-1-dot-6-x-ide | |
link for board manager: | |
https://adafruit.github.io/arduino-board-index/package_adafruit_index.json | |
*/ | |
// 'roboface' example sketch for Adafruit I2C 8x8 LED backpacks: | |
// | |
// www.adafruit.com/products/870 www.adafruit.com/products/1049 | |
// www.adafruit.com/products/871 www.adafruit.com/products/1050 | |
// www.adafruit.com/products/872 www.adafruit.com/products/1051 | |
// www.adafruit.com/products/959 www.adafruit.com/products/1052 | |
// | |
// Requires Adafruit_LEDBackpack and Adafruit_GFX libraries. | |
// For a simpler introduction, see the 'matrix8x8' example. | |
// | |
// This sketch demonstrates a couple of useful techniques: | |
// 1) Addressing multiple matrices (using the 'A0' and 'A1' solder | |
// pads on the back to select unique I2C addresses for each). | |
// 2) Displaying the same data on multiple matrices by sharing the | |
// same I2C address. | |
// | |
// This example uses 5 matrices at 4 addresses (two share an address) | |
// to animate a face: | |
// (for workshop we are using just eyes and middle mouth "2" position) | |
// | |
// 0 0 | |
// | |
// 1 2 3 | |
// | |
// The 'eyes' both display the same image (always looking the same | |
// direction -- can't go cross-eyed) and thus share the same address | |
// (0x70). The three matrices forming the mouth have unique addresses | |
// (0x71, 0x72 and 0x73). | |
// | |
// The face animation as written is here semi-random; this neither | |
// generates nor responds to actual sound, it's simply a visual effect | |
// Consider this a stepping off point for your own project. Maybe you | |
// could 'puppet' the face using joysticks, or synchronize the lips to | |
// audio from a Wave Shield (see wavface example). Currently there are | |
// only six images for the mouth. This is often sufficient for simple | |
// animation, as explained here: | |
// http://www.idleworm.com/how/anm/03t/talk1.shtml | |
// | |
// Adafruit invests time and resources providing this open source code, | |
// please support Adafruit and open-source hardware by purchasing | |
// products from Adafruit! | |
// | |
// Written by P. Burgess for Adafruit Industries. | |
// BSD license, all text above must be included in any redistribution. | |
#include <Arduino.h> | |
#include <Wire.h> | |
#include "Adafruit_LEDBackpack.h" | |
#include "Adafruit_GFX.h" | |
// Because the two eye matrices share the same address, only four | |
// matrix objects are needed for the five displays: | |
#define MATRIX_EYES 0 | |
#define MATRIX_MOUTH_LEFT 1 | |
#define MATRIX_MOUTH_MIDDLE 2 | |
#define MATRIX_MOUTH_RIGHT 3 | |
Adafruit_8x8matrix matrix[4] = { // Array of Adafruit_8x8matrix objects | |
Adafruit_8x8matrix(), Adafruit_8x8matrix(), | |
Adafruit_8x8matrix(), Adafruit_8x8matrix() }; | |
// Rather than assigning matrix addresses sequentially in a loop, each | |
// has a spot in this array. This makes it easier if you inadvertently | |
// install one or more matrices in the wrong physical position -- | |
// re-order the addresses in this table and you can still refer to | |
// matrices by index above, no other code or wiring needs to change. | |
static const uint8_t matrixAddr[] = { 0x70, 0x71, 0x72, 0x73 }; | |
static const uint8_t PROGMEM // Bitmaps are stored in program memory | |
blinkImg[][8] = { // Eye animation frames | |
{ B00111100, // Fully open eye | |
B01111110, | |
B11111111, | |
B11111111, | |
B11111111, | |
B11111111, | |
B01111110, | |
B00111100 }, | |
{ B00000000, | |
B01111110, | |
B11111111, | |
B11111111, | |
B11111111, | |
B11111111, | |
B01111110, | |
B00111100 }, | |
{ B00000000, | |
B00000000, | |
B00111100, | |
B11111111, | |
B11111111, | |
B11111111, | |
B00111100, | |
B00000000 }, | |
{ B00000000, | |
B00000000, | |
B00000000, | |
B00111100, | |
B11111111, | |
B01111110, | |
B00011000, | |
B00000000 }, | |
{ B00000000, // Fully closed eye | |
B00000000, | |
B00000000, | |
B00000000, | |
B10000001, | |
B01111110, | |
B00000000, | |
B00000000 } }, | |
//modified middle column since only one matrix is being used for | |
// this project: | |
mouthImg[][24] = { // Mouth animation frames | |
{ B00000000, B00000000, B00000000, // Mouth position A | |
B00000000, B00000000, B00000000, | |
B01111111, B11111111, B11111110, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000 }, | |
{ B00000000, B00000000, B00000000, // Mouth position B | |
B00000000, B00000000, B00000000, | |
B00111111, B01111110, B11111100, | |
B00000111, B10000001, B11100000, | |
B00000000, B01111110, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000 }, | |
{ B00000000, B00000000, B00000000, // Mouth position C | |
B00000000, B00000000, B00000000, | |
B00111111, B01111110, B11111100, | |
B00001000, B10000001, B00010000, | |
B00000110, B10000001, B01100000, | |
B00000001, B01000010, B10000000, | |
B00000000, B00111100, B00000000, | |
B00000000, B00000000, B00000000 }, | |
{ B00000000, B00000000, B00000000, // Mouth position D | |
B00000000, B00000000, B00000000, | |
B00111111, B00111100, B11111100, | |
B00100000, B01000010, B00000100, | |
B00010000, B01000010, B00001000, | |
B00001100, B10000001, B00110000, | |
B00000011, B01000010, B11000000, | |
B00000000, B00111100, B00000000 }, | |
{ B00000000, B00000000, B00000000, // Mouth position E | |
B00000000, B00111100, B00000000, | |
B00011111, B11000011, B11111000, | |
B00000011, B10000001, B11000000, | |
B00000000, B01111110, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000 }, | |
{ B00000000, B00111100, B00000000, // Mouth position F | |
B00000000, B11000011, B00000000, | |
B00001111, B10000001, B11110000, | |
B00000001, B10000001, B10000000, | |
B00000000, B11000011, B00000000, | |
B00000000, B00111100, B00000000, | |
B00000000, B00000000, B00000000, | |
B00000000, B00000000, B00000000 } }; | |
uint8_t | |
blinkIndex[] = { 1, 2, 3, 4, 3, 2, 1 }, // Blink bitmap sequence | |
blinkCountdown = 100, // Countdown to next blink (in frames) | |
gazeCountdown = 75, // Countdown to next eye movement | |
gazeFrames = 50, // Duration of eye movement (smaller = faster) | |
mouthPos = 0, // Current image number for mouth | |
mouthCountdown = 10; // Countdown to next mouth change | |
int8_t | |
eyeX = 3, eyeY = 3, // Current eye position | |
newX = 3, newY = 3, // Next eye position | |
dX = 0, dY = 0; // Distance from prior to new position | |
void setup() { | |
// Seed random number generator from an unused analog input: | |
randomSeed(analogRead(A0)); | |
// Initialize each matrix object: | |
for(uint8_t i=0; i<4; i++) { | |
matrix[i].begin(matrixAddr[i]); | |
// If using 'small' (1.2") displays vs. 'mini' (0.8"), enable this: | |
// matrix[i].setRotation(3); | |
} | |
} | |
void loop() { | |
// Draw eyeball in current state of blinkyness (no pupil). Note that | |
// only one eye needs to be drawn. Because the two eye matrices share | |
// the same address, the same data will be received by both. | |
matrix[MATRIX_EYES].clear(); | |
// When counting down to the next blink, show the eye in the fully- | |
// open state. On the last few counts (during the blink), look up | |
// the corresponding bitmap index. | |
matrix[MATRIX_EYES].drawBitmap(0, 0, | |
blinkImg[ | |
(blinkCountdown < sizeof(blinkIndex)) ? // Currently blinking? | |
blinkIndex[blinkCountdown] : // Yes, look up bitmap # | |
0 // No, show bitmap 0 | |
], 8, 8, LED_ON); | |
// Decrement blink counter. At end, set random time for next blink. | |
if(--blinkCountdown == 0) blinkCountdown = random(5, 180); | |
// Add a pupil (2x2 black square) atop the blinky eyeball bitmap. | |
// Periodically, the pupil moves to a new position... | |
if(--gazeCountdown <= gazeFrames) { | |
// Eyes are in motion - draw pupil at interim position | |
matrix[MATRIX_EYES].fillRect( | |
newX - (dX * gazeCountdown / gazeFrames), | |
newY - (dY * gazeCountdown / gazeFrames), | |
2, 2, LED_OFF); | |
if(gazeCountdown == 0) { // Last frame? | |
eyeX = newX; eyeY = newY; // Yes. What's new is old, then... | |
do { // Pick random positions until one is within the eye circle | |
newX = random(7); newY = random(7); | |
dX = newX - 3; dY = newY - 3; | |
} while((dX * dX + dY * dY) >= 10); // Thank you Pythagoras | |
dX = newX - eyeX; // Horizontal distance to move | |
dY = newY - eyeY; // Vertical distance to move | |
gazeFrames = random(3, 15); // Duration of eye movement | |
gazeCountdown = random(gazeFrames, 120); // Count to end of next movement | |
} | |
} else { | |
// Not in motion yet -- draw pupil at current static position | |
matrix[MATRIX_EYES].fillRect(eyeX, eyeY, 2, 2, LED_OFF); | |
} | |
// Draw mouth, switch to new random image periodically | |
drawMouth(mouthImg[mouthPos]); | |
if(--mouthCountdown == 0) { | |
mouthPos = random(6); // Random image | |
// If the 'neutral' position was chosen, there's a 1-in-5 chance we'll | |
// select a longer hold time. This gives the appearance of periodic | |
// pauses in speech (e.g. between sentences, etc.). | |
mouthCountdown = ((mouthPos == 0) && (random(5) == 0)) ? | |
random(10, 40) : // Longer random duration | |
random(2, 8); // Shorter random duration | |
} | |
// Refresh all of the matrices in one quick pass | |
for(uint8_t i=0; i<4; i++) matrix[i].writeDisplay(); | |
delay(20); // ~50 FPS | |
} | |
// Draw mouth image across three adjacent displays | |
void drawMouth(const uint8_t *img) { | |
for(uint8_t i=0; i<3; i++) { | |
matrix[MATRIX_MOUTH_LEFT + i].clear(); | |
matrix[MATRIX_MOUTH_LEFT + i].drawBitmap(i * -8, 0, img, 24, 8, LED_ON); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment