Skip to content

Instantly share code, notes, and snippets.

@neilk
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neilk/2cb82b3d9e059ef20472 to your computer and use it in GitHub Desktop.
Save neilk/2cb82b3d9e059ef20472 to your computer and use it in GitHub Desktop.
softStars for weatherStrip ElectricImp project
// WS2812 "Neopixel" LED Driver
// Copyright (C) 2014 Electric Imp, inc.
//
// Uses SPI to emulate 1-wire
// http://learn.adafruit.com/adafruit-neopixel-uberguide/advanced-coding
// This class requires the use of SPI257, which must be run at 7.5MHz
// to support neopixel timing.
const SPICLK = 7500; // kHz
// This is used for timing testing only
us <- hardware.micros.bindenv(hardware);
class NeoPixels {
// This class uses SPI to emulate the newpixels' one-wire protocol.
// This requires one byte per bit to send data at 7.5 MHz via SPI.
// These consts define the "waveform" to represent a zero or one
ZERO = 0xC0;
ONE = 0xF8;
BYTESPERPIXEL = 24;
// when instantiated, the neopixel class will fill this array with blobs to
// represent the waveforms to send the numbers 0 to 255. This allows the blobs to be
// copied in directly, instead of being built for each pixel - which makes the class faster.
bits = null;
// Like bits, this blob holds the waveform to send the color [0,0,0], to clear pixels faster
clearblob = blob(12);
// private variables passed into the constructor
spi = null; // imp SPI interface (pre-configured)
frameSize = null; // number of pixels per frame
frame = null; // a blob to hold the current frame
// _spi - A configured spi (MSB_FIRST, 7.5MHz)
// _frameSize - Number of Pixels per frame
constructor(_spi, _frameSize) {
this.spi = _spi;
this.frameSize = _frameSize;
this.frame = blob(frameSize*BYTESPERPIXEL + 1);
this.frame[frameSize*BYTESPERPIXEL] = 0;
// prepare the bits array and the clearblob blob
initialize();
clearFrame();
writeFrame();
}
// fill the array of representative 1-wire waveforms.
// done by the constructor at instantiation.
function initialize() {
// fill the bits array first
bits = array(256);
for (local i = 0; i < 256; i++) {
local valblob = blob(BYTESPERPIXEL / 3);
valblob.writen((i & 0x80) ? ONE:ZERO,'b');
valblob.writen((i & 0x40) ? ONE:ZERO,'b');
valblob.writen((i & 0x20) ? ONE:ZERO,'b');
valblob.writen((i & 0x10) ? ONE:ZERO,'b');
valblob.writen((i & 0x08) ? ONE:ZERO,'b');
valblob.writen((i & 0x04) ? ONE:ZERO,'b');
valblob.writen((i & 0x02) ? ONE:ZERO,'b');
valblob.writen((i & 0x01) ? ONE:ZERO,'b');
bits[i] = valblob;
}
// now fill the clearblob
for(local j = 0; j < BYTESPERPIXEL; j++) {
clearblob.writen(ZERO, 'b');
}
}
// sets a pixel in the frame buffer
// but does not write it to the pixel strip
// color is an array of the form [r, g, b]
function writePixel(p, color) {
frame.seek(p*BYTESPERPIXEL);
// red and green are swapped for some reason, so swizzle them back
frame.writeblob(bits[color[1]]);
frame.writeblob(bits[color[0]]);
frame.writeblob(bits[color[2]]);
}
// Clears the frame buffer
// but does not write it to the pixel strip
function clearFrame() {
frame.seek(0);
for (local p = 0; p < frameSize; p++) frame.writeblob(clearblob);
}
// writes the frame buffer to the pixel strip
// ie - this function changes the pixel strip
function writeFrame() {
spi.write(frame);
}
}
/* RUNTIME STARTS HERE -------------------------------------------------------*/
const NUMPIXELS = 60;
const ANIMATION_FRAMES = 50;
const TAU = 6.282; // 2*PI
spi <- hardware.spi257;
spi.configure(MSB_FIRST, SPICLK);
pixelStrip <- NeoPixels(spi, NUMPIXELS);
pixelAngle <- array(60, 0.0);
stepRadians <- (TAU/2.0) / ANIMATION_FRAMES;
skyColor <- [0, 0, 5];
starColor <- [75, 75, 255];
function blendComponent(c1, c2, percentage) {
return c1 + ((c2 - c1).tofloat() * percentage).tointeger()
}
function blend(rgb1, rgb2, percentage) {
return [
blendComponent(rgb1[0], rgb2[0], percentage),
blendComponent(rgb1[1], rgb2[1], percentage),
blendComponent(rgb1[2], rgb2[2], percentage)
]
}
function softStars(d = null) {
r <- 1.0 * math.rand() / RAND_MAX;
// start animation on some stars, randomly
// if not already in an animation
if (r > 0.95) {
// pick a random pixel
randIndex <- (60.0 * math.rand() / RAND_MAX).tointeger();
// if pixel not in an animation, start it at PI radians
if (pixelAngle[randIndex] == 0.0) {
pixelAngle[randIndex] = TAU / 2.0
}
}
// figure out colors for each pixel
for (local i = 0; i < NUMPIXELS; i++) {
blendPercentage <- 0.0
if (pixelAngle[i] > 0.0) {
blendPercentage <- math.sin(pixelAngle[i]);
}
color <- blend(skyColor, starColor, blendPercentage)
pixelStrip.writePixel(i, color);
// decay
if (pixelAngle[i] > 0) {
pixelAngle[i] = pixelAngle[i] - stepRadians
}
if (pixelAngle[i] < 0) {
pixelAngle[i] = 0;
}
}
// and draw it
pixelStrip.writeFrame();
imp.wakeup(0, softStars);
}
softStars();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment