Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Monster M4sk: user control of eyes, user config from config.eye, user wiichuck control eyes&neopixels #915

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2b4827c
M4_Eyes.ino: add a set of user_callable functions to control the eyes…
TonyLHansen Nov 2, 2019
79d3589
Merge branch 'master' into master
TonyLHansen Nov 5, 2019
abec3a9
Merge branch 'master' into master
TonyLHansen Nov 6, 2019
bced28b
Merge branch 'master' into master
TonyLHansen Nov 6, 2019
9a0ea23
Merge branch 'master' into master
TonyLHansen Nov 8, 2019
3863073
Merge branch 'master' into master
TonyLHansen Nov 12, 2019
6263a01
Merge branch 'master' into master
TonyLHansen Nov 14, 2019
f36b7d6
Merge branch 'master' into master
TonyLHansen Nov 19, 2019
5d63771
Merge branch 'master' into master
TonyLHansen Nov 19, 2019
8e8d0ee
Merge branch 'master' into master
TonyLHansen Nov 21, 2019
24074a4
Merge branch 'master' into master
TonyLHansen Dec 9, 2019
e31f3f0
Merge branch 'master' into master
TonyLHansen Dec 16, 2019
fc48cd4
Merge branch 'master' into master
TonyLHansen Dec 18, 2019
72e7c0b
Merge branch 'master' into master
TonyLHansen Dec 21, 2019
2b625de
Merge branch 'master' into master
TonyLHansen Jan 1, 2020
6110f14
Merge branch 'master' into master
TonyLHansen Jan 4, 2020
d37f0ac
Merge branch 'master' into master
TonyLHansen Jan 8, 2020
b3b3d19
Merge branch 'master' into master
TonyLHansen Jan 14, 2020
8a56899
Merge branch 'master' into master
TonyLHansen Jan 17, 2020
2258778
Merge branch 'master' into master
TonyLHansen Jan 29, 2020
dadd025
Merge branch 'master' into master
TonyLHansen Feb 7, 2020
2ff55c5
Merge branch 'master' into master
TonyLHansen Feb 9, 2020
77122c8
Merge branch 'master' into master
TonyLHansen Feb 19, 2020
f41231e
Merge branch 'master' into master
TonyLHansen Apr 2, 2020
407ae11
Merge branch 'master' into master
TonyLHansen Apr 7, 2020
e0a94c0
Merge branch 'master' into master
TonyLHansen Apr 24, 2020
9c4df1d
Merge branch 'master' into master
TonyLHansen May 4, 2020
224eb59
Merge branch 'master' into master
TonyLHansen May 23, 2020
b8a41f0
Merge branch 'master' into master
TonyLHansen May 25, 2020
e28f8af
Merge branch 'master' into master
TonyLHansen May 27, 2020
7cdeccc
Merge branch 'master' into master
TonyLHansen May 29, 2020
07428a1
Merge branch 'master' into master
TonyLHansen Jun 4, 2020
c461360
Merge branch 'master' into master
TonyLHansen Oct 29, 2020
34226a6
Fix compilation errors from merge.
darkgrue Nov 5, 2020
fec4273
Style and debugging updates.
darkgrue Nov 5, 2020
d5f3b29
Fix eyesWide() function only opening one eye.
darkgrue Nov 5, 2020
a66999f
Remove stray tabs.
darkgrue Nov 5, 2020
1a7b469
Made eyesWide() user function distinct from eyesBoop(), a new function.
darkgrue Nov 29, 2020
5d06193
Added more user-callible eye functions.
darkgrue Dec 5, 2020
dab12ed
Added eagle eyes.
darkgrue Dec 5, 2020
97fcaa2
Re-enabled user.cpp as default.
darkgrue Dec 5, 2020
aa023c1
Tweaks to eagle eyes.
darkgrue Dec 6, 2020
4d9bf4c
Update config.eye
darkgrue Jan 1, 2021
9fe2e59
Merge pull request #1 from darkgrue/master
TonyLHansen Sep 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions M4_Eyes/HeatSensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void HeatSensor::setup()

