Advertisement
Guest User

Eyes

a guest
Jul 11th, 2017
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 21.19 KB | None | 0 0
  1. //--------------------------------------------------------------------------
  2. // Uncanny eyes for PJRC Teensy 3.1 with Adafruit 1.5" OLED (product #1431)
  3. // or 1.44" TFT LCD (#2088).  This uses Teensy-3.1-specific features and
  4. // WILL NOT work on normal Arduino or other boards!  Use 72 MHz (Optimized)
  5. // board speed -- OLED does not work at 96 MHz.
  6. //
  7. // Adafruit invests time and resources providing this open source code,
  8. // please support Adafruit and open-source hardware by purchasing products
  9. // from Adafruit!
  10. //
  11. // Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
  12. // MIT license.  SPI FIFO insight from Paul Stoffregen's ILI9341_t3 library.
  13. // Inspired by David Boccabella's (Marcwolf) hybrid servo/OLED eye concept.
  14. //--------------------------------------------------------------------------
  15.  
  16. #include <SPI.h>
  17. #include <Adafruit_GFX.h>      // Core graphics lib for Adafruit displays
  18. // Enable ONE of these #includes -- HUGE graphics tables for various eyes:
  19. #include "defaultEye.h"        // Standard human-ish hazel eye
  20. //#include "noScleraEye.h"       // Large iris, no sclera
  21. //#include "dragonEye.h"         // Slit pupil fiery dragon/demon eye
  22. //#include "goatEye.h"           // Horizontal pupil goat/Krampus eye
  23. // Then tweak settings below, e.g. change IRIS_MIN/MAX or disable TRACKING.
  24.  
  25. // DISPLAY HARDWARE CONFIG -------------------------------------------------
  26.  
  27. #include <Adafruit_SSD1351.h>  // OLED display library -OR-
  28. //#include <Adafruit_ST7735.h> // TFT display library (enable one only)
  29.  
  30. #ifdef _ADAFRUIT_ST7735H_
  31. typedef Adafruit_ST7735  displayType; // Using TFT display(s)
  32. #else
  33. typedef Adafruit_SSD1351 displayType; // Using OLED display(s)
  34. #endif
  35.  
  36. #define DISPLAY_DC      7 // Data/command pin for BOTH displays
  37. #define DISPLAY_RESET   8 // Reset pin for BOTH displays
  38. #define SELECT_L_PIN    9 // LEFT eye chip select pin
  39. #define SELECT_R_PIN   10 // RIGHT eye chip select pin
  40.  
  41. // INPUT CONFIG (for eye motion -- enable or comment out as needed) --------
  42.  
  43. //#define JOYSTICK_X_PIN A0 // Analog pin for eye horiz pos (else auto)
  44. //#define JOYSTICK_Y_PIN A1 // Analog pin for eye vert position (")
  45. //#define JOYSTICK_X_FLIP   // If set, reverse stick X axis
  46. //#define JOYSTICK_Y_FLIP   // If set, reverse stick Y axis
  47. #define TRACKING          // If enabled, eyelid tracks pupil
  48. #define IRIS_PIN       A2 // Photocell or potentiometer (else auto iris)
  49. //#define IRIS_PIN_FLIP     // If set, reverse reading from dial/photocell
  50. #define IRIS_SMOOTH       // If enabled, filter input from IRIS_PIN
  51. #define IRIS_MIN      120 // Clip lower analogRead() range from IRIS_PIN
  52. #define IRIS_MAX      720 // Clip upper "
  53. #define WINK_L_PIN      0 // Pin for LEFT eye wink button
  54. #define BLINK_PIN       1 // Pin for blink button (BOTH eyes)
  55. #define WINK_R_PIN      2 // Pin for RIGHT eye wink button
  56. #define AUTOBLINK         // If enabled, eyes blink autonomously
  57.  
  58. // Probably don't need to edit any config below this line, -----------------
  59. // unless building a single-eye project (pendant, etc.), in which case one
  60. // of the two elements in the eye[] array further down can be commented out.
  61.  
  62. // Eye blinks are a tiny 3-state machine.  Per-eye allows winks + blinks.
  63. #define NOBLINK 0     // Not currently engaged in a blink
  64. #define ENBLINK 1     // Eyelid is currently closing
  65. #define DEBLINK 2     // Eyelid is currently opening
  66. typedef struct {
  67.   int8_t   pin;       // Optional button here for indiv. wink
  68.   uint8_t  state;     // NOBLINK/ENBLINK/DEBLINK
  69.   uint32_t duration;  // Duration of blink state (micros)
  70.   uint32_t startTime; // Time (micros) of last state change
  71. } eyeBlink;
  72.  
  73. struct {
  74.   displayType display; // OLED/TFT object
  75.   uint8_t     cs;      // Chip select pin
  76.   eyeBlink    blink;   // Current blink state
  77. } eye[] = { // OK to comment out one of these for single-eye display:
  78.   displayType(SELECT_L_PIN,DISPLAY_DC,0),SELECT_L_PIN,{WINK_L_PIN,NOBLINK},
  79.   displayType(SELECT_R_PIN,DISPLAY_DC,0),SELECT_R_PIN,{WINK_R_PIN,NOBLINK},
  80. };
  81. #define NUM_EYES (sizeof(eye) / sizeof(eye[0]))
  82.  
  83.  
  84. // INITIALIZATION -- runs once at startup ----------------------------------
  85.  
  86. void setup(void) {
  87.   uint8_t e;
  88.  
  89.   Serial.begin(115200);
  90.   randomSeed(analogRead(A3)); // Seed random() from floating analog input
  91.  
  92.   // Both displays share a common reset line; 0 is passed to display
  93.   // constructor (so no reset in begin()) -- must reset manually here:
  94.   pinMode(DISPLAY_RESET, OUTPUT);
  95.   digitalWrite(DISPLAY_RESET, LOW);  delay(1);
  96.   digitalWrite(DISPLAY_RESET, HIGH); delay(50);
  97.  
  98.   for(e=0; e<NUM_EYES; e++) { // Deselect all
  99.     pinMode(eye[e].cs, OUTPUT);
  100.     digitalWrite(eye[e].cs, HIGH);
  101.   }
  102.   for(e=0; e<NUM_EYES; e++) {
  103.     digitalWrite(eye[e].cs, LOW); // Select one eye for init
  104. #ifdef _ADAFRUIT_ST7735H_ // TFT
  105.     eye[e].display.initR(INITR_144GREENTAB);
  106. #else // OLED
  107.     eye[e].display.begin();
  108. #endif
  109.     if(eye[e].blink.pin >= 0) pinMode(eye[e].blink.pin, INPUT_PULLUP);
  110.     digitalWrite(eye[e].cs, HIGH); // Deselect
  111.   }
  112. #ifdef BLINK_PIN
  113.   pinMode(BLINK_PIN, INPUT_PULLUP);
  114. #endif
  115.  
  116.   // One of the displays is configured to mirror on the X axis.  Simplifies
  117.   // eyelid handling in the drawEye() function -- no need for distinct
  118.   // L-to-R or R-to-L inner loops.  Just the X coordinate of the iris is
  119.   // then reversed when drawing this eye, so they move the same.  Magic!
  120. #ifdef _ADAFRUIT_ST7735H_ // TFT
  121.   digitalWrite(eye[0].cs , LOW);
  122.   digitalWrite(DISPLAY_DC, LOW);
  123.   SPI.transfer(ST7735_MADCTL);
  124.   digitalWrite(DISPLAY_DC, HIGH);
  125.   SPI.transfer(0x88); // MADCTL_MY | MADCTL_BGR
  126.   digitalWrite(eye[0].cs , HIGH);
  127. #else // OLED
  128.   eye[0].display.writeCommand(SSD1351_CMD_SETREMAP);
  129.   eye[0].display.writeData(0x76);
  130. #endif
  131. }
  132.  
  133.  
  134. // EYE-RENDERING FUNCTION --------------------------------------------------
  135.  
  136. SPISettings settings(24000000, MSBFIRST, SPI_MODE3); // Teensy 3.1 max SPI
  137.  
  138. void drawEye( // Renders one eye.  Inputs must be pre-clipped & valid.
  139.   uint8_t  e,       // Eye array index; 0 or 1 for left/right
  140.   uint32_t iScale,  // Scale factor for iris
  141.   uint8_t  scleraX, // First pixel X offset into sclera image
  142.   uint8_t  scleraY, // First pixel Y offset into sclera image
  143.   uint8_t  uT,      // Upper eyelid threshold value
  144.   uint8_t  lT) {    // Lower eyelid threshold value
  145.  
  146.   uint8_t  screenX, screenY, scleraXsave;
  147.   int16_t  irisX, irisY;
  148.   uint16_t p, a;
  149.   uint32_t d;
  150.  
  151.   // Set up raw pixel dump to entire screen.  Although such writes can wrap
  152.   // around automatically from end of rect back to beginning, the region is
  153.   // reset on each frame here in case of an SPI glitch.
  154.   SPI.beginTransaction(settings);
  155. #ifdef _ADAFRUIT_ST7735H_ // TFT
  156.   eye[e].display.setAddrWindow(0, 0, 127, 1);
  157. #else // OLED
  158.   eye[e].display.writeCommand(SSD1351_CMD_SETROW);    // Y range
  159.   eye[e].display.writeData(0); eye[e].display.writeData(SCREEN_HEIGHT - 1);
  160.   eye[e].display.writeCommand(SSD1351_CMD_SETCOLUMN); // X range
  161.   eye[e].display.writeData(0); eye[e].display.writeData(SCREEN_WIDTH  - 1);
  162.   eye[e].display.writeCommand(SSD1351_CMD_WRITERAM);  // Begin write
  163. #endif
  164.   digitalWrite(eye[e].cs, LOW);                       // Chip select
  165.   digitalWrite(DISPLAY_DC, HIGH);                     // Data mode
  166.   // Now just issue raw 16-bit values for every pixel...
  167.  
  168.   scleraXsave = scleraX; // Save initial X value to reset on each line
  169.   irisY       = scleraY - (SCLERA_HEIGHT - IRIS_HEIGHT) / 2;
  170.   for(screenY=0; screenY<SCREEN_HEIGHT; screenY++, scleraY++, irisY++) {
  171.     scleraX = scleraXsave;
  172.     irisX   = scleraXsave - (SCLERA_WIDTH - IRIS_WIDTH) / 2;
  173.     for(screenX=0; screenX<SCREEN_WIDTH; screenX++, scleraX++, irisX++) {
  174.       if((lower[screenY][screenX] <= lT) ||
  175.          (upper[screenY][screenX] <= uT)) {             // Covered by eyelid
  176.         p = 0;
  177.       } else if((irisY < 0) || (irisY >= IRIS_HEIGHT) ||
  178.                 (irisX < 0) || (irisX >= IRIS_WIDTH)) { // In sclera
  179.         p = sclera[scleraY][scleraX];
  180.       } else {                                          // Maybe iris...
  181.         p = polar[irisY][irisX];                        // Polar angle/dist
  182.         d = (iScale * (p & 0x7F)) / 128;                // Distance (Y)
  183.         if(d < IRIS_MAP_HEIGHT) {                       // Within iris area
  184.           a = (IRIS_MAP_WIDTH * (p >> 7)) / 512;        // Angle (X)
  185.           p = iris[d][a];                               // Pixel = iris
  186.         } else {                                        // Not in iris
  187.           p = sclera[scleraY][scleraX];                 // Pixel = sclera
  188.         }
  189.       }
  190.       // SPI FIFO technique from Paul Stoffregen's ILI9341_t3 library:
  191.       while(KINETISK_SPI0.SR & 0xC000); // Wait for space in FIFO
  192.       KINETISK_SPI0.PUSHR = p | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT;
  193.     }
  194.   }
  195.  
  196.   KINETISK_SPI0.SR |= SPI_SR_TCF;         // Clear transfer flag
  197.   while((KINETISK_SPI0.SR & 0xF000) ||    // Wait for SPI FIFO to drain
  198.        !(KINETISK_SPI0.SR & SPI_SR_TCF)); // Wait for last bit out
  199.   digitalWrite(eye[e].cs, HIGH);          // Deselect
  200.   SPI.endTransaction();
  201. }
  202.  
  203.  
  204. // EYE ANIMATION -----------------------------------------------------------
  205.  
  206. const uint8_t ease[] = { // Ease in/out curve for eye movements 3*t^2-2*t^3
  207.     0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  2,  2,  2,  3,   // T
  208.     3,  3,  4,  4,  4,  5,  5,  6,  6,  7,  7,  8,  9,  9, 10, 10,   // h
  209.    11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23,   // x
  210.    24, 25, 26, 27, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39,   // 2
  211.    40, 41, 42, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58,   // A
  212.    60, 61, 62, 63, 65, 66, 67, 69, 70, 72, 73, 74, 76, 77, 78, 80,   // l
  213.    81, 83, 84, 85, 87, 88, 90, 91, 93, 94, 96, 97, 98,100,101,103,   // e
  214.   104,106,107,109,110,112,113,115,116,118,119,121,122,124,125,127,   // c
  215.   128,130,131,133,134,136,137,139,140,142,143,145,146,148,149,151,   // J
  216.   152,154,155,157,158,159,161,162,164,165,167,168,170,171,172,174,   // a
  217.   175,177,178,179,181,182,183,185,186,188,189,190,192,193,194,195,   // c
  218.   197,198,199,201,202,203,204,205,207,208,209,210,211,213,214,215,   // o
  219.   216,217,218,219,220,221,222,224,225,226,227,228,228,229,230,231,   // b
  220.   232,233,234,235,236,237,237,238,239,240,240,241,242,243,243,244,   // s
  221.   245,245,246,246,247,248,248,249,249,250,250,251,251,251,252,252,   // o
  222.   252,253,253,253,254,254,254,254,254,255,255,255,255,255,255,255 }; // n
  223.  
  224. #ifdef AUTOBLINK
  225. uint32_t timeOfLastBlink = 0L, timeToNextBlink = 0L;
  226. #endif
  227.  
  228. void frame( // Process motion for a single frame of left or right eye
  229.   uint16_t        iScale) {     // Iris scale (0-1023) passed in
  230.   static uint32_t frames   = 0; // Used in frame rate calculation
  231.   static uint8_t  eyeIndex = 0; // eye[] array counter
  232.   int16_t         eyeX, eyeY;
  233.   uint32_t        t = micros(); // Time at start of function
  234.  
  235.   Serial.println((++frames * 1000) / millis()); // Show frame rate
  236.  
  237.   if(++eyeIndex >= NUM_EYES) eyeIndex = 0; // Cycle through eyes, 1 per call
  238.  
  239.   // X/Y movement
  240.  
  241. #if defined(JOYSTICK_X_PIN) && (JOYSTICK_X_PIN >= 0) && \
  242.     defined(JOYSTICK_Y_PIN) && (JOYSTICK_Y_PIN >= 0)
  243.  
  244.   // Read X/Y from joystick, constrain to circle
  245.   int16_t dx, dy;
  246.   int32_t d;
  247.   eyeX = analogRead(JOYSTICK_X_PIN); // Raw (unclipped) X/Y reading
  248.   eyeY = analogRead(JOYSTICK_Y_PIN);
  249. #ifdef JOYSTICK_X_FLIP
  250.   eyeX = 1023 - eyeX;
  251. #endif
  252. #ifdef JOYSTICK_Y_FLIP
  253.   eyeY = 1023 - eyeY;
  254. #endif
  255.   dx = (eyeX * 2) - 1023; // A/D exact center is at 511.5.  Scale coords
  256.   dy = (eyeY * 2) - 1023; // X2 so range is -1023 to +1023 w/center at 0.
  257.   if((d = (dx * dx + dy * dy)) > (1023 * 1023)) { // Outside circle
  258.     d    = (int32_t)sqrt((float)d);               // Distance from center
  259.     eyeX = ((dx * 1023 / d) + 1023) / 2;          // Clip to circle edge,
  260.     eyeY = ((dy * 1023 / d) + 1023) / 2;          // scale back to 0-1023
  261.   }
  262.  
  263. #else // Autonomous X/Y eye motion
  264.       // Periodically initiates motion to a new random point, random speed,
  265.       // holds there for random period until next motion.
  266.  
  267.   static boolean  eyeInMotion      = false;
  268.   static int16_t  eyeOldX=512, eyeOldY=512, eyeNewX=512, eyeNewY=512;
  269.   static uint32_t eyeMoveStartTime = 0L;
  270.   static int32_t  eyeMoveDuration  = 0L;
  271.  
  272.   int32_t dt = t - eyeMoveStartTime;      // uS elapsed since last eye event
  273.   if(eyeInMotion) {                       // Currently moving?
  274.     if(dt >= eyeMoveDuration) {           // Time up?  Destination reached.
  275.       eyeInMotion      = false;           // Stop moving
  276.       eyeMoveDuration  = random(3000000); // 0-3 sec stop
  277.       eyeMoveStartTime = t;               // Save initial time of stop
  278.       eyeX = eyeOldX = eyeNewX;           // Save position
  279.       eyeY = eyeOldY = eyeNewY;
  280.     } else { // Move time's not yet fully elapsed -- interpolate position
  281.       int16_t e = ease[255 * dt / eyeMoveDuration] + 1;   // Ease curve
  282.       eyeX = eyeOldX + (((eyeNewX - eyeOldX) * e) / 256); // Interp X
  283.       eyeY = eyeOldY + (((eyeNewY - eyeOldY) * e) / 256); // and Y
  284.     }
  285.   } else {                                // Eye stopped
  286.     eyeX = eyeOldX;
  287.     eyeY = eyeOldY;
  288.     if(dt > eyeMoveDuration) {            // Time up?  Begin new move.
  289.       int16_t  dx, dy;
  290.       uint32_t d;
  291.       do {                                // Pick new dest in circle
  292.         eyeNewX = random(1024);
  293.         eyeNewY = random(1024);
  294.         dx      = (eyeNewX * 2) - 1023;
  295.         dy      = (eyeNewY * 2) - 1023;
  296.       } while((d = (dx * dx + dy * dy)) > (1023 * 1023)); // Keep trying
  297.       eyeMoveDuration  = random(72000, 144000); // ~1/14 - ~1/7 sec
  298.       eyeMoveStartTime = t;               // Save initial time of move
  299.       eyeInMotion      = true;            // Start move on next frame
  300.     }
  301.   }
  302.  
  303. #endif // JOYSTICK_X_PIN etc.
  304.  
  305.   // Blinking
  306.  
  307. #ifdef AUTOBLINK
  308.   // Similar to the autonomous eye movement above -- blink start times
  309.   // and durations are random (within ranges).
  310.   if((t - timeOfLastBlink) >= timeToNextBlink) { // Start new blink?
  311.     timeOfLastBlink = t;
  312.     uint32_t blinkDuration = random(36000, 72000); // ~1/28 - ~1/14 sec
  313.     // Set up durations for both eyes (if not already winking)
  314.     for(uint8_t e=0; e<NUM_EYES; e++) {
  315.       if(eye[e].blink.state == NOBLINK) {
  316.         eye[e].blink.state     = ENBLINK;
  317.         eye[e].blink.startTime = t;
  318.         eye[e].blink.duration  = blinkDuration;
  319.       }
  320.     }
  321.     timeToNextBlink = blinkDuration * 3 + random(4000000);
  322.   }
  323. #endif
  324.  
  325.   if(eye[eyeIndex].blink.state) { // Eye currently blinking?
  326.     // Check if current blink state time has elapsed
  327.     if((t - eye[eyeIndex].blink.startTime) >= eye[eyeIndex].blink.duration) {
  328.       // Yes -- increment blink state, unless...
  329.       if((eye[eyeIndex].blink.state == ENBLINK) &&  // Enblinking and...
  330.         ((digitalRead(BLINK_PIN) == LOW) ||         // blink or wink held...
  331.           digitalRead(eye[eyeIndex].blink.pin) == LOW)) {
  332.         // Don't advance state yet -- eye is held closed instead
  333.       } else { // No buttons, or other state...
  334.         if(++eye[eyeIndex].blink.state > DEBLINK) { // Deblinking finished?
  335.           eye[eyeIndex].blink.state = NOBLINK;      // No longer blinking
  336.         } else { // Advancing from ENBLINK to DEBLINK mode
  337.           eye[eyeIndex].blink.duration *= 2; // DEBLINK is 1/2 ENBLINK speed
  338.           eye[eyeIndex].blink.startTime = t;
  339.         }
  340.       }
  341.     }
  342.   } else { // Not currently blinking...check buttons!
  343.     if(digitalRead(BLINK_PIN) == LOW) {
  344.       // Manually-initiated blinks have random durations like auto-blink
  345.       uint32_t blinkDuration = random(36000, 72000);
  346.       for(uint8_t e=0; e<NUM_EYES; e++) {
  347.         if(eye[e].blink.state == NOBLINK) {
  348.           eye[e].blink.state     = ENBLINK;
  349.           eye[e].blink.startTime = t;
  350.           eye[e].blink.duration  = blinkDuration;
  351.         }
  352.       }
  353.     } else if(digitalRead(eye[eyeIndex].blink.pin) == LOW) { // Wink!
  354.       eye[eyeIndex].blink.state     = ENBLINK;
  355.       eye[eyeIndex].blink.startTime = t;
  356.       eye[eyeIndex].blink.duration  = random(45000, 90000);
  357.     }
  358.   }
  359.  
  360.   // Process motion, blinking and iris scale into renderable values
  361.  
  362.   // Iris scaling: remap from 0-1023 input to iris map height pixel units
  363.   iScale = ((IRIS_MAP_HEIGHT + 1) * 1024) /
  364.            (1024 - (iScale * (IRIS_MAP_HEIGHT - 1) / IRIS_MAP_HEIGHT));
  365.  
  366.   // Scale eye X/Y positions (0-1023) to pixel units used by drawEye()
  367.   eyeX = map(eyeX, 0, 1023, 0, SCLERA_WIDTH  - 128);
  368.   eyeY = map(eyeY, 0, 1023, 0, SCLERA_HEIGHT - 128);
  369.   if(eyeIndex == 1) eyeX = (SCLERA_WIDTH - 128) - eyeX; // Mirrored display
  370.  
  371.   // Horizontal position is offset so that eyes are very slightly crossed
  372.   // to appear fixated (converged) at a conversational distance.  Number
  373.   // here was extracted from my posterior and not mathematically based.
  374.   // I suppose one could get all clever with a range sensor, but for now...
  375.   eyeX += 4;
  376.   if(eyeX > (SCLERA_WIDTH - 128)) eyeX = (SCLERA_WIDTH - 128);
  377.  
  378.   // Eyelids are rendered using a brightness threshold image.  This same
  379.   // map can be used to simplify another problem: making the upper eyelid
  380.   // track the pupil (eyes tend to open only as much as needed -- e.g. look
  381.   // down and the upper eyelid drops).  Just sample a point in the upper
  382.   // lid map slightly above the pupil to determine the rendering threshold.
  383.   static uint8_t uThreshold = 128;
  384.   uint8_t        lThreshold, n;
  385. #ifdef TRACKING
  386.   int16_t sampleX = SCLERA_WIDTH  / 2 - (eyeX / 2), // Reduce X influence
  387.           sampleY = SCLERA_HEIGHT / 2 - (eyeY + IRIS_HEIGHT / 4);
  388.   // Eyelid is slightly asymmetrical, so two readings are taken, averaged
  389.   if(sampleY < 0) n = 0;
  390.   else            n = (upper[sampleY][sampleX] +
  391.                        upper[sampleY][SCREEN_WIDTH - 1 - sampleX]) / 2;
  392.   uThreshold = (uThreshold * 3 + n) / 4; // Filter/soften motion
  393.   // Lower eyelid doesn't track the same way, but seems to be pulled upward
  394.   // by tension from the upper lid.
  395.   lThreshold = 254 - uThreshold;
  396. #else // No tracking -- eyelids full open unless blink modifies them
  397.   uThreshold = lThreshold = 0;
  398. #endif
  399.  
  400.   // The upper/lower thresholds are then scaled relative to the current
  401.   // blink position so that blinks work together with pupil tracking.
  402.   if(eye[eyeIndex].blink.state) { // Eye currently blinking?
  403.     uint32_t s = (t - eye[eyeIndex].blink.startTime);
  404.     if(s >= eye[eyeIndex].blink.duration) s = 255;   // At or past blink end
  405.     else s = 255 * s / eye[eyeIndex].blink.duration; // Mid-blink
  406.     s          = (eye[eyeIndex].blink.state == DEBLINK) ? 1 + s : 256 - s;
  407.     n          = (uThreshold * s + 254 * (257 - s)) / 256;
  408.     lThreshold = (lThreshold * s + 254 * (257 - s)) / 256;
  409.   } else {
  410.     n          = uThreshold;
  411.   }
  412.  
  413.   // Pass all the derived values to the eye-rendering function:
  414.   drawEye(eyeIndex, iScale, eyeX, eyeY, n, lThreshold);
  415. }
  416.  
  417.  
  418. // AUTONOMOUS IRIS SCALING (if no photocell or dial) -----------------------
  419.  
  420. #if !defined(IRIS_PIN) || (IRIS_PIN < 0)
  421.  
  422. // Autonomous iris motion uses a fractal behavior to similate both the major
  423. // reaction of the eye plus the continuous smaller adjustments that occur.
  424.  
  425. uint16_t oldIris = (IRIS_MIN + IRIS_MAX) / 2, newIris;
  426.  
  427. void split( // Subdivides motion path into two sub-paths w/randimization
  428.   int16_t  startValue, // Iris scale value (IRIS_MIN to IRIS_MAX) at start
  429.   int16_t  endValue,   // Iris scale value at end
  430.   uint32_t startTime,  // micros() at start
  431.   int32_t  duration,   // Start-to-end time, in microseconds
  432.   int16_t  range) {    // Allowable scale value variance when subdividing
  433.  
  434.   if(range >= 8) {     // Limit subdvision count, because recursion
  435.     range    /= 2;     // Split range & time in half for subdivision,
  436.     duration /= 2;     // then pick random center point within range:
  437.     int16_t  midValue = (startValue + endValue - range) / 2 + random(range);
  438.     uint32_t midTime  = startTime + duration;
  439.     split(startValue, midValue, startTime, duration, range); // First half
  440.     split(midValue  , endValue, midTime  , duration, range); // Second half
  441.   } else {             // No more subdivisons, do iris motion...
  442.     int32_t dt;        // Time (micros) since start of motion
  443.     int16_t v;         // Interim value
  444.     while((dt = (micros() - startTime)) < duration) {
  445.       v = startValue + (((endValue - startValue) * dt) / duration);
  446.       if(v < IRIS_MIN)      v = IRIS_MIN; // Clip just in case
  447.       else if(v > IRIS_MAX) v = IRIS_MAX;
  448.       frame(v);        // Draw frame w/interim iris scale value
  449.     }
  450.   }
  451. }
  452.  
  453. #endif // !IRIS_PIN
  454.  
  455.  
  456. // MAIN LOOP -- runs continuously after setup() ----------------------------
  457.  
  458. void loop() {
  459.  
  460. #if defined(IRIS_PIN) && (IRIS_PIN >= 0) // Interactive iris
  461.  
  462.   uint16_t v = analogRead(IRIS_PIN);       // Raw dial/photocell reading
  463. #ifdef IRIS_PIN_FLIP
  464.   v = 1023 - v;
  465. #endif
  466.   v = map(v, 0, 1023, IRIS_MIN, IRIS_MAX); // Scale to iris range
  467. #ifdef IRIS_SMOOTH // Filter input (gradual motion)
  468.   static uint16_t irisValue = (IRIS_MIN + IRIS_MAX) / 2;
  469.   irisValue = ((irisValue * 15) + v) / 16;
  470.   frame(irisValue);
  471. #else // Unfiltered (immediate motion)
  472.   frame(v);
  473. #endif // IRIS_SMOOTH
  474.  
  475. #else  // Autonomous iris scaling -- invoke recursive function
  476.  
  477.   newIris = random(IRIS_MIN, IRIS_MAX);
  478.   split(oldIris, newIris, micros(), 10000000L, IRIS_MAX - IRIS_MIN);
  479.   oldIris = newIris;
  480.  
  481. #endif // IRIS_PIN
  482. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement