Prop shield & WS2812b Fill_Gradient question

Status
Not open for further replies.

propa

Well-known member
Hi my name's Richard and just got a 3.2 today and soldered on the prop shield. Went through calibration and I'm actually surprised how simple the set up process was, wicked work Teensy peeps!

So I have a project where I've got a strip of LEDs and would like to be able to move the prop shield to control a directional fade of colour, I've got some simple sketches working, and can turn on a single pixel and make it move from side to side based on the pitch orientation, done by mapping the pitch data from 0 - number of LEDs, and then clearing the strip on the next loop round.

I've got an idea for some code, but I'm not entirely sure how to execute it, I've been fiddling around with the fill_gradient, but I'm not sure how to progress it further. What I'm trying to achieve is when the prop shield is moved from right to left the pixels change into a new colour, or rainbow one direction and then the inverse rainbow the opposite direction.

Here's a shortened down version of the code I'm playing about with, does anyone have any ideas how to progress this? Or has done something similar before? Any heads up would be greatly appreciated!

Code:
void fillGradient(float pitch, float roll) {

    

  fill_gradient(leds, 0, CHSV(pitch,255,255), NUM_LEDS, CHSV(roll,255,255), SHORTEST_HUES); 

   FastLED.show();


}



Here's the full load of rambling if anyone's interested and has a strip of LEDs and a prop shield knocking around, apologies for being very hacky:
Code:
// Full orientation sensing using NXP's advanced sensor fusion algorithm.
//
// You *must* perform a magnetic calibration before this code will work.
//
// To view this data, use the Arduino Serial Monitor to watch the
// scrolling angles, or run the OrientationVisualiser example in Processing.

#include <NXPMotionSense.h>
#include <Wire.h>
#include <EEPROM.h>
#include <FastLED.h>
#define DATA_PIN     11  // output pin of Teensy to input pin of prop shield
// connect the data line of the LED strip to the LED_DAT output pin of the prop shield
#define NUM_LEDS    10  // dot 0-143

CRGB leds[NUM_LEDS];

uint8_t gHue = 0;

NXPMotionSense imu;
NXPSensorFusion filter;

void setup() {
  Serial.begin(9600);
  imu.begin();
  filter.begin(100);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); // power strip from USB port

  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);  // enable access to LEDs
}

void loop() {
  float ax, ay, az;
  float gx, gy, gz;
  float mx, my, mz;
  float roll, pitch, heading;

  if (imu.available()) {
    // Read the motion sensors
    imu.readMotionSensor(ax, ay, az, gx, gy, gz, mx, my, mz);

    // Update the SensorFusion filter
    filter.update(gx, gy, gz, ax, ay, az, mx, my, mz);

    // print the heading, pitch and roll
    roll = filter.getRoll();
    pitch = filter.getPitch();
    heading = filter.getYaw();
    //nxp2HSV (roll, pitch, heading);
    //rainbow(pitch, heading);
    //FastLED.show(); 

    //nxp2Pos(roll,pitch, heading);
    fillGradient(pitch,roll);
    EVERY_N_MILLISECONDS( 20 ) { gHue++; }
   // FastLED.clear(); 
    //rainbowInverse(pitch, heading);
    //FastLED.clear(); 
  //  FastLED.show(); 
  }
}

void rainbow(float pitch, float heading)
{

  byte pitch2byte = map(pitch, 0,180, 0, NUM_LEDS);
  
  fill_rainbow( leds, pitch2byte, gHue, 255/NUM_LEDS);
 
}

void rainbowInverse(float pitch, float heading)
{

  byte pitch2byte = map(pitch, 0,180, NUM_LEDS, 0);
  
  fill_rainbow( leds, pitch2byte, gHue, 255/NUM_LEDS);
 
}

void nxp2Pos(float roll, float pitch, float heading)
{

   byte pitchMap = map(pitch, 0,170, 0, NUM_LEDS);

   leds[pitchMap].setRGB (255,255,0);
   FastLED.show(); 
   leds[pitchMap].setRGB (0,0,0);
//  for (int i = 0; i > NUM_LEDS; i++)
//  { 
//     leds[i].setRGB (255,255,255);
////      if (i = !pitchMap)
////      {
////     
////       
////       //fill_rainbow( leds, NUM_LEDS, gHue, 255/NUM_LEDS);
////      }
//fill_rainbow( leds, NUM_LEDS, gHue, 255/NUM_LEDS);
     FastLED.show(); 
 // }

}


void nxp2HSV (float roll, float pitch, float heading)
{
  byte rollByte = abs(roll);
  byte pitchByte = abs(pitch);
  byte headingByte = map(heading, 0,170, 0, 255);

    Serial.print("Orientation: ");
    Serial.print(roll);
    Serial.print(" ");
    Serial.print(pitch);
    Serial.print(" ");
    Serial.println(heading);
  
  setColorHSV(pitch, 255 , 255);
  FastLED.show();
  
}

void setColorHSV(byte h, byte s, byte v) {
  // create a new HSV color
  CHSV color = CHSV(h, s, v);

  // use FastLED to set the color of all LEDs in the strip to the same color
  fill_solid(leds, NUM_LEDS, color);
}


void oldRainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<NUM_LEDS; i++) {
      leds[i].setHSV (Wheel((i+j) & 255), 255, 255);
       
    }
   FastLED.show();
    delay(wait);
  }
}

void fillGradient(float pitch, float roll) {

    

  fill_gradient(leds, 0, CHSV(pitch,255,255), NUM_LEDS, CHSV(roll,255,255), SHORTEST_HUES); 

   FastLED.show();


}



 Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return (255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return (0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return (WheelPos * 3, 255 - WheelPos * 3, 0);
}
 
Ok apologies for lack of any visual context in the last post, here's roughly what I'm trying to achieve:


It's my take on the nano leaf with a wearable twist, comically named u(micro or mu) Root :p

They're cool with just the rainbow mode from the neopixel library, but wanted to step it up using the propshield and have them respond to some sort of movement influence. Mainly because I kept on getting feedback along the lines of "What do they do"... Well they're a light, they don't do anything (yet).

I'm really inspired by this video:

But sadly no source to look/learn from. Also I think the glasses work best when all LEDs are on at the same time, doesn't look as good when only some of the LEDs are switched on.


So maybe a better reworded question would be, I have an orientation range from -180 to 180, how would you personally go about mapping in a meaningful way orientation to colour change? There's also limits on movement to how far you can move your head as well, so a direct mapping (using map) may not be the best way as in practice you're not able to get to some of the positions whilst wearing the glasses, for instance 99% of the time users may not be upside down, so you'd never see the colour it was mapped to!

I would love to achieve some sort of Seesaw type effect, when the user tips one way the LEDs wipe over into a different colour and visa versa, but I don't know where to begin writing the function, should it look like a for loop, or should I be testing whether the orientation has gone beyond a threshold value and then just trigger an animation function, which I think would look clunky and predictable. Or should I be checking rate of change and use that value for a delay so an animation could happen slower or quicker?

Apologies for the amount of words, and think I may have posted in the wrong section, maybe the technical part would have been better, but for fear of double posting I'll just leave this here. Sorry and thank you in advance!
 
ok so random thoughts on how to go about making a seesaw visualiser with the prop shield.

Work out which direction the orientation is traveling by making an array of a certain window size say 8 or 16 ints, fill with a rounded off reading from one of the axis of orientation e.g. pitch, run a for loop on the array to see if the value is increasing or decreasing to tell if going one way or the other, clear array after calculation and refill. Use the direction inferred from the array calculation to change between two for loops one going ++ and one going - - for changing the colour starting from different ends of the led strip, maybe use a delay in the for loop and map rate of change (speed/velocity) to the delay value to get a certain change in animation speed?

Does that sound like it makes sense? I've never played around with programming/reading an IMU before, so this is new grounds for me! Maybe a little ambitious for a first project!

EDIT, here's my latest test with the array checking descending/ascending, all very hacky and doesn't seem like it's working at all!
Code:
// Full orientation sensing using NXP's advanced sensor fusion algorithm.
//
// You *must* perform a magnetic calibration before this code will work.
//
// To view this data, use the Arduino Serial Monitor to watch the
// scrolling angles, or run the OrientationVisualiser example in Processing.

#include <NXPMotionSense.h>
#include <Wire.h>
#include <EEPROM.h>
#include <FastLED.h>
#define DATA_PIN     11  // output pin of Teensy to input pin of prop shield
// connect the data line of the LED strip to the LED_DAT output pin of the prop shield
#define NUM_LEDS    20  // dot 0-143

CRGB leds[NUM_LEDS];

uint8_t gHue = 0;

NXPMotionSense imu;
NXPSensorFusion filter;

int windowSize = 16;
int directionArray[16];

void setup() {
  Serial.begin(9600);
  imu.begin();
  filter.begin(100);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); // power strip from USB port
  FastLED.setBrightness(100);
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);  // enable access to LEDs
}

