Skip to content

Commit 9a21261

Browse files
authored
Merge pull request #881 from teejaydub/master
Integrate Watcher project via user module
2 parents 7e717e1 + 6be8e7e commit 9a21261

6 files changed

Lines changed: 225 additions & 24 deletions

File tree

M4_Eyes/HeatSensor.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/* Read the IR sensor and try to figure out where the heat is located.
2+
*/
3+
4+
#include "HeatSensor.h"
5+
6+
#include <Wire.h>
7+
#include <Adafruit_AMG88xx.h>
8+
9+
Adafruit_AMG88xx amg;
10+
11+
float pixels[AMG88xx_PIXEL_ARRAY_SIZE];
12+
13+
void HeatSensor::setup()
14+
{
15+
x = 0;
16+
y = 0;
17+
magnitude = 0;
18+
19+
bool status;
20+
21+
// default settings
22+
status = amg.begin();
23+
if (!status) {
24+
Serial.println("Could not find a valid AMG88xx sensor, check wiring!");
25+
while (1);
26+
}
27+
28+
yield();
29+
delay(100); // let sensor boot up
30+
}
31+
32+
// Find the approximate X and Y values of the peak temperature in the pixel array,
33+
// along with the magnitude of the brightest spot.
34+
void HeatSensor::find_focus()
35+
{
36+
amg.readPixels(pixels);
37+
yield();
38+
39+
x = 0, y = 0, magnitude = 0;
40+
float minVal = 100, maxVal = 0;
41+
int i = 0;
42+
for (float yPos = 3.5; yPos > -4; yPos -= 1.0) {
43+
for (float xPos = 3.5; xPos > -4; xPos -= 1.0) {
44+
float p = pixels[i];
45+
x += xPos * p;
46+
y += yPos * p;
47+
minVal = min(minVal, p);
48+
maxVal = max(maxVal, p);
49+
i++;
50+
}
51+
}
52+
x = - x / AMG88xx_PIXEL_ARRAY_SIZE / 5.0;
53+
y = y / AMG88xx_PIXEL_ARRAY_SIZE / 5.0;
54+
x = max(-1.0, min(1.0, x));
55+
y = max(-1.0, min(1.0, y));
56+
magnitude = max(0, min(50, maxVal - 20));
57+
58+
// Report.
59+
#define SERIAL_OUT 3
60+
#if SERIAL_OUT == 1
61+
// Print raw values
62+
Serial.print("[");
63+
for(int i=1; i<=AMG88xx_PIXEL_ARRAY_SIZE; i++){
64+
Serial.print(pixels[i-1]);
65+
Serial.print(", ");
66+
if( i%8 == 0 ) Serial.println();
67+
}
68+
Serial.println("]");
69+
Serial.println();
70+
#endif
71+
#if SERIAL_OUT == 2
72+
// Print character-graphic array
73+
const char charPixels[] = " .-*o0#";
74+
Serial.println("========");
75+
for (int i = 1; i <= AMG88xx_PIXEL_ARRAY_SIZE; i++) {
76+
int val = min(5, round(max(0, pixels[i-1] - 20) / 2));
77+
Serial.print(charPixels[val]);
78+
if (i % 8 == 0)
79+
Serial.println();
80+
}
81+
Serial.println();
82+
#endif
83+
#if SERIAL_OUT == 3 || SERIAL_OUT == 2
84+
// Print coordinates and brightness
85+
Serial.print(x);
86+
Serial.print(' ');
87+
Serial.print(y);
88+
Serial.print(' ');
89+
Serial.println(magnitude);
90+
#endif
91+
}
92+
93+
/*
94+
void loop() {
95+
// Read all the pixels
96+
97+
// Find the focal point.
98+
float x, y, magnitude;
99+
find_focus(x, y, magnitude);
100+
101+
// Set diagnostic LEDs.
102+
analogWrite(CENTER_LED, round(max(0, magnitude / 30) * 255));
103+
analogWrite(RIGHT_LED, round(min(1.0, max(0.0, -x / 3)) * 255));
104+
analogWrite(LEFT_LED, round(min(1.0, max(0.0, x / 3)) * 255));
105+
analogWrite(UP_LED, round(min(1.0, max(0.0, y / 3)) * 255));
106+
analogWrite(DOWN_LED, round(min(1.0, max(0.0, -y / 3)) * 255));
107+
108+
delay(200);
109+
}
110+
*/

M4_Eyes/HeatSensor.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* Read the IR sensor and try to figure out where the heat is located.
2+
3+
Orientation: Looking into the sensor, the window is on the bottom of the silver package,
4+
and the Adafruit star is at the upper left.
5+
X goes from about -1.0 on the left, facing out of the sensor, to +1.0 on the right.
6+
Y goes roughly from -1.0 (less?) on the bottom to +1.0 on the top.
7+
The sensor is oriented with its text upside down for the moment.
8+
Magnitude is currently the maximum temperature of any pixel, in degrees C.
9+
*/
10+
11+
#ifndef __HEAT_SENSOR_H
12+
#define __HEAT_SENSOR_H
13+
14+
class HeatSensor {
15+
public:
16+
// The current focus position, each from -1.0 .. +1.0.
17+
float x, y;
18+
19+
// The current magnitude estimate, in degrees C.
20+
float magnitude;
21+
22+
// Must be called once.
23+
void setup();
24+
25+
// Reads the sensor and updates x, y, and magnitude.
26+
void find_focus();
27+
};
28+
29+
#endif

M4_Eyes/M4WATCH.UF2

1 MB
Binary file not shown.

M4_Eyes/M4_Eyes.ino

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -187,22 +187,24 @@ void setup() {
187187
#endif
188188

189189
yield();
190-
if (arcada.drawBMP((char *)"/splash.bmp", 0, 0, (eye[0].display)) == IMAGE_SUCCESS) {
191-
Serial.println("Splashing");
192-
if (NUM_EYES > 1) { // other eye
193-
yield();
194-
arcada.drawBMP((char *)"/splash.bmp", 0, 0, (eye[1].display));
195-
}
196-
// backlight on for a bit
197-
for (int bl=0; bl<=250; bl+=20) {
198-
arcada.setBacklight(bl);
199-
delay(20);
200-
}
201-
delay(2000);
202-
// backlight back off
203-
for (int bl=250; bl>=0; bl-=20) {
204-
arcada.setBacklight(bl);
205-
delay(20);
190+
if (showSplashScreen) {
191+
if (arcada.drawBMP((char *)"/splash.bmp", 0, 0, (eye[0].display)) == IMAGE_SUCCESS) {
192+
Serial.println("Splashing");
193+
if (NUM_EYES > 1) { // other eye
194+
yield();
195+
arcada.drawBMP((char *)"/splash.bmp", 0, 0, (eye[1].display));
196+
}
197+
// backlight on for a bit
198+
for (int bl=0; bl<=250; bl+=20) {
199+
arcada.setBacklight(bl);
200+
delay(20);
201+
}
202+
delay(2000);
203+
// backlight back off
204+
for (int bl=250; bl>=0; bl-=20) {
205+
arcada.setBacklight(bl);
206+
delay(20);
207+
}
206208
}
207209
}
208210

@@ -475,8 +477,10 @@ void loop() {
475477
if(eyeInMotion) { // Currently moving?
476478
if(dt >= eyeMoveDuration) { // Time up? Destination reached.
477479
eyeInMotion = false; // Stop moving
478-
eyeMoveDuration = random(10000, 3000000); // 0.01-3 sec stop
479-
eyeMoveStartTime = t; // Save initial time of stop
480+
if (moveEyesRandomly) {
481+
eyeMoveDuration = random(10000, 3000000); // 0.01-3 sec stop
482+
eyeMoveStartTime = t; // Save initial time of stop
483+
}
480484
eyeX = eyeOldX = eyeNewX; // Save position
481485
eyeY = eyeOldY = eyeNewY;
482486
} else { // Move time's not yet fully elapsed -- interpolate position
@@ -489,13 +493,23 @@ void loop() {
489493
eyeX = eyeOldX;
490494
eyeY = eyeOldY;
491495
if(dt > eyeMoveDuration) { // Time up? Begin new move.
496+
// r is the radius in X and Y that the eye can go, from (0,0) in the center.
492497
float r = (float)mapDiameter - (float)DISPLAY_SIZE * M_PI_2; // radius of motion
493-
r *= 0.6;
494-
eyeNewX = random(-r, r);
495-
float h = sqrt(r * r - x * x);
496-
eyeNewY = random(-h, h);
498+
r *= 0.6; // calibration constant
499+
500+
if (moveEyesRandomly) {
501+
eyeNewX = random(-r, r);
502+
float h = sqrt(r * r - x * x);
503+
eyeNewY = random(-h, h);
504+
} else {
505+
eyeNewX = eyeTargetX * r;
506+
eyeNewY = eyeTargetY * r;
507+
}
508+
497509
eyeNewX += mapRadius;
498510
eyeNewY += mapRadius;
511+
512+
// Set the duration for this move, and start it going.
499513
eyeMoveDuration = random(83000, 166000); // ~1/12 - ~1/6 sec
500514
eyeMoveStartTime = t; // Save initial time of move
501515
eyeInMotion = true; // Start move on next frame
@@ -870,8 +884,7 @@ void loop() {
870884
lightSensorPin = -1; // Stop trying to use the light sensor
871885
} else {
872886
lastLightReadTime = t - LIGHT_INTERVAL + 30000; // Try again in 30 ms
873-
}
874-
}
887+
} }
875888
}
876889
irisValue = (irisValue * 0.97) + (lastLightValue * 0.03); // Filter response for smooth reaction
877890
} else {

M4_Eyes/globals.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
GLOBAL_VAR Adafruit_Arcada arcada;
2424

25+
GLOBAL_VAR bool showSplashScreen GLOBAL_INIT(true); // Clear to suppress the splash screen
26+
2527
#define MAX_DISPLAY_SIZE 240
2628
GLOBAL_VAR int DISPLAY_SIZE GLOBAL_INIT(240); // Start with assuming a 240x240 display
2729
GLOBAL_VAR int DISPLAY_X_OFFSET GLOBAL_INIT(0); // Used with rectangular screens
@@ -68,6 +70,11 @@ GLOBAL_VAR float irisRange GLOBAL_INIT(0.35);
6870
GLOBAL_VAR bool tracking GLOBAL_INIT(true);
6971
GLOBAL_VAR float trackFactor GLOBAL_INIT(0.5);
7072

73+
// Random eye motion: provided by the base project, but overridable by user code.
74+
GLOBAL_VAR bool moveEyesRandomly GLOBAL_INIT(true); // Clear to suppress random eye motion and let user code control it
75+
GLOBAL_VAR float eyeTargetX GLOBAL_INIT(0.0); // THen set these continuously in user_loop.
76+
GLOBAL_VAR float eyeTargetY GLOBAL_INIT(0.0); // Range is from -1.0 to +1.0.
77+
7178
// Pin definition stuff will go here
7279

7380
GLOBAL_VAR int8_t lightSensorPin GLOBAL_INIT(-1);

M4_Eyes/user_watch.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#if 0 // Change to 0 to disable this code (must enable ONE user*.cpp only!)
2+
3+
#include "globals.h"
4+
#include "heatSensor.h"
5+
6+
7+
// For heat sensing
8+
HeatSensor heatSensor;
9+
10+
// This file provides a crude way to "drop in" user code to the eyes,
11+
// allowing concurrent operations without having to maintain a bunch of
12+
// special derivatives of the eye code (which is still undergoing a lot
13+
// of development). Just replace the source code contents of THIS TAB ONLY,
14+
// compile and upload to board. Shouldn't need to modify other eye code.
15+
16+
// User globals can go here, recommend declaring as static, e.g.:
17+
// static int foo = 42;
18+
19+
// Called once near the end of the setup() function. If your code requires
20+
// a lot of time to initialize, make periodic calls to yield() to keep the
21+
// USB mass storage filesystem alive.
22+
void user_setup(void) {
23+
showSplashScreen = false;
24+
moveEyesRandomly = false;
25+
heatSensor.setup();
26+
}
27+
28+
// Called periodically during eye animation. This is invoked in the
29+
// interval before starting drawing on the last eye (left eye on MONSTER
30+
// M4SK, sole eye on HalloWing M0) so it won't exacerbate visible tearing
31+
// in eye rendering. This is also SPI "quiet time" on the MONSTER M4SK so
32+
// it's OK to do I2C or other communication across the bridge.
33+
void user_loop(void) {
34+
// Estimate the focus position.
35+
heatSensor.find_focus();
36+
37+
// Set values for the new X and Y.
38+
eyeTargetX = heatSensor.x;
39+
eyeTargetY = -heatSensor.y;
40+
}
41+
42+
#endif // 0

0 commit comments

Comments
 (0)