Last active
August 1, 2024 00:54
-
-
Save fortuna/89062f7204b5ec53c440ff01faf748c7 to your computer and use it in GitHub Desktop.
This file contains 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
#include <CapacitiveSensor.h> | |
#include <Adafruit_NeoPixel.h> | |
#include <math.h> | |
#define LED_PIN 2 // which pin is your LED data line plugged into? | |
#define LED_COUNT 16 // how many LEDs are in the strip or ring you're driving? | |
class FifoBuffer { | |
private: | |
static const int MAX_SIZE = 1000; // Maximum size of the buffer | |
int buffer[MAX_SIZE]; // Array to store the buffer | |
int front; // Index of the front element | |
int rear; // Index of the rear element | |
public: | |
FifoBuffer() { | |
front = -1; | |
rear = -1; | |
} | |
bool isEmpty() { | |
return front == -1; | |
} | |
bool isFull() { | |
return (rear + 1) % MAX_SIZE == front; | |
} | |
void push(int value) { | |
if (isFull()) { | |
// Buffer is full, overwrite the front element | |
front = (front + 1) % MAX_SIZE; | |
} | |
rear = (rear + 1) % MAX_SIZE; | |
buffer[rear] = value; | |
if (front == -1) { | |
front = rear; | |
} | |
} | |
double calculateStandardDeviation() { | |
if (isEmpty()) { | |
return 0.0; | |
} | |
double sum = 0.0; | |
int count = 0; | |
int current = front; | |
while (current != rear) { | |
sum += buffer[current]; | |
current = (current + 1) % MAX_SIZE; | |
count++; | |
} | |
sum += buffer[rear]; | |
count++; | |
double mean = sum / count; | |
double squaredSum = 0.0; | |
current = front; | |
while (current != rear) { | |
double diff = buffer[current] - mean; | |
squaredSum += diff * diff; | |
current = (current + 1) % MAX_SIZE; | |
} | |
double diff = buffer[rear] - mean; | |
squaredSum += diff * diff; | |
double variance = squaredSum / count; | |
double standardDeviation = sqrt(variance); | |
return standardDeviation; | |
} | |
double calculateMean() { | |
if (isEmpty()) { | |
return 0.0; | |
} | |
double sum = 0.0; | |
int count = 0; | |
int current = front; | |
while (current != rear) { | |
sum += buffer[current]; | |
current = (current + 1) % MAX_SIZE; | |
count++; | |
} | |
sum += buffer[rear]; | |
count++; | |
double mean = sum / count; | |
return mean; | |
} | |
}; | |
FifoBuffer buffer; | |
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); | |
CapacitiveSensor cs_4_2 = CapacitiveSensor(19, 21); // 19, 21 (teensy) or 8 11 (metro) 10M resistor between pins 4 & 2, pin 2 is sensor pin, add a wire and or foil if desired | |
void setup() { | |
Serial.begin(9600); | |
strip.begin(); | |
strip.show(); | |
delay(2000); | |
Serial.println("Autocalibration in progress..."); | |
} | |
void lightup(int onoff, int red, int green, int blue) { | |
// lightup the whole ring a given color, yes or no | |
if (onoff == 0) { | |
for (int i = 0; i < LED_COUNT; i++) { | |
strip.setPixelColor(i, 0, 0, 0); | |
} | |
} else { | |
for (int i = 0; i < LED_COUNT; i++) { | |
strip.setPixelColor(i, red, green, blue); | |
} | |
} | |
strip.show(); | |
} | |
void fraction_lightup(int meter) { | |
// light up a fraction of the ring | |
for (int i = 0; i < LED_COUNT; i++) { | |
if (i < meter) { | |
strip.setPixelColor(i, 3, 0, 12); | |
} else { | |
strip.setPixelColor(i, 0, 0, 0); | |
} | |
} | |
strip.show(); | |
} | |
void statecycle(int stateindex) { | |
// rotate through preset states | |
if (stateindex % 7 == 0) { | |
lightup(1, 10, 0, 0); | |
} else if (stateindex % 7 == 1) { | |
lightup(1, 10, 2, 0); | |
} else if (stateindex % 7 == 2) { | |
lightup(1, 10, 8, 0); | |
} else if (stateindex % 7 == 3) { | |
lightup(1, 0, 10, 0); | |
} else if (stateindex % 7 == 4) { | |
lightup(1, 0, 0, 10); | |
} else if (stateindex % 7 == 5) { | |
lightup(1, 3, 0, 12); | |
} else if (stateindex % 7 == 6) { | |
lightup(1, 9, 0, 9); | |
} | |
} | |
long maxReading = 0; | |
int activations = 0; | |
int cumulative = 0; | |
bool on = false; | |
bool off = true; | |
int onstreak = 0; | |
int offstreak = 0; | |
int sdcutoff = 4; | |
int transition_length = 1; | |
size_t prev_norm_reading = 0; | |
void printReading(size_t prev, size_t pos, size_t cutoff) { | |
char line[65]; | |
size_t start = min(prev, pos); | |
size_t end = max(prev, pos); | |
for (size_t i = 0; i < 64; i++) { | |
if (i < start || i > end) { | |
line[i] = ' '; | |
} else if (i == pos) { | |
if (prev > pos) { | |
line[i] = '/'; | |
} else if (prev < pos) { | |
line[i] = '\\'; | |
} else { | |
line[i] = '|'; | |
} | |
} else if (i == prev) { | |
if (prev > pos) { | |
line[i] = '/'; | |
} else { | |
line[i] = '\\'; | |
} | |
} else { | |
line[i] = '-'; | |
} | |
} | |
line[64] = 0; | |
line[cutoff] = '*'; | |
Serial.print(line); | |
} | |
void loop() { | |
// Read the capacitive sensor. | |
long reading = cs_4_2.capacitiveSensor(30); | |
if (reading > maxReading) { | |
maxReading = reading; | |
} | |
// reset variables | |
int trig = 0; | |
if (!buffer.isFull()) { | |
// fill the buffer on startup; will take a few seconds to self-calibrate | |
buffer.push(reading); | |
return; | |
} | |
double mean = buffer.calculateMean(); | |
double sd = buffer.calculateStandardDeviation(); | |
if (abs(reading - mean) / sd < sdcutoff) { | |
// we're close to background; update the buffer | |
buffer.push(reading); | |
onstreak = 0; | |
offstreak++; | |
cumulative--; | |
if (cumulative < 0) { cumulative = 0; } | |
} else if ((reading - mean) / sd > sdcutoff) { | |
// we're detecting something interesting | |
trig = (reading - mean) / sd - sdcutoff; | |
onstreak++; | |
cumulative++; | |
if (cumulative > LED_COUNT) { cumulative = LED_COUNT; } | |
offstreak = 0; | |
} | |
// smooth out on versus off state management | |
// opportunity to teach about buffering | |
if (off && onstreak >= transition_length) { | |
off = false; | |
on = true; | |
activations += 1; | |
} else if (on && offstreak >= transition_length) { | |
off = true; | |
on = false; | |
} | |
// part 1: tap to state change | |
// 1a. on / off | |
// lightup(int((activations % 2) == 1), 3, 0, 12); | |
// 1b. state cycle | |
// statecycle(activations); | |
// part 2: charge / discharge | |
// fraction_lightup(cumulative); | |
// part 3: push to talk | |
// 3a. contact; set sdcutoff to 20 | |
// 3b. proximity; set sdcutoff to 5 | |
lightup(int(on), 3,0,12); | |
size_t norm_reading = reading * 64 / (maxReading + 1); | |
size_t norm_cutoff = int(mean + sdcutoff * sd) * 64 / (maxReading + 1); | |
printReading(prev_norm_reading, norm_reading, norm_cutoff); | |
prev_norm_reading = norm_reading; | |
Serial.print(" Reading: "); | |
Serial.print(reading); | |
Serial.print("\t"); | |
Serial.print("Average: "); | |
Serial.print(mean); | |
Serial.print("\t\t"); | |
Serial.print("Deviation: "); | |
Serial.print(sd); | |
Serial.print("\n"); | |
delay(1); // arbitrary delay to limit data to serial port | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment