Last active
July 10, 2023 22:16
-
-
Save EvanBalster/4ea8642c582752d03ae654206851c580 to your computer and use it in GitHub Desktop.
Revisions
-
EvanBalster revised this gist
Jul 10, 2023 . 1 changed file with 185 additions and 79 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,12 @@ #include <Adafruit_CircuitPlayground.h> const long FrameDT = 16; unsigned long frame_index = 0; unsigned long frame_mark = 0; struct Timer { long started; @@ -44,9 +51,6 @@ TeaType teaType = SENCHA; long resteeps = 0; struct Accel { float x, y, z; @@ -63,6 +67,9 @@ struct Accel Accel gravity; static uint16_t NeoPixel_Gamma_x128[256]; static float NeoPixel_BrightnessMod = 1.f; // the setup function runs once when you press reset or power the board void setup() { @@ -78,44 +85,66 @@ void setup() { CircuitPlayground.begin(); // Calculate 16-bit gamma table... for (unsigned i = 0; i < 255; ++i) { NeoPixel_Gamma_x128[i] = uint16_t(pow(i/255.f,2.6f)*32640.f+0.5f); } CircuitPlayground.setBrightness(255); teaType = MAX_TEA_TYPE; resteeps = 3; refreshPixels(); } uint32_t pcg16_state = 0x406832dd; uint16_t pcg16_random(void) { uint32_t oldstate = pcg16_state; pcg16_state = pcg16_state * 747796405U + 1U; uint16_t value = ((oldstate >> 10U) ^ oldstate) >> 12U; uint32_t rot = oldstate >> 28U; return (value >> rot) | (value << ((- rot) & 15)); } void setPixel(uint16_t n, uint16_t r, uint16_t g, uint16_t b, uint16_t w = 0) { // Convert RGB into linear brightness. r = NeoPixel_Gamma_x128[min(r, uint16_t(255))]; g = NeoPixel_Gamma_x128[min(g, uint16_t(255))]; b = NeoPixel_Gamma_x128[min(b, uint16_t(255))]; w = NeoPixel_Gamma_x128[min(w, uint16_t(255))]; // Brightness scaling. float brightness = min(NeoPixel_BrightnessMod, 2.0f); r = uint16_t(r*brightness + .5f); g = uint16_t(g*brightness + .5f); b = uint16_t(b*brightness + .5f); w = uint16_t(w*brightness + .5f); // Dither randomly within one ULP of brightness (128) unsigned spinDither = 2*frame_index + 3*n + 3*(n>=5); unsigned rand = pcg16_random(); r += 2 + 2*(spinDither++&15) + 2*( rand &15); g += 2 + 2*(spinDither++&15) + 2*((rand>> 4)&15); b += 2 + 2*(spinDither++&15) + 2*((rand>> 8)&15); w += 2 + 2*(spinDither++&15) + 2*((rand>>12)&15); static const uint16_t L_MAX = (128*255), L_MIN = (255); // Excess RGB overflows into white. if (r > 32640) {w += (r-L_MAX)/3; r = L_MAX;} if (g > 32640) {w += (g-L_MAX)/2; g = L_MAX;} if (b > 32640) {w += (g-L_MAX)/4; b = L_MAX;} if (w > 32640) w = L_MAX; CircuitPlayground.setPixelColor(n, uint8_t(r>>7), uint8_t(g>>7), uint8_t(b>>7)); //uint8_t(w>>7) } void setPixel(uint16_t n, uint32_t color) @@ -129,45 +158,68 @@ void setPixel(uint16_t n, uint32_t color) void refreshPixels() { // Calculate brightness adjustment. float brightness = 1.f; if (alarm.started) { float pulse = .5f + .5f * sin(TWO_PI * countdown.remaining()/500.f); brightness = pulse*pulse*2.0f; } else if (countdown.started) { float pulse = .5f + .5f * sin(TWO_PI * countdown.remaining()/5000.f); pulse = pulse*pulse*pulse; brightness = .07f + .07f * pulse; } else { brightness = 2.f - (millis() - last_moved) / 10000.f; if (brightness > 1.f) brightness = 1.f; if (brightness < 0.f) brightness = 0.f; brightness *= .1f; } NeoPixel_BrightnessMod = brightness; if (countdown.started) { float progress = 1.0f - float(countdown.remaining()) / float(countdown.length); for (long i = 0; i < 10; ++i) { float fill = (progress-.1f*float(i))/.1f; if (fill < 0.f) fill = 0.f; if (fill > 1.f) fill = 1.f; setPixel(i, 192.f * fill, 127.f, 192.f - 192.f*fill); } } else { switch (teaType) { case SENCHA: setPixel(0, 0x007f5f); setPixel(1, 0x2b9348); setPixel(2, 0x55a630); setPixel(3, 0xaacc00); setPixel(4, 0x80b918); break; case BLACK: setPixel(0, 0x03071e); setPixel(1, 0x370617); setPixel(2, 0xdc2f02); setPixel(3, 0xf48c06); setPixel(4, 0xe85d04); break; default: setPixel(0, 0x7F007F); setPixel(1, 0x550055); setPixel(2, 0xBB00BB); setPixel(3, 0x550055); setPixel(4, 0x7F007F); break; } for (long i = 0; i < 5; ++i) { setPixel(5+i, (resteeps > i) ? 0xfdd85d : 0x6798c0); @@ -180,6 +232,14 @@ PinStatus b_right_prev = LOW, b_left_prev = LOW; static const long ARP_PATTERN[8] = {440, 550, 660, 1600, 880, 1320, 1100, 880}; long teaPower(long base, long resteeps) { long value = base << (resteeps>>1); if (resteeps & 1) {value = (17*value) / 12;} return value; } // the loop function runs over and over again forever void loop() { PinStatus b_left = digitalRead(CPLAY_LEFTBUTTON); @@ -188,7 +248,19 @@ void loop() { bool p_left = (b_left && !b_left_prev); bool p_right = (b_right && !b_right_prev); if (countdown.started) { if (p_right) // Cancels the alarm. { p_left = false; p_right = false; countdown.reset(); alarm.begin(250); } refreshPixels(); } else { if (teaType == MAX_TEA_TYPE) { @@ -211,14 +283,17 @@ void loop() { refreshPixels(); tone(A0, 440); delay(50); tone(A0, 330); delay(50); tone(A0, 660); delay(50); tone(A0, 880); delay(100); noTone(A0); } else { // Beep! refreshPixels(); tone(A0, teaPower(275, 5-resteeps)); delay(50); tone(A0, teaPower(220, 5-resteeps)); delay(50); noTone(A0); } } @@ -238,21 +313,16 @@ void loop() { while (upscale > 1) {baseTime *= 2; upscale -= 2;} if (upscale == 1) {baseTime = (17*baseTime) / 12;} countdown.begin(1000 * baseTime); // Beep! for (int i = 0; i < 10; ++i) setPixel(i, 0x7F00FF00); tone(A0, 880); delay(50); tone(A0, 1320); delay(50); tone(A0, 1760); delay(100); noTone(A0); } } { Accel accel; @@ -264,11 +334,6 @@ void loop() { if (sqrt(dx*dx+dy*dy+dz*dz)/dtf > 150.f) { last_moved = millis(); } float a = pow(.95f, dtf); @@ -281,19 +346,46 @@ void loop() { if (countdown.done()) { resteeps += 1; alarm.begin(5000); countdown.reset(); } static unsigned long alarm_arp = 0; long delay_this_loop = FrameDT; if (alarm.started) { if (alarm.done() || p_left || p_right) { p_left = false; p_right = false; alarm.reset(); noTone(A0); } else { tone(A0, ARP_PATTERN[alarm_arp&7]); ++alarm_arp; delay_this_loop = 40; } } else { alarm_arp = 0; if (countdown.started) { // tick, tock... digitalWrite(A0, (countdown.remaining()/1000) & 1); } else { noTone(A0); } } /*if (alarm.started) { @@ -304,7 +396,21 @@ void loop() { digitalWrite(CPLAY_REDLED, countdown.remaining() % 1000 < 100); }*/ // Simple framerate regulator { unsigned long frame_cur = millis(); unsigned long frame_len = (frame_cur - frame_mark); if (delay_this_loop > frame_len) { //digitalWrite(CPLAY_REDLED, 0); delay(delay_this_loop - frame_len); } //else digitalWrite(CPLAY_REDLED, 1); ++frame_index; frame_mark = frame_cur; } b_right_prev = b_right; b_left_prev = b_left; -
EvanBalster revised this gist
Jul 4, 2023 . 1 changed file with 35 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -83,12 +83,12 @@ void setup() { refreshPixels(); } void setPixel(uint16_t n, uint16_t r, uint16_t g, uint16_t b, uint16_t w = 0) { float brightness = 1.f; if (alarm.started) { float pulse = .8f + .8f * sin(TWO_PI * countdown.remaining()/500.f); brightness = pulse; } else if (countdown.started) @@ -104,10 +104,16 @@ void setPixel(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) if (brightness < 0.f) brightness = 0.f; } r = uint16_t(r*brightness); g = uint16_t(g*brightness); b = uint16_t(b*brightness); w = uint16_t(w*brightness); // Excess RGB overflow into white. if (r > 255) {w += (r-253)/3; r = 255;} if (g > 255) {w += (g-254)/2; g = 255;} if (b > 255) {w += (g-252)/4; b = 255;} if (w > 255) w = 255; CircuitPlayground.setPixelColor(n, Adafruit_CPlay_NeoPixel::gamma32(Adafruit_CPlay_NeoPixel::Color(r, g, b, w))); } @@ -200,7 +206,21 @@ void loop() { { resteeps = 0; teaType = TeaType((long(teaType)+1) % MAX_TEA_TYPE); // Beep! refreshPixels(); tone(A0, 440); delay(50); tone(A0, 330); delay(50); tone(A0, 220); delay(100); } else { // Beep! refreshPixels(); tone(A0, 440); delay(50); tone(A0, 550); delay(50); } } refreshPixels(); @@ -218,7 +238,15 @@ void loop() { while (upscale > 1) {baseTime *= 2; upscale -= 2;} if (upscale == 1) {baseTime = (17*baseTime) / 12;} resteeps += 1; countdown.begin(1000 * baseTime); // Beep! for (int i = 0; i < 10; ++i) setPixel(i, 0x7F00FF00); tone(A0, 880); delay(50); tone(A0, 1320); delay(50); tone(A0, 1760); delay(100); } } else @@ -233,7 +261,7 @@ void loop() { float dx = accel.x-gravity.x, dy = accel.y-gravity.y, dz = accel.z-gravity.z; if (sqrt(dx*dx+dy*dy+dz*dz)/dtf > 150.f) { last_moved = millis(); digitalWrite(CPLAY_REDLED, 1); -
EvanBalster created this gist
Jul 3, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,283 @@ #include <Adafruit_CircuitPlayground.h> struct Timer { long started; long length; Timer() { reset(); } long remaining() { return length - (millis()-started); } bool done() { return started && remaining() <= 0; } void begin(long length) { started = millis(); this->length = length; } void reset() { started = 0; length = 0; } }; Timer countdown; Timer alarm; long last_moved = 0; enum TeaType { SENCHA = 0, BLACK = 1, MAX_TEA_TYPE = 2 }; TeaType teaType = SENCHA; long resteeps = 0; const long DT = 8; struct Accel { float x, y, z; long time; void readSensor() { x = CircuitPlayground.motionX(); y = CircuitPlayground.motionY(); z = CircuitPlayground.motionZ(); time = millis(); } }; Accel gravity; // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin 13 as an output. //pinMode(13, OUTPUT); // Buttons //pinMode(A0, OUTPUT); //pinMode(4, INPUT); pinMode(5, INPUT); //pinMode(PA30, OUTPUT); //digitalWrite(PA30, HIGH); CircuitPlayground.begin(); teaType = MAX_TEA_TYPE; resteeps = 3; refreshPixels(); } void setPixel(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { float brightness = 1.f; if (alarm.started) { float pulse = .5f + .5f * sin(TWO_PI * countdown.remaining()/500.f); brightness = pulse; } else if (countdown.started) { float pulse = .5f + .5f * sin(TWO_PI * countdown.remaining()/5000.f); pulse = pulse*pulse*pulse; brightness = .7f + .3f * pulse; } else { brightness = 2.f - (millis() - last_moved) / 10000.f; if (brightness > 1.f) brightness = 1.f; if (brightness < 0.f) brightness = 0.f; } r = uint8_t(r*brightness); g = uint8_t(g*brightness); b = uint8_t(b*brightness); w = uint8_t(w*brightness); CircuitPlayground.setPixelColor(n, Adafruit_CPlay_NeoPixel::gamma32(Adafruit_CPlay_NeoPixel::Color(r, g, b, w))); } void setPixel(uint16_t n, uint32_t color) { setPixel(n, ((color>>16)&255), ((color>>8)&255), (color&255), (color>>24)); } void refreshPixels() { switch (teaType) { case SENCHA: setPixel(0, 0x007f5f); setPixel(1, 0x2b9348); setPixel(2, 0x55a630); setPixel(3, 0xaacc00); setPixel(4, 0x80b918); break; case BLACK: setPixel(0, 0x03071e); setPixel(1, 0x370617); setPixel(2, 0xdc2f02); setPixel(3, 0xf48c06); setPixel(4, 0xe85d04); break; default: setPixel(0, 0x7F007F); setPixel(1, 0x550055); setPixel(2, 0xBB00BB); setPixel(3, 0x550055); setPixel(4, 0x7F007F); break; } if (countdown.started) { float progress = 1.0f - float(countdown.remaining()) / float(countdown.length); for (long i = 0; i < 5; ++i) { float fill = (progress-.2f*float(i))/.2f; if (fill < 0.f) fill = 0.f; if (fill > 1.f) fill = 1.f; setPixel(5+i, 192.f * fill, 127.f, 192.f - 192.f*fill); } } else { for (long i = 0; i < 5; ++i) { setPixel(5+i, (resteeps > i) ? 0xfdd85d : 0x6798c0); } } } PinStatus b_right_prev = LOW, b_left_prev = LOW; static const long ARP_PATTERN[8] = {440, 550, 660, 1600, 880, 1320, 1100, 880}; // the loop function runs over and over again forever void loop() { PinStatus b_left = digitalRead(CPLAY_LEFTBUTTON); PinStatus b_right = digitalRead(CPLAY_RIGHTBUTTON); bool p_left = (b_left && !b_left_prev); bool p_right = (b_right && !b_right_prev); if (!countdown.started) { if (teaType == MAX_TEA_TYPE) { // Just started loop teaType = SENCHA; resteeps = 0; gravity.readSensor(); last_moved = millis(); } if (p_right) { resteeps += 1; if (resteeps > 5) { resteeps = 0; teaType = TeaType((long(teaType)+1) % MAX_TEA_TYPE); } } refreshPixels(); if (p_left) { long baseTime = 60; switch (teaType) { case SENCHA: baseTime = 60; break; case BLACK: baseTime = 120; break; } long upscale = resteeps; while (upscale > 1) {baseTime *= 2; upscale -= 2;} if (upscale == 1) {baseTime = (17*baseTime) / 12;} countdown.begin(1000 * baseTime); } } else { refreshPixels(); } { Accel accel; accel.readSensor(); float dtf = (accel.time - gravity.time) / 1000.f; float dx = accel.x-gravity.x, dy = accel.y-gravity.y, dz = accel.z-gravity.z; if (sqrt(dx*dx+dy*dy+dz*dz)/dtf > 100.f) { last_moved = millis(); digitalWrite(CPLAY_REDLED, 1); } else { digitalWrite(CPLAY_REDLED, 0); } float a = pow(.95f, dtf); gravity.x += a*dx; gravity.y += a*dy; gravity.z += a*dz; gravity.time = accel.time; } if (countdown.done()) { alarm.begin(5000); countdown.reset(); } if (alarm.started) { unsigned long arp = alarm.remaining() / 96; tone(A0, ARP_PATTERN[7-(arp&7)]); if (alarm.done()) alarm.reset(); } else noTone(A0); /*if (alarm.started) { digitalWrite(CPLAY_REDLED, alarm.remaining() % 250 < 125); } else if (countdown.started) { digitalWrite(CPLAY_REDLED, countdown.remaining() % 1000 < 100); }*/ delay(DT); b_right_prev = b_right; b_left_prev = b_left; }