Skip to content

Instantly share code, notes, and snippets.

@jasoncoon
Last active January 30, 2023 16:13
Show Gist options
  • Save jasoncoon/c8972403ed67a7f8fc63613c6854fd47 to your computer and use it in GitHub Desktop.
Save jasoncoon/c8972403ed67a7f8fc63613c6854fd47 to your computer and use it in GitHub Desktop.
/*
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