5v version before synchrotron improvements

This commit is contained in:
Neale Pickett 2016-09-25 16:57:35 -06:00
parent 021c9ffb26
commit e87a3f66b4
1 changed files with 216 additions and 155 deletions

View File

@ -1,142 +1,189 @@
// Proton Pack with NeoPixels
// Boy howdy do these make everything easy
#include <SPI.h> #include <SPI.h>
#include <Wire.h> #include <Wire.h>
#include "Adafruit_LEDBackpack.h" #include <SD.h>
#include "Adafruit_GFX.h" #include <Adafruit_NeoPixel.h>
#include <Adafruit_VS1053.h>
#include <Adafruit_LEDBackpack.h>
#include <Adafruit_GFX.h>
#define LTCH 8 #define DEBUG 12
#define RED 9
#define GREEN 10 // Music Player object
#define BLUE 11 #define SHIELD_RESET -1 // VS1053 reset pin (unused!)
#define DEBUG 13 #define SHIELD_CS 7 // VS1053 chip select pin (output)
#define TRIGGER 4 #define SHIELD_DCS 6 // VS1053 Data/command select pin (output)
#define CARDCS 4 // Card chip select
#define DREQ 1 // VS1053 Data request (an interrupt pin)
Adafruit_VS1053_FilePlayer musicPlayer = Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);
// NeoPixel: so cool
#define SYNCHROTRON_PIN 5
#define SYNCHROTRON_PIXELS 24 // I'm using the middle-sized NeoPixel ring
Adafruit_NeoPixel synchrotron = Adafruit_NeoPixel(SYNCHROTRON_PIXELS, SYNCHROTRON_PIN, NEO_GRB | NEO_KHZ800);
// 7-segment displays
Adafruit_7segment disp1 = Adafruit_7segment();
// Inputs
#define TRIGGER 8
// Nominal brightness
#define brightness 64
const byte powerColor[3] = {0xff, 0, 0};
const byte dispBright = 10; const byte dispBright = 10;
unsigned long jiffies = 0; unsigned long jiffies = 0;
Adafruit_7segment disp1;
void rgbPWM(byte r, byte g, byte b) { void rgbPWM(byte r, byte g, byte b) {
analogWrite(RED, 0xff - r); // XXX: do this
analogWrite(GREEN, 0xff - g);
analogWrite(BLUE, 0xff - b);
} }
void rgb(byte r, byte g, byte b) { void rgb(byte r, byte g, byte b) {
SPI.transfer(b); for (int i = 0; i < SYNCHROTRON_PIXELS; i += 1) {
SPI.transfer(g); synchrotron.setPixelColor(i, synchrotron.Color(r, g, b));
SPI.transfer(r); }
digitalWrite(LTCH, HIGH); synchrotron.show();
digitalWrite(LTCH, LOW);
} }
void setup() { void setup() {
randomSeed(analogRead(12)); randomSeed(analogRead(12));
SPI.begin(); // synchrotron
SPI.setDataMode(SPI_MODE0); synchrotron.begin();
SPI.setClockDivider(SPI_CLOCK_DIV2); synchrotron.show(); // Turn everything off
SPI.setBitOrder(LSBFIRST);
disp1 = Adafruit_7segment(); // inputs
disp1.begin(0x70);
pinMode(LTCH, OUTPUT);
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
pinMode(BLUE, OUTPUT);
pinMode(DEBUG, OUTPUT);
pinMode(TRIGGER, INPUT_PULLUP); pinMode(TRIGGER, INPUT_PULLUP);
// music player, this sets up SPI for us
SD.begin(CARDCS);
musicPlayer.begin();
musicPlayer.setVolume(20, 20); // lower = louder
// We don't set useInterrupt, since we do our own polling for smoother operations
// 7-segment displays.
// These also use SPI, in i2c mode.
// Since the music player has a CS line,
// and we're unlikely to send the right i2c command to the 7-segment to wake it up,
// it's okay to use the same SPI bus for both.
disp1.begin(0x70);
} }
// Cycle through colors, one spoke at a time. // Synchrotron needs to "spin up"
// Since we can only control brightness by color component for all spokes, // We start slow, with red, then work our way through the rainbow to blue
// we can't do a fancier trick per-spoke. bool charge() {
// But this one isn't that bad, really. static uint32_t count = 0;
bool doStartup() { static int every = 9;
static int count = 0; static int reps = 0;
static byte cur[3] = {0, 0, 0}; uint32_t color_count;
byte r, g, b;
static int whichout = 0;
// Run this every 12 jiffies // Play startup sound at the start
if (jiffies % 6 != 0) { if (count == 0) {
return false; musicPlayer.startPlayingFile("track001.mp3");
} }
int weight = 0; // Make the animation play out a little more slowly,
int pos = count % 8; // while still allowing a nice fast rotation
int color = 6 - (count / 8); color_count = count / 4;
count += 1; // Give the illusion of something spinning up
if (every == 1) {
for (int i = 0; i < 3; i += 1) { whichout = (whichout + 1) % SYNCHROTRON_PIXELS;
int bit = (color & (1 << i))?1:0; } else if (count % every == 0) {
weight += bit; whichout = (whichout + 1) % SYNCHROTRON_PIXELS;
// Shift the current color in from the LSB to the MSB reps += 1;
cur[i] = (cur[i] << 1) | bit; if (reps == 20 - every) {
every -= 1;
reps = 0;
} }
rgb(cur[0], cur[1], cur[2]);
rgbPWM(32 * weight, 32 * weight, 32 * weight);
for (int i = 0; i < 5; i += 1) {
disp1.writeDigitRaw(i, random(256));
} }
disp1.setBrightness(random(16));
disp1.writeDisplay();
if ((color == 1) && (pos == 7)) { // Start at blue, go through hue to red
rgb(powerColor[0], powerColor[1], powerColor[2]); switch (color_count / brightness) {
case 0:
r = color_count % brightness;
g = 0;
b = 0;
break;
case 1:
r = brightness - (color_count % brightness) - 1;
g = color_count % brightness;
b = 0;
break;
case 2:
r = 0;
g = brightness - (color_count % brightness) - 1;
b = color_count % brightness;
break;
default:
rgb(brightness, 0, 0);
return true;
}
// Set 'em up pixels
for (int i = 0; i < SYNCHROTRON_PIXELS; i += 1) {
if (whichout == i) {
synchrotron.setPixelColor(i, 0);
} else if ((whichout == (i+1) % SYNCHROTRON_PIXELS) || ((whichout+1) % SYNCHROTRON_PIXELS == i)) {
synchrotron.setPixelColor(i, synchrotron.Color(r/4, g/4, b/4));
} else {
synchrotron.setPixelColor(i, synchrotron.Color(r, g, b));
}
}
synchrotron.show();
disp1.clear(); disp1.clear();
disp1.printNumber(0xb00, HEX); disp1.printNumber(0xb00, HEX);
disp1.setBrightness(dispBright); disp1.setBrightness(dispBright);
disp1.writeDisplay(); disp1.writeDisplay();
return true;
} count += 1;
return false; return false;
} }
// Pulse to an extreme, then back // Do a sort of mirrored KITT effect
bool pulse(byte initial, int pct) { bool kitt() {
static int prev = 0; static int count = 0;
static int state = 0; int out = count % (SYNCHROTRON_PIXELS/2);
static int val = 0;
int cur = (pct << 8) | initial;
int newval = initial;
// Reset if called with new values if (jiffies % 12 != 0) {
if (prev != cur) { return false;
state = 0;
prev = cur;
} }
switch (state) { for (int i = 0; i < SYNCHROTRON_PIXELS; i += 1) {
case 0: int pixnum = (SYNCHROTRON_PIXELS/2) - abs(i - (SYNCHROTRON_PIXELS / 2));
state = 1; int intensity;
val = initial;
break; if (count < SYNCHROTRON_PIXELS/2) {
case 1: intensity = 100;
val = (val * pct) / 100; if (pixnum == out) {
if ((val <= 1) || (val >= 255)) { intensity = 50;
state = 2; } else if (pixnum < out) {
intensity = 10;
} }
break; } else {
case 2: intensity = 10;
// discrete exponentiation, woo woo if (pixnum == out) {
while ((newval * pct) / 100 != val) { intensity = 50;
newval = (newval * pct) / 100; } else if (pixnum < out) {
intensity = 100;
} }
val = newval;
if (val == initial) {
state = 3;
} }
break; synchrotron.setPixelColor(i, synchrotron.Color(brightness * intensity / 100, 0, 0));
case 3: }
state = 0; synchrotron.show();
val = 0;
count += 1;
if (count > SYNCHROTRON_PIXELS) {
rgb(brightness, 0, 0);
count = 0;
return true; return true;
} }
newval = min(val, 255);
rgbPWM(newval, newval, newval);
return false; return false;
} }
@ -144,17 +191,16 @@ bool glitch(int r, int g, int b) {
static int state = 0; static int state = 0;
int i; int i;
if (jiffies % 5 != 0) { if (jiffies % 10 != 0) {
return false; return false;
} }
switch (state) { switch (state) {
case 0: case 0:
// pick a random bit and clear it // glitch to a random color
i = random(8); r = random(brightness / 6);
r &= ~(1 << i); g = random(brightness / 6);
g &= ~(1 << i); b = random(brightness / 6);
b &= ~(1 << i);
rgb(r, g, b); rgb(r, g, b);
state = 1; state = 1;
break; break;
@ -169,17 +215,21 @@ bool glitch(int r, int g, int b) {
} }
void fire() { void fire() {
rgb(0, 0xff, 0xff); rgb(0, brightness, brightness);
pulse(32, 160);
} }
void fireDone() { void fireDone() {
rgb(powerColor[0], powerColor[1], powerColor[2]); rgb(brightness, 0, 0);
rgbPWM(64, 64, 64);
} }
void flashDebug() {
if (jiffies % 50 == 0) {
int val = digitalRead(DEBUG);
digitalWrite(DEBUG, (val==HIGH)?LOW:HIGH);
}
}
int doPowered() { void tick() {
static int doing = 0; static int doing = 0;
static float val1 = 584.2; static float val1 = 584.2;
static bool firing = false; static bool firing = false;
@ -194,26 +244,24 @@ int doPowered() {
switch (doing) { switch (doing) {
case 0: // doing nothing case 0: // doing nothing
if (jiffies % 200 == 0) { if (jiffies % 300 == 0) {
doing = 1; // pulse doing = 1; // KITT
} else if (random(350) == 0) { } else if (random(350) == 0) {
doing = 2; // surge doing = 2; // surge
} else if (random(200) == 0) { } else if (random(400) == 0) {
doing = 3; // glitch doing = 3; // glitch
} }
break; break;
case 1: case 1:
if (pulse(64, 80)) { if (kitt()) {
doing = 0; doing = 0;
} }
break; break;
case 2: case 2:
if (pulse(64, 120)) {
doing = 0; doing = 0;
}
break; break;
case 3: case 3:
if (glitch(powerColor[0], powerColor[1], powerColor[2])) { if (glitch(brightness, 0, 0)) {
doing = 0; doing = 0;
} }
break; break;
@ -249,37 +297,50 @@ int doPowered() {
disp1.writeDisplay(); disp1.writeDisplay();
} }
flashDebug();
return 1;
}
void flashDebug() {
if (jiffies % 50 == 0) {
int val = digitalRead(DEBUG);
digitalWrite(DEBUG, (val==HIGH)?LOW:HIGH);
}
} }
void loop() { void loop() {
static int state = 0; // 6 seems to be about what my overly-critical brain needs to buffer out
// any music player delays so that they're unnoticeable
unsigned long new_jiffies = millis() / 6;
// state machine if (new_jiffies > jiffies) {
// The delay is *outside* the state machine, you'll notice. jiffies = new_jiffies;
// So don't call sleep in your state function. tick();
switch (state) {
case 0:
if (doStartup()) {
state = 1;
}
break;
case 1:
state = doPowered();
break;
} }
flashDebug(); /* Cleverness ensues
*
* The Adafruit library is written to let you go off and do whatever you need,
* hooking into an interrupt to sort of act like a multitasking operating system,
* interrupting your program periodically.
*
* That's smart, since it makes it easy to use,
* but we want this to be responsive, and can't handle something barging in and taking up lots of time:
* it makes things look really uneven as our display code pauses to fill the buffer.
* Fortunately, we don't have to fill the entire buffer at once, we can trickle data in.
* That's what this does.
*
* Since the entire program is polling, without ever calling delay,
* and hopefully doing what needs to be done quickly,
* we check to see if the music chip wants more data.
* If it does, we give it one chunk, and only one chunk,
* rather than filling its buffer back up completely.
*
* There is still some weirdness with this loop,
* possibly because the SPI routines are masking interrupts used to increment millis.
* But it's remarkably more fluid than the other way.
*/
delay(12); if (musicPlayer.playingMusic && musicPlayer.readyForData()) {
jiffies += 1; int bytesread = musicPlayer.currentTrack.read(musicPlayer.mp3buffer, VS1053_DATABUFFERLEN);
if (bytesread == 0) {
musicPlayer.playingMusic = false;
musicPlayer.currentTrack.close();
} else {
musicPlayer.playData(musicPlayer.mp3buffer, bytesread);
}
}
} }