-
Notifications
You must be signed in to change notification settings - Fork 0
/
lightshow.ino
247 lines (200 loc) · 6.41 KB
/
lightshow.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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include <Adafruit_NeoPixel.h>
#include "lightstrip.h"
#define PWM_PORT 2
#define RING_FRONT_PORT 5
#define RING_BACK_PORT 6
////////////////////////////////////////////////////////////////////////////
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
#define WHITE 0xFFFFFF
////////////////////////////////////////////////////////////////////////////
// These are individual light shows. They will be called repeatedly so
// should represent a single, full cycle of animation. See lightshows
// below to define the PWM pulse width that triggers each show.
// Assumes back ring is mounted upside-down vs. front ring
#define RING_COUNT 2
LightStrip ring[RING_COUNT] = { LightStrip(16, RING_FRONT_PORT),
LightStrip(16, RING_BACK_PORT) };
// Set brightness on one or all rings
void setBrightness(uint8_t brightness, int index = -1) {
if (index < 0) {
for (int i = 0; i < RING_COUNT; ++i) { ring[i].setBrightness(brightness); }
} else if (index < RING_COUNT) {
ring[index].setBrightness(brightness);
}
}
// Set color for one or all rings
void setColor(uint32_t color, uint32_t mask, int index = -1) {
if (index < 0) {
for (int i = 0; i < RING_COUNT; ++i) { ring[i].setColor(color, mask); }
} else if (index < RING_COUNT) {
ring[index].setColor(color, mask);
}
}
// clear one or all rings
void clear(int index = -1) {
setColor(0, 0xFFFFFFFF, index);
}
// show one or all rings
void show(int index = -1) {
if (index < 0) {
for (int i = 0; i < RING_COUNT; ++i) { ring[i].show(); }
} else if (index < RING_COUNT) {
ring[index].show();
}
}
// Alternates even/odd between color
void alternating(uint32_t color1, uint32_t color2) {
setBrightness(0xFF);
setColor(color1, 0xAAAA);
setColor(color2, 0x5555);
show();
delay(250);
setColor(color1, 0x5555);
setColor(color2, 0xAAAA);
show();
delay(250);
}
void autonomous() {
setBrightness(0xFF);
for (int i = 0 ; i < 8 ; ++i) {
setColor(RED, 1L << i);
setColor(BLUE, 1L << (i+8));
show();
delay(100);
}
for (int i = 0 ; i < 8 ; ++i) {
setColor(BLUE, 1L << i);
setColor(RED, 1L << (i+8));
show();
delay(100);
}
}
void ramp0() {
setColor(RED, 0xFFFF);
setBrightness(0xFF);
show();
}
void ramp1() {
setColor(GREEN, 0xFF00);
setColor(RED, 0x00FF);
setBrightness(0xFF);
show();
}
void ramp2() {
setColor(RED, 0xFF00);
setColor(GREEN, 0x00FF);
setBrightness(0xFF);
show();
}
void ramp3() {
setColor(GREEN, 0xFFFF);
setBrightness(0xFF);
show();
}
void blackout() {
clear();
show();
}
void camera() {
setColor(WHITE, 0xFFFF);
setBrightness(0xFF);
show();
}
////////////////////////////////////////////////////////////////////////////
typedef void (*funcptr)(); // a lightshow function
// Lightshow data
typedef struct {
uint32_t lowerBound; // lower bound for pwm signal
uint32_t upperBound; // upper bound for pwm signal
funcptr function; // function to invoke
char * name; // name of lightshow
} pwm_lightshow_t;
// define the lightshows with pwm time boundaries in microseconds. The overlap
// between entries allows for some jitter and a new state should not be entered
// until the neighboring state has been left. Values measured off the cRIO:
// 650us Autonomous
// 1000us Showoff
// 1300us Catching
// 1650us Disabled
// 2000us Teleop
pwm_lightshow_t lightshows[] = {
{ 0, 500, &blackout, "blackout"}, // blackout
{ 450, 850, &ramp0, "ramp_neither"}, // ramp sensors detect: neither
{ 800, 1200, &ramp1, "ramp_left" }, // ramp sensors detect: left
{ 1100, 1500, &ramp2, "ramp_right" }, // ramp sensors detect: right
{ 1450, 1850, &ramp3, "ramp_both" }, // ramp sensors detect: both
{ 1800, 2200, &autonomous, "autonomous" }, // spinner
{ 2150, 9000, &camera, "camera" }, // camera illumination
{ 9999, 9999, &blackout, "boundary" } // no lights
};
pwm_lightshow_t * currentLightShow = lightshows;
// Function declarations
void reportLightShow(pwm_lightshow_t * lightshow, uint32_t pulse);
void selectAndRunLightShow(uint32_t pulse);
////////////////////////////////////////////////////////////////////////////
// Interrupt Service Routine to handle PWM signal output by the cRIO. The
// main robot code will output a 198 Hz PWM signal (5.05ms).
volatile uint32_t pwmRisingEdge; // timestamp of current rising edge
volatile uint32_t pwmPulseWidth; // duration of pulse in microseconds
void isr() {
uint32_t now = micros();
if (digitalRead(PWM_PORT) == LOW) {
pwmPulseWidth = now - pwmRisingEdge;
} else {
pwmRisingEdge = now;
}
}
////////////////////////////////////////////////////////////////////////////
// Report light show data on the serial port
void reportLightShow(pwm_lightshow_t * lightshow, uint32_t pulse) {
if (currentLightShow == lightshow) { return; }
Serial.print(lightshow->name);
Serial.print(": ");
Serial.print(lightshow->lowerBound);
Serial.print("/");
Serial.print(pulse);
Serial.print("/");
Serial.println(lightshow->upperBound);
}
// Run a lightshow based on the current received pwm pulse width received
// from the cRIO and defined in lightshows above.
void selectAndRunLightShow(uint32_t pulse) {
pwm_lightshow_t * lightshow = lightshows;
while (lightshow->lowerBound < 9999) {
if (pulse >= lightshow->lowerBound &&
pulse < lightshow->upperBound) {
// Pluse is within the window for a light show. Make sure it has exited
// the neighboring light show via a guard band (hysteresis) -- prevents
// bouncing rapidly between two lightshows should we get a borderline
// signal from the cRIO.
if ((pulse < (lightshow+1)->lowerBound) &&
(lightshow == lightshows || pulse > (lightshow-1)->upperBound)) {
reportLightShow(lightshow, pulse);
currentLightShow = lightshow;
break;
}
}
lightshow++;
}
(lightshow->function)();
}
////////////////////////////////////////////////////////////////////////////
void setup() {
pwmRisingEdge = micros();
pwmPulseWidth = 0;
currentLightShow = 0;
Serial.begin(9600);
for (int i = 0; i < RING_COUNT ; ++i) {
ring[i].begin();
ring[i].show();
}
pinMode(PWM_PORT, INPUT);
pinMode(RING_FRONT_PORT, OUTPUT);
pinMode(RING_BACK_PORT, OUTPUT);
attachInterrupt(0, isr, CHANGE);
}
void loop() {
selectAndRunLightShow(pwmPulseWidth);
}