Skip to content

Commit 39302ff

Browse files
Merge pull request #814 from adafruit/philb-branch
M$_Eyes: read light sensor during the "no traffic" period.
2 parents fcc0787 + 666323f commit 39302ff

1 file changed

Lines changed: 56 additions & 52 deletions

File tree

M4_Eyes/M4_Eyes.ino

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -394,15 +394,15 @@ an independent frame rate depending on particular complexity at the moment).
394394
void loop() {
395395
if(++eyeNum >= NUM_EYES) eyeNum = 0; // Cycle through eyes...
396396

397-
uint8_t x = eye[eyeNum].colNum;
397+
uint8_t x = eye[eyeNum].colNum;
398+
uint32_t t = micros();
398399

399400
// If next column for this eye is not yet rendered...
400401
if(!eye[eyeNum].column_ready) {
401402
if(!x) { // If it's the first column...
402403

403404
// ONCE-PER-FRAME EYE ANIMATION LOGIC HAPPENS HERE -------------------
404405

405-
uint32_t t = micros();
406406
float eyeX, eyeY;
407407
// Eye movement
408408
int32_t dt = t - eyeMoveStartTime; // uS elapsed since last eye event
@@ -445,58 +445,11 @@ void loop() {
445445
eye[eyeNum].eyeX = eyeX;
446446
eye[eyeNum].eyeY = eyeY;
447447

448-
// Handle pupil scaling
449-
if(lightSensorPin >= 0) {
450-
// Read light sensor, but not too often (Seesaw hates that)
451-
#define LIGHT_INTERVAL (1000000 / 8) // 8 Hz, don't poll Seesaw too often
452-
if((t - lastLightReadTime) >= LIGHT_INTERVAL) {
453-
// Fun fact: eyes have a "consensual response" to light -- both
454-
// pupils will react even if the opposite eye is stimulated.
455-
// Meaning we can get away with using a single light sensor for
456-
// both eyes. This comment has nothing to do with the code.
457-
uint16_t rawReading = (lightSensorPin >= 100) ?
458-
seesaw.analogRead(lightSensorPin - 100) : analogRead(lightSensorPin);
459-
if(rawReading <= 1023) {
460-
if(rawReading < lightSensorMin) rawReading = lightSensorMin; // Clamp light sensor range
461-
else if(rawReading > lightSensorMax) rawReading = lightSensorMax; // to within usable range
462-
float v = (float)(rawReading - lightSensorMin) / (float)(lightSensorMax - lightSensorMin); // 0.0 to 1.0
463-
v = pow(v, lightSensorCurve);
464-
lastLightValue = irisMin + v * irisRange;
465-
lastLightReadTime = t;
466-
lightSensorFailCount = 0;
467-
} else { // I2C error
468-
if(++lightSensorFailCount >= 50) { // If repeated errors in succession...
469-
lightSensorPin = -1; // Stop trying to use the light sensor
470-
} else {
471-
lastLightReadTime = t - LIGHT_INTERVAL + 40000; // Try again in 40 ms
472-
}
473-
}
474-
}
475-
irisValue = (irisValue * 0.97) + (lastLightValue * 0.03); // Filter response for smooth reaction
476-
} else {
477-
// Not light responsive. Use autonomous iris w/fractal subdivision
478-
float n, sum = 0.5;
479-
for(uint16_t i=0; i<IRIS_LEVELS; i++) { // 0,1,2,3,...
480-
uint16_t iexp = 1 << (i+1); // 2,4,8,16,...
481-
uint16_t imask = (iexp - 1); // 2^i-1 (1,3,7,15,...)
482-
uint16_t ibits = iris_frame & imask; // 0 to mask
483-
if(ibits) {
484-
float weight = (float)ibits / (float)iexp; // 0.0 to <1.0
485-
n = iris_prev[i] * (1.0 - weight) + iris_next[i] * weight;
486-
} else {
487-
n = iris_next[i];
488-
iris_prev[i] = iris_next[i];
489-
iris_next[i] = -0.5 + ((float)random(1000) / 999.0); // -0.5 to +0.5
490-
}
491-
iexp = 1 << (IRIS_LEVELS - i); // ...8,4,2,1
492-
sum += n / (float)iexp;
493-
}
494-
irisValue = irisMin + (sum * irisRange); // 0.0-1.0 -> iris min/max
495-
if((++iris_frame) >= (1 << IRIS_LEVELS)) iris_frame = 0;
496-
}
497-
498448
// pupilFactor? irisValue? TO DO: pick a name and stick with it
499449
eye[eyeNum].pupilFactor = irisValue;
450+
// Also note - irisValue is calculated at the END of this function
451+
// for the next frame (because the sensor must be read when there's
452+
// no SPI traffic to the left eye)
500453

501454
// Similar to the autonomous eye movement above -- blink start times
502455
// and durations are random (within ranges).
@@ -821,6 +774,57 @@ void loop() {
821774
eye[eyeNum].display->setAddrWindow(0, 0, 240, 240);
822775
delayMicroseconds(1);
823776
digitalWrite(eye[eyeNum].dc, HIGH); // Data mode
777+
if(eyeNum == (NUM_EYES-1)) {
778+
// Handle pupil scaling
779+
if(lightSensorPin >= 0) {
780+
// Read light sensor, but not too often (Seesaw hates that)
781+
#define LIGHT_INTERVAL (1000000 / 10) // 10 Hz, don't poll Seesaw too often
782+
if((t - lastLightReadTime) >= LIGHT_INTERVAL) {
783+
// Fun fact: eyes have a "consensual response" to light -- both
784+
// pupils will react even if the opposite eye is stimulated.
785+
// Meaning we can get away with using a single light sensor for
786+
// both eyes. This comment has nothing to do with the code.
787+
uint16_t rawReading = (lightSensorPin >= 100) ?
788+
seesaw.analogRead(lightSensorPin - 100) : analogRead(lightSensorPin);
789+
if(rawReading <= 1023) {
790+
if(rawReading < lightSensorMin) rawReading = lightSensorMin; // Clamp light sensor range
791+
else if(rawReading > lightSensorMax) rawReading = lightSensorMax; // to within usable range
792+
float v = (float)(rawReading - lightSensorMin) / (float)(lightSensorMax - lightSensorMin); // 0.0 to 1.0
793+
v = pow(v, lightSensorCurve);
794+
lastLightValue = irisMin + v * irisRange;
795+
lastLightReadTime = t;
796+
lightSensorFailCount = 0;
797+
} else { // I2C error
798+
if(++lightSensorFailCount >= 25) { // If repeated errors in succession...
799+
lightSensorPin = -1; // Stop trying to use the light sensor
800+
} else {
801+
lastLightReadTime = t - LIGHT_INTERVAL + 30000; // Try again in 30 ms
802+
}
803+
}
804+
}
805+
irisValue = (irisValue * 0.97) + (lastLightValue * 0.03); // Filter response for smooth reaction
806+
} else {
807+
// Not light responsive. Use autonomous iris w/fractal subdivision
808+
float n, sum = 0.5;
809+
for(uint16_t i=0; i<IRIS_LEVELS; i++) { // 0,1,2,3,...
810+
uint16_t iexp = 1 << (i+1); // 2,4,8,16,...
811+
uint16_t imask = (iexp - 1); // 2^i-1 (1,3,7,15,...)
812+
uint16_t ibits = iris_frame & imask; // 0 to mask
813+
if(ibits) {
814+
float weight = (float)ibits / (float)iexp; // 0.0 to <1.0
815+
n = iris_prev[i] * (1.0 - weight) + iris_next[i] * weight;
816+
} else {
817+
n = iris_next[i];
818+
iris_prev[i] = iris_next[i];
819+
iris_next[i] = -0.5 + ((float)random(1000) / 999.0); // -0.5 to +0.5
820+
}
821+
iexp = 1 << (IRIS_LEVELS - i); // ...8,4,2,1
822+
sum += n / (float)iexp;
823+
}
824+
irisValue = irisMin + (sum * irisRange); // 0.0-1.0 -> iris min/max
825+
if((++iris_frame) >= (1 << IRIS_LEVELS)) iris_frame = 0;
826+
}
827+
}
824828
} // end first-column check
825829

826830
// MUST read the booper when there’s no SPI traffic across the nose!

0 commit comments

Comments
 (0)