Test program for my Multi_WS2811 library that started out as a fork of heroic/WS2811. My library uses hardware DMA on the FRDM-KL25Z to drive up to 16 strings of WS2811 or WS2812 LEDs in parallel.
Dependencies: Multi_WS2811 mbed MMA8451Q
Fork of WS2811 by
NOTE: I have accidentally pushed changes for another fork of this program that I used in the recent Georgetown Carnival Power Tool Races. When I get some time, I will restore the test program to its original glory.
You can see my power tool racer (Nevermore's Revenge) here

This tests my FRDM-KL25Z multi-string WS2811/WS2812 library. It uses the accelerometer to change the rainbow phase on two strings of LEDs as well as the touch sense to change brightness.
A video of this program in operation is here.
Here is the library that I developed to run the LEDs:
Import libraryMulti_WS2811
Library allowing up to 16 strings of 60 WS2811 or WS2812 LEDs to be driven from a single FRDM-KL25Z board. Uses hardware DMA to do a full 800 KHz rate without much CPU burden.
Revision 38:3b1ce6902a1b, committed 2015-06-13
- Comitter:
- Ned Konz
- Date:
- Sat Jun 13 00:18:26 2015 -0700
- Parent:
- 37:e25d212ee3fe
- Child:
- 39:e735259e1d2e
- Commit message:
- Worked out flapping and color animation
Changed in this revision
--- a/.vimrc Fri Jun 12 20:13:03 2015 -0700 +++ b/.vimrc Sat Jun 13 00:18:26 2015 -0700 @@ -1,2 +1,2 @@ set path=,.,TSI,MMA8451Q,mbed,mbed/TARGET_KL25Z,mbed/TARGET_KL25Z/TOOLCHAIN_GCC_ARM,mbed/TARGET_KL25Z/TARGET_Freescale,mbed/TARGET_KL25Z/TARGET_Freescale/TARGET_KLXX,mbed/TARGET_KL25Z/TARGET_Freescale/TARGET_KLXX/TARGET_KL25Z,Multi_WS2811 -set makeprg=remake\ $*\ \&\&cp\ Nevermores_Revenge.bin\ /Volumes/MBED +set makeprg=remake
--- a/TSI.lib Fri Jun 12 20:13:03 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://mbed.org/users/mbed_official/code/TSI/#1a60ef257879
--- a/main.cpp Fri Jun 12 20:13:03 2015 -0700
+++ b/main.cpp Sat Jun 13 00:18:26 2015 -0700
@@ -8,8 +8,6 @@
#include "WS2811.h"
// I/O pin usage
-// PTD0 TPM0 CH0 monitor
-// PTD1 TPM0 CH1 monitor
// PTD2 (D11) data output for strip# 1
// PTD3 (D12) data output for strip# 2
// PTA2 (D3) blinking eyes output (HI = ON)
@@ -17,7 +15,9 @@
const unsigned DATA_OUT_PIN1 = 2; // PTD2
const unsigned DATA_OUT_PIN2 = 3; // PTD3
-const unsigned MAX_LEDS_PER_STRIP = 32;
+
+// actually, sides have 21 LEDs each, and ends have 10 LEDs each.
+const unsigned MAX_LEDS_PER_STRIP = 31;
// per LED: 3 * 20 mA = 60mA max
// 60 LEDs: 60 * 60mA = 3600 mA max
@@ -28,38 +28,76 @@
typedef WS2811<MAX_LEDS_PER_STRIP> MyWS2811;
-MyWS2811 lightStrip1(nLEDs, DATA_OUT_PIN1);
-MyWS2811 lightStrip2(nLEDs, DATA_OUT_PIN2);
+static MyWS2811 lightStrip1(nLEDs, DATA_OUT_PIN1);
+static MyWS2811 lightStrip2(nLEDs, DATA_OUT_PIN2);
Serial pc(USBTX, USBRX);
-MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS);
-PwmOut rled(LED_RED); // max = 0.0
-PwmOut gled(LED_GREEN); // max = 0.0
+// accelerometer
+static MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS);
+
+// RGB LED on FRDM board
+static PwmOut rled(LED_RED); // max = 0.0
+static PwmOut gled(LED_GREEN); // max = 0.0
// LED_BLUE is on PTD1
-PwmOut eyes(D3); // also redLED1; max = 1.0
-PwmOut servo(D5);
-DigitalOut greenLED2(D4); // max = 1.0
-DigitalIn button1(D6); // low=ON, debounced
-DigitalIn button2(D7); // low=ON, debounced
+
+static PwmOut eyes(D3); // also redLED1; max = 1.0
+static PwmOut servo(D5);
+static DigitalOut greenLED2(D4); // max = 1.0
+static DigitalIn button1(D6); // low=ON, debounced
+static DigitalIn button2(D7); // low=ON, debounced
// Limits
const float maxBrite = 0.5;
-
const float minServo = -0.7; // -1.0 = -60°
const float maxServo = 0.6; // 1.0 = +60°
+const float minFlapTime = (maxServo - minServo) * 0.17; // 0.17 seconds / 60° at 4.8V
+const float maxFlapTime = 1.0;
+
+// Globals
+// test run was about 6 meters in about 5 seconds, for
+// a final velocity of 2.4m/s, and a uniform acceleration of 0.48 m/s^2
+// or about 0.048g
+static float restZAccel; // in m/s^2 1g = 9.8 m/s^2
+static float currentZAccel; // in m/s^2
+static float currentSpeed; // in m/s
+
+const float speedUpdateInterval = 0.1;
+static Ticker speedUpdateTicker;
+
+static Ticker eyeUpdateTicker;
+
+static Ticker stripUpdateTicker;
+
+static float wingFlapTime = maxFlapTime;
+static Ticker wingUpdateTicker;
+
+// we have to know delta T to compute speed.
+// So this is called at speedUpdateInterval seconds intervals.
+static void updateSpeedAndAcceleration()
+{
+ currentZAccel = acc.getAccZ() * 9.8;
+ currentSpeed += (currentZAccel - restZAccel) * speedUpdateInterval;
+}
+
+static void resetSpeedAndAcceleration()
+{
+ restZAccel = currentZAccel;
+ currentSpeed = 0.0;
+}
// @brief sets different colors in each of the LEDs of a strip
// @param strip the light strip
// @param sat saturation, 0.0 - 1.0
// @param brite brightness, 0.0 - 1.0
// @param hueShift shift, 0.0 - 1.0 is equivalent to 0 - 360 degrees
-static void showRainbow(MyWS2811 &strip, float sat, float brite, float hueShift)
+static void showRainbow(MyWS2811 &strip, float sat, float brite, float hueShift, float hueRange = 1.0)
{
unsigned nLEDs = strip.numPixels();
- for (unsigned i = 0; i < nLEDs; i++) {
+ for (unsigned i = 0; i < nLEDs; i++)
+ {
uint8_t r, g, b;
- float hue = ((float)i / (float)nLEDs) + hueShift;
+ float hue = (i * hueRange / nLEDs) + hueShift;
HSBtoRGB(hue, sat, brite, &r, &g, &b);
strip.setPixelColor(i, r, g, b);
}
@@ -69,7 +107,8 @@
static void showSolidColor(MyWS2811 &strip, uint8_t r, uint8_t g, uint8_t b)
{
unsigned nLEDs = strip.numPixels();
- for (unsigned i = 0; i < nLEDs; i++) {
+ for (unsigned i = 0; i < nLEDs; i++)
+ {
strip.setPixelColor(i, r, g, b);
}
strip.show();
@@ -78,8 +117,22 @@
// range is -1.0 (full CCW) to +1.0 (full CW)
static void positionServo(float pos)
{
- if (pos < minServo) pos = minServo;
- else if (pos > maxServo) pos = maxServo;
+ if (pos < minServo)
+ pos = minServo;
+ else if (pos > maxServo)
+ pos = maxServo;
+
+ if (pos < 0.0) {
+ rled = pos + 1.0;
+ gled = 1.0;
+ }
+ else if (pos > 0.0) {
+ rled = 1.0;
+ gled = 1.0 - pos ;
+ }
+ else {
+ rled = gled = 0.5;
+ }
servo.pulsewidth_us((1.5 + (pos / 2.0)) * 1000.0);
}
@@ -101,29 +154,44 @@
{
pc.printf("LEDs .");
rled = 0.0; // red LED on
- wait(1.0);
+ wait(0.5);
pc.printf(".");
rled = 1.0; // red LED off, green LED on
gled = 0.0;
- wait(1.0);
+ wait(0.5);
pc.printf(".");
gled = 1.0; // green LED off, eyes on
eyes = 1.0;
- wait(1.0);
+ wait(0.5);
pc.printf(".");
eyes = 0.0;
pc.printf("\r\n");
}
+static void refreshLightStrips()
+{
+ MyWS2811::startDMA();
+ // 24 bits per LED, 800kHz (1.25usec/bit)
+ // wait_us((MAX_LEDS_PER_STRIP * 24 * 10 / 8) + 100);
+}
+
+static void blankLightStrips()
+{
+ showSolidColor(lightStrip1, 0, 0, 0);
+ showSolidColor(lightStrip2, 0, 0, 0);
+ refreshLightStrips();
+}
+
static void selfTestLightStrips()
{
+ blankLightStrips();
pc.printf("light strips");
uint8_t rgb[4] = { (uint8_t)(255 * maxBrite), 0, 0, 0 };
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++)
+ {
showSolidColor(lightStrip1, rgb[0], rgb[1], rgb[2]);
showSolidColor(lightStrip2, rgb[1], rgb[2], rgb[0]);
- MyWS2811::startDMA();
- if (i == 2) break;
+ refreshLightStrips();
wait(1.0);
rgb[3] = rgb[2];
rgb[2] = rgb[1];
@@ -131,7 +199,7 @@
rgb[0] = rgb[3];
pc.printf(".");
}
-
+ blankLightStrips();
pc.printf("\r\n");
}
@@ -144,6 +212,60 @@
selfTestLightStrips();
}
+void updateEyes()
+{
+ static float brite = 1.0;
+ static float increment = -0.1;
+
+ eyes = brite;
+
+ brite += increment;
+ if (brite >= 1.0)
+ {
+ increment = -0.05;
+ brite = 1.0;
+ }
+ else if (brite <= 0.0)
+ {
+ increment = 0.05;
+ brite = 0.0;
+ }
+}
+
+// rainbow that wraps around entire frame
+void updateStripsRainbow()
+{
+ showRainbow(lightStrip1, 1.0, maxBrite, currentSpeed, 0.5);
+ showRainbow(lightStrip2, 1.0, maxBrite, currentSpeed + 0.5, 0.5);
+ refreshLightStrips();
+}
+
+// callback
+void updateWings()
+{
+ static float currentPosition = 1.0;
+
+ currentPosition = -currentPosition;
+ positionServo(currentPosition);
+}
+
+void setWingFlapTime(float desired)
+{
+ static float lastWingFlapTime = 0.0;
+ if (desired < minFlapTime)
+ desired = minFlapTime;
+ else if (desired > maxFlapTime)
+ desired = maxFlapTime;
+ wingFlapTime = desired;
+
+ if (lastWingFlapTime != wingFlapTime)
+ {
+ wingUpdateTicker.detach();
+ wingUpdateTicker.attach(updateWings, wingFlapTime);
+ lastWingFlapTime = wingFlapTime;
+ }
+}
+
int main(void)
{
pc.baud(115200);
@@ -152,29 +274,40 @@
lightStrip1.begin();
lightStrip2.begin();
- rled = 1.0;
- gled = 1.0;
- eyes = 0.0;
+ rled = 1.0;
+ gled = 1.0;
greenLED2 = 0.0;
servo.period_ms(20);
selfTest();
- float xyz[3];
- unsigned rep = 0;
+ eyeUpdateTicker.attach(updateEyes, 0.05);
+ wingUpdateTicker.attach(updateWings, wingFlapTime);
+
+ resetSpeedAndAcceleration();
+ speedUpdateTicker.attach(updateSpeedAndAcceleration, speedUpdateInterval);
+
+ stripUpdateTicker.attach(updateStripsRainbow, 0.1);
- for (;;) {
- rep ++;
- acc.getAccAllAxis(xyz);
- pc.printf("%8u x: %f y: %f z: %f\r\n", rep, xyz[0], xyz[1], xyz[2]);
- rled = 1.0 - fabs(xyz[0]);
- gled = 1.0 - fabs(xyz[1]);
- eyes = fabs(xyz[2]);
- showRainbow(lightStrip1, 1.0, maxBrite, fabs(xyz[0]));
- showRainbow(lightStrip2, 1.0, maxBrite, fabs(xyz[1]));
- MyWS2811::startDMA();
- MyWS2811::wait_for_dma_done();
- wait_us(100);
+ float lastCurrentSpeed = 0.0;
+ for (;; )
+ {
+ float relativeAccel = fabs(currentZAccel - restZAccel);
+ if ((relativeAccel < 1.0) || !button2.read())
+ {
+ resetSpeedAndAcceleration();
+ setWingFlapTime(maxFlapTime);
+ }
+ else
+ {
+ setWingFlapTime(minFlapTime);
+ }
+
+ if (lastCurrentSpeed != currentSpeed)
+ {
+ lastCurrentSpeed = currentSpeed;
+ pc.printf("%f %f %f\r\n", relativeAccel, currentSpeed, wingFlapTime);
+ }
+ wait(0.1);
}
}
-
Ned Konz


Generic WS2811/WS2812