roboface.pde 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // 'roboface' example sketch for Adafruit I2C 8x8 LED backpacks:
  2. //
  3. // www.adafruit.com/products/870 www.adafruit.com/products/1049
  4. // www.adafruit.com/products/871 www.adafruit.com/products/1050
  5. // www.adafruit.com/products/872 www.adafruit.com/products/1051
  6. // www.adafruit.com/products/959 www.adafruit.com/products/1052
  7. //
  8. // Requires Adafruit_LEDBackpack and Adafruit_GFX libraries.
  9. // For a simpler introduction, see the 'matrix8x8' example.
  10. //
  11. // This sketch demonstrates a couple of useful techniques:
  12. // 1) Addressing multiple matrices (using the 'A0' and 'A1' solder
  13. // pads on the back to select unique I2C addresses for each).
  14. // 2) Displaying the same data on multiple matrices by sharing the
  15. // same I2C address.
  16. //
  17. // This example uses 5 matrices at 4 addresses (two share an address)
  18. // to animate a face:
  19. //
  20. // 0 0
  21. //
  22. // 1 2 3
  23. //
  24. // The 'eyes' both display the same image (always looking the same
  25. // direction -- can't go cross-eyed) and thus share the same address
  26. // (0x70). The three matrices forming the mouth have unique addresses
  27. // (0x71, 0x72 and 0x73).
  28. //
  29. // The face animation as written is here semi-random; this neither
  30. // generates nor responds to actual sound, it's simply a visual effect
  31. // Consider this a stepping off point for your own project. Maybe you
  32. // could 'puppet' the face using joysticks, or synchronize the lips to
  33. // audio from a Wave Shield (see wavface example). Currently there are
  34. // only six images for the mouth. This is often sufficient for simple
  35. // animation, as explained here:
  36. // http://www.idleworm.com/how/anm/03t/talk1.shtml
  37. //
  38. // Adafruit invests time and resources providing this open source code,
  39. // please support Adafruit and open-source hardware by purchasing
  40. // products from Adafruit!
  41. //
  42. // Written by P. Burgess for Adafruit Industries.
  43. // BSD license, all text above must be included in any redistribution.
  44. #include <Arduino.h>
  45. #include <Wire.h>
  46. #include <Adafruit_GFX.h>
  47. #include "Adafruit_LEDBackpack.h"
  48. // Because the two eye matrices share the same address, only four
  49. // matrix objects are needed for the five displays:
  50. #define MATRIX_EYES 0
  51. #define MATRIX_MOUTH_LEFT 1
  52. #define MATRIX_MOUTH_MIDDLE 2
  53. #define MATRIX_MOUTH_RIGHT 3
  54. Adafruit_8x8matrix matrix[4] = { // Array of Adafruit_8x8matrix objects
  55. Adafruit_8x8matrix(), Adafruit_8x8matrix(),
  56. Adafruit_8x8matrix(), Adafruit_8x8matrix() };
  57. // Rather than assigning matrix addresses sequentially in a loop, each
  58. // has a spot in this array. This makes it easier if you inadvertently
  59. // install one or more matrices in the wrong physical position --
  60. // re-order the addresses in this table and you can still refer to
  61. // matrices by index above, no other code or wiring needs to change.
  62. static const uint8_t matrixAddr[] = { 0x70, 0x71, 0x72, 0x73 };
  63. static const uint8_t PROGMEM // Bitmaps are stored in program memory
  64. blinkImg[][8] = { // Eye animation frames
  65. { B00111100, // Fully open eye
  66. B01111110,
  67. B11111111,
  68. B11111111,
  69. B11111111,
  70. B11111111,
  71. B01111110,
  72. B00111100 },
  73. { B00000000,
  74. B01111110,
  75. B11111111,
  76. B11111111,
  77. B11111111,
  78. B11111111,
  79. B01111110,
  80. B00111100 },
  81. { B00000000,
  82. B00000000,
  83. B00111100,
  84. B11111111,
  85. B11111111,
  86. B11111111,
  87. B00111100,
  88. B00000000 },
  89. { B00000000,
  90. B00000000,
  91. B00000000,
  92. B00111100,
  93. B11111111,
  94. B01111110,
  95. B00011000,
  96. B00000000 },
  97. { B00000000, // Fully closed eye
  98. B00000000,
  99. B00000000,
  100. B00000000,
  101. B10000001,
  102. B01111110,
  103. B00000000,
  104. B00000000 } },
  105. mouthImg[][24] = { // Mouth animation frames
  106. { B00000000, B00000000, B00000000, // Mouth position A
  107. B00000000, B00000000, B00000000,
  108. B01111111, B11111111, B11111110,
  109. B00000000, B00000000, B00000000,
  110. B00000000, B00000000, B00000000,
  111. B00000000, B00000000, B00000000,
  112. B00000000, B00000000, B00000000,
  113. B00000000, B00000000, B00000000 },
  114. { B00000000, B00000000, B00000000, // Mouth position B
  115. B00000000, B00000000, B00000000,
  116. B00111111, B11111111, B11111100,
  117. B00000111, B00000000, B11100000,
  118. B00000000, B11111111, B00000000,
  119. B00000000, B00000000, B00000000,
  120. B00000000, B00000000, B00000000,
  121. B00000000, B00000000, B00000000 },
  122. { B00000000, B00000000, B00000000, // Mouth position C
  123. B00000000, B00000000, B00000000,
  124. B00111111, B11111111, B11111100,
  125. B00001000, B00000000, B00010000,
  126. B00000110, B00000000, B01100000,
  127. B00000001, B11000011, B10000000,
  128. B00000000, B00111100, B00000000,
  129. B00000000, B00000000, B00000000 },
  130. { B00000000, B00000000, B00000000, // Mouth position D
  131. B00000000, B00000000, B00000000,
  132. B00111111, B11111111, B11111100,
  133. B00100000, B00000000, B00000100,
  134. B00010000, B00000000, B00001000,
  135. B00001100, B00000000, B00110000,
  136. B00000011, B10000001, B11000000,
  137. B00000000, B01111110, B00000000 },
  138. { B00000000, B00000000, B00000000, // Mouth position E
  139. B00000000, B00111100, B00000000,
  140. B00011111, B11000011, B11111000,
  141. B00000011, B10000001, B11000000,
  142. B00000000, B01111110, B00000000,
  143. B00000000, B00000000, B00000000,
  144. B00000000, B00000000, B00000000,
  145. B00000000, B00000000, B00000000 },
  146. { B00000000, B00111100, B00000000, // Mouth position F
  147. B00000000, B11000011, B00000000,
  148. B00001111, B00000000, B11110000,
  149. B00000001, B00000000, B10000000,
  150. B00000000, B11000011, B00000000,
  151. B00000000, B00111100, B00000000,
  152. B00000000, B00000000, B00000000,
  153. B00000000, B00000000, B00000000 } };
  154. uint8_t
  155. blinkIndex[] = { 1, 2, 3, 4, 3, 2, 1 }, // Blink bitmap sequence
  156. blinkCountdown = 100, // Countdown to next blink (in frames)
  157. gazeCountdown = 75, // Countdown to next eye movement
  158. gazeFrames = 50, // Duration of eye movement (smaller = faster)
  159. mouthPos = 0, // Current image number for mouth
  160. mouthCountdown = 10; // Countdown to next mouth change
  161. int8_t
  162. eyeX = 3, eyeY = 3, // Current eye position
  163. newX = 3, newY = 3, // Next eye position
  164. dX = 0, dY = 0; // Distance from prior to new position
  165. void setup() {
  166. // Seed random number generator from an unused analog input:
  167. randomSeed(analogRead(A0));
  168. // Initialize each matrix object:
  169. for(uint8_t i=0; i<4; i++) {
  170. matrix[i].begin(matrixAddr[i]);
  171. // If using 'small' (1.2") displays vs. 'mini' (0.8"), enable this:
  172. // matrix[i].setRotation(3);
  173. }
  174. }
  175. void loop() {
  176. // Draw eyeball in current state of blinkyness (no pupil). Note that
  177. // only one eye needs to be drawn. Because the two eye matrices share
  178. // the same address, the same data will be received by both.
  179. matrix[MATRIX_EYES].clear();
  180. // When counting down to the next blink, show the eye in the fully-
  181. // open state. On the last few counts (during the blink), look up
  182. // the corresponding bitmap index.
  183. matrix[MATRIX_EYES].drawBitmap(0, 0,
  184. blinkImg[
  185. (blinkCountdown < sizeof(blinkIndex)) ? // Currently blinking?
  186. blinkIndex[blinkCountdown] : // Yes, look up bitmap #
  187. 0 // No, show bitmap 0
  188. ], 8, 8, LED_ON);
  189. // Decrement blink counter. At end, set random time for next blink.
  190. if(--blinkCountdown == 0) blinkCountdown = random(5, 180);
  191. // Add a pupil (2x2 black square) atop the blinky eyeball bitmap.
  192. // Periodically, the pupil moves to a new position...
  193. if(--gazeCountdown <= gazeFrames) {
  194. // Eyes are in motion - draw pupil at interim position
  195. matrix[MATRIX_EYES].fillRect(
  196. newX - (dX * gazeCountdown / gazeFrames),
  197. newY - (dY * gazeCountdown / gazeFrames),
  198. 2, 2, LED_OFF);
  199. if(gazeCountdown == 0) { // Last frame?
  200. eyeX = newX; eyeY = newY; // Yes. What's new is old, then...
  201. do { // Pick random positions until one is within the eye circle
  202. newX = random(7); newY = random(7);
  203. dX = newX - 3; dY = newY - 3;
  204. } while((dX * dX + dY * dY) >= 10); // Thank you Pythagoras
  205. dX = newX - eyeX; // Horizontal distance to move
  206. dY = newY - eyeY; // Vertical distance to move
  207. gazeFrames = random(3, 15); // Duration of eye movement
  208. gazeCountdown = random(gazeFrames, 120); // Count to end of next movement
  209. }
  210. } else {
  211. // Not in motion yet -- draw pupil at current static position
  212. matrix[MATRIX_EYES].fillRect(eyeX, eyeY, 2, 2, LED_OFF);
  213. }
  214. // Draw mouth, switch to new random image periodically
  215. drawMouth(mouthImg[mouthPos]);
  216. if(--mouthCountdown == 0) {
  217. mouthPos = random(6); // Random image
  218. // If the 'neutral' position was chosen, there's a 1-in-5 chance we'll
  219. // select a longer hold time. This gives the appearance of periodic
  220. // pauses in speech (e.g. between sentences, etc.).
  221. mouthCountdown = ((mouthPos == 0) && (random(5) == 0)) ?
  222. random(10, 40) : // Longer random duration
  223. random(2, 8); // Shorter random duration
  224. }
  225. // Refresh all of the matrices in one quick pass
  226. for(uint8_t i=0; i<4; i++) matrix[i].writeDisplay();
  227. delay(20); // ~50 FPS
  228. }
  229. // Draw mouth image across three adjacent displays
  230. void drawMouth(const uint8_t *img) {
  231. for(uint8_t i=0; i<3; i++) {
  232. matrix[MATRIX_MOUTH_LEFT + i].clear();
  233. matrix[MATRIX_MOUTH_LEFT + i].drawBitmap(i * -8, 0, img, 24, 8, LED_ON);
  234. }
  235. }