// default settings
status = amg.begin();
if (!status) {
if(!status) {
Serial.println("Could not find a valid AMG88xx sensor, check wiring!");
while (1);
}
Expand Down Expand Up @@ -77,7 +77,7 @@ void HeatSensor::find_focus()
for (int i = 1; i <= AMG88xx_PIXEL_ARRAY_SIZE; i++) {
int val = min(5, round(max(0, pixels[i-1] - 20) / 2));
Serial.print(charPixels[val]);
if (i % 8 == 0)
if(i % 8 == 0)
Serial.println();
}
Serial.println();
Expand Down
115 changes: 89 additions & 26 deletions M4_Eyes/M4_Eyes.ino
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ int iPupilFactor = 42;
uint32_t boopSum = 0,
boopSumFiltered = 0;
bool booped = false;
bool eyelidsClosed = false;
bool eyelidsWide = false;
int fixate = 7;
uint8_t lightSensorFailCount = 0;

Expand Down Expand Up @@ -133,9 +135,54 @@ uint32_t availableRAM(void) {
return &top - (char *)sbrk(0); // Top of stack minus end of heap
}

// USER CALLABLE FUNCTIONS

// Start a blink.
void eyesBlink() {
Serial.println("eyesBlink()");
timeToNextBlink = 0;
}

// Force the booped flag to be set true.
void eyesBoop() {
Serial.println("eyesBoop()");
boopSum = 99999;
}

// Close eyelids.
void eyesClose() {
Serial.println("eyesClose()");
eyelidsClosed = true;
}

// Return the eyes to normal random movement.
void eyesNormal() {
// Serial.println("eyesNormal()");
moveEyesRandomly = true;
}

// Open eyelids wide.
void eyesWide() {
Serial.println("eyesWide()");
eyelidsWide = true;
}

// Force the eyes to a position on the screen.
void eyesToCorner(float x, float y, bool immediate) {
// Serial.println("eyesToCorner(" + String(x) + ", " + String(-y) + ", " + (immediate ? "TRUE" : "FALSE") + ")");
moveEyesRandomly = false;
eyeTargetX = x;
eyeTargetY = y;
if(immediate)
eyeMoveDuration = 0;
}


// SETUP FUNCTION - CALLED ONCE AT PROGRAM START ---------------------------

void setup() {
Serial.println("SETUP BEGINS");

if(!arcada.arcadaBegin()) fatal("Arcada init fail!", 100);
#if defined(USE_TINYUSB)
if(!arcada.filesysBeginMSD()) fatal("No filesystem found!", 250);
Expand Down Expand Up @@ -187,10 +234,10 @@ void setup() {
#endif

yield();
if (showSplashScreen) {
if (arcada.drawBMP((char *)"/splash.bmp", 0, 0, (eye[0].display)) == IMAGE_SUCCESS) {
if(showSplashScreen) {
if(arcada.drawBMP((char *)"/splash.bmp", 0, 0, (eye[0].display)) == IMAGE_SUCCESS) {
Serial.println("Splashing");
if (NUM_EYES > 1) { // other eye
if(NUM_EYES > 1) { // other eye
yield();
arcada.drawBMP((char *)"/splash.bmp", 0, 0, (eye[1].display));
}
Expand Down Expand Up @@ -421,10 +468,11 @@ void setup() {
}

lastLightReadTime = micros() + 2000000; // Delay initial light reading

Serial.println("END OF SETUP");
}



// LOOP FUNCTION - CALLED REPEATEDLY UNTIL POWER-OFF -----------------------

/*
Expand Down Expand Up @@ -475,29 +523,29 @@ void loop() {
// Eye movement
int32_t dt = t - eyeMoveStartTime; // uS elapsed since last eye event
if(eyeInMotion) { // Currently moving?
if(dt >= eyeMoveDuration) { // Time up? Destination reached.
eyeInMotion = false; // Stop moving
if (moveEyesRandomly) {
eyeMoveDuration = random(10000, 3000000); // 0.01-3 sec stop
eyeMoveStartTime = t; // Save initial time of stop
if(dt >= eyeMoveDuration) { // Time up? Destination reached.
eyeInMotion = false; // Stop moving
if(moveEyesRandomly) {
eyeMoveDuration = random(10000, 3000000); // 0.01-3 sec stop
eyeMoveStartTime = t; // Save initial time of stop
}
eyeX = eyeOldX = eyeNewX; // Save position
eyeY = eyeOldY = eyeNewY;
} else { // Move time's not yet fully elapsed -- interpolate position
float e = (float)dt / float(eyeMoveDuration); // 0.0 to 1.0 during move
e = 3 * e * e - 2 * e * e * e; // Easing function: 3*e^2-2*e^3 0.0 to 1.0
eyeX = eyeOldX + (eyeNewX - eyeOldX) * e; // Interp X
eyeY = eyeOldY + (eyeNewY - eyeOldY) * e; // and Y
eyeX = eyeOldX + (eyeNewX - eyeOldX) * e; // Interp X
eyeY = eyeOldY + (eyeNewY - eyeOldY) * e; // and Y
}
} else { // Eye stopped
} else { // Eye stopped
eyeX = eyeOldX;
eyeY = eyeOldY;
if(dt > eyeMoveDuration) { // Time up? Begin new move.
if(dt > eyeMoveDuration) { // Time up? Begin new move.
// r is the radius in X and Y that the eye can go, from (0,0) in the center.
float r = (float)mapDiameter - (float)DISPLAY_SIZE * M_PI_2; // radius of motion
r *= 0.6; // calibration constant

if (moveEyesRandomly) {
if(moveEyesRandomly) {
eyeNewX = random(-r, r);
float h = sqrt(r * r - x * x);
eyeNewY = random(-h, h);
Expand All @@ -510,9 +558,9 @@ void loop() {
eyeNewY += mapRadius;

// Set the duration for this move, and start it going.
eyeMoveDuration = random(83000, 166000); // ~1/12 - ~1/6 sec
eyeMoveStartTime = t; // Save initial time of move
eyeInMotion = true; // Start move on next frame
eyeMoveDuration = random(83000, 166000); // ~1/12 - ~1/6 sec
eyeMoveStartTime = t; // Save initial time of move
eyeInMotion = true; // Start move on next frame
}
}

Expand Down Expand Up @@ -554,24 +602,31 @@ void loop() {
iy = (int)map2screen(mapRadius - eye[eyeNum].eyeY) + (DISPLAY_SIZE/2); // on screen
iy += irisRadius * trackFactor;
if(eyeNum & 1) ix = DISPLAY_SIZE - 1 - ix; // Flip for right eye
if(iy > upperOpen[ix]) {
if(eyelidsWide) {
uq = 1.0;
} else if(iy < upperClosed[ix]) {
uq = 0.0;
} else {
uq = (float)(iy - upperClosed[ix]) / (float)(upperOpen[ix] - upperClosed[ix]);
}
if(booped) {
lq = 1.0;
} else if(booped) {
uq = 0.9;
lq = 0.7;
} else {
if(iy > upperOpen[ix]) {
uq = 1.0;
} else if(iy < upperClosed[ix]) {
uq = 0.0;
} else {
uq = (float)(iy - upperClosed[ix]) / (float)(upperOpen[ix] - upperClosed[ix]);
}
lq = 1.0 - uq;
}
} else {
// If no tracking, eye is FULLY OPEN when not blinking
uq = 1.0;
lq = 1.0;
}
if(eyelidsClosed) {
uq = 0.0;
lq = 0.0;
}
// Dampen eyelid movements slightly
// SAVE upper & lower lid factors per eye,
// they need to stay consistent across frame
Expand Down Expand Up @@ -602,13 +657,14 @@ void loop() {
// of both screens is about 1/2 this.
frames++;
if(((t - lastFrameRateReportTime) >= 1000000) && t) { // Once per sec.
Serial.println((frames * 1000) / (t / 1000));
Serial.printf("Frame rate: %d\n", (frames * 1000) / (t / 1000));
lastFrameRateReportTime = t;
}

// Once per frame (of eye #0), reset boopSum...
if((eyeNum == 0) && (boopPin >= 0)) {
boopSumFiltered = ((boopSumFiltered * 3) + boopSum) / 4;
// Serial.printf("boopSum: %d, boopSumFiltered: %d, boopThreshold: %d, booped: %s\n", boopSum, boopSumFiltered, boopThreshold, (booped ? "true" : "false"));
if(boopSumFiltered > boopThreshold) {
if(!booped) {
Serial.println("BOOP!");
Expand All @@ -620,6 +676,12 @@ void loop() {
boopSum = 0;
}

// Once per frame (of eye #1), reset eyelid states...
if(eyeNum == 1) {
eyelidsClosed = false;
eyelidsWide = false;
}

float mins = (float)millis() / 60000.0;
if(eye[eyeNum].iris.iSpin) {
// Spin works in fixed amount per frame (eyes may lose sync, but "wagon wheel" tricks work)
Expand Down Expand Up @@ -884,7 +946,8 @@ void loop() {
lightSensorPin = -1; // Stop trying to use the light sensor
} else {
lastLightReadTime = t - LIGHT_INTERVAL + 30000; // Try again in 30 ms
} }
}
}
}
irisValue = (irisValue * 0.97) + (lastLightValue * 0.03); // Filter response for smooth reaction
} else {
Expand Down
41 changes: 41 additions & 0 deletions M4_Eyes/eyes/eagle/config.eye
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"boopThreshold" : 17500, // lower is more sensitive
"eyeRadius" : 125, // radius, in pixels
"irisRadius" : 120, // radius, in pixels
"slitPupilRadius" : 0, // height, in pixels; 0 is round pupil

"irisTexture" : "eagle/iris.bmp",
// "scleraTexture" : "eagle/sclera.bmp",
"scleraColor" : [ 64, 24, 22 ],
"pupilColor" : [ 0, 0, 0 ],
"backColor" : [ 140, 40, 20 ], // covers the outermost/backmost part of the eye where the sclera texture map (or color) doesn’t reach
"eyelidIndex" : "0x00", // 8-bit value; from table learn.adafruit.com/assets/61921

// independent irisTexture, scleraTexture, irisColor, scleraColor,
// pupilColor, backColor, irisAngle, scleraAngle, irisSpin, scleraSpin,
// irisMirror, scleraMirror, and rotate can be specified
"left" : {
},
"right" : {
},

"upperEyelid" : "eagle/upper.bmp",
"lowerEyelid" : "eagle/lower.bmp",
"tracking" : true,
"squint" : 0.5, // offsets eyelid center point vertically

"lightSensor" : 102, // light sensor pin; 102 is MONSTER M4SK, 21 is HalloWing M4
"pupilMin" : 0.05, // smallest pupil size as a fraction of iris size; from 0.0 to 1.0
"pupilMax" : 0.3, // largest pupil size as a fraction of iris size; from 0.0 to 1.0

"voice" : false,
"pitch" : 1.0,
"gain" : 1.0, // microphone gain (sensitivity)
// "waveform" : "sine" // "square", "sine", "tri" and "saw" are supported
// "modulate" : 30 // waveform modulation, in Hz

"wiichuck" : {
"min" : 28,
"max" : 229
}
}
Binary file added M4_Eyes/eyes/eagle/eyelid.psd
Binary file not shown.
Binary file added M4_Eyes/eyes/eagle/iris.bmp
Binary file not shown.
Binary file added M4_Eyes/eyes/eagle/iris.psd
Binary file not shown.
Binary file added M4_Eyes/eyes/eagle/lower.bmp
Binary file not shown.
Binary file added M4_Eyes/eyes/eagle/sclera.bmp
Binary file not shown.
Binary file added M4_Eyes/eyes/eagle/upper.bmp
Binary file not shown.
30 changes: 22 additions & 8 deletions M4_Eyes/file.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//34567890123456789012345678901234567890123456789012345678901234567890123456

#define ARDUINOJSON_ENABLE_COMMENTS 1
#include <ArduinoJson.h> // JSON config file functions
#define ARDUINOJSON_ENABLE_COMMENTS 1 // ARDUINOJSON_ENABLE_COMMENTS must be set to 1 before including the library.
// The same value of ARDUINOJSON_ENABLE_COMMENTS must be set in each compilation unit.
#include <ArduinoJson.h> // JSON config file functions
#include "globals.h"

extern Adafruit_Arcada arcada;
Expand Down Expand Up @@ -89,7 +90,7 @@ void loadConfig(char *filename) {
DeserializationError error = deserializeJson(doc, file);
yield();
if(error) {
Serial.println("Config file error, using default settings");
Serial.println("Config file error, using default settings.");
Serial.println(error.c_str());
} else {
uint8_t e;
Expand Down Expand Up @@ -284,8 +285,9 @@ void loadConfig(char *filename) {
#endif // ADAFRUIT_MONSTER_M4SK_EXPRESS
}
file.close();
user_setup(doc);
} else {
Serial.println("Can't open config file, using default settings");
Serial.println("Can't open config file, using default settings.");
}

// INITIALIZE DEFAULT VALUES if config file missing or in error ----------
Expand Down Expand Up @@ -329,7 +331,7 @@ ImageReturnCode loadEyelid(char *filename,
Adafruit_ImageReader *reader;

reader = arcada.getImageReader();
if (!reader) {
if(!reader) {
return IMAGE_ERR_FILE_NOT_FOUND;
}

Expand All @@ -339,7 +341,7 @@ ImageReturnCode loadEyelid(char *filename,
// This is the "booster seat" described in m4eyes.ino
if(reader->bmpDimensions(filename, &w, &h) == IMAGE_SUCCESS) {
tempBytes = ((w + 7) / 8) * h; // Bitmap size in bytes
if (maxRam > tempBytes) {
if(maxRam > tempBytes) {
if((tempPtr = (uint8_t *)malloc(maxRam - tempBytes)) != NULL) {
// Make SOME tempPtr reference, or optimizer removes the alloc!
tempPtr[0] = 0;
Expand Down Expand Up @@ -415,14 +417,14 @@ ImageReturnCode loadTexture(char *filename, uint16_t **data,
Adafruit_ImageReader *reader;

reader = arcada.getImageReader();
if (!reader) {
if(!reader) {
return IMAGE_ERR_FILE_NOT_FOUND;
}

// This is the "booster seat" described in m4eyes.ino
if(reader->bmpDimensions(filename, &w, &h) == IMAGE_SUCCESS) {
tempBytes = w * h * 2; // Image size in bytes (converted to 16bpp)
if (maxRam > tempBytes) {
if(maxRam > tempBytes) {
if((tempPtr = (uint8_t *)malloc(maxRam - tempBytes)) != NULL) {
// Make SOME tempPtr reference, or optimizer removes the alloc!
tempPtr[0] = 0;
Expand Down Expand Up @@ -455,3 +457,15 @@ ImageReturnCode loadTexture(char *filename, uint16_t **data,

return status;
}

// Utility functions for use by user functions to grab an integer value from the config file
// using dwim() and providing a default value.
int32_t getDocInt(StaticJsonDocument<2048> &doc, const char *nm, int32_t def) {
return dwim(doc[nm], def);
}
int32_t getDocInt(StaticJsonDocument<2048> &doc, const char *nm, const char *nm2, int32_t def) {
return dwim(doc[nm][nm2], def);
}
int32_t getDocInt(StaticJsonDocument<2048> &doc, const char *nm, const char *nm2, const char *nm3, int32_t def) {
return dwim(doc[nm][nm2][nm3], def);
}
Loading