void loop() {
  float ax, ay, az;
  float gx, gy, gz;
  float mx, my, mz;
  float roll, pitch, heading;

  if (imu.available()) {
    // Read the motion sensors
    imu.readMotionSensor(ax, ay, az, gx, gy, gz, mx, my, mz);

    // Update the SensorFusion filter
    filter.update(gx, gy, gz, ax, ay, az, mx, my, mz);

    // print the heading, pitch and roll
    roll = filter.getRoll();
    pitch = filter.getPitch();
    heading = filter.getYaw();

    for (int i = 0; i < windowSize; i++)
    { // I have no idea what I'm doing. Should I have initialised the array or something?
      directionArray[i] = roll;
      Serial.println(directionArray[i]);
      if (i == windowSize-1)
      {
        if (isSorted(false) == false);
       {
       Serial.println("descending");
       // would trigger animation function here if this worked
       }

       if (isSorted(false) == true);
       {
       Serial.println("ascending");
       // would trigger animation function here if this worked
       }
       memset(directionArray, 0, windowSize);
      }
    }
    
    FastLED.show(); 

    //nxp2Pos(roll,pitch, heading);
    fillGradient(pitch,roll);
    EVERY_N_MILLISECONDS( 20 ) { gHue++; }
    FastLED.clear(); 
  }
}

 bool isSorted(bool ascending) {
    for (int i = 1; i < windowSize; i++) {
        if (directionArray[i-1] == directionArray[i]) {
            continue;
        }
        if ((directionArray[i-1] > directionArray[i]) == ascending) {
            return false;
        }
    }
    return true;
}

void rainbow(float pitch, float heading)
{

  byte pitch2byte = floatMap(pitch, -90,90, 0, NUM_LEDS);
  
  fill_rainbow( leds, pitch2byte, gHue, 255/NUM_LEDS);
 
}

void nxp2Pos(float roll, float pitch, float heading)
{
   byte pitchMap = floatMap(pitch, -90,90, 0, NUM_LEDS);

   leds[pitchMap].setRGB (255,255,0);
   FastLED.show(); 
   leds[pitchMap].setRGB (0,0,0);
     FastLED.show(); 
}


void nxp2HSV (float roll, float pitch, float heading)
{
  byte rollByte = abs(roll);
  byte pitchByte = abs(pitch);
  byte headingByte = floatMap(heading, -180,180, 0, 255);
    Serial.print("Orientation: ");
    Serial.print(roll);
    Serial.print(" ");
    Serial.print(pitch);
    Serial.print(" ");
    Serial.println(heading);  
  setColorHSV(headingByte, 255 , 255);
  FastLED.show();
  
}

void setColorHSV(byte h, byte s, byte v) {
  // create a new HSV color
  CHSV color = CHSV(h, s, v);
  // use FastLED to set the color of all LEDs in the strip to the same color
  fill_solid(leds, NUM_LEDS, color);
}


void oldRainbow(uint8_t wait) 
{ // Quick test to try and make the old rainbow effect from the Neopixel library
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<NUM_LEDS; i++) {
      leds[i].setHSV (Wheel((i+j) & 255), 255, 255);
       
    }
   FastLED.show();
    delay(wait);
  }
}

