Advertisement
Guest User

Untitled

a guest
Dec 9th, 2019
228
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 24.54 KB | None | 0 0
  1. #include <SPI.h>
  2. #include <Adafruit_GFX.h>
  3. #ifdef ARDUINO_ARCH_SAMD
  4. #include <Adafruit_ZeroDMA.h>
  5. #endif
  6.  
  7. typedef struct { // Struct is defined before including config.h --
  8. int8_t select; // pin numbers for each eye's screen select line
  9. int8_t wink; // and wink button (or -1 if none) specified there,
  10. uint8_t rotation; // also display rotation.
  11. } eyeInfo_t;
  12.  
  13. #include "config.h" // ****** CONFIGURATION IS DONE IN HERE ******
  14.  
  15. #if defined(_ADAFRUIT_ST7735H_) || defined(_ADAFRUIT_ST77XXH_)
  16. typedef Adafruit_ST7735 displayType; // Using TFT display(s)
  17. #else
  18. typedef Adafruit_SSD1351 displayType; // Using OLED display(s)
  19. #endif
  20.  
  21. // A simple state machine is used to control eye blinks/winks:
  22. #define NOBLINK 0 // Not currently engaged in a blink
  23. #define ENBLINK 1 // Eyelid is currently closing
  24. #define DEBLINK 2 // Eyelid is currently opening
  25. typedef struct {
  26. uint8_t state; // NOBLINK/ENBLINK/DEBLINK
  27. uint32_t duration; // Duration of blink state (micros)
  28. uint32_t startTime; // Time (micros) of last state change
  29. uint32_t timeOfLastBlink = 0L, timeToNextBlink = 0L;
  30. } eyeBlink;
  31.  
  32. typedef struct {
  33. boolean eyeInMotion = false;
  34. int16_t eyeOldX=512, eyeOldY=512, eyeNewX=512, eyeNewY=512;
  35. uint32_t eyeMoveStartTime = 0L;
  36. int32_t eyeMoveDuration = 0L;
  37. int16_t eyeX, eyeY;
  38. uint8_t uThreshold = 128;
  39. } eyeMove;
  40.  
  41. #define NUM_EYES (sizeof eyeInfo / sizeof eyeInfo[0]) // config.h pin list
  42.  
  43. struct { // One-per-eye structure
  44. displayType *display; // -> OLED/TFT object
  45. eyeBlink blink; // Current blink/wink state
  46. eyeMove move;
  47. } eye[NUM_EYES];
  48.  
  49. #ifdef ARDUINO_ARCH_SAMD
  50. // SAMD boards use DMA (Teensy uses SPI FIFO instead):
  51. // Two single-line 128-pixel buffers (16bpp) are used for DMA.
  52. // Though you'd think fewer larger transfers would improve speed,
  53. // multi-line buffering made no appreciable difference.
  54. uint16_t dmaBuf[2][128];
  55. uint8_t dmaIdx = 0; // Active DMA buffer # (alternate fill/send)
  56. Adafruit_ZeroDMA dma;
  57. DmacDescriptor *descriptor;
  58.  
  59. // DMA transfer-in-progress indicator and callback
  60. static volatile bool dma_busy = false;
  61. static void dma_callback(Adafruit_ZeroDMA *dma) { dma_busy = false; }
  62. #endif
  63.  
  64. uint32_t startTime; // For FPS indicator
  65.  
  66.  
  67. // INITIALIZATION -- runs once at startup ----------------------------------
  68.  
  69. void setup(void) {
  70. uint8_t e; // Eye index, 0 to NUM_EYES-1
  71.  
  72. Serial.begin(115200);
  73. randomSeed(analogRead(A3)); // Seed random() from floating analog input
  74.  
  75. #ifdef DISPLAY_BACKLIGHT
  76. // Enable backlight pin, initially off
  77. pinMode(DISPLAY_BACKLIGHT, OUTPUT);
  78. digitalWrite(DISPLAY_BACKLIGHT, LOW);
  79. #endif
  80.  
  81. // Initialize eye objects based on eyeInfo list in config.h:
  82. for(e=0; e<NUM_EYES; e++) {
  83. eye[e].display = new displayType(eyeInfo[e].select, DISPLAY_DC, -1);
  84. eye[e].blink.state = NOBLINK;
  85. // If project involves only ONE eye and NO other SPI devices, its
  86. // select line can be permanently tied to GND and corresponding pin
  87. // in config.h set to -1. Best to use it though.
  88. if(eyeInfo[e].select >= 0) {
  89. pinMode(eyeInfo[e].select, OUTPUT);
  90. digitalWrite(eyeInfo[e].select, HIGH); // Deselect them all
  91. }
  92. // Also set up an individual eye-wink pin if defined:
  93. if(eyeInfo[e].wink >= 0) pinMode(eyeInfo[e].wink, INPUT_PULLUP);
  94. }
  95. #if defined(BLINK_PIN) && (BLINK_PIN >= 0)
  96. pinMode(BLINK_PIN, INPUT_PULLUP); // Ditto for all-eyes blink pin
  97. #endif
  98.  
  99. #if defined(DISPLAY_RESET) && (DISPLAY_RESET >= 0)
  100. // Because both displays share a common reset pin, -1 is passed to
  101. // the display constructor above to prevent the begin() function from
  102. // resetting both displays after one is initialized. Instead, handle
  103. // the reset manually here to take care of both displays just once:
  104. pinMode(DISPLAY_RESET, OUTPUT);
  105. digitalWrite(DISPLAY_RESET, LOW); delay(1);
  106. digitalWrite(DISPLAY_RESET, HIGH); delay(50);
  107. // Alternately, all display reset pin(s) could be connected to the
  108. // microcontroller reset, in which case DISPLAY_RESET should be set
  109. // to -1 or left undefined in config.h.
  110. #endif
  111.  
  112. // After all-displays reset, now call init/begin func for each display:
  113. for(e=0; e<NUM_EYES; e++) {
  114. #if defined(_ADAFRUIT_ST7735H_) || defined(_ADAFRUIT_ST77XXH_) // TFT
  115. eye[e].display->initR(INITR_144GREENTAB);
  116. #else // OLED
  117. eye[e].display->begin();
  118. #endif
  119. eye[e].display->setRotation(eyeInfo[e].rotation);
  120. }
  121.  
  122. #if defined(LOGO_TOP_WIDTH) || defined(COLOR_LOGO_WIDTH)
  123. // I noticed lots of folks getting right/left eyes flipped, or
  124. // installing upside-down, etc. Logo split across screens may help:
  125. for(e=0; e<NUM_EYES; e++) { // Another pass, after all screen inits
  126. eye[e].display->fillScreen(0);
  127. #ifdef LOGO_TOP_WIDTH
  128. // Monochrome Adafruit logo is 2 mono bitmaps:
  129. eye[e].display->drawBitmap(NUM_EYES*64 - e*128 - 20,
  130. 0, logo_top, LOGO_TOP_WIDTH, LOGO_TOP_HEIGHT, 0xFFFF);
  131. eye[e].display->drawBitmap(NUM_EYES*64 - e*128 - LOGO_BOTTOM_WIDTH/2,
  132. LOGO_TOP_HEIGHT, logo_bottom, LOGO_BOTTOM_WIDTH, LOGO_BOTTOM_HEIGHT,
  133. 0xFFFF);
  134. #else
  135. // Color sponsor logo is one RGB bitmap:
  136. eye[e].display->fillScreen(color_logo[0]);
  137. eye[0].display->drawRGBBitmap(
  138. (eye[e].display->width() - COLOR_LOGO_WIDTH ) / 2,
  139. (eye[e].display->height() - COLOR_LOGO_HEIGHT) / 2,
  140. color_logo, COLOR_LOGO_WIDTH, COLOR_LOGO_HEIGHT);
  141. #endif
  142. // After logo is drawn
  143. }
  144. #ifdef DISPLAY_BACKLIGHT
  145. int i;
  146. for(i=0; i<BACKLIGHT_MAX; i++) { // Fade logo in
  147. analogWrite(DISPLAY_BACKLIGHT, i);
  148. delay(2);
  149. }
  150. delay(1400); // Pause for screen layout/orientation
  151. for(; i>=0; i--) {
  152. analogWrite(DISPLAY_BACKLIGHT, i);
  153. delay(2);
  154. }
  155. for(e=0; e<NUM_EYES; e++) { // Clear display(s)
  156. eye[e].display->fillScreen(0);
  157. }
  158. delay(100);
  159. #else
  160. delay(2000); // Pause for screen layout/orientation
  161. #endif // DISPLAY_BACKLIGHT
  162. #endif // LOGO_TOP_WIDTH
  163.  
  164. // One of the displays is configured to mirror on the X axis. Simplifies
  165. // eyelid handling in the drawEye() function -- no need for distinct
  166. // L-to-R or R-to-L inner loops. Just the X coordinate of the iris is
  167. // then reversed when drawing this eye, so they move the same. Magic!
  168. #if defined(_ADAFRUIT_ST7735H_) || defined(_ADAFRUIT_ST77XXH_) // TFT
  169. const uint8_t mirrorTFT[] = { 0x88, 0x28, 0x48, 0xE8 }; // Mirror+rotate
  170. digitalWrite(eyeInfo[0].select, LOW);
  171. digitalWrite(DISPLAY_DC, LOW);
  172. #ifdef ST77XX_MADCTL
  173. SPI.transfer(ST77XX_MADCTL); // Current TFT lib
  174. #else
  175. SPI.transfer(ST7735_MADCTL); // Older TFT lib
  176. #endif
  177. digitalWrite(DISPLAY_DC, HIGH);
  178. SPI.transfer(mirrorTFT[eyeInfo[0].rotation & 3]);
  179. digitalWrite(eyeInfo[0].select , HIGH);
  180. #else // OLED
  181. const uint8_t rotateOLED[] = { 0x74, 0x77, 0x66, 0x65 },
  182. mirrorOLED[] = { 0x76, 0x67, 0x64, 0x75 }; // Mirror+rotate
  183. // If OLED, loop through ALL eyes and set up remap register
  184. // from either mirrorOLED[] (first eye) or rotateOLED[] (others).
  185. // The OLED library doesn't normally use the remap reg (TFT does).
  186. for(e=0; e<NUM_EYES; e++) {
  187. eye[e].display->writeCommand(SSD1351_CMD_SETREMAP);
  188. eye[e].display->writeData(e ?
  189. rotateOLED[eyeInfo[e].rotation & 3] :
  190. mirrorOLED[eyeInfo[e].rotation & 3]);
  191. }
  192. #endif
  193.  
  194. #ifdef ARDUINO_ARCH_SAMD
  195. // Set up SPI DMA on SAMD boards:
  196. int dmac_id;
  197. volatile uint32_t *data_reg;
  198. if(&PERIPH_SPI == &sercom0) {
  199. dmac_id = SERCOM0_DMAC_ID_TX;
  200. data_reg = &SERCOM0->SPI.DATA.reg;
  201. #if defined SERCOM1
  202. } else if(&PERIPH_SPI == &sercom1) {
  203. dmac_id = SERCOM1_DMAC_ID_TX;
  204. data_reg = &SERCOM1->SPI.DATA.reg;
  205. #endif
  206. #if defined SERCOM2
  207. } else if(&PERIPH_SPI == &sercom2) {
  208. dmac_id = SERCOM2_DMAC_ID_TX;
  209. data_reg = &SERCOM2->SPI.DATA.reg;
  210. #endif
  211. #if defined SERCOM3
  212. } else if(&PERIPH_SPI == &sercom3) {
  213. dmac_id = SERCOM3_DMAC_ID_TX;
  214. data_reg = &SERCOM3->SPI.DATA.reg;
  215. #endif
  216. #if defined SERCOM4
  217. } else if(&PERIPH_SPI == &sercom4) {
  218. dmac_id = SERCOM4_DMAC_ID_TX;
  219. data_reg = &SERCOM4->SPI.DATA.reg;
  220. #endif
  221. #if defined SERCOM5
  222. } else if(&PERIPH_SPI == &sercom5) {
  223. dmac_id = SERCOM5_DMAC_ID_TX;
  224. data_reg = &SERCOM5->SPI.DATA.reg;
  225. #endif
  226. }
  227.  
  228. dma.allocate();
  229. dma.setTrigger(dmac_id);
  230. dma.setAction(DMA_TRIGGER_ACTON_BEAT);
  231. descriptor = dma.addDescriptor(
  232. NULL, // move data
  233. (void *)data_reg, // to here
  234. sizeof dmaBuf[0], // this many...
  235. DMA_BEAT_SIZE_BYTE, // bytes/hword/words
  236. true, // increment source addr?
  237. false); // increment dest addr?
  238. dma.setCallback(dma_callback);
  239.  
  240. #endif // End SAMD-specific SPI DMA init
  241.  
  242. #ifdef DISPLAY_BACKLIGHT
  243. analogWrite(DISPLAY_BACKLIGHT, BACKLIGHT_MAX);
  244. #endif
  245.  
  246. startTime = millis(); // For frame-rate calculation
  247. }
  248.  
  249.  
  250. // EYE-RENDERING FUNCTION --------------------------------------------------
  251.  
  252. SPISettings settings(SPI_FREQ, MSBFIRST, SPI_MODE0);
  253.  
  254. void drawEye( // Renders one eye. Inputs must be pre-clipped & valid.
  255. uint8_t e, // Eye array index; 0 or 1 for left/right
  256. uint32_t iScale, // Scale factor for iris
  257. uint8_t scleraX, // First pixel X offset into sclera image
  258. uint8_t scleraY, // First pixel Y offset into sclera image
  259. uint8_t uT, // Upper eyelid threshold value
  260. uint8_t lT) { // Lower eyelid threshold value
  261.  
  262. uint8_t screenX, screenY, scleraXsave;
  263. int16_t irisX, irisY;
  264. uint16_t p, a;
  265. uint32_t d;
  266.  
  267. // Set up raw pixel dump to entire screen. Although such writes can wrap
  268. // around automatically from end of rect back to beginning, the region is
  269. // reset on each frame here in case of an SPI glitch.
  270. SPI.beginTransaction(settings);
  271. digitalWrite(eyeInfo[e].select, LOW); // Chip select
  272. #if defined(_ADAFRUIT_ST7735H_) || defined(_ADAFRUIT_ST77XXH_) // TFT
  273. eye[e].display->setAddrWindow(0, 0, 128, 128);
  274. #else // OLED
  275. eye[e].display->writeCommand(SSD1351_CMD_SETROW); // Y range
  276. eye[e].display->writeData(0); eye[e].display->writeData(SCREEN_HEIGHT - 1);
  277. eye[e].display->writeCommand(SSD1351_CMD_SETCOLUMN); // X range
  278. eye[e].display->writeData(0); eye[e].display->writeData(SCREEN_WIDTH - 1);
  279. eye[e].display->writeCommand(SSD1351_CMD_WRITERAM); // Begin write
  280. #endif
  281. digitalWrite(eyeInfo[e].select, LOW); // Re-chip-select
  282. digitalWrite(DISPLAY_DC, HIGH); // Data mode
  283. // Now just issue raw 16-bit values for every pixel...
  284.  
  285. scleraXsave = scleraX; // Save initial X value to reset on each line
  286. irisY = scleraY - (SCLERA_HEIGHT - IRIS_HEIGHT) / 2;
  287. for(screenY=0; screenY<SCREEN_HEIGHT; screenY++, scleraY++, irisY++) {
  288. #ifdef ARDUINO_ARCH_SAMD
  289. uint16_t *ptr = &dmaBuf[dmaIdx][0];
  290. #endif
  291. scleraX = scleraXsave;
  292. irisX = scleraXsave - (SCLERA_WIDTH - IRIS_WIDTH) / 2;
  293. for(screenX=0; screenX<SCREEN_WIDTH; screenX++, scleraX++, irisX++) {
  294. if((lower[screenY][screenX] <= lT) ||
  295. (upper[screenY][screenX] <= uT)) { // Covered by eyelid
  296. p = 0;
  297. } else if((irisY < 0) || (irisY >= IRIS_HEIGHT) ||
  298. (irisX < 0) || (irisX >= IRIS_WIDTH)) { // In sclera
  299. p = sclera[scleraY][scleraX];
  300. } else { // Maybe iris...
  301. p = polar[irisY][irisX]; // Polar angle/dist
  302. d = (iScale * (p & 0x7F)) / 128; // Distance (Y)
  303. if(d < IRIS_MAP_HEIGHT) { // Within iris area
  304. a = (IRIS_MAP_WIDTH * (p >> 7)) / 512; // Angle (X)
  305. p = iris[d][a]; // Pixel = iris
  306. } else { // Not in iris
  307. p = sclera[scleraY][scleraX]; // Pixel = sclera
  308. }
  309. }
  310. #ifdef ARDUINO_ARCH_SAMD
  311. *ptr++ = __builtin_bswap16(p); // DMA: store in scanline buffer
  312. #else
  313. // SPI FIFO technique from Paul Stoffregen's ILI9341_t3 library:
  314. while(KINETISK_SPI0.SR & 0xC000); // Wait for space in FIFO
  315. KINETISK_SPI0.PUSHR = p | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT;
  316. #endif
  317. } // end column
  318. #ifdef ARDUINO_ARCH_SAMD
  319. while(dma_busy); // Wait for prior DMA xfer to finish
  320. descriptor->SRCADDR.reg = (uint32_t)&dmaBuf[dmaIdx] + sizeof dmaBuf[0];
  321. dma_busy = true;
  322. dmaIdx = 1 - dmaIdx;
  323. dma.startJob();
  324. #endif
  325. } // end scanline
  326.  
  327. #ifdef ARDUINO_ARCH_SAMD
  328. while(dma_busy); // Wait for last scanline to transmit
  329. #else
  330. KINETISK_SPI0.SR |= SPI_SR_TCF; // Clear transfer flag
  331. while((KINETISK_SPI0.SR & 0xF000) || // Wait for SPI FIFO to drain
  332. !(KINETISK_SPI0.SR & SPI_SR_TCF)); // Wait for last bit out
  333. #endif
  334.  
  335. digitalWrite(eyeInfo[e].select, HIGH); // Deselect
  336. SPI.endTransaction();
  337. }
  338.  
  339.  
  340. // EYE ANIMATION -----------------------------------------------------------
  341.  
  342. const uint8_t ease[] = { // Ease in/out curve for eye movements 3*t^2-2*t^3
  343. 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, // T
  344. 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, // h
  345. 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, // x
  346. 24, 25, 26, 27, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, // 2
  347. 40, 41, 42, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58, // A
  348. 60, 61, 62, 63, 65, 66, 67, 69, 70, 72, 73, 74, 76, 77, 78, 80, // l
  349. 81, 83, 84, 85, 87, 88, 90, 91, 93, 94, 96, 97, 98,100,101,103, // e
  350. 104,106,107,109,110,112,113,115,116,118,119,121,122,124,125,127, // c
  351. 128,130,131,133,134,136,137,139,140,142,143,145,146,148,149,151, // J
  352. 152,154,155,157,158,159,161,162,164,165,167,168,170,171,172,174, // a
  353. 175,177,178,179,181,182,183,185,186,188,189,190,192,193,194,195, // c
  354. 197,198,199,201,202,203,204,205,207,208,209,210,211,213,214,215, // o
  355. 216,217,218,219,220,221,222,224,225,226,227,228,228,229,230,231, // b
  356. 232,233,234,235,236,237,237,238,239,240,240,241,242,243,243,244, // s
  357. 245,245,246,246,247,248,248,249,249,250,250,251,251,251,252,252, // o
  358. 252,253,253,253,254,254,254,254,254,255,255,255,255,255,255,255 }; // n
  359.  
  360. void frame( // Process motion for a single frame of left or right eye
  361. uint16_t iScale) { // Iris scale (0-1023) passed in
  362. static uint32_t frames = 0; // Used in frame rate calculation
  363.  
  364. uint32_t t = micros(); // Time at start of function
  365.  
  366. if(!(++frames & 255)) { // Every 256 frames...
  367. uint32_t elapsed = (millis() - startTime) / 1000;
  368. if(elapsed) Serial.println(frames / elapsed); // Print FPS
  369. }
  370.  
  371. for(uint8_t e=0; e<NUM_EYES; e++) {
  372. // X/Y movement
  373. // Autonomous X/Y eye motion
  374. // Periodically initiates motion to a new random point, random speed,
  375. // holds there for random period until next motion.
  376. int32_t dt = t - eye[e].move.eyeMoveStartTime; // uS elapsed since last eye event
  377. if(eye[e].move.eyeInMotion) { // Currently moving?
  378. if(dt >= eye[e].move.eyeMoveDuration) { // Time up? Destination reached.
  379. eye[e].move.eyeInMotion = false; // Stop moving
  380. eye[e].move.eyeMoveDuration = random(3000000); // 0-3 sec stop
  381. eye[e].move.eyeMoveStartTime = t; // Save initial time of stop
  382. eye[e].move.eyeX = eye[e].move.eyeOldX = eye[e].move.eyeNewX; // Save position
  383. eye[e].move.eyeY = eye[e].move.eyeOldY = eye[e].move.eyeNewY;
  384. } else { // Move time's not yet fully elapsed -- interpolate position
  385. int16_t easeVal = ease[255 * dt / eye[e].move.eyeMoveDuration] + 1; // Ease curve
  386. eye[e].move.eyeX = eye[e].move.eyeOldX + (((eye[e].move.eyeNewX - eye[e].move.eyeOldX) * easeVal) / 256); // Interp X
  387. eye[e].move.eyeY = eye[e].move.eyeOldY + (((eye[e].move.eyeNewY - eye[e].move.eyeOldY) * easeVal) / 256); // and Y
  388. }
  389. } else { // Eye stopped
  390. eye[e].move.eyeX = eye[e].move.eyeOldX;
  391. eye[e].move.eyeY = eye[e].move.eyeOldY;
  392. if(dt > eye[e].move.eyeMoveDuration) { // Time up? Begin new move.
  393. int16_t dx, dy;
  394. uint32_t d;
  395. do { // Pick new dest in circle
  396. eye[e].move.eyeNewX = random(1024);
  397. eye[e].move.eyeNewY = random(1024);
  398. dx = (eye[e].move.eyeNewX * 2) - 1023;
  399. dy = (eye[e].move.eyeNewY * 2) - 1023;
  400. } while((d = (dx * dx + dy * dy)) > (1023 * 1023)); // Keep trying
  401. eye[e].move.eyeMoveDuration = random(72000, 144000); // ~1/14 - ~1/7 sec
  402. eye[e].move.eyeMoveStartTime = t; // Save initial time of move
  403. eye[e].move.eyeInMotion = true; // Start move on next frame
  404. }
  405. }
  406.  
  407.  
  408. // Blinking
  409.  
  410. #ifdef AUTOBLINK
  411. // Similar to the autonomous eye movement above -- blink start times
  412. // and durations are random (within ranges).
  413. if((t - eye[e].blink.timeOfLastBlink) >= eye[e].blink.timeToNextBlink) { // Start new blink?
  414. eye[e].blink.timeOfLastBlink = t;
  415. eye[e].blink.duration = random(36000, 72000); // ~1/28 - ~1/14 sec
  416. // Set up durations for both eyes (if not already winking)
  417. // for(uint8_t e=0; e<NUM_EYES; e++) {
  418. if(eye[e].blink.state == NOBLINK) {
  419. eye[e].blink.state = ENBLINK;
  420. eye[e].blink.startTime = t;
  421. }
  422. // }
  423. eye[e].blink.timeToNextBlink = eye[e].blink.duration * 3 + random(8000000);
  424. }
  425. #endif
  426.  
  427. if(eye[e].blink.state) { // Eye currently blinking?
  428. // Check if current blink state time has elapsed
  429. if((t - eye[e].blink.startTime) >= eye[e].blink.duration) {
  430. // Yes -- increment blink state, unless...
  431. if((eye[e].blink.state == ENBLINK) && ( // Enblinking and...
  432. ((eyeInfo[e].wink >= 0) &&
  433. digitalRead(eyeInfo[e].wink) == LOW) )) {
  434. // Don't advance state yet -- eye is held closed instead
  435. } else { // No buttons, or other state...
  436. if(++eye[e].blink.state > DEBLINK) { // Deblinking finished?
  437. eye[e].blink.state = NOBLINK; // No longer blinking
  438. } else { // Advancing from ENBLINK to DEBLINK mode
  439. eye[e].blink.duration *= 2; // DEBLINK is 1/2 ENBLINK speed
  440. eye[e].blink.startTime = t;
  441. }
  442. }
  443. }
  444. } else { // Not currently blinking...check buttons!
  445. if((eyeInfo[e].wink >= 0) &&
  446. (digitalRead(eyeInfo[e].wink) == LOW)) { // Wink!
  447. eye[e].blink.state = ENBLINK;
  448. eye[e].blink.startTime = t;
  449. eye[e].blink.duration = random(45000, 90000);
  450. }
  451. }
  452.  
  453. // Process motion, blinking and iris scale into renderable values
  454.  
  455. // Iris scaling: remap from 0-1023 input to iris map height pixel units
  456. iScale = ((IRIS_MAP_HEIGHT + 1) * 1024) /
  457. (1024 - (iScale * (IRIS_MAP_HEIGHT - 1) / IRIS_MAP_HEIGHT));
  458.  
  459. // Scale eye X/Y positions (0-1023) to pixel units used by drawEye()
  460. eye[e].move.eyeX = map(eye[e].move.eyeX, 0, 1023, 0, SCLERA_WIDTH - 128);
  461. eye[e].move.eyeY = map(eye[e].move.eyeY, 0, 1023, 0, SCLERA_HEIGHT - 128);
  462. if(e == 1) eye[e].move.eyeX = (SCLERA_WIDTH - 128) - eye[e].move.eyeX; // Mirrored display
  463.  
  464. // Horizontal position is offset so that eyes are very slightly crossed
  465. // to appear fixated (converged) at a conversational distance. Number
  466. // here was extracted from my posterior and not mathematically based.
  467. // I suppose one could get all clever with a range sensor, but for now...
  468. if(NUM_EYES > 1) eye[e].move.eyeX += 4;
  469. if(eye[e].move.eyeX > (SCLERA_WIDTH - 128)) eye[e].move.eyeX = (SCLERA_WIDTH - 128);
  470.  
  471. // Eyelids are rendered using a brightness threshold image. This same
  472. // map can be used to simplify another problem: making the upper eyelid
  473. // track the pupil (eyes tend to open only as much as needed -- e.g. look
  474. // down and the upper eyelid drops). Just sample a point in the upper
  475. // lid map slightly above the pupil to determine the rendering threshold.
  476. uint8_t lThreshold, n;
  477. #ifdef TRACKING
  478. int16_t sampleX = SCLERA_WIDTH / 2 - (eye[e].move.eyeX / 2), // Reduce X influence
  479. sampleY = SCLERA_HEIGHT / 2 - (eye[e].move.eyeY + IRIS_HEIGHT / 4);
  480. // Eyelid is slightly asymmetrical, so two readings are taken, averaged
  481. if(sampleY < 0) n = 0;
  482. else n = (upper[sampleY][sampleX] +
  483. upper[sampleY][SCREEN_WIDTH - 1 - sampleX]) / 2;
  484. eye[e].move.uThreshold = (eye[e].move.uThreshold * 3 + n) / 4; // Filter/soften motion
  485. // Lower eyelid doesn't track the same way, but seems to be pulled upward
  486. // by tension from the upper lid.
  487. lThreshold = 254 - eye[e].move.uThreshold;
  488. #else // No tracking -- eyelids full open unless blink modifies them
  489. uThreshold = lThreshold = 0;
  490. #endif
  491.  
  492. // The upper/lower thresholds are then scaled relative to the current
  493. // blink position so that blinks work together with pupil tracking.
  494. if(eye[e].blink.state) { // Eye currently blinking?
  495. uint32_t s = (t - eye[e].blink.startTime);
  496. if(s >= eye[e].blink.duration) s = 255; // At or past blink end
  497. else s = 255 * s / eye[e].blink.duration; // Mid-blink
  498. s = (eye[e].blink.state == DEBLINK) ? 1 + s : 256 - s;
  499. n = (eye[e].move.uThreshold * s + 254 * (257 - s)) / 256;
  500. lThreshold = (lThreshold * s + 254 * (257 - s)) / 256;
  501. } else {
  502. n = eye[e].move.uThreshold;
  503. }
  504.  
  505. // Pass all the derived values to the eye-rendering function:
  506. drawEye(e, iScale, eye[e].move.eyeX, eye[e].move.eyeY, n, lThreshold);
  507. }
  508. }
  509.  
  510.  
  511. // AUTONOMOUS IRIS SCALING (if no photocell or dial) -----------------------
  512.  
  513. #if !defined(LIGHT_PIN) || (LIGHT_PIN < 0)
  514.  
  515. // Autonomous iris motion uses a fractal behavior to similate both the major
  516. // reaction of the eye plus the continuous smaller adjustments that occur.
  517.  
  518. uint16_t oldIris = (IRIS_MIN + IRIS_MAX) / 2, newIris;
  519.  
  520. void split( // Subdivides motion path into two sub-paths w/randimization
  521. int16_t startValue, // Iris scale value (IRIS_MIN to IRIS_MAX) at start
  522. int16_t endValue, // Iris scale value at end
  523. uint32_t startTime, // micros() at start
  524. int32_t duration, // Start-to-end time, in microseconds
  525. int16_t range) { // Allowable scale value variance when subdividing
  526.  
  527. if(range >= 8) { // Limit subdvision count, because recursion
  528. range /= 2; // Split range & time in half for subdivision,
  529. duration /= 2; // then pick random center point within range:
  530. int16_t midValue = (startValue + endValue - range) / 2 + random(range);
  531. uint32_t midTime = startTime + duration;
  532. split(startValue, midValue, startTime, duration, range); // First half
  533. split(midValue , endValue, midTime , duration, range); // Second half
  534. } else { // No more subdivisons, do iris motion...
  535. int32_t dt; // Time (micros) since start of motion
  536. int16_t v; // Interim value
  537. while((dt = (micros() - startTime)) < duration) {
  538. v = startValue + (((endValue - startValue) * dt) / duration);
  539. if(v < IRIS_MIN) v = IRIS_MIN; // Clip just in case
  540. else if(v > IRIS_MAX) v = IRIS_MAX;
  541. frame(v); // Draw frame w/interim iris scale value
  542. }
  543. }
  544. }
  545.  
  546. #endif // !LIGHT_PIN
  547.  
  548.  
  549. // MAIN LOOP -- runs continuously after setup() ----------------------------
  550.  
  551. void loop() {
  552.  
  553. #if defined(LIGHT_PIN) && (LIGHT_PIN >= 0) // Interactive iris
  554.  
  555. int16_t v = analogRead(LIGHT_PIN); // Raw dial/photocell reading
  556. #ifdef LIGHT_PIN_FLIP
  557. v = 1023 - v; // Reverse reading from sensor
  558. #endif
  559. if(v < LIGHT_MIN) v = LIGHT_MIN; // Clamp light sensor range
  560. else if(v > LIGHT_MAX) v = LIGHT_MAX;
  561. v -= LIGHT_MIN; // 0 to (LIGHT_MAX - LIGHT_MIN)
  562. #ifdef LIGHT_CURVE // Apply gamma curve to sensor input?
  563. v = (int16_t)(pow((double)v / (double)(LIGHT_MAX - LIGHT_MIN),
  564. LIGHT_CURVE) * (double)(LIGHT_MAX - LIGHT_MIN));
  565. #endif
  566. // And scale to iris range (IRIS_MAX is size at LIGHT_MIN)
  567. v = map(v, 0, (LIGHT_MAX - LIGHT_MIN), IRIS_MAX, IRIS_MIN);
  568. #ifdef IRIS_SMOOTH // Filter input (gradual motion)
  569. static int16_t irisValue = (IRIS_MIN + IRIS_MAX) / 2;
  570. irisValue = ((irisValue * 15) + v) / 16;
  571. frame(irisValue);
  572. #else // Unfiltered (immediate motion)
  573. frame(v);
  574. #endif // IRIS_SMOOTH
  575.  
  576. #else // Autonomous iris scaling -- invoke recursive function
  577.  
  578. newIris = random(IRIS_MIN, IRIS_MAX);
  579. split(oldIris, newIris, micros(), 10000000L, IRIS_MAX - IRIS_MIN);
  580. oldIris = newIris;
  581.  
  582. #endif // LIGHT_PIN
  583. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement