Skip to content

Instantly share code, notes, and snippets.

@smukkejohan
Last active December 21, 2015 14:49
Show Gist options
  • Save smukkejohan/6322733 to your computer and use it in GitHub Desktop.
Save smukkejohan/6322733 to your computer and use it in GitHub Desktop.
Burning Man Lanterns - Lost Connections
#include <lightuino5.h>
static int lantern_number=8; // number of lanterns
float x1,y1,x2,y2,persistence;
int octaves;
int i=0,val,volume;
int numPatterns = 6; // store the number of patterns
int currentPattern=0; // the current pattern being shown on the LEDs
long switchTime=60000; // time (in ms) between switching patterns
long lastPatternTime; // last time in millis that the light pattern was changed
long lastsamplemicros; // store the last time a microphone sample was taken, so the next read will be 1ms later.
long lastsamplelooptime; // store the last time we started sampling for performance testing.
////// Sound Sampling
float subbassr, subbassi, subbass; // real and imaginary parts of each frequency being sampled, and the measured amplitude at that frequency
float bassr, bassi, bass;
float midr, midi, mid;
float trebr, trebi, treb;
const prog_int8_t Sinewave[256] PROGMEM = {
0, 3, 6, 9, 12, 15, 18, 21,
24, 28, 31, 34, 37, 40, 43, 46,
48, 51, 54, 57, 60, 63, 65, 68,
71, 73, 76, 78, 81, 83, 85, 88,
90, 92, 94, 96, 98, 100, 102, 104,
106, 108, 109, 111, 112, 114, 115, 117,
118, 119, 120, 121, 122, 123, 124, 124,
125, 126, 126, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 126, 126,
125, 124, 124, 123, 122, 121, 120, 119,
118, 117, 115, 114, 112, 111, 109, 108,
106, 104, 102, 100, 98, 96, 94, 92,
90, 88, 85, 83, 81, 78, 76, 73,
71, 68, 65, 63, 60, 57, 54, 51,
48, 46, 43, 40, 37, 34, 31, 28,
24, 21, 18, 15, 12, 9, 6, 3,
0, -3, -6, -9, -12, -15, -18, -21,
-24, -28, -31, -34, -37, -40, -43, -46,
-48, -51, -54, -57, -60, -63, -65, -68,
-71, -73, -76, -78, -81, -83, -85, -88,
-90, -92, -94, -96, -98, -100, -102, -104,
-106, -108, -109, -111, -112, -114, -115, -117,
-118, -119, -120, -121, -122, -123, -124, -124,
-125, -126, -126, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -126, -126,
-125, -124, -124, -123, -122, -121, -120, -119,
-118, -117, -115, -114, -112, -111, -109, -108,
-106, -104, -102, -100, -98, -96, -94, -92,
-90, -88, -85, -83, -81, -78, -76, -73,
-71, -68, -65, -63, -60, -57, -54, -51,
-48, -46, -43, -40, -37, -34, -31, -28,
-24, -21, -18, -15, -12, -9, -6, -3
};
// timer code is taken from http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/
#define TIMER_CLOCK_FREQ (F_CPU/1024.0) //Found this frequency by trial and error
unsigned int timer1Latency;
unsigned int timer1LoadValue;
unsigned char timerCounter = 0;
ISR(TIMER1_OVF_vect) {
val = analogRead(0)/4 -128;
subbassr += val * Sinewave[timerCounter * 16 % 256]; //62 hertz
subbassi += val * Sinewave[(timerCounter * 16 + 64 ) % 256];
bassr += val * Sinewave[timerCounter * 31 % 256]; // 121 hertz
bassi += val * Sinewave[(timerCounter * 31 + 64 ) % 256];
midr += val * Sinewave[timerCounter * 200 % 256]; //781 hertz
midi += val * Sinewave[(timerCounter * 130 + 64 ) % 256];
trebr += val * Sinewave[timerCounter * 768 % 256]; //3000 hertz
trebi += val * Sinewave[(timerCounter * 768 + 64 ) % 256];
timerCounter++;
//Capture the current timer value. This is how much error we have
//due to interrupt latency and the work in this function
timer1Latency=TCNT1;
//Reload the timer and correct for latency.
TCNT1=timer1Latency+timer1LoadValue;
}
void StartSoundSampling(float timeoutFrequency)
{
//Calculate the timer load value
timer1LoadValue=(unsigned int)((65535-(TIMER_CLOCK_FREQ/timeoutFrequency))+0.5); //the 0.5 is for rounding;
TCCR1A = 0;
TCCR1B = 1<<CS22 | 0<<CS21 | 1<<CS20;
//Timer2 Overflow Interrupt Enable
TIMSK1 = 1<<TOIE2;
//load the timer for its first cycle
TCNT1=timer1LoadValue;
}
// Create the basic Lightuino 70 LED sink controller (the pins in the 2 40-pin IDE connectors)
LightuinoSink sinks;
// Create the Lightuino 16 channel source driver controller (the 16 pin connector)
LightuinoSourceDriver sources;
// This object PWMs the Lightuino sinks allowing individual LED brightness control, and provides array-based access to the Leds
FlickerBrightness pwm(sinks);
//?? Turn all the LEDs and source drivers off
void AllOff(void)
{
sources.set(0);
sinks.set(0,0,0);
}
// set a lantern color with RGB values, each from 0 to 8192
void setLanternColor(int lantern, int r, int g, int b)
{
// only 35 sink pins on each side, so we have to subtract 1 to compensate for laterns 6-11.
pwm.brightness[lantern * 6 - (lantern > 5)]= r;
pwm.brightness[lantern * 6 - (lantern > 5) + 1]= g;
pwm.brightness[lantern * 6 - (lantern > 5) + 2]= b;
}
// set a lantern color with a hue (between 0 and 360) and brightness value
void setLanternColor(int lantern, int hue, int brightness)
{
// when hue is between 0 and 60, r = brightness, b = 0.0 and g goes linearly from
// 0.0 to brightness; similarly, r goes down when 60<hue<120, b ramps up when 120<hue<180,
// g goes down when 180<hue<240, r ramps up when 240<hue<300 and b - down when 300<hue<360.
static int r, g, b;
r = (60*(hue<60) + (120-hue)*((hue>=60)&&(hue<120)) + /* 0*((hue>=120)&&(hue<240))/60 + */
(hue-240)*((hue>=240)&&(hue<300)) + 60*(hue>=300))*(brightness/60);
g = (hue*(hue<60) + 60*((hue>=60)&&(hue<180)) + (240-hue)*((hue>=180)&&(hue<240))
/* + 0*(hue>240) */ )*(brightness/60);
b = (/* 0*(hue<120) + */ (hue-120)*((hue>=120)&&(hue<180)) + 60*((hue>=180)&&(hue<300)) +
(360-hue)*(hue>=300))*(brightness/60);
setLanternColor(lantern, r, g, b);
}
void setUV(int lantern, int brightness)
{
pwm.brightness[lantern * 6 - (lantern > 5) + 3]= brightness;
}
void setup()
{
setup_noise();
analogReference(DEFAULT); // Use default (5v) aref voltage.
// Start up the serial port. I don't think this is actually working, but the USB works.
Serial.begin(9600);
Serial.println("serial initialized");
// Start up the Lightuino's USB serial port.
#ifdef Lightuino_USB // This #ifdef,#endif wrapper means the the code inside will only compile if your Lightuino has a USB port.
// That way this sketch will work with multiple versions of the circuitboard.
// But since you probably don't care that your sketch does so, you can leave these lines out.
Usb.begin();
#endif // This line need to be removed if #ifdef is removed too!
AllOff(); // When the board boots up there will be random values in various chips resulting in some lights being on.
pwm.StartAutoLoop(3000);
StartSoundSampling(1000);
setLanternColor(10, Lightuino_MAX_BRIGHTNESS/2, Lightuino_MAX_BRIGHTNESS/2, Lightuino_MAX_BRIGHTNESS/2);
setLanternColor(11, Lightuino_MAX_BRIGHTNESS/2, Lightuino_MAX_BRIGHTNESS/2, Lightuino_MAX_BRIGHTNESS/2);
lastPatternTime = millis();
};
void loop()
{
Usb.print("Time since last read loop (ms): ");
Usb.println(millis()-lastsamplelooptime);
lastsamplelooptime = millis();
Usb.print("Number of samples since last loop: ");
Usb.println(timerCounter);
////// Analyze Sound
subbass = sqrt(subbassr * subbassr + subbassi * subbassi)/ timerCounter;
bass = sqrt(bassr * bassr + bassi * bassi)/ timerCounter;
mid = sqrt(midr * midr + midi * midi)/ timerCounter;
treb = sqrt(trebr * trebr + trebi * trebi)/ timerCounter;
subbassr=0; subbassi=0;
bassr=0; bassi=0;
midr=0; midi=0;
trebr=0; trebi=0;
timerCounter=0;
volume = (subbass + bass + mid + treb)/4;
Usb.print(" Volume: ");
Usb.println(volume);
////// Change the light pattern every so often
if (millis()-lastPatternTime > switchTime){
currentPattern = (currentPattern + 1) % numPatterns; // just scroll through for now
lastPatternTime = millis();
}
////// Update LEDs
switch(currentPattern){ // for diagnostic mode, change this to switch(-1)
case (-1):
diagnostic();
break;
case (0):
whiteVolumeMeter();
break;
case (1):
rgbuLoop();
break;
case(2):
high_center_linear_equalizer();
break;
case(3):
pressured_hues();
break;
case(4):
color_wheel_chase();
break;
default:
color_wheel_static();
break;
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Light Patterns for the LED Lights
////////////////////////////////////////////////////////////////////////////////////////////////////////////
void whiteVolumeMeter() // sets all lanters to produce white light proportional to the current volume... unsure why *4? Should work for the new pattern
{
int brightness;
brightness = max(500, min(Lightuino_MAX_BRIGHTNESS, volume*4));
for (i=0; i<10; i++){
setLanternColor(i, brightness, brightness, brightness);
}
}
void rgbuLoop() // Should be fine for linear
{
static int color=0;
static long lastColorTime=0;
if (millis()-lastColorTime>1000){
color = (color +1) %4;
lastColorTime=millis();
for (i=0; i<10; i++){
setLanternColor(i, ((color + i) % 4 == 0 || (color + i) % 4 == 3) * Lightuino_MAX_BRIGHTNESS,
((color + i) % 4 == 1 || (color + i) % 4 == 3) * Lightuino_MAX_BRIGHTNESS,
((color + i) % 4 == 2 || (color + i) % 4 == 3) * Lightuino_MAX_BRIGHTNESS);
}
}
}
void high_center_linear_equalizer()//setup for new pattern. Function to attach frequency ranges to each of R,G,B and UV lights in a descending from the center 2 lights (3 and 4)
{
const float incre = 0.2; // incremental decrease in brightness from center
int Vsubbass, Vbass, Vmid,Vtreb,Vvolume;
//set volume variables to ranged values either 0 or from 500 to 8912
Vsubbass = subbass*2;
if (Vsubbass < 500) Vsubbass = 0;
Vbass = bass*2;
if (Vbass < 500) Vbass = 0;
Vmid = mid*2;
if (Vmid < 500) Vmid = 0;
Vtreb = treb*2;
if (Vtreb < 500) Vtreb = 0;
Vvolume = volume*2;
if (Vvolume < 500) Vvolume = 0;
//set intensity of light in lanterns so that R,G,B and UV track different frequency ranges with the center lanterns being the most powerful.
setLanternColor(0, (1-incre*3)*Vbass,(1-incre*3)*Vmid,(1-incre*3)*Vtreb);
setLanternColor(1, (1-incre*2)*Vbass,(1-incre*2)*Vmid,(1-incre*2)*Vtreb);
setLanternColor(2, (1-incre)*Vbass,(1-incre)*Vmid,(1-incre)*Vtreb);
setLanternColor(3, Vbass,Vmid,Vtreb);
setLanternColor(4, Vbass,Vmid,Vtreb);
setLanternColor(5, (1-incre)*Vbass,(1-incre)*Vmid,(1-incre)*Vtreb);
setLanternColor(6, (1-incre*2)*Vbass,(1-incre*2)*Vmid,(1-incre*2)*Vtreb);
setLanternColor(7, (1-incre*3)*Vbass,(1-incre*3)*Vmid,(1-incre*3)*Vtreb);
setUV(0,(1-3*incre)*Vsubbass);
setUV(1,(1-2*incre)*Vsubbass);
setUV(2,(1-incre)*Vsubbass);
setUV(3,Vsubbass);
setUV(7,(1-3*incre)*Vsubbass);
setUV(6,(1-2*incre)*Vsubbass);
setUV(5,(1-incre)*Vsubbass);
setUV(4,Vsubbass);
setUV(8,Vsubbass/2);
setUV(9,Vsubbass/2);
setLanternColor(8, Vvolume,Vvolume,Vvolume);
setLanternColor(9, Vvolume,Vvolume,Vvolume);
}
void pressured_hues() //show a cycle of hues going outward from the center and inwards from the edges. Meeting in 'nodes' between A and B
{
const int hue_inc = 5;
const int brightness = 4500;
static int hue_pos = 0;
static int dhue_pos = 0;
static long last_time = 0;
if (millis() - last_time > 500) //wait until 0.5 second has gone by
{
setLanternColor(0, 360 - hue_pos, brightness);
setLanternColor(1, 360 - dhue_pos, brightness);
setLanternColor(2, dhue_pos, brightness);
setLanternColor(3, hue_pos, brightness);
setLanternColor(4, 360 - hue_pos, brightness);
setLanternColor(5, 360 - dhue_pos, brightness);
setLanternColor(6, dhue_pos, brightness);
setLanternColor(7, hue_pos, brightness);
setLanternColor(8,abs(dhue_pos - hue_pos),brightness);
setLanternColor(9, 360 - abs(dhue_pos - hue_pos),brightness);
setUV(8,brightness);
setUV(9,brightness);
hue_pos = (hue_pos + hue_inc) % 360;
dhue_pos = (dhue_pos + hue_inc*2) % 360;
last_time = millis();
}
}
void color_wheel_chase() //not setup for new pattern -Paul: I had this running earlier, and it actually doesn't look that bad.
{
static int hue=0; // the hue
static int lantern_number=10; // number of lanterns
int h=hue;
for (int i=0; i<lantern_number; i++) {
setLanternColor(i, h, max(4000, min(volume*2, Lightuino_MAX_BRIGHTNESS)));
h = (h + (360/lantern_number)) % 360;
}
hue = (hue + max(1, volume/50-15)) % 360;
}
void color_wheel_static() // not setup for new pattern -Paul: this one doesn't look great with the new setup, but leaving it in until we have more patterns
{
static int h=0; // the hue
static int uvTimer=0;
for (int i=0; i<10; i++) {
setLanternColor(i, h, Lightuino_MAX_BRIGHTNESS);
setUV(i, 0 + (uvTimer>30) * Lightuino_MAX_BRIGHTNESS );
}
h = (h + 1) % 360;
uvTimer = (uvTimer+1) %60;
}
/*
OLD CODE from 2011
void twin_light_chase() // not yet modified for new light setup
{
static int hue=0; // the hue
static int lantern=0; // current lantern position
static int timeSinceSwitch=0;
if (30-timeSinceSwitch++ < min(15, max(0, volume / 30 - 20)))
{
setLanternColor(lantern, 0,0,0);
setLanternColor((lantern + 4) % 8, 0,0,0);
lantern = (lantern + 1) % 8;
setLanternColor(lantern, hue, Lightuino_MAX_BRIGHTNESS);
setLanternColor((lantern + 4) % 8, hue, Lightuino_MAX_BRIGHTNESS);
timeSinceSwitch=0;
}
hue = (hue + 1) % 360;
}
*/
/*void equalizer() // not setup for new light pattern
{
setLanternColor(0, 0, max(500, subbass*2));
setLanternColor(1, 0, max(500, subbass*2));
setLanternColor(2, 90, max(500, bass*2));
setLanternColor(3, 90, max(500, bass*2));
setLanternColor(4, 180, max(500, mid*2));
setLanternColor(5, 180, max(500, mid*2));
setLanternColor(6, 270, max(500, treb*2));
setLanternColor(7, 270, max(500, treb*2));
setLanternColor(8, max(500, volume*2), max(500, volume*2), max(500, volume*2));
setLanternColor(9, max(500, volume*2), max(500, volume*2), max(500, volume*2));
}*/
void diagnostic()
{
static long lastColorTime=0;
static int color=0;
if (millis()-lastColorTime>1000){
lastColorTime=millis();
color=(color + 1) % 5;
for (int i=0; i<12; i++) {
setLanternColor(i, (color ==0)*Lightuino_MAX_BRIGHTNESS, (color ==1)*Lightuino_MAX_BRIGHTNESS, (color ==2)*Lightuino_MAX_BRIGHTNESS);
setUV(i, (color ==3)*Lightuino_MAX_BRIGHTNESS);
}
}
}
void monochrome_perlin_waves()
{
// rgb up to 8192 Lightuino_MAX_BRIGHTNESS
int half = Lightuino_MAX_BRIGHTNESS/2;
for (int i=0; i<lantern_number; i++) {
float offset = i * 10.0f;
x1 = float(millis() + offset)/100.0f;
y1 = 10.0f;
//x2 = float(millis())/100.0f;
//y2 = 11.0f;
int b = int(PerlinNoise2(x1,y1,persistence,octaves)*half+half); // 0 - Max
setLanternColor(i, b, b, b );
}
}
void red_perlin_waves()
{
// rgb up to 8192 Lightuino_MAX_BRIGHTNESS
int half = Lightuino_MAX_BRIGHTNESS/2;
for (int i=0; i<lantern_number; i++) {
float offset = i * 15.0f;
x1 = float(millis() + offset)/110.0f;
y1 = 10.0f;
int b = int(PerlinNoise2(x1,y1,persistence,octaves)*half+half); // 0 - Max
setLanternColor(i, b, 0, 0 );
}
}
void hue_perlin_waves()
{
// rgb up to 8192 Lightuino_MAX_BRIGHTNESS
int half = Lightuino_MAX_BRIGHTNESS/2;
for (int i=0; i<lantern_number; i++) {
float offset = i * 15.0f;
x1 = float(millis() + offset)/110.0f;
y1 = 10.0f;
int h = int(PerlinNoise2(x1,y1,persistence,octaves)*180+180); // 0 - 360
setLanternColor(i, h, Lightuino_MAX_BRIGHTNESS);
}
}
/*
copyright 2007 Mike Edwards
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; version 2 of the License.
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.
*/
#include <math.h>
void setup_noise()
{
//pinMode(5,OUTPUT);
//pinMode(6,OUTPUT);
//analogWrite(5,0);
//analogWrite(6,0);
//persistence affects the degree to which the "finer" noise is seen
persistence = 0.25;
//octaves are the number of "layers" of noise that get computed
octaves = 3;
}
/*
void loop()
{
x1 = float(millis())/100.0f;
y1 = 10.0f;
x2 = float(millis())/100.0f;
y2 = 11.0f;
//PerlinNoise2 results in a float between -1 and 1
//below we convert to a n int between 0 and 255
int m = int(PerlinNoise2(x1,y1,persistence,octaves)*128+128);
int n = int(PerlinNoise2(x2,y2,persistence,octaves)*128+128);
} */
//using the algorithm from http://freespace.virgin.net/hugo.elias/models/m_perlin.html
// thanks to hugo elias
float Noise2(float x, float y)
{
long noise;
noise = x + y * 57;
noise = pow(noise << 13,noise);
return ( 1.0 - ( long(noise * (noise * noise * 15731L + 789221L) + 1376312589L) & 0x7fffffff) / 1073741824.0);
}
float SmoothNoise2(float x, float y)
{
float corners, sides, center;
corners = ( Noise2(x-1, y-1)+Noise2(x+1, y-1)+Noise2(x-1, y+1)+Noise2(x+1, y+1) ) / 16;
sides = ( Noise2(x-1, y) +Noise2(x+1, y) +Noise2(x, y-1) +Noise2(x, y+1) ) / 8;
center = Noise2(x, y) / 4;
return (corners + sides + center);
}
float InterpolatedNoise2(float x, float y)
{
float v1,v2,v3,v4,i1,i2,fractionX,fractionY;
long longX,longY;
longX = long(x);
fractionX = x - longX;
longY = long(y);
fractionY = y - longY;
v1 = SmoothNoise2(longX, longY);
v2 = SmoothNoise2(longX + 1, longY);
v3 = SmoothNoise2(longX, longY + 1);
v4 = SmoothNoise2(longX + 1, longY + 1);
i1 = Interpolate(v1 , v2 , fractionX);
i2 = Interpolate(v3 , v4 , fractionX);
return(Interpolate(i1 , i2 , fractionY));
}
float Interpolate(float a, float b, float x)
{
//cosine interpolations
return(CosineInterpolate(a, b, x));
}
float LinearInterpolate(float a, float b, float x)
{
return(a*(1-x) + b*x);
}
float CosineInterpolate(float a, float b, float x)
{
float ft = x * 3.1415927;
float f = (1 - cos(ft)) * .5;
return(a*(1-f) + b*f);
}
float PerlinNoise2(float x, float y, float persistance, int octaves)
{
float frequency, amplitude;
float total = 0.0;
for (int i = 0; i <= octaves - 1; i++)
{
frequency = pow(2,i);
amplitude = pow(persistence,i);
total = total + InterpolatedNoise2(x * frequency, y * frequency) * amplitude;
}
return(total);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment