Last active
January 30, 2023 16:13
-
-
Save jasoncoon/c8972403ed67a7f8fc63613c6854fd47 to your computer and use it in GitHub Desktop.
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 characters
/* | |
Fibonacci64 Touch Demo: https://www.evilgeniuslabs.org/fibonacci64-micro | |
Copyright (C) 2021 Jason Coon | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#include <FastLED.h> // https://github.com/FastLED/FastLED | |
#include "Adafruit_FreeTouch.h" //https://github.com/adafruit/Adafruit_FreeTouch | |
FASTLED_USING_NAMESPACE | |
#define DATA_PIN A10 | |
#define LED_TYPE WS2812B | |
#define COLOR_ORDER GRB | |
#define NUM_LEDS 64 | |
#define MILLI_AMPS 320 // IMPORTANT: set the max milli-Amps of your power supply (4A = 4000mA) | |
#define FRAMES_PER_SECOND 120 | |
CRGB leds[NUM_LEDS]; | |
uint8_t brightness = 32; | |
Adafruit_FreeTouch touch0 = Adafruit_FreeTouch(A0, OVERSAMPLE_4, RESISTOR_0, FREQ_MODE_NONE); | |
Adafruit_FreeTouch touch1 = Adafruit_FreeTouch(A1, OVERSAMPLE_4, RESISTOR_0, FREQ_MODE_NONE); | |
Adafruit_FreeTouch touch2 = Adafruit_FreeTouch(A2, OVERSAMPLE_4, RESISTOR_0, FREQ_MODE_NONE); | |
Adafruit_FreeTouch touch3 = Adafruit_FreeTouch(A3, OVERSAMPLE_4, RESISTOR_0, FREQ_MODE_NONE); | |
// These values were discovered using the commented-out Serial.print statements in handleTouch below | |
// minimum values for each touch pad, used to filter out noise | |
uint16_t touchMin[4] = { 558, 259, 418, 368 }; | |
// maximum values for each touch pad, used to determine when a pad is touched | |
uint16_t touchMax[4] = { 1016, 1016, 1016, 1016 }; | |
// raw capacitive touch sensor readings | |
uint16_t touchRaw[4] = { 0, 0, 0, 0 }; | |
// capacitive touch sensor readings, mapped/scaled one one byte each (0-255) | |
uint8_t touch[4] = { 0, 0, 0, 0 }; | |
// coordinates of the touch points | |
uint8_t touchPointX[4] = { 255, 0, 0, 255 }; | |
uint8_t touchPointY[4] = { 0, 0, 255, 255 }; | |
uint16_t untouchedPulseDelay = 1000; // time in milliseconds between random pulses, if no touches have been detected | |
// XY coordinates of the Fibonacci64 Micro, mapped to one byte each | |
uint8_t coordsX[NUM_LEDS] = { 140, 189, 208, 214, 208, 146, 168, 180, 180, 162, 152, 146, 129, 103, 72, 40, 70, 97, 120, 131, 107, 79, 50, 23, 0, 7, 23, 46, 76, 93, 57, 37, 28, 29, 87, 68, 59, 62, 80, 113, 91, 94, 109, 133, 202, 172, 145, 125, 117, 145, 170, 198, 227, 253, 255, 235, 210, 181, 148, 175, 207, 228, 240, 244 }; | |
uint8_t coordsY[NUM_LEDS] = { 128, 114, 91, 63, 34, 0, 21, 48, 76, 106, 78, 47, 25, 11, 5, 38, 35, 42, 61, 101, 87, 69, 68, 78, 98, 143, 118, 102, 98, 122, 131, 152, 179, 209, 255, 230, 202, 174, 148, 142, 181, 210, 235, 252, 235, 234, 224, 203, 170, 183, 201, 205, 198, 181, 134, 157, 171, 173, 153, 145, 138, 120, 93, 63 }; | |
void setup() { | |
Serial.begin(115200); | |
// delay(3000); | |
if (!touch0.begin()) | |
Serial.println("Failed to begin qt on pin A0"); | |
if (!touch1.begin()) | |
Serial.println("Failed to begin qt on pin A1"); | |
if (!touch2.begin()) | |
Serial.println("Failed to begin qt on pin A2"); | |
if (!touch3.begin()) | |
Serial.println("Failed to begin qt on pin A3"); | |
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS); | |
FastLED.setDither(false); | |
FastLED.setCorrection(TypicalSMD5050); | |
FastLED.setBrightness(brightness); | |
FastLED.setMaxPowerInVoltsAndMilliamps(5, MILLI_AMPS); | |
fill_solid(leds, NUM_LEDS, CRGB::Black); | |
FastLED.show(); | |
FastLED.setBrightness(brightness); | |
} | |
void loop() { | |
// Add entropy to random number generator; we use a lot of it. | |
random16_add_entropy(random()); | |
handleTouch(); | |
touchDemo(); | |
FastLED.show(); | |
// insert a delay to keep the framerate modest | |
FastLED.delay(1000 / FRAMES_PER_SECOND); | |
} | |
bool touchChanged = true; | |
void handleTouch() { | |
for (uint8_t i = 0; i < 4; i++) { | |
if (i == 0) touchRaw[i] = touch0.measure(); | |
else if (i == 1) touchRaw[i] = touch1.measure(); | |
else if (i == 2) touchRaw[i] = touch2.measure(); | |
else if (i == 3) touchRaw[i] = touch3.measure(); | |
// // uncomment to display raw touch values in the serial monitor/plotter | |
// Serial.print(touchRaw[i]); | |
// Serial.print(" "); | |
if (touchRaw[i] < touchMin[i]) { | |
touchMin[i] = touchRaw[i]; | |
touchChanged = true; | |
} | |
if (touchRaw[i] > touchMax[i]) { | |
touchMax[i] = touchRaw[i]; | |
touchChanged = true; | |
} | |
touch[i] = map(touchRaw[i], touchMin[i], touchMax[i], 0, 255); | |
// // uncomment to display mapped/scaled touch values in the serial monitor/plotter | |
// Serial.print(touch[i]); | |
// Serial.print(" "); | |
} | |
// // uncomment to display raw and/or mapped/scaled touch values in the serial monitor/plotter | |
// Serial.println(); | |
// uncomment to display raw, scaled, min, max touch values in the serial monitor/plotter | |
// if (touchChanged) { | |
// for (uint8_t i = 0; i < 4; i++) { | |
// Serial.print(touchRaw[i]); | |
// Serial.print(" "); | |
// Serial.print(touch[i]); | |
// Serial.print(" "); | |
// Serial.print(touchMin[i]); | |
// Serial.print(" "); | |
// Serial.print(touchMax[i]); | |
// Serial.print(" "); | |
// } | |
// | |
// Serial.println(); | |
// | |
// touchChanged = false; | |
// } | |
} | |
// adds a color to a pixel given it's XY coordinates and a "thickness" of the logical pixel | |
// since we're using a sparse logical grid for mapping, there isn't an LED at every XY coordinate | |
// thickness adds a little "fuzziness" | |
void addColorXY(int x, int y, CRGB color, uint8_t thickness = 0) | |
{ | |
// ignore coordinates outside of our one byte map range | |
if (x < 0 || x > 255 || y < 0 || y > 255) return; | |
// loop through all of the LEDs | |
for (uint8_t i = 0; i < NUM_LEDS; i++) { | |
// get the XY coordinates of the current LED | |
uint8_t ix = coordsX[i]; | |
uint8_t iy = coordsY[i]; | |
// are the current LED's coordinates within the square centered | |
// at X,Y, with width and height of thickness? | |
if (ix >= x - thickness && ix <= x + thickness && | |
iy >= y - thickness && iy <= y + thickness) { | |
// add to the color instead of just setting it | |
// so that colors blend | |
// FastLED automatically prevents overflowing over 255 | |
leds[i] += color; | |
} | |
} | |
} | |
// algorithm from http://en.wikipedia.org/wiki/Midpoint_circle_algorithm | |
void drawCircle(int x0, int y0, int radius, const CRGB color, uint8_t thickness = 0) | |
{ | |
int a = radius, b = 0; | |
int radiusError = 1 - a; | |
if (radius == 0) { | |
addColorXY(x0, y0, color, thickness); | |
return; | |
} | |
while (a >= b) | |
{ | |
addColorXY(a + x0, b + y0, color, thickness); | |
addColorXY(b + x0, a + y0, color, thickness); | |
addColorXY(-a + x0, b + y0, color, thickness); | |
addColorXY(-b + x0, a + y0, color, thickness); | |
addColorXY(-a + x0, -b + y0, color, thickness); | |
addColorXY(-b + x0, -a + y0, color, thickness); | |
addColorXY(a + x0, -b + y0, color, thickness); | |
addColorXY(b + x0, -a + y0, color, thickness); | |
b++; | |
if (radiusError < 0) | |
radiusError += 2 * b + 1; | |
else | |
{ | |
a--; | |
radiusError += 2 * (b - a + 1); | |
} | |
} | |
} | |
// track the XY coordinates and radius of each wave | |
uint16_t radii[4]; | |
uint8_t waveX[4]; | |
uint8_t waveY[4]; | |
CRGB waveColor[4]; | |
// we want the radius of the waves to reach the opposite side of the display | |
// using a map with dimensions 256 x 256, 362 is the length of the diagonal | |
// a²+b²=c² == 256²+256² = 131072 | |
// √131072 ~= 362 | |
const uint16_t maxRadius = 362; | |
unsigned long lastPulse = 0; | |
void touchDemo() { | |
// fade all of the LEDs a small amount each frame | |
// increasing this number makes the waves fade faster | |
fadeToBlackBy(leds, NUM_LEDS, 40); | |
for (uint8_t i = 0; i < 4; i++) { | |
// increment radii if it's already been set in motion | |
if (radii[i] > 0 && radii[i] < maxRadius) radii[i] = radii[i] + 8; | |
// start new waves when there's a new touch | |
if (touch[i] > 127 && radii[i] == 0) { | |
radii[i] = 32; | |
waveX[i] = touchPointX[i]; | |
waveY[i] = touchPointY[i]; | |
waveColor[i] = CHSV(random8(), 255, 255); | |
} | |
// reset waves already at max | |
if (radii[i] >= maxRadius) | |
radii[i] = 0; | |
if (radii[i] == 0) | |
continue; | |
lastPulse = millis(); | |
CRGB color = waveColor[i]; | |
uint8_t x = waveX[i]; | |
uint8_t y = waveY[i]; | |
// draw waves starting from the corner closest to each touch sensor | |
drawCircle(x, y, radii[i], color, 4); | |
} | |
// if no touches have been detected, randomly pulse periodically | |
if ((millis() - lastPulse) > untouchedPulseDelay) { | |
uint8_t i = random8(0, 3); | |
waveX[i] = random8(); | |
waveY[i] = random8(); | |
radii[i] = 1; | |
waveColor[i] = CHSV(random8(), 255, 255); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment