Skip to content

Instantly share code, notes, and snippets.

@JontyMC
Created July 12, 2018 09:25
Show Gist options
  • Save JontyMC/198603ce7430f363bf3b28f0b7acef3b to your computer and use it in GitHub Desktop.
Save JontyMC/198603ce7430f363bf3b28f0b7acef3b to your computer and use it in GitHub Desktop.
#include <bluefruit.h>
#include <Adafruit_NeoPixel.h>
#define NUMPIX 54
#define POT_PIN A5 //potentiometer
#define MIC_PIN A2 // Microphone is attached to this analog pin
#define LED_PIN 30 // NeoPixel LED strand is connected to this pin
#define BUTTON_PIN 7 // NeoPixel LED strand is connected to this pin
#define DC_OFFSET 0 // DC offset in mic signal - if unusure, leave 0
#define NOISE 30 // Noise/hum/interference in mic signal
#define SAMPLES 60 // Length of buffer for dynamic level adjustment
#define WSIDE1 0
#define WSIDE2 6
#define WSIDE3 25
#define WSIDE4 35
#define TOP1 8 // Allow dot to go slightly off scale
#define TOP2 21 // Allow dot to go slightly off scale`
#define TOP3 12 // Allow dot to go slightly off scale`
#define TOP4 21 // Allow dot to go slightly off scale`
#define PEAK_FALL 60 // Rate of peak falling dot
byte
peak1 = 0, // Used for falling dot
peak2 = 0, // Used for falling dot
peak3 = 0, // Used for falling dot
peak4 = 0, // Used for falling dot
dotCount = 0, // Frame counter for delaying dot-falling speed
volCount = 0; // Frame counter for storing past volume data
int
vol[SAMPLES], // Collection of prior volume samples
lvl = 10, // Current "dampened" audio level
minLvlAvg = 0, // For dynamic adjustment of graph low & high
maxLvlAvg1 = 512,
maxLvlAvg2 = 512,
maxLvlAvg3 = 512,
maxLvlAvg4 = 512,
mode = 0,
buttonState = 0,
potThreshold = 10,
potLastValue = 0,
brightness = 230,
cycleNum = 0,
cycleNum2 = 0,
colorCycleNum = 0,
wait = 50,
paletteLength;
unsigned long time,
temperatureInterval = 1000,
temperatureLastRefresh = 0,
buttonInterval = 150,
buttonLastRefresh = 50,
modeLastRefresh = 0;
uint32_t palette[10];
int defaultShowNum = 0, defaultShowCycles = 0;
bool switchShow = false;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIX, LED_PIN, NEO_RGB + NEO_KHZ400);
static uint8_t wagonServiceId[31] = { 0x01,0x01,0x06, 0x1A,0xFF,0x4C,0x00,0x02,0x15,0x71,0x3d,0x00,0x00,0x50,0x3e,0x4c,0x75,0xba,0x94,0x31,0x48,0xf1,0x8d,0x94,0x1e,0x00,0x00,0x00,0x00,0xC5 };
static uint8_t wagonCharacteristic1Id[31] = { 0x02,0x01,0x06, 0x1A,0xFF,0x4C,0x00,0x02,0x15,0x71,0x3d,0x00,0x00,0x50,0x3e,0x4c,0x75,0xba,0x94,0x31,0x48,0xf1,0x8d,0x94,0x1e,0x00,0x00,0x00,0x00,0xC5 };
static uint8_t wagonCharacteristic2Id[31] = { 0x03,0x01,0x06, 0x1A,0xFF,0x4C,0x00,0x02,0x15,0x71,0x3d,0x00,0x00,0x50,0x3e,0x4c,0x75,0xba,0x94,0x31,0x48,0xf1,0x8d,0x94,0x1e,0x00,0x00,0x00,0x00,0xC5 };
static uint8_t wagonCharacteristic3Id[31] = { 0x04,0x01,0x06, 0x1A,0xFF,0x4C,0x00,0x02,0x15,0x71,0x3d,0x00,0x00,0x50,0x3e,0x4c,0x75,0xba,0x94,0x31,0x48,0xf1,0x8d,0x94,0x1e,0x00,0x00,0x00,0x00,0xC5 };
static uint8_t wagonCharacteristic4Id[31] = { 0x05,0x01,0x06, 0x1A,0xFF,0x4C,0x00,0x02,0x15,0x71,0x3d,0x00,0x00,0x50,0x3e,0x4c,0x75,0xba,0x94,0x31,0x48,0xf1,0x8d,0x94,0x1e,0x00,0x00,0x00,0x00,0xC5 };
BLEService wagonService = BLEService(wagonServiceId);
BLECharacteristic wagonCharacteristic1 = BLECharacteristic(wagonCharacteristic1Id);
BLECharacteristic wagonCharacteristic2 = BLECharacteristic(wagonCharacteristic2Id);
BLECharacteristic wagonCharacteristic3 = BLECharacteristic(wagonCharacteristic3Id);
BLECharacteristic wagonCharacteristic4 = BLECharacteristic(wagonCharacteristic4Id);
void setup() {
Serial.begin(115200);
Serial.println("Wagon Lights Server");
Serial.println("-----------------------");
pinMode(BUTTON_PIN, INPUT);
memset(vol, 0, sizeof(vol));
strip.begin();
changePalette(2);
Bluefruit.begin();
Bluefruit.setName("Wagon");
setupWagonService();
startAdv();
}
void loop() {
time = millis();
if (mode != 2) {
strip.setBrightness(brightness);
}
switch (mode) {
default: //also case 0
defaultShow();
break;
case 1:
soundReactive();
break;
case 2:
solid();
break;
case 3:
fade();
break;
case 4:
rainbow();
break;
case 5:
rainbowDist();
break;
case 6:
colorWipe();
break;
case 7:
theaterChase();
break;
case 8:
ledsOff();
break;
//TODO: Kitt, new kitt, fill up one at a time, bounce back and forth, rotating strips
}
if(time - temperatureLastRefresh >= temperatureInterval)
{
temperatureLastRefresh = time;
takeTemperature();
}
if (time - buttonLastRefresh >= buttonInterval) {
buttonLastRefresh = time;
int newButtonState = digitalRead(BUTTON_PIN);
if (newButtonState == HIGH && buttonState != HIGH) {
Serial.println("b on");
incrementMode();
}
buttonState = newButtonState;
int reading = analogRead(POT_PIN);
if (abs(potLastValue - reading) > potThreshold) {
potLastValue = reading;
int brightness = map(reading,0,1000,0,255);
Serial.println(brightness);
strip.setBrightness(brightness);
}
}
}
void incrementMode() {
mode += 1;
if (mode > 6) {
mode = 0;
}
}
void takeTemperature() {
int reading = analogRead(A0);
float voltage = reading * 3.3;
voltage /= 1024.0;
// print out the voltage
Serial.print(voltage); Serial.println(" volts");
// now print out the temperature
float temperatureC = (voltage - 0.5) * 100 ; //converting from 10 mv per degree wit 500 mV offset
//to degrees ((voltage - 500mV) times 100)
Serial.print(temperatureC); Serial.println(" degrees C");
char str[3];
sprintf(str, "%d", (int)temperatureC);
wagonCharacteristic1.notify(str);
}
void ledsOff() {
setAll(strip.Color(0, 0, 0));
}
void setAll(uint32_t c) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();
}
void defaultShow() {
if (switchShow == true) {
switchShow = false;
defaultShowNum++;
if (defaultShowNum > 5){
defaultShowNum = 0;
}
}
switch (defaultShowNum) {
case 0:
solid();
break;
case 1:
rainbow();
break;
case 2:
colorWipe();
break;
case 3:
theaterChase();
break;
case 4:
rainbowDist();
break;
case 5:
fade();
break;
}
}
void solid() {
defaultShowCycles = 8;
if (time - modeLastRefresh >= wait) {
modeLastRefresh = time;
if (cycleNum > 30) {
cycleNum = 0;
incrementPalette();
}
setAll(palette[colorCycleNum]);
cycleNum++;
}
}
void fade() {
defaultShowCycles = 4;
if (time - modeLastRefresh >= wait/100) {
modeLastRefresh = time;
if (cycleNum > 1200) {
cycleNum = 0;
incrementPalette();
}
int brightness = 0;
if (cycleNum < 500) {
brightness = map(cycleNum,0,499,0,150);
} else if (cycleNum < 1000) {
brightness = map(cycleNum,500,1000,150,0);
}
strip.setBrightness(brightness);
setAll(palette[colorCycleNum]);
cycleNum++;
}
}
void colorWipe() {
defaultShowCycles = 4;
if (time - modeLastRefresh >= wait) {
modeLastRefresh = time;
if (cycleNum > strip.numPixels()) {
cycleNum = 0;
incrementPalette();
}
strip.setPixelColor(cycleNum, palette[colorCycleNum]);
strip.show();
cycleNum++;
}
}
int currentShowCycles = 1;
void incrementPalette() {
colorCycleNum++;
if (colorCycleNum >= paletteLength) {
colorCycleNum = 0;
currentShowCycles++;
if (currentShowCycles >= defaultShowCycles) {
switchShow = true;
currentShowCycles = 1;
}
}
}
void theaterChase() {
defaultShowCycles = 2;
if (time - modeLastRefresh >= wait) {
modeLastRefresh = time;
if (cycleNum > 2) {
cycleNum = 0;
}
if (cycleNum2 > 54) {
cycleNum2 = 0;
incrementPalette();
}
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
uint16_t previous = i+cycleNum - 1;
if (previous < 0) {
previous = strip.numPixels();
}
strip.setPixelColor(previous, 0); //turn every third pixel off
}
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+cycleNum, palette[colorCycleNum]); //turn every third pixel on
}
strip.show();
cycleNum++;
cycleNum2++;
}
}
void rainbow() {
defaultShowCycles = 1;
if (time - modeLastRefresh >= wait) {
modeLastRefresh = time;
if (cycleNum > 255) {
cycleNum = 0;
currentShowCycles++;
if (currentShowCycles >= defaultShowCycles) {
switchShow = true;
currentShowCycles = 1;
}
}
for(uint16_t i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + cycleNum) & 255));
}
strip.show();
cycleNum++;
}
}
void rainbowDist() {
defaultShowCycles = 1;
if (time - modeLastRefresh >= wait) {
modeLastRefresh = time;
if (cycleNum > 255) {
cycleNum = 0;
currentShowCycles++;
if (currentShowCycles >= defaultShowCycles) {
switchShow = true;
currentShowCycles = 1;
}
}
for(uint16_t i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + cycleNum) & 255));
}
strip.show();
cycleNum++;
}
}
void soundReactive() {
uint8_t i;
uint16_t minLvl, maxLvl1, maxLvl2, maxLvl3, maxLvl4;
int n;
n = analogRead(MIC_PIN); // Raw reading from mic
n = abs(n - 512 - DC_OFFSET); // Center on zero
n = (n <= NOISE) ? 0 : (n - NOISE); // Remove noise/hum
lvl = ((lvl * 7) + n) >> 3; // "Dampened" reading (else looks twitchy)
peak1 = setVolPixels(WSIDE1, WSIDE2-1, peak1, TOP1, maxLvlAvg1);
peak2 = setVolPixels(WSIDE2, WSIDE3-1, peak2, TOP2, maxLvlAvg2);
peak3 = setVolPixels(WSIDE3, WSIDE4-1, peak3, TOP3, maxLvlAvg3);
peak4 = setVolPixels(WSIDE4, NUMPIX-1, peak4, TOP4, maxLvlAvg4);
strip.show(); // Update strip
// Every few frames, make the peak pixel drop by 1:
if(++dotCount >= PEAK_FALL) { //fall rate
if(peak1 > 0) peak1--;
if(peak2 > 0) peak2--;
if(peak3 > 0) peak3--;
if(peak4 > 0) peak4--;
dotCount = 0;
}
vol[volCount] = n; // Save sample for dynamic leveling
if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
// Get volume range of prior frames
minLvl = maxLvl1 = maxLvl2 = maxLvl3 = maxLvl4 = vol[0];
for(i=1; i<SAMPLES; i++) {
if(vol[i] < minLvl) minLvl = vol[i];
else {
if(vol[i] > maxLvl1) maxLvl1 = vol[i];
if(vol[i] > maxLvl2) maxLvl2 = vol[i];
if(vol[i] > maxLvl3) maxLvl3 = vol[i];
if(vol[i] > maxLvl4) maxLvl4 = vol[i];
}
}
// minLvl and maxLvl indicate the volume range over prior frames, used
// for vertically scaling the output graph (so it looks interesting
// regardless of volume level). If they're too close together though
// (e.g. at very low volume levels) the graph becomes super coarse
// and 'jumpy'...so keep some minimum distance between them (this
// also lets the graph go to zero when no sound is playing):
if((maxLvl1 - minLvl) < TOP1) maxLvl1 = minLvl + TOP1;
if((maxLvl2 - minLvl) < TOP2) maxLvl2 = minLvl + TOP2;
if((maxLvl3 - minLvl) < TOP3) maxLvl3 = minLvl + TOP3;
if((maxLvl4 - minLvl) < TOP4) maxLvl4 = minLvl + TOP4;
minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
maxLvlAvg1 = (maxLvlAvg1 * 63 + maxLvl1) >> 6; // (fake rolling average)
maxLvlAvg2 = (maxLvlAvg2 * 63 + maxLvl2) >> 6; // (fake rolling average)
maxLvlAvg3 = (maxLvlAvg3 * 63 + maxLvl3) >> 6; // (fake rolling average)
maxLvlAvg4 = (maxLvlAvg4 * 63 + maxLvl4) >> 6; // (fake rolling average)
}
byte setVolPixels(uint8_t first, uint8_t last, byte peak, int top, int maxLvlAvg) {
// Calculate bar height based on dynamic min/max levels (fixed point):
int height = top * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
if(height < 0L) height = 0; // Clip output
else if(height > top) height = top;
if(height > peak) peak = height; // Keep 'peak' dot at top
// Color pixels based on rainbow gradient
uint8_t numPixels = last-first+1;
for(uint8_t i=0; i<numPixels; i++) {
if(i >= height) strip.setPixelColor(i + first, 0, 0, 0);
else strip.setPixelColor(i + first,Wheel(map(i,0,numPixels-1,30,150)));
}
// Draw peak dot
if(peak > 0 && peak <= numPixels-1) strip.setPixelColor(peak+first,Wheel(map(peak,0,19-1,30,150)));
return peak;
}
uint32_t Wheel(byte WheelPos) {
if(WheelPos < 85) {
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
void setupWagonService(void)
{
wagonService.begin();
wagonCharacteristic1.setProperties(CHR_PROPS_NOTIFY);
wagonCharacteristic1.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
wagonCharacteristic1.setFixedLen(2);
wagonCharacteristic1.begin();
wagonCharacteristic2.setProperties(CHR_PROPS_WRITE);
wagonCharacteristic2.setPermission(SECMODE_OPEN, SECMODE_OPEN);
wagonCharacteristic2.setFixedLen(20);
wagonCharacteristic2.setWriteCallback(*write_cb);
wagonCharacteristic2.begin();
wagonCharacteristic3.setProperties(CHR_PROPS_WRITE);
wagonCharacteristic3.setPermission(SECMODE_OPEN, SECMODE_OPEN);
wagonCharacteristic3.setFixedLen(20);
wagonCharacteristic3.setWriteCallback(*write_cb2);
wagonCharacteristic3.begin();
wagonCharacteristic4.setProperties(CHR_PROPS_WRITE);
wagonCharacteristic4.setPermission(SECMODE_OPEN, SECMODE_OPEN);
wagonCharacteristic4.setFixedLen(20);
wagonCharacteristic4.setWriteCallback(*write_cb3);
wagonCharacteristic4.begin();
}
union arrayToUint32 {
byte array[4];
uint32_t integer;
};
void write_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset)
{
Serial.println("BLE: Change mode");
arrayToUint32 converter = {data[0],data[1],data[2],data[3]}; //Create a converter
uint32_t i = converter.integer;
Serial.println(i);
mode = i;
}
void changePalette(int num) {
uint32_t red = strip.Color(255, 0, 0);
uint32_t green = strip.Color(0, 255, 0);
uint32_t blue = strip.Color(0, 0, 255);
uint32_t yellow = strip.Color(255, 255, 0);
uint32_t orange = strip.Color(255, 100, 0);
uint32_t pink = strip.Color(255, 105, 180);
uint32_t purple = strip.Color(128, 0, 128);
uint32_t white = strip.Color(255, 255, 255);
switch (num) {
default: //also case 0
paletteLength = 7;
palette[0] = red;
palette[1] = orange;
palette[2] = yellow;
palette[3] = strip.Color(0, 128, 0);
palette[4] = blue;
palette[5] = strip.Color(75, 99, 130);
palette[6] = strip.Color(189, 49, 255);
break;
case 1:
paletteLength = 3;
palette[0] = red;
palette[1] = green;
palette[2] = blue;
break;
case 2:
paletteLength = 3;
palette[0] = red;
palette[1] = white;
palette[2] = blue;
break;
case 3:
paletteLength = 3;
palette[0] = blue;
palette[1] = yellow;
break;
}
}
void write_cb2(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset)
{
Serial.println("BLE: Change brightness");
arrayToUint32 converter = {data[0],data[1],data[2],data[3]}; //Create a converter
uint32_t i = converter.integer;
Serial.println(i);
brightness = i;
}
void write_cb3(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset)
{
Serial.println("BLE: Change palette");
arrayToUint32 converter = {data[0],data[1],data[2],data[3]}; //Create a converter
uint32_t i = converter.integer;
Serial.println(i);
changePalette(i);
}
void startAdv(void)
{
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addService(wagonService);
Bluefruit.ScanResponse.addName();
Bluefruit.Advertising.start();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment