-
Notifications
You must be signed in to change notification settings - Fork 0
/
curious_cube.ino
209 lines (175 loc) · 6.05 KB
/
curious_cube.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#include <CapacitiveSensor.h>
#include <Wire.h>
#include "Adafruit_DRV2605.h"
Adafruit_DRV2605 drv;
// A curious cube that acts like a cat
// its default state is asleep
// when it detects a person approaching, it lights up in recognition
// and enters either PEACE or SENSITIVE mode
// if person pets cube in PEACE mode, it can enter either HAPPY or ANGRY mode
// if person pets cube in SENSITIVE mode, it will 100% enter ANGRY mode
// from ANGRY mode, it can either stay in ANGRY mode or go back to SENSITIVE mode
// from SENSITIVE mode it can either stay in SENSITIVE mode or go to PEACE mode
#define PEACE 0
#define SENSITIVE 1
#define HAPPY 2
#define ANGRY 3
#define ASLEEP 4
#define distancePin 2
#define whitePin 9
#define yellowPin 10
#define awakeTime 60000 // milliseconds cube will stay awake
#define evaluateTime 5000 // evaluate next state every N seconds
CapacitiveSensor capSensor = CapacitiveSensor(4, 6); // 10M resistor between pins 4 & 6, pin 6 is sensor pin
long capSensorThreshold = 1000; // if capacitive sensor reading is over threshold, then cube is touched
// when user is not interacting with cube
// then state will be revaluated every N seconds
int possibleNextStates[5][3] = {
{PEACE, SENSITIVE, 80}, // from PEACE, probability of staying PEACE is 80%
{SENSITIVE, PEACE, 80}, // from SENSITIVE
{HAPPY, PEACE, 60}, // from HAPPY
{ANGRY, SENSITIVE, 60}, // from ANGRY
{PEACE, SENSITIVE, 60} // from ASLEEP
};
// when user touches the cube, reevaluate state
int touchNextStates[5][3] = {
{HAPPY, ANGRY, 90}, // from PEACE
{ANGRY, ANGRY, 100}, // from SENSITIVE
{HAPPY, HAPPY, 100}, // from HAPPY
{ANGRY, ANGRY, 100}, // from ANGRY
{ANGRY, ANGRY, 100} // from ASLEEP
};
int currentState = ASLEEP;
int lastWakeupTime = -1;
int lastEvaluateTime = -1;
int prevDistanceValue = LOW;
void setup() {
Serial.begin(9600);
// temporary for button, will be replaced with sensor later
pinMode(distancePin, INPUT);
pinMode(whitePin, OUTPUT);
pinMode(yellowPin, OUTPUT);
// make sure random() is truly random
randomSeed(analogRead(0));
// motor
drv.begin();
drv.selectLibrary(1);
// I2C trigger by sending 'go' command
// default, internal trigger when sending GO command
drv.setMode(DRV2605_MODE_INTTRIG);
}
void loop() {
// get distance sensor reading (temp button)
int distanceReading = digitalRead(distancePin);
long touchReading = capSensor.capacitiveSensor(30);
int seePerson = distanceReading == HIGH && prevDistanceValue == LOW;
int touchCube = touchReading > capSensorThreshold;
int currentTime = millis();
// if we have positive reading on distance sensor
// update lastWakeupTime to current time so we can reset countdown for cube sleeping
if (seePerson) {
lastWakeupTime = currentTime;
Serial.println("saw person");
}
if (touchCube) {
// if cube is touched, determine next state based on
// probabilities outlined in touchNextStates
determineNextState(touchNextStates);
// since we've evaluated state, also update lastEvaluateTime to now
lastEvaluateTime = currentTime;
// also reset wakeup time since we've touched it
lastWakeupTime = currentTime;
} else {
// if cube is not touched
// case when cube is asleep
if (currentState == ASLEEP) {
// cube sees person so it wakes up
if (seePerson) {
// first light LED to white to give visual indicator that cube is awake
analogWrite(whitePin, 255); // turn on white LED
delay(1000);
analogWrite(whitePin, 0); // turn off white LED
// determine next state
determineNextState(possibleNextStates);
}
} else {
// cube is awake
// check if it should go to sleep
// if current time is more than lastWakeupTime + awakeTime
if (currentTime > (lastWakeupTime + awakeTime)) {
currentState = ASLEEP;
Serial.println("went to sleep!");
} else {
// if it doesn't need to go to sleep yet
// evaluate what next state should be every N seconds
if (currentTime > (lastEvaluateTime + evaluateTime)) {
determineNextState(possibleNextStates);
lastEvaluateTime = currentTime;
}
}
}
}
// SET LED & MOTOR VALUES
int value = 0; // default to value 0 for ASLEEP
int effect = 0;
if (currentState != ASLEEP) {
// if awake
if (currentState == ANGRY) {
value = (currentTime / 100) % 2;
value = value * 255;
// if LED is blinking on, then buzz motor
if (value) {
effect = 47;
drv.setWaveform(0, effect); // strong click
drv.go();
}
} else {
float speed = 0.25; // HAPPY & PEACE
if (currentState == SENSITIVE) {
speed = 1.5; // SENSITIVE
}
// brightness of LED is a sine wave of time (current - lastEvaluateTime)
float time = speed * currentTime / 1000.0;
value = 128.0 + 128 * sin( time * 2.0 * PI );
if (currentTime == HAPPY) {
if (!value) {
effect = 82;
} else if (value == 255) {
effect = 70;
}
drv.setWaveform(0, effect); // strong click
drv.go();
}
}
}
if (currentState == HAPPY) {
// just for happy turn on yellow LED instead of white
analogWrite(yellowPin, value);
analogWrite(whitePin, 0);
} else {
// for everything else turn on white pin
analogWrite(whitePin, value);
analogWrite(yellowPin, 0);
}
// set the effect to play
// play the effect!
delay(1);
prevDistanceValue = distanceReading;
}
void determineNextState(int nextStates[5][3]) {
int randomNum = random(100);
// get probability of current state going to the first option of next state
int probability = nextStates[currentState][2];
Serial.print("current state: ");
Serial.print(currentState);
Serial.print(", random number: ");
Serial.print(randomNum);
// update current state
if (randomNum < probability) {
currentState = nextStates[currentState][0];
} else {
currentState = nextStates[currentState][1];
}
Serial.print(", next state: ");
Serial.println(currentState);
}