Skip to content

Commit 3fb89ee

Browse files
authored
2 parents 789df2d + ccbd97c commit 3fb89ee

File tree

6 files changed

+3325
-1
lines changed

6 files changed

+3325
-1
lines changed

.github/workflows/githubci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
strategy:
88
fail-fast: false
99
matrix:
10-
arduino-platform: ["uno", "nrf52832", "cpx_ada", "pyportal", "protrinket_3v", "protrinket_5v", "metro_m0", "esp8266", "esp32", "trinket_3v", "trinket_5v", "gemma", "flora", "feather32u4", "feather_m0_express", "gemma_m0", "trinket_m0", "hallowing_m0", "monster_m4sk", "hallowing_m4", "neotrellis_m4", "pybadge", "cpb"]
10+
arduino-platform: ["uno", "nrf52832", "cpx_ada", "pyportal", "protrinket_3v", "protrinket_5v", "metro_m0", "esp8266", "esp32", "trinket_3v", "trinket_5v", "gemma", "flora", "feather32u4", "feather_m0_express", "gemma_m0", "trinket_m0", "hallowing_m0", "monster_m4sk", "hallowing_m4", "neotrellis_m4", "pybadge", "cpb", "cpc"]
1111

1212
runs-on: ubuntu-latest
1313

CircuitPlaygroundMakeBelieve/.cpc.test.only

Whitespace-only changes.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// "Make-Believe" sketch for Adafruit Circuit Playground.
2+
// Lights and sounds react to motion and taps.
3+
4+
#include <Adafruit_CircuitPlayground.h>
5+
6+
// Enable ONE of these lines to select a theme:
7+
//#include "knight.h" // Swoosh & clank metal sword
8+
//#include "laser.h" // MWAAAW! "laser" sword
9+
#include "wand.h" // Magic wand
10+
11+
void setup() {
12+
// Initialize Circuit Playground board, accelerometer, NeoPixels
13+
CircuitPlayground.begin();
14+
CircuitPlayground.setAccelRange(LIS3DH_RANGE_16_G);
15+
CircuitPlayground.strip.setBrightness(255); // Full brightness
16+
17+
// Start default "idle" looping animation and sound
18+
playAnim( idlePixelData, idleFPS , sizeof(idlePixelData), true);
19+
playSound(idleAudioData, idleSampleRate, sizeof(idleAudioData), true);
20+
}
21+
22+
// Global values used by the animation and sound functions
23+
uint16_t *pixelBaseAddr, // Address of active animation table
24+
pixelLen, // Number of pixels in active table
25+
pixelIdx, // Index of first pixel of current frame
26+
audioLen; // Number of audio samples in active table
27+
volatile uint16_t audioIdx; // Index of current audio sample
28+
uint8_t pixelFPS, // Frames/second for active animation
29+
*audioBaseAddr; // Address of active sound table
30+
boolean pixelLoop, // If true, animation repeats
31+
audioLoop; // If true, audio repeats
32+
33+
// Begin playing a sound from PROGMEM table (unless NULL)
34+
void playSound(const uint8_t *addr, uint16_t rate, uint16_t len, boolean repeat) {
35+
audioBaseAddr = addr;
36+
if(addr) {
37+
CircuitPlayground.speaker.begin();
38+
audioLen = len;
39+
audioLoop = repeat;
40+
audioIdx = 0;
41+
42+
// Timer/Counter 1 interrupt is used to play sound in background
43+
TCCR1A = 0; // Timer1: No PWM output
44+
TCCR1B = _BV(WGM12) | _BV(CS10); // CTC mode, no prescale
45+
OCR1A = (F_CPU + (rate / 2)) / rate; // Value for timer match
46+
TCNT1 = 0; // Reset counter
47+
TIMSK1 = _BV(OCIE1A); // Start interrupt
48+
} else { // NULL addr = audio OFF
49+
TIMSK1 = 0; // Stop interrupt
50+
while(OCR4A) { // Ease speaker into off position
51+
OCR4A--; // to avoid audible 'pop' at end
52+
delayMicroseconds(3);
53+
}
54+
CircuitPlayground.speaker.end();
55+
}
56+
}
57+
58+
// Timer/Counter 1 interrupt; periodically sets PWM speaker output from audio table
59+
ISR(TIMER1_COMPA_vect) {
60+
OCR4A = pgm_read_byte(&audioBaseAddr[audioIdx]);
61+
if(++audioIdx >= audioLen) { // Past end of table?
62+
if(audioLoop) { // Loop sound?
63+
audioIdx = 0; // Reset counter to start of table
64+
} else { // Don't loop...
65+
playSound(idleAudioData, idleSampleRate, sizeof(idleAudioData), true);
66+
}
67+
}
68+
}
69+
70+
// Begin playing a NeoPixel animation from a PROGMEM table
71+
void playAnim(const uint16_t *addr, uint8_t fps, uint16_t bytes, boolean repeat) {
72+
pixelBaseAddr = addr;
73+
if(addr) {
74+
pixelFPS = fps;
75+
pixelLen = bytes / 2;
76+
pixelLoop = repeat;
77+
pixelIdx = 0;
78+
} else {
79+
CircuitPlayground.strip.clear();
80+
}
81+
}
82+
83+
uint32_t prev = 0; // Time of last NeoPixel refresh
84+
85+
void loop() {
86+
uint32_t t; // Current time in milliseconds
87+
// Until the next animation frame interval has elapsed...
88+
while(((t = millis()) - prev) < (1000 / pixelFPS)) {
89+
// Read accelerometer, test for swing/tap thresholds
90+
float x = CircuitPlayground.motionX() / 9.8, // m/s2 to G
91+
y = CircuitPlayground.motionY() / 9.8,
92+
z = CircuitPlayground.motionZ() / 9.8,
93+
d = sqrt(x * x + y * y + z * z); // Acceleration magnitude in 3D
94+
d = fabs(d - 1.0); // Neutral is 1G; d is relative acceleration now
95+
if(d >= TAP_THRESHOLD) { // Big acceleration?
96+
if(audioBaseAddr != tapAudioData) { // If not already playing tap sound,
97+
// Start playing tap sound and animation
98+
playSound(tapAudioData, tapSampleRate, sizeof(tapAudioData), false);
99+
playAnim(tapPixelData, tapFPS, sizeof(tapPixelData), false);
100+
}
101+
} else if(d >= SWING_THRESHOLD) { // Medium acceleration?
102+
if(audioBaseAddr == idleAudioData) { // If not already swinging or tapping,
103+
// start playing swing sound and animation
104+
playSound(swingAudioData, swingSampleRate, sizeof(swingAudioData), false);
105+
playAnim(swingPixelData, swingFPS, sizeof(swingPixelData), false);
106+
}
107+
}
108+
}
109+
110+
// Show LEDs rendered on prior pass. It's done this way so animation timing
111+
// is a bit more consistent (frame rendering time may vary slightly).
112+
CircuitPlayground.strip.show();
113+
prev = t; // Save refresh time for next frame sync
114+
115+
if(pixelBaseAddr) {
116+
for(uint8_t i=0; i<10; i++) { // For each NeoPixel...
117+
// Read pixel color from PROGMEM table
118+
uint16_t rgb = pgm_read_word(&pixelBaseAddr[pixelIdx++]);
119+
// Expand 16-bit color to 24 bits using gamma tables
120+
// RRRRRGGGGGGBBBBB -> RRRRRRRR GGGGGGGG BBBBBBBB
121+
CircuitPlayground.strip.setPixelColor(i,
122+
pgm_read_byte(&gamma5[ rgb >> 11 ]),
123+
pgm_read_byte(&gamma6[(rgb >> 5) & 0x3F]),
124+
pgm_read_byte(&gamma5[ rgb & 0x1F]));
125+
}
126+
127+
if(pixelIdx >= pixelLen) { // End of animation table reached?
128+
if(pixelLoop) { // Repeat animation?
129+
pixelIdx = 0; // Reset index to start of table
130+
} else { // else switch back to "idle" animation
131+
playAnim(idlePixelData, idleFPS, sizeof(idlePixelData), true);
132+
}
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)