void fillGradient(float pitch, float roll) {

    byte pitchMap = floatMap(pitch, -180,180, 0, 255); 
    byte rollMap = floatMap(pitch, -180,180, 0, 255);

  fill_gradient(leds, 0, CHSV(pitchMap,255,255), NUM_LEDS, CHSV(rollMap,255,255), SHORTEST_HUES); 
   FastLED.show();
   delay(1);

}

float floatMap(float x, 
               float inMin, 
               float inMax, 
               float outMin, 
               float outMax){

  // Set bounds
  if ( x < inMin ) x = inMin;
  if ( x > inMax ) x = inMax;
  
  return (x - inMin) * (outMax - outMin) / 
          (inMax - inMin) + outMin;
}

 //Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) { // test to get the wheel function back
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return (255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return (0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return (WheelPos * 3, 255 - WheelPos * 3, 0);
}
 
Last edited:
To anyone that maybe interested down the line that wants to make some sort of rainbow spirit level or seesaw type effect, here's my best attempt so far:

I kept thinking while I was making it, "It can't be this simple"

Code:
// Full orientation sensing using NXP's advanced sensor fusion algorithm.
//
// You *must* perform a magnetic calibration before this code will work.
//
// To view this data, use the Arduino Serial Monitor to watch the
// scrolling angles, or run the OrientationVisualiser example in Processing.

#include <NXPMotionSense.h>
#include <Wire.h>
#include <EEPROM.h>
#include <FastLED.h>
#define DATA_PIN     11  // output pin of Teensy to input pin of prop shield
// connect the data line of the LED strip to the LED_DAT output pin of the prop shield
#define NUM_LEDS    20  // dot 0-143

CRGB leds[NUM_LEDS];

NXPMotionSense imu;
NXPSensorFusion filter;

int lastPitch = 0;
int lastRoll= 0;
long previousMillis = 0;        // will store last time LED was updated
long previousMillis2 = 0;
 uint16_t hue = 10;

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1;  
long interval2= 50;  

void setup() {
  Serial.begin(9600);
  imu.begin();
  filter.begin(100);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); // power strip from USB port
  FastLED.setBrightness(100);
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);  // enable access to LEDs

}

void loop() {
  float ax, ay, az;
  float gx, gy, gz;
  float mx, my, mz;
  float roll, pitch, heading;

  if (imu.available()) {
    // Read the motion sensors
    imu.readMotionSensor(ax, ay, az, gx, gy, gz, mx, my, mz);

    // Update the SensorFusion filter
    filter.update(gx, gy, gz, ax, ay, az, mx, my, mz);

    // print the heading, pitch and roll
    roll = filter.getRoll();
    pitch = filter.getPitch();
    heading = filter.getYaw();

  interval2 = map(abs(pitch)/16, 0, 6, 15, 0);
  
  Serial.println(interval2);

  unsigned long currentMillis = millis();
   unsigned long currentMillis2 = millis();
  int i;

    if(currentMillis2 - previousMillis2 > interval2) 
    {
       previousMillis2 = currentMillis2;
      
       if (pitch > 0)
       hue++;
       else if (pitch < 0)
       hue--;
    }
 
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    int intPitch = pitch;   
    int intRoll= roll;
    if (hue < 255*3)
    {
    fill_rainbow( &(leds[0]), NUM_LEDS, hue /*starting hue*/);    
    FastLED.show();  
    }
     }

    if (hue >= 255*3)
    {
      hue = 10;
    }
    i++;

    if (hue <= 0)
    {
      hue = 255;
    }
   
    if (i >= 255)
    {
      i=0;
     }
    }   
}

void forwardWipe(uint32_t c, uint16_t wait)
{
  for(uint16_t i=0; i<NUM_LEDS; i++) {
    leds[i].setHSV (c,255,255);
    FastLED.show(); 
    delay(wait);
  }

}

void backwardsWipe(uint32_t c, uint16_t wait)
{
    for(uint16_t i=NUM_LEDS; i>0; i--) {
    leds[i].setHSV (c,255,255);
    FastLED.show(); 
    delay(wait);
  }

}
 
Status
Not open for further replies.
Back
Top