<img  style="vertical-align: top;" />

Holiday Special 19

Enter Your Holiday Season Project for a Chance to Win Over $20,000 Worth of Prizes and Gifts to Give!

Back to The Project14 homepage <img  />

Project14 Home
Monthly Themes
Monthly Theme Poll

 

LED Firework Suite: Catherine Wheel

 

 

1.    Introduction

About four years ago, for some unknown reason, I designed my first LED firework effect (which ran using a bespoke PCB and Microchip microcontroller). This initial design 'free ran' and looked really nice (IMO) whilst also benefiting from no effort to setup the display.

 

 

I have been making complementary effects each year since: the following year I was still intrigued with choreographing my lights and played around with Vixen3 and making use of the Raspberry Pi to extract my UDP streamed data and display it on some WS2812 RGB LEDs. When that project worked it was actually awesome, but I had issues with the home router, it slowed everything down, I started to loose WiFi, one of the RPi broke, some of the LEDs stopped working and I had to run up my computer just to display them....I'm sure that feeling is quite familiar to many of us when you start going backwards quicker than the progress being made. I still quite liked the simplicity of the initial LED fireworks just requiring 5v. So I went back to that idea.

 

Last year I added some other designs including a LED tail to one of the units where the 'rocket' would go up before the firework exploded. I also made a temporary Roman Candle and that also looked great as it gave that stepped shadow effect on nearby objects in one of several prime colours. But what I really still wanted was a Catherine Wheel (the type of firework that rotates on the spot) but my attempts to coil strings of LEDs just looked like a mess. That year was soon over and I had to park my aspiration of making a Catherine Wheel effect.... but decided to have a fresh go this year.

The central bosses on the old designs consisted of plywood and being left outside in all weathers for several weeks or wind and rain had a negative effect as they started to fall apart. Last year I made a new central boss using aluminium and a hot melt glue; that stood up well.

 

2.    Previous Attempt at Catherine Wheel

I somehow thought I could get this effect by coiling a string of WS2812 LEDs around an old bicycle wheel again and again. I never actually filmed what that looked like and that was probably a good job as it didn't remind me at all of the fireworks I had seen in displays. For that year I parked the idea and decided to re-think it later on.

 

3.    New Design: After a Rethink

I decided instead of paralleling up the separate arms that I needed to keep the LED individually programmable and in series. That would allow me to make any designs I wanted and what I should now be able to do is implement in code the following effects:

 

  • A starburst as previously
  • A rocket tail and startburst
  • Use the tail to make a Roman candle effect
  • The elusive Catherine Wheel I eagerly seeked
  • Some sort of "after effect" with flashes to indicate sparking of particles
  • A 'hidden' extra

 

4.    3D Printed Hub

Being the proud owner of a 3D printer now I was able to design and print a hub for this new LED Firework effect. I used some old aluminium section from a glasshouse to make the spokes which slotted into the 3D printed hub. After some investigation of the lengths of aluminium I had available and what might be a good spacing of the LEDs I decided to make each arm 450mm long plus a 40mm angled end that fitted into the hub. This would result in a reasonably large structure of approximately 1m across: this might seem quite large for a house decoration but when viewed from afar allows the units to be seen still. The downside of such a large design will come after Christmas when I am trying to store it as it will be awkward to manoeuvre into either the shed or into the loft.

 

Each of the aluminium arms were fixed with a small amount of epoxy resin and some bolts passing through. The top cap had some locating sections also to help keep the design locked together. I'm hopeful that this will be both weatherproof and strong - the latter is important as anyone who puts up outside decorations knows the wind and rain often can wreak a great display.

 

{gallery} My Gallery Title
// Xmas Firework Hub - for 10 Al. angles

// 14rhb
// An Element14 Project
// v1   First print
// v2   Minor adjustments as there were two rotate commands on each part.

echo(version=version());

color("Red") section();

//rotate([0,0,0])
//translate([radius*2.5,0,0])
//translate([0,0,height*1.05])
//cap();


//Variables for user modification
radius=80/2;  
height=15;


//arm metal work - using L shaped metalwork for the arms of this effect
angle_thickness=2;
angle_height=8.5;    //height of the aluminuium section upstand
angle_embed=30;     //amount to glue into the hub
angle_width=21;      //width of the top half

bolt_rad=4.4/2;
bolt_percentage1=85;
bolt_percentage2=45;
bolt_offset=5;

fixing_hole_rad=6.5/2;

//Cap parameters
cap_height=10;

module section()
{
    $fn=100;

    difference(){
        translate([0,0,0])
        cylinder(height, radius, radius, center=true);    
    

        //arm slots
        
        for (i = [0:36:324]){
            //rotate([0,0,22.5])
            rotate([0,0,i])
            translate([-angle_thickness/2,radius-angle_embed,(height/2)-(angle_height)]) 
            cube([angle_thickness,angle_embed,angle_height*1.05], center=false);
        
    }

        //bolt holes
        for (i = [0:36:324]){
            //rotate([0,0,22.5])
            rotate([0,0,i])
            translate([bolt_offset,(bolt_percentage1/100)*radius,0]) 
            cylinder(height*2, bolt_rad, bolt_rad, center=true);
        }         

        //bolt holes
        for (i = [0:36:324]){
            //rotate([0,0,22.5])
            rotate([0,0,i])
            translate([bolt_offset,(bolt_percentage2/100)*radius,0]) 
            cylinder(height*2, bolt_rad, bolt_rad, center=true);
        }  

        //main fixing hole
        rotate([0,0,0])
        translate([0,0,0]) 
        cylinder(height*2, fixing_hole_rad, fixing_hole_rad, center=true);  
        
    }//difference
  
}


module cap()
{
    $fn=100;

    difference(){
        translate([0,0,0])
        cylinder(cap_height, radius, radius, center=true);    

        //arm recesses
        color("Green") 
        for (i = [0:36:324]){
            //rotate([0,0,22.5])
            rotate([0,0,i])
            translate([(angle_width/2)-angle_thickness/2,radius-(angle_embed/2),(angle_thickness*1.5)-(height/2)]) 
            cube([angle_width,angle_embed,angle_thickness], center=true);
        }
        
        //bolt holes
        for (i = [0:36:324]){
//rotate([0,0,22.5])
            rotate([0,0,i])
            translate([bolt_offset,(bolt_percentage1/100)*radius,0]) 
            cylinder(cap_height*2, bolt_rad, bolt_rad, center=true);
        }         

        //bolt holes
        for (i = [0:36:324]){
            //rotate([0,0,22.5])
            rotate([0,0,i])
            translate([bolt_offset,(bolt_percentage2/100)*radius,0]) 
            cylinder(cap_height*2, bolt_rad, bolt_rad, center=true);
        }  

        //main fixing hole
        rotate([0,0,0])
        translate([0,0,0]) 
        cylinder(height*2, fixing_hole_rad, fixing_hole_rad, center=true);         
    }//difference
  
}

 

 

The ends of each aluminium arm required some cutting and filing to allow them to come together properly.

Close-up showing the securing cap and through holes for bolting it together.

The 1m diameter design prior to adding the securing cap in the middle.

5.    Manufacturer

I started off by purchasing a 100x WS2812 wafer and some new electrical wire and as I laid it out on my bench I started to wonder what I had let myself in for.

 

I couldn't find the assembly jig from my last designs so had to make a new one. It is simply a piece of scrap wood suitably marked to assist in placing of the LEDs and cutting wires to length. The jig was for each arm (which has ten LED units). I also added a data line from the last LED back down the length: this would be to pass the data into the next arm. My previous designs has all the LED arms in parallel.

[Lots of individual wires were cut, twisted, tinned and connected to the LED dies: approx 270 ! ]

 

I managed to get the assembly time down to under one hour (per 10x LED string). Unfortunately my automatic wire strippers didn't allow me to strip the insulation off these fine wires and so I carefully nicked each with a sharp craft knife: a quick roll was enough to allow the insulation to be removed with side cutters. The ends were then twisted and tinned. I used some Blutak to hold the LED into each recess.

 

I tinned all 6 pads on the LED units and then started to add the wires using a pair of tweezers. I just had to knuckle down and get on with the build but after some concentrated effort my pot of LEDs was starting to get empty:

[The final ten LEDs to be connected. I also achieved a 100% component success]

 

Once assembled I used a simple Arduino program to test them out. I had a 100% success rate with all 100 LEDs working correctly.

 

I then moved on to waterproofing the strings...

 

6.    Waterproofing

This has been my real headache on previous designs. This time around I took no chances and the initial strands of LED were given a going over with rapid (4 min) epoxy resin. I then used hot melt glue to attach them to the aluminium spars, with a good capping over the entire LED with more molten hot-melt glue. The final clear silicon diffusers will also help to add waterproofing if applied in a generous way.

 

Epoxy resin is not very nice and inevitable gets on things it shouldn't - I tried wearing gloves but the intricate work made them difficult. I then decided to stop wearing gloves and all was fine for the first few arms....then I managed to get my fingers in it. Then I dropped a string into one of the two parts of the glue (so it never set but stayed nicely sticky). I did find a household cleaner than seemed to get my hands clean though - not fully endorsed but I used:

[Epoxy resin to waterproof. The Blutak is to hold the wires in shape while the epoxy sets.]

 

Using Blutak again to hold the wires neatly while adding epoxy resin. Note even  the quick set has dripped slightly.

What I might like to build for future lights is a metal die that can be heated to melt the glue fully around each LED and which can then be cooled by passing water through to set the unit, before popping the LED/cables out.

 

From previous years I discovered silicone sealant on its own was not very good. Many types of silicone give off acetic acid as they cure and that attacks the circuit components and small PCB. Also I think there are small holes around the wiring which allow water to track along and onto the board.

 

7.    Driver Circuit

For this project I moved away from the bespoke PIC microcontroller to the more flexible Arduino and decided to use one of my MKR devices. The WS2812 LEDs are very easy to program with the Adafruit Neopixel library although I do prefer the tighter control I had on my previous designs by using the Configurable Logic Cell (CLC) of the PIC devices to generate the WS2812 protocol.

 

8.    Coding It up

Initial coding went well and I soon had some nice simple effects such as 'clock hands' and multiple colour fades (the latter is kept in the final video shown below). For the actual Catherine Wheel effect I essentially coded up a 'burning' arm that would get 'rotated' in the software to calculate the dimmer values for each of the other arms. In one push all LEDs are updated from the bitmap (an array of RGB values).

 

As the code became more complex strange things started to happen and the LEDs flickered and randomly lit up ruining the effect.

 

I spent day upon day over the festive period trying to recode them and debug: I found the Arduino IDE really unhelpful in that respect and also it started to get fiddly with uploading as I had to double-click the reset to force an upload. I considered changing over to a PIC microcontroller but then decided to push onwards as I hoped I would soon solve the issues....I didn't !

 

Instead I had to compromise the Catherine Wheel effect. This included keeping to a single LED colour while the effect burnt inwards.

 

The speed of rotation, fading between the rotor arms, the fade inwards all change in realtime as the effect carries on.

 

I'll get my code tidied up, as it is a complete mess having tried out all sorts of ideas to fix the issues, and post it here should anyone like to build their own LED Firework.

 

9.    Final Effect

I'm still tweaking the code and have mounted the unit inside facing out to the street to complement the existing 'LED fireworks'. This is the current cycle of settings which are interesting. It looks slightly better in reality (IMO) than the camera has managed to capture - probably due to frame rates. Anyway hopefully it gives an idea of what it can do.

 

10.    An Extra Effect

As this project was meant to be entered into the Holiday Special it needed to show warmth and giving - hopefully the light display does add that to anyone passing by who sees it. I know the previous ones have had families stopping to watch and the children pointing. But to add that extra bit I've coded in a little extra at startup for my wife, who has had to put up with me making stuff all these years instead of painting the house or tidying the garden :-)

[A Little Extra]

 

11.    Summary

This is my last project of 2019; although I'll likely carry on tweaking the code for some time and may port the entire project to a PIC microcontroller (to allow better debug operation).

 

I also left the tail off for now as the design was to be used indoors initially during development. Had I perfected it then the addition of the tail for the rocket effect would have been fairly simple coding which drew upon other code functions.

 

Whilst on the subject of spinning fireworks, I found this video on Youtube:

[External Website Link: https://www.youtube.com/watch?v=1V8h6bpwBG4  ]

 

12.    Code Changes [dated: 09 Jan 2020]

Following the new year I continued to try to get my code to work as I had intended: almost everything I did using the Arduino resulted in stable results quickly deteriorating into a flicker. All LEDs with a single colour appeared to behave but as soon as some changed then I ran a higher risk of others randomly changing colour. I was convinced in the end that the switching currents were causing the data line to glitch and that was the issue rather than the actual software, which I had been concentrating on. However looking with an oscilloscope didn't reveal anything unusual in the general pulse shape to the LEDs, unless of course that changed further along the data line. The bad news there was that I had glued the whole setup together and was reluctant to start scraping away and removing cable ties.

 

The longer term inspection of the data was a bit trickier and this could just be a facet of the sample time on my scope but did appear to show distinctive bursts of data lasting approx. 3.2ms which could easily be the full 100 bytes of data being sent....[a rough calculation: bits are 1.24us, 9.92us per byte, 29.7us per LED, 2.9ms per 100 LEDs ]....which it obviously is. Unfortunately my scope doesn't allow an easy way to zoom into that data which still having reference to the main trace. I've grabbed some screen shots that might be showing there is an output issue with the Arduino whilst also clearly showing those 3.2ms bursts. I'll post another update when I work out what is going on.

 

{gallery} WS2812_DataLine

Below is my rather long and messy Arduino code for anyone wanting to make a similar effect.

 

/* Code by Rod 14rhb

Aimed for Arduino MKR1300/1010/1000 etc

I am making a new LED Firework for Christmas 2019, and detailing in the Element14 Community as a way of spreading good wishes
and I also hope other members may have a go at copying and improving on this project.

The final arrangement will randomly select between effects but the main difference is that each arm is in series and therefore each LED can be programmed individually.

The initial effects are:
  Starbust as before
  Rocket tail and starburst
  Use the tail as a Roman Candle
  Scintilattion After Effect
  Catherine Wheel

*/

#include <Adafruit_NeoPixel.h>

#define WS2812_PIN 6

#define numInTail 50      //number of pixels in the tail section
#define numInArm  10      //number of pixels in each arm
#define numArms   10      //the actual number of arms in the structure

#define tailFeedIn 4
#define armFeedIn 16
#define PIXELCOUNT 100    //should be = [ numInTail + (numInArm*numArms) ]
#define NUM_LEDS 100 //not used?

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXELCOUNT, WS2812_PIN, NEO_GRB + NEO_KHZ800);

//Setup the monitor so I can debug easily ! Debug on my MKR1300 is very intermittent, often not working
#define BAUDRATE (9600)

struct pixel {
  uint8_t red;
  uint8_t green;
  uint8_t blue;
};

struct pixel pixelArrayTail[numInTail + tailFeedIn];
struct pixel pixelArrayArm[numInArm + armFeedIn];
struct pixel pixelHead[numInArm*numArms];

void FSR_Firework(void);
void FSR_Roman_Candle(void);
void FSR_Catherine_Wheel(void);
void FSR_Test_Mode(void);
void FSR_Fades(void);
void delay_of_1s(void);
void SnowSparkle(byte red, byte green, byte blue, int SparkleDelay, int SpeedDelay);
void FSR_Meteor(void);
void lovinTheHeart(void);
void FSR_Simple(void);

void RunningLights(byte red, byte green, byte blue, int WaveDelay);

enum RelayState { RELAY_OFF = HIGH, RELAY_ON = LOW };

enum  mode_FSR {
  PreDelay=1,
  Ignite=2,
  PreDelay2=3,
  Explode=4,
};

enum state_FSR {
  WS2812test=1,
  FireworkFSR=2,
  RomanCandle=3,
  Scintilation=4,
  CatherineWheel=5,
  SnowSparkleFSR=6,
  Fades=7,
  Meteor=8
};
  
enum state_FSR thisMode;
enum mode_FSR currentFSR;

//*******************************************
//*             ARDUINO SETUP
//*******************************************
void setup() {
  
  // SETUP THE FIXED OPERATION MODE
  thisMode=CatherineWheel;
     
  pinMode(WS2812_PIN,OUTPUT); 
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

  Serial.begin(9600);
  Serial.print("Welcome to Firework Controller by 14rhb");
  }

//*******************************************
//*             ARDUINO MAIN LOOP
//*******************************************
void loop() {

  //*******************************************
  //*             Mode FSR
  //*******************************************
    lovinTheHeart();
    delay_of_1s();
    delay_of_1s();
    delay_of_1s();
    delay_of_1s();

  //Run around these prescribed effects, setting the FSR to the next desired programm effect: could also do this randomly
  while(1){

    while(1){
      FSR_Fades();
      delay_of_1s();
      delay_of_1s();
    }

    //Firework
    if (thisMode==FireworkFSR) FSR_Firework();

    //Roman Candle
    else if (thisMode==RomanCandle){
      FSR_Roman_Candle();
      thisMode=CatherineWheel;
    }

    //Catherine Wheel
    else if (thisMode==CatherineWheel){
      FSR_Catherine_Wheel();
      thisMode=Fades;
    }

    //Test Mode
    else if (thisMode==WS2812test) FSR_Test_Mode();  

    //All illuminated fading between colours randomly
    else if (thisMode==Fades) {
      FSR_Fades();
      thisMode=Meteor;
    }

    //Snow Sparkle Effect
    else if (thisMode==SnowSparkleFSR) SnowSparkle(0x10, 0x10, 0x10, 20, random(100,1000));

    //Meteor Effect like on Star Wars Millenium Falcon
    else if (thisMode==Meteor) {
      FSR_Meteor();
      thisMode=CatherineWheel;
    }

    //Delay between effects repeating
    delay_of_1s();
    delay_of_1s();
      
  }//while(1) Loop
}//end of main Arduino Loop

//*******************************************
//*             Show Strip
//*******************************************
void showStrip() {

 #ifdef ADAFRUIT_NEOPIXEL_H

   // NeoPixel
   strip.show();
 #endif

 #ifndef ADAFRUIT_NEOPIXEL_H

   // FastLED
   FastLED.show();
 #endif
  return;
}

//*******************************************
//*             Set Pixel
//*******************************************
void setPixel(int Pixel, byte red, byte green, byte blue) {

 #ifdef ADAFRUIT_NEOPIXEL_H

   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));

 #endif

 #ifndef ADAFRUIT_NEOPIXEL_H

   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;

 #endif
 return;
}

//*******************************************
//*             Set All
//*******************************************
void setAll(byte red, byte green, byte blue) {

  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue); 
  }

  showStrip();
  return;
}

//*******************************************
//*             Chase
//*******************************************
//Uses numpixels to create effect
static void chase(uint32_t c) {
  for(uint16_t i=0; i<strip.numPixels()+4; i++) {
      strip.setPixelColor(i  , c); // Draw new pixel
      strip.setPixelColor(i-4, 0); // Erase pixel a few steps back
      strip.show();
      delay(20);
  }
  return;
}

//for preset arms of 10x pixels
static void chase10(uint32_t c) {
  for(uint16_t i=0; i<14; i++) {
      strip.setPixelColor(i  , c); // Draw new pixel
      strip.setPixelColor(i-4, 0); // Erase pixel a few steps back
      strip.show();
  }
  return;
}

//*******************************************
//*             Firework
//*******************************************
void FSR_Firework(){
  
    Serial.println("Running FSR Firework Effect");

    long randNumber;
    currentFSR=PreDelay;
    
    //switch(currentFSR){
  
      //the wait before it gets lit
        randNumber = random(500, 5000);
        delay(randNumber);
        Serial.println("Pre-delay");
        currentFSR=Ignite;
  
      //the rocket going up into the sky
      //the effect where the rocket flies into the sky, slows and dims
        Serial.println("Ignite");
        FireworkShootUp();
        currentFSR=PreDelay2;

  
      //a pause before it explodes
        Serial.println("Predelay2");
        randNumber = random(100, 1000);
        Serial.println("r");
        delay(randNumber);
        Serial.println("x");
        currentFSR=Explode;
        Serial.println("xx");
  
      //the explosion
        Serial.println("Explode");
        FireworkExplode();
        currentFSR=PreDelay;

    return;
}

//*******************************************
//*             Roman Candle
//*******************************************
void FSR_Roman_Candle(void){

  //This was intended for the tail part but gives an interesting effect to the head also
    Serial.println("Running FSR Roman Candle Effect");
  
      //run the Roman Candle Effect
      chase(strip.Color(255, 0, 0)); // Red
      delay(10);
      chase(strip.Color(0, 255, 0)); // Green
      delay(10);
      chase(strip.Color(0, 0, 255)); // Blue 
      delay(10);
    
      chase(strip.Color(255, 255, 0));
      delay(10);
      chase(strip.Color(0, 255, 255));
      delay(10);
      chase(strip.Color(255, 0, 255));
      delay(10);

      return;
}

//*******************************************
//*             Catherine Wheel
//*******************************************
void FSR_Catherine_Wheel(void){
  
  Serial.println("Running FSR Catherine Wheel Effect");
  
  int stageCount=0;
  struct pixel pixelRotor[numInArm];        //this holds pattern for the current burning arm
  struct pixel pixelTemp[numInArm];           // to temp hold a faded pattern before copying to the main long array?
  
  struct pixel pixelFeedIn[(numInArm*3)];

  pixelFeedIn[29]={0,0,0};
  pixelFeedIn[28]={0,0,0};
  pixelFeedIn[27]={0,0,0};
  pixelFeedIn[26]={0,0,0};
  pixelFeedIn[25]={0,0,0};
  pixelFeedIn[24]={0,0,0};
  pixelFeedIn[23]={0,0,0};
  pixelFeedIn[22]={0,0,0};
  pixelFeedIn[21]={0,0,0};
  pixelFeedIn[20]={0,0,0};

  //Select a random colour for the Catherine Wheel
  int ranColour = random (1,3);
  if (ranColour==1){
    pixelFeedIn[19]={255,0,0};
    pixelFeedIn[18]={50,0,0};
    pixelFeedIn[17]={45,0,0};
    pixelFeedIn[16]={15,0,0};
    pixelFeedIn[15]={30,0,0};
    pixelFeedIn[14]={10,0,0};
    pixelFeedIn[13]={10,0,0};
    pixelFeedIn[12]={8,0,0};
    pixelFeedIn[11]={6,0,0};
    pixelFeedIn[10]={2,0,0};
  }
  else if (ranColour==2){
    pixelFeedIn[19]={0,255,0};
    pixelFeedIn[18]={0,50,0};
    pixelFeedIn[17]={0,45,0};
    pixelFeedIn[16]={0,40,0};
    pixelFeedIn[15]={0,30,0};
    pixelFeedIn[14]={0,25,0};
    pixelFeedIn[13]={0,20,0};
    pixelFeedIn[12]={0,15,0};
    pixelFeedIn[11]={0,10,0};
    pixelFeedIn[10]={0,5,0};
  }
  else{
    pixelFeedIn[19]={0,0,255};
    pixelFeedIn[18]={0,0,50};
    pixelFeedIn[17]={0,0,45};
    pixelFeedIn[16]={0,0,40};
    pixelFeedIn[15]={0,0,35};
    pixelFeedIn[14]={0,0,30};
    pixelFeedIn[13]={0,0,25};
    pixelFeedIn[12]={0,0,20};
    pixelFeedIn[11]={0,0,10};
    pixelFeedIn[10]={0,0,5};
  }
  
  pixelFeedIn[9]={0,0,0};
  pixelFeedIn[8]={0,0,0};
  pixelFeedIn[7]={0,0,0};
  pixelFeedIn[6]={0,0,0};
  pixelFeedIn[5]={0,0,0};
  pixelFeedIn[4]={0,0,0};
  pixelFeedIn[3]={0,0,0};
  pixelFeedIn[2]={0,0,0};
  pixelFeedIn[1]={0,0,0};
  pixelFeedIn[0]={0,0,0};
     
    
  uint8_t lr=255;
  uint8_t lg=255;
  uint8_t lb=255;
  int intensity=20;
  int rotorOffset;
  int rotateCounterA=0;
  int rotateCounterB=0; 
  uint8_t actPixel=0;
  uint8_t redFaded;
  uint8_t greenFaded;
  uint8_t blueFaded;
  int finishedEffect=0;
  
  uint8_t fadeRate=0;
  double fadeRate3=0.55;     //a decimal fader value
  
  
  long delay_period=100;

  //for active rotor burning inwards....
  double lr2=0;
  double lg2=0;
  double lb2=0;
  double fadeRate2=0.75;

  uint8_t numRotatesForBurnIn=5;
  uint8_t activeArm;
  
    while(finishedEffect==0){
      for (activeArm=0; activeArm<10; activeArm++){
           
        Serial.println("Active Arm");
        //adust the burning arm (burn in and colour) and set to current position

//              lr=lr+intensity;
//              if (lr>255) lr=0;
//              lg=lg+intensity;
//              if (lg>255) lg=0;
//              lb=lb+intensity;
//              if (lb>255) lb=255;
//
//              double rFadeIn;
//              double gFadeIn;
//              double bFadeIn;


//            BURN IN FADE OUT
//              //currently effects a basic burn inwards, by illuminating the active pixel/ring that has been burnt down to
//              uint8_t pixelVal=9;
//              for (uint8_t counter=0; counter<10; counter++){
//                 pixelRotor[pixelVal]={0,0,0};
//                  if (pixelVal>actPixel){
//                    //pixelRotor[pixelVal]={0,0,0};                    
//                  }
//
//                  else if (pixelVal==actPixel){
//                    pixelRotor[pixelVal]={lr,lg,lb};
//
////                    lr2=(double)lr;
////                    lg2=(double)lg;
////                    lb2=(double)lb;
////
////                    lr2=lr2*fadeRate2;
////                    lg2=lg2*fadeRate2;
////                    lb2=lb2*fadeRate2;
//                                        
//                  }
//
//                  else { 
//                    //pixelRotor[pixelVal].red = (uint8_t)lr2;
//                    //pixelRotor[pixelVal].green = (uint8_t)lg2;
//                    //pixelRotor[pixelVal].blue = (uint8_t)lb2;
//                    //pixelRotor[pixelVal]={(uint8_t)lr2,(uint8_t)lg2,(uint8_t)lb2};
//                    
////                    lr2=lr2*fadeRate2;
////                    lg2=lg2*fadeRate2;
////                    lb2=lb2*fadeRate2;
//                                         
//                  }
//
//                  pixelVal--;
//                
//              }                           

//pixelRotor[9]={0,0,0};
//pixelRotor[8]={0,0,0};
//pixelRotor[7]={255,80,0};
//pixelRotor[6]={0,0,0};
//pixelRotor[5]={0,0,0};
//pixelRotor[4]={255,255,255};
//pixelRotor[3]={0,255,0};
//pixelRotor[2]={255,40,0};
//pixelRotor[1]={20,255,20};
//pixelRotor[0]={0,0,0};

//pixelRotor[9]={255,255,0};
//pixelRotor[8]={60,255,0};
//pixelRotor[7]={255,80,0};
//pixelRotor[6]={255,255,0};
//pixelRotor[5]={255,255,0};
//pixelRotor[4]={255,255,255};
//pixelRotor[3]={0,255,0};
//pixelRotor[2]={255,40,0};
//pixelRotor[1]={20,255,20};
//pixelRotor[0]={255,255,0};

////the following also glitches if I add in all stages or is it the values I'm using????
//if (actPixel==0){
//  pixelRotor[9]={0,0,0};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,0};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,255};
//}
//
//else if (actPixel==1){
//  pixelRotor[9]={0,0,0};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,0};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,40,0};
//  pixelRotor[3]={0,40,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,255};
//  pixelRotor[0]={0,0,50};
//}
//
//else if (actPixel==2){
//  pixelRotor[9]={0,0,0};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,0};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={255,255,255};
//  pixelRotor[1]={50,50,50};
//  pixelRotor[0]={10,10,10};
//}
//
//else if (actPixel==3){
//  pixelRotor[9]={5,5,5};
//  pixelRotor[8]={5,5,5};
//  pixelRotor[7]={5,5,5};
//  pixelRotor[6]={5,5,5};
//  pixelRotor[5]={5,5,5}; 
//  pixelRotor[4]={5,5,5};
//  pixelRotor[3]={255,255,255};
//  pixelRotor[2]={50,50,50};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
//else if (actPixel==4){
//  pixelRotor[9]={0,0,5};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,0};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={255,255,255};
//  pixelRotor[3]={0,40,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={40,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
//else if (actPixel==5){
////  pixelRotor[9]={0,0,0};
////  pixelRotor[8]={0,0,0};
////  pixelRotor[7]={0,0,0};
////  pixelRotor[6]={0,0,0};
////  pixelRotor[5]={255,255,255};
////  pixelRotor[4]={200,200,150};
////  pixelRotor[3]={150,150,100};
////  pixelRotor[2]={100,100,80};
////  pixelRotor[1]={80,80,50};
////  pixelRotor[0]={50,50,0};
//
//  pixelRotor[9]={0,0,0};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,0};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={255,255,255};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,40,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={40,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
////
//else if (actPixel==6){
//  pixelRotor[9]={0,0,0};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,0};
//  pixelRotor[6]={255,255,255};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
////
//else if (actPixel==7){
//  pixelRotor[9]={0,0,0};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={255,255,255};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={50,50,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
////
//else if (actPixel==8){
//  pixelRotor[9]={0,0,0};
//  pixelRotor[8]={255,255,255};
//  pixelRotor[7]={200,200,150};
//  pixelRotor[6]={150,150,100};
//  pixelRotor[5]={100,100,80};
//  pixelRotor[4]={80,80,50};
//  pixelRotor[3]={50,50,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
////
//else if (actPixel==9){
//  pixelRotor[9]={200,200,150};
//  pixelRotor[8]={150,150,100};
//  pixelRotor[7]={100,100,80};
//  pixelRotor[6]={80,80,50};
//  pixelRotor[5]={50,50,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
//else if (actPixel==10){
//  pixelRotor[9]={150,150,100};
//  pixelRotor[8]={100,100,80};
//  pixelRotor[7]={80,80,50};
//  pixelRotor[6]={50,50,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
//else if (actPixel==11){
//  pixelRotor[9]={100,100,80};
//  pixelRotor[8]={80,80,50};
//  pixelRotor[7]={50,50,0};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
//else if (actPixel==12){
//  pixelRotor[9]={80,80,50};
//  pixelRotor[8]={50,50,0};
//  pixelRotor[7]={90,90,90};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0};
//}
////
//else if (actPixel==13){
//  pixelRotor[9]={50,50,0};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,0};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,0,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={0,0,0}; 
//}
////
//else {
//  pixelRotor[9]={255,50,255};
//  pixelRotor[8]={0,0,0};
//  pixelRotor[7]={0,0,255};
//  pixelRotor[6]={0,0,0};
//  pixelRotor[5]={0,0,0};
//  pixelRotor[4]={0,255,0};
//  pixelRotor[3]={0,0,0};
//  pixelRotor[2]={0,0,0};
//  pixelRotor[1]={0,0,0};
//  pixelRotor[0]={255,0,0}; 
//}

  pixelRotor[9]=pixelFeedIn[(28-actPixel)];
  pixelRotor[8]=pixelFeedIn[(27-actPixel)];
  pixelRotor[7]=pixelFeedIn[(26-actPixel)];
  pixelRotor[6]=pixelFeedIn[(25-actPixel)];
  pixelRotor[5]=pixelFeedIn[(24-actPixel)];
  pixelRotor[4]=pixelFeedIn[(23-actPixel)];
  pixelRotor[3]=pixelFeedIn[(22-actPixel)];
  pixelRotor[2]=pixelFeedIn[(21-actPixel)];
  pixelRotor[1]=pixelFeedIn[(20-actPixel)];
  pixelRotor[0]=pixelFeedIn[(19-actPixel)];



              // ROTATION FADE OUT
               
              //ROTOR arm moves so move it to the next arm, change it slightly if appropriate by time etc. FADE the previous arm? Always overright the new ACTIVE arm position

              //copy the ROTOR pattern to the final array at the right position
//              if (activeArm>0) rotorOffset=activeArm*10;
//              else rotorOffset=0;
              
              //place rotor image into the final bitmap of pixelHead
              for(uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                //pixelHead[rotorOffset+pixelNum]=pixelRotor[pixelNum];
                //pixelHead[(activeArm*10)+pixelNum]={30,30,100};
                pixelHead[(activeArm*10)+pixelNum]=pixelRotor[pixelNum];
              } 

              //make a copy of the ROTOR, so it can be manipulated and faded across the other arm positions without destroying the active rotor pattern
              for (int cp=0; cp<10; cp++){
                pixelTemp[cp]=pixelRotor[cp];
              }

              uint8_t arm=0;
              uint8_t tRed;
              uint8_t tGreen;
              uint8_t tBlue;

              //arms 9 to 1
              if (activeArm==0){
                arm=9; 
                while (arm>0){
                  fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);
                  for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                    pixelHead[(arm*10)+pixelNum]=pixelTemp[pixelNum];
//                    pixelHead[(arm*10)+pixelNum].red=pixelTemp[pixelNum].red;           //the above line is not the issue !
//                    pixelHead[(arm*10)+pixelNum].green=pixelTemp[pixelNum].green;
//                    pixelHead[(arm*10)+pixelNum].blue=pixelTemp[pixelNum].blue;  
                    //pixelHead[(arm*10)+pixelNum]={200,20,20};       //this works and gives a steady output with no flicker !
                    
                  }//for

                  arm--;
                }              
               }


              else if (activeArm==1){
                  arm=0;
                  fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);
                  for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                    pixelHead[(arm*10)+pixelNum]=pixelTemp[pixelNum]; 
                  }
                
                for (arm=9; arm>activeArm; arm--){
                  fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);
                  for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                    pixelHead[(arm*10)+pixelNum]=pixelTemp[pixelNum]; 
                  }//for
                  
                }


              }//activeArm==1

              else if (activeArm==8){
                  arm=8;      //arm+1 to allow loop to work
                  while(arm>0){
                    fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);                  
                    for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                      pixelHead[((arm-1)*10)+pixelNum]=pixelTemp[pixelNum]; 
                    } 
                    arm--;
                            
                  }
                  
                arm=9;
                fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);
                  for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                    pixelHead[(arm*10)+pixelNum]=pixelTemp[pixelNum]; 
                  }                
              }//activeArm==8
              

              else if (activeArm==9){
                  arm=9;      //actual arm+1 for loop to work
                  while(arm>0){
                    fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);     
                    for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                      pixelHead[((arm-1)*10)+pixelNum]=pixelTemp[pixelNum]; 
                    } 
                    arm--;                    
                  } //while
                }//activeArm==9  

              else {
                  arm=activeArm-1;
                  while(arm>0){
                    fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);
                    for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                      pixelHead[(arm*10)+pixelNum]=pixelTemp[pixelNum]; 
                    }                      
                    arm--;
                  }//while
                
                for (arm=9; arm>(activeArm); arm--){
                  fadeTheArm(pixelTemp, 10, fadeRate, fadeRate3);
                  for (uint8_t pixelNum=0; pixelNum<10; pixelNum++){
                    pixelHead[(arm*10)+pixelNum]=pixelTemp[pixelNum]; 
                  }                 
                }
              } //else
                          
               

              //output effect: ALL LEDS: tail + 10 * arms in a single push of data
              for (uint8_t j=0; j<(numInArm*numArms); j++){
                setPixel(j, pixelHead[j].red,  pixelHead[j].green,  pixelHead[j].blue); 
              }//j
              strip.show();

//      strip.setPixelColor(i  , c); // Draw new pixel
//      strip.setPixelColor(i-4, 0); // Erase pixel a few steps back
//      strip.show();

      
              //pause between each shift of the rotors
              delay_of_x(delay_period); 
      
              //change delay as it steps around depending on the layer burning aka actPixel AND limit rates of spin           
              //slow start up
              if (actPixel<=1){
                delay_period=delay_period-1;
                fadeRate3=0.75;
                if (delay_period<50) delay_period=50;
              } 
              //main burn
              else if((actPixel>1)&&(actPixel<10)){
                delay_period=delay_period-1;
                fadeRate3=0.8;
                if (delay_period<10) delay_period=10;                
              }
              //reducing burn
              else if((actPixel>=10)&&(actPixel<14)){
                delay_period=delay_period+1;
                fadeRate3=0.7;
                numRotatesForBurnIn=2;
                if (delay_period>80) delay_period=80;                
              } 
              //reducing burn
              else if((actPixel>14)&&(actPixel<18)){
                delay_period=delay_period+1;
                fadeRate3=0.9;
                if (delay_period>300) delay_period=300;                
              }                                        
              else {
                delay_period=delay_period+1;
                fadeRate3=0.4;
                if (delay_period>500) delay_period=500;                
              }


//    
//            //Speeds up

//            //Burns down

//    
//            //Slows


//    
//            //Off



                          //it gets here and finally sticks here !!
                         //         setAll(0,255,0);
                         //       delay_of_x(300);  
                         // caused by my arm loop being 1-10 not 0-9 !!!   
        
      }//active arm


      //rotate the active arm on another position...
      rotateCounterA++;
      rotateCounterB++;

      //this creates the burn in where actPixel is the one actually burning aka the layer where 0 is outer and 9 is inner
      if (rotateCounterB>numRotatesForBurnIn) {
        rotateCounterB=0;
        actPixel++;
        if (actPixel>19) {
          actPixel=19;
          finishedEffect=1;
        }

      }

      
    }//layer burn in

    
    return;
}

//*******************************************
//*             TEST WS2812
//*******************************************
void FSR_Test_Mode(void){

  //WS2812 test
  if (thisMode==WS2812test){
    Serial.println("WS2812 test loop");
    uint32_t greenishwhite = strip.Color(0, 64, 0);
    int iRed;
    int iGreen;
    int iBlue;
    int step=2;

    for (int i=0; i<PIXELCOUNT; i++){
      strip.setPixelColor(i, iRed, iGreen, iBlue);    
    }    
    strip.show();

    while(1){

      iRed=0;
      iGreen=255;
      iBlue=0;
      
      for (int i=255; i>0; i=i-1){
        if (iRed>step) iRed=iRed-step;
        if (iGreen>step) iGreen=iGreen-step;
        if (iBlue>step) iBlue=iBlue-step;
        
      for (int i=0; i<PIXELCOUNT; i++){
        strip.setPixelColor(i, iRed, iGreen, iBlue);    
      } 
        strip.show();
        delay(10);
      }
      
  }//while (1)  
}////WS2812 test
}


//*******************************************
//*******************************************
//*   SUBROUTINES FOR THE ABOVE FUNCTIONS
//*******************************************
//*******************************************

//*******************************************
//*             Firework - Shoot Upwards
//*******************************************
//it slows as it goes further up!
void FireworkShootUp(){

double dimFactor = 0.95;

  int shootUpDelay;
  //shootUpDelay = random(10, 50);
  shootUpDelay=20;


    //preload the start N bytes of the array
    pixelArrayTail[0]={30,30,30};
    pixelArrayTail[1]={100,100,100};
    pixelArrayTail[2]={150,150,150};
    pixelArrayTail[3]={255,255,255};
    for (int i=tailFeedIn; i<(numInTail+tailFeedIn); i++){
      pixelArrayTail[i]={0,0,0};   
    }

    //Loop and display as shuffled along
    for (int i=0; i<(numInTail+tailFeedIn); i++){
      //Output the array
      for (int j=0; j<numInTail; j++){
        setPixel(j, pixelArrayTail[j+tailFeedIn].red,  pixelArrayTail[j+tailFeedIn].green,  pixelArrayTail[j+tailFeedIn].blue); 
      }//j1
      showStrip();
      delay(shootUpDelay);
      shootUpDelay=shootUpDelay+1;
      //Shuffle the array along
      for (int j=(numInTail+tailFeedIn-1); j>0; j--){
        pixelArrayTail[j+1].red = (int) pixelArrayTail[j].red * dimFactor;
        pixelArrayTail[j+1].green = (int) pixelArrayTail[j].green * dimFactor;
        pixelArrayTail[j+1].blue = (int) pixelArrayTail[j].blue * dimFactor;  
      }//j2    
      pixelArrayTail[1]={0,0,0};
      pixelArrayTail[0]={0,0,0};
    }//i
    
    
}

//*******************************************
//*             Firework - Explode
//*******************************************
void FireworkExplode(){

 int explodeDelay;
  explodeDelay=random(20,50);

long pattern = random(8);
//pattern=6;

  if (pattern==0){
    //preload the start N bytes of the array
    pixelArrayArm[0]={30,30,0};
    pixelArrayArm[1]={80,80,0};
    pixelArrayArm[2]={150,150,0};
    pixelArrayArm[3]={255,255,0};
    pixelArrayArm[4]={0,0,0};
    pixelArrayArm[5]={255,255,255};
    pixelArrayArm[6]={100,100,0};
    pixelArrayArm[7]={200,200,0};
    pixelArrayArm[8]={0,0,0};
    pixelArrayArm[9]={0,0,0};
    pixelArrayArm[10]={100,100,0};
    pixelArrayArm[11]={200,200,0};
    pixelArrayArm[12]={255,255,0};
    pixelArrayArm[13]={255,255,255};
    pixelArrayArm[14]={100,100,100};
    pixelArrayArm[15]={255,255,255};
  }

  else if (pattern==1){
    //preload the start N bytes of the array
    pixelArrayArm[0]={0,30,30};
    pixelArrayArm[1]={0,80,80};
    pixelArrayArm[2]={0,150,150};
    pixelArrayArm[3]={0,255,255};
    pixelArrayArm[4]={0,0,0};
    pixelArrayArm[5]={255,255,255};
    pixelArrayArm[6]={0,100,100};
    pixelArrayArm[7]={0,200,200};
    pixelArrayArm[8]={0,0,0};
    pixelArrayArm[9]={0,0,0};
    pixelArrayArm[10]={0,100,100};
    pixelArrayArm[11]={0,200,200};
    pixelArrayArm[12]={0,255,0};
    pixelArrayArm[13]={255,255,255};
    pixelArrayArm[14]={100,100,100};
    pixelArrayArm[15]={255,255,255};
  } 
  else if (pattern==2){
    //preload the start N bytes of the array
    pixelArrayArm[0]={0,30,00};
    pixelArrayArm[1]={0x0, 0x81, 0x0};
    pixelArrayArm[2]={0x0, 0x8A, 0x0};
    pixelArrayArm[3]={0x0, 0x93, 0x0};
    pixelArrayArm[4]={0x0, 0x9C, 0x0};
    pixelArrayArm[5]={0x0, 0xA5, 0x0};
    pixelArrayArm[6]={0x0, 0xAE, 0x0};
    pixelArrayArm[7]={0x0, 0x00, 0x0};
    pixelArrayArm[8]={0xA0, 0xA0, 0xA0};
    pixelArrayArm[9]={0x0, 0xC9, 0x0};
    pixelArrayArm[10]={0x0, 0xD2, 0x0};
    pixelArrayArm[11]={0x0, 0x00, 0x0,};
    pixelArrayArm[12]={0xff, 0xff, 0xff};
    pixelArrayArm[13]={0x0, 0x00, 0x0};
    pixelArrayArm[14]={0x0, 0xF6, 0x0};
    pixelArrayArm[15]={0x0, 0xFF, 0x0};
  }  
  else if (pattern==3){
    //preload the start N bytes of the array
    pixelArrayArm[0]={5,0,0};
    pixelArrayArm[1]={20,0,0};
    pixelArrayArm[2]={0,0,0};
    pixelArrayArm[3]={50,0,0};
    pixelArrayArm[4]={100,0,0};
    pixelArrayArm[5]={0,0,0};
    pixelArrayArm[6]={150,150,150};
    pixelArrayArm[7]={100,0,0};
    pixelArrayArm[8]={0,0,0};
    pixelArrayArm[9]={100,0,0};
    pixelArrayArm[10]={150,0,0};
    pixelArrayArm[11]={200,0,0};
    pixelArrayArm[12]={255,0,0};
    pixelArrayArm[13]={255,255,255};
    pixelArrayArm[14]={50,0,0};
    pixelArrayArm[15]={255,0,0};
  }  
  else if (pattern==4){
    //preload the start N bytes of the array
    pixelArrayArm[0]={0,0,10};
    pixelArrayArm[1]={0,0,50};
    pixelArrayArm[2]={0,0,100};
    pixelArrayArm[3]={0,0,150};
    pixelArrayArm[4]={0,0,0};
    pixelArrayArm[5]={50,50,50};
    pixelArrayArm[6]={150,150,150};
    pixelArrayArm[7]={0,0,0};
    pixelArrayArm[8]={0,0,100};
    pixelArrayArm[9]={0,0,255};
    pixelArrayArm[10]={0,0,20};
    pixelArrayArm[11]={0,0,100};
    pixelArrayArm[12]={0,0,255};
    pixelArrayArm[13]={0,0,255};
    pixelArrayArm[14]={0,0,0};
    pixelArrayArm[15]={255,255,255};
  }  
  //RGB
  else if (pattern==5){
    //preload the start N bytes of the array
    pixelArrayArm[0]={255,0,0};
    pixelArrayArm[1]={200,0,0};
    pixelArrayArm[2]={0,0,0};
    pixelArrayArm[3]={0,255,0};
    pixelArrayArm[4]={0,200,0};
    pixelArrayArm[5]={0,0,255};
    pixelArrayArm[6]={0,0,200};
    pixelArrayArm[7]={0,0,0};
    pixelArrayArm[8]={100,0,0};
    pixelArrayArm[9]={0,100,0};
    pixelArrayArm[10]={0,0,100};
    pixelArrayArm[11]={50,0,0};
    pixelArrayArm[12]={0,50,0};
    pixelArrayArm[13]={50,50,50};
    pixelArrayArm[14]={10,10,10};
    pixelArrayArm[15]={200,200,200};
  }
  //???
  else if (pattern==6){
    //preload the start N bytes of the array
    pixelArrayArm[0]={30,30,30};
    pixelArrayArm[1]={100,100,100};
    pixelArrayArm[2]={255,255,255};
    pixelArrayArm[3]={50,50,50};
    pixelArrayArm[4]={90,90,90};
    pixelArrayArm[5]={255,255,255};
    pixelArrayArm[6]={0,0,0};
    pixelArrayArm[7]={200,200,200};
    pixelArrayArm[8]={255,255,255};
    pixelArrayArm[9]={200,200,200};
    pixelArrayArm[10]={75,75,75};
    pixelArrayArm[11]={255,255,255};
    pixelArrayArm[12]={0,0,0};
    pixelArrayArm[13]={80,80,80};
    pixelArrayArm[14]={60,60,60};
    pixelArrayArm[15]={20,20,20};
  }
  else  {
    //preload the start N bytes of the array
    pixelArrayArm[0]={30,0,30};
    pixelArrayArm[1]={80,0,80};
    pixelArrayArm[2]={150,0,150};
    pixelArrayArm[3]={255,0,255};
    pixelArrayArm[4]={0,0,0};
    pixelArrayArm[5]={255,255,255};
    pixelArrayArm[6]={100,0,100};
    pixelArrayArm[7]={200,0,200};
    pixelArrayArm[8]={0,0,0};
    pixelArrayArm[9]={0,0,0};
    pixelArrayArm[10]={100,0,100};
    pixelArrayArm[11]={200,0,200};
    pixelArrayArm[12]={255,0,255};
    pixelArrayArm[13]={255,255,255};
    pixelArrayArm[14]={100,100,100};
    pixelArrayArm[15]={255,255,255};
  }
    
    for (int i=armFeedIn; i<(numInArm+armFeedIn); i++){
      pixelArrayArm[i].red=0;
      pixelArrayArm[i].green=0;
      pixelArrayArm[i].blue=0;  
    }

    //Loop and display as shuffled along
    for (int i=0; i<(numInArm+armFeedIn); i++){
      
      //Output the array
      for (int j=0; j<numInArm; j++){
        setPixel(j+numInTail, pixelArrayArm[j+armFeedIn].red,  pixelArrayArm[j+armFeedIn].green,  pixelArrayArm[j+armFeedIn].blue); 
      }//j1
      showStrip();
      delay(explodeDelay);
      explodeDelay=explodeDelay+7;
      
      //Shuffle the array along
      for (int j=(numInArm+armFeedIn-1); j>0; j--){
        pixelArrayArm[j+1].red = pixelArrayArm[j].red;
        pixelArrayArm[j+1].green = pixelArrayArm[j].green;
        pixelArrayArm[j+1].blue = pixelArrayArm[j].blue;  
      }//j2    
      pixelArrayArm[1]={0,0,0};
      pixelArrayArm[0]={0,0,0};
    }//i  
}


//*******************************************
//*             FSR_Fades
//*******************************************
void FSR_Fades(void){
  uint8_t  Red=20;
  uint8_t  Green=100;
  uint8_t Blue=3;

  uint8_t targetRed;
  uint8_t targetGreen;
  uint8_t targetBlue;

  long currentMillis;
  long previousMillis;
  long timeout=10000;
  uint8_t finished=0;

  //noInterrupts ();
  currentMillis = millis();
  previousMillis=currentMillis;

 while(finished==0){
    
    //randNumber = random(500, 5000);
  targetRed = random(1,200);
  targetGreen = random(1,200);
  targetBlue=random(1,200);

    for (int fade=0; fade<255; fade++){
      
      Red=fade;
      Green=255-fade;
      Blue=fade;
      setAll(Red,Green,Blue); 
      
      if (Red<targetRed) Red=Red+0x01;
      if (Blue<targetBlue) Blue=Blue+0x01;
      if (Green<targetGreen) Green=Green+0x01;
      if (Red>targetRed) Red=Red-0x01;
      if (Blue>targetBlue) Blue=Blue-0x01;
      if (Green>targetGreen) Green=Green-0x01;

    }

    
    currentMillis = millis();
    if (currentMillis - previousMillis > timeout) finished=1;

      //pixels.clear();

      //  for(int i=0; i<NUMPIXELS; i++) {
      //
      //    pixels.setPixelColor(i, pixels.Color(0, 150, 0));
      //    pixels.show();

    

 }
}

//*******************************************
//*             delay_of_1s
//*******************************************
void delay_of_1s(void){
  
  long currentMillis;
  long previousMillis;
  
    currentMillis = millis();
    previousMillis=currentMillis;
    
    while (currentMillis - previousMillis < 1000) {
      currentMillis = millis();  
    }
    //previousMillis = currentMillis;
    return;
}

//*******************************************
//*             delay_of_x
//*******************************************
void delay_of_x(int num_ms){
  long currentMillis;
  long previousMillis;
  
    currentMillis = millis();
    previousMillis=currentMillis;
    
    while (currentMillis - previousMillis < num_ms) {
      currentMillis = millis();  
    }
    //previousMillis = currentMillis;
    return;
}

//from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectSnowSparkle
//*******************************************
//*             SnowSparkle
//*******************************************
void SnowSparkle(byte red, byte green, byte blue, int SparkleDelay, int SpeedDelay) {
  setAll(red,green,blue);

  int Pixel = random(NUM_LEDS);
  setPixel(Pixel,0xff,0xff,0xff);
  showStrip();
  delay(SparkleDelay);
  setPixel(Pixel,red,green,blue);
  showStrip();
  delay(SpeedDelay);
}

//*******************************************
//*             FSR_Meteor
//*******************************************
void FSR_Meteor(void){
  
  int colCount=0;
  int col=0;
  int finished=0;
  long currentMillis;
  long previousMillis;
  long timeout=30000;
  int nextString;
  int colourCounter;
  
  setAll(0,0,0);
  showStrip(); 

  currentMillis = millis();
  previousMillis=currentMillis;
  noInterrupts ();

  while(finished==0){
    
    colourCounter = random (50, 200);
    while (colCount<colourCounter){
    
      nextString = random(0, 30);
    
      for (int arm=0; arm<10; arm++){
        for (int pos=0; pos<10; pos++){
          pixelHead[(arm*10)+pos]=pixelHead[(arm*10)+pos+1];  
        }
        if (nextString<10) {
          if (col==0 )pixelHead[(nextString*10)+9]={255,255,255}; 
          else if (col==1 )pixelHead[(nextString*10)+9]={0,255,255};
          else if (col==2 )pixelHead[(nextString*10)+9]={255,0,255};
          else if (col==3 )pixelHead[(nextString*10)+9]={255,255,0}; 
          else if (col==4 )pixelHead[(nextString*10)+9]={255,0,0}; 
          else if (col==5 )pixelHead[(nextString*10)+9]={0,255,0}; 
          else if (col==6 )pixelHead[(nextString*10)+9]={0,0,255};  
          }
        }
    
      //output effect: ALL LEDS: tail + 10 * arms in a single push of data
      for (int j=0; j<=(numInArm*numArms); j++){
        setPixel(j, pixelHead[j].red,  pixelHead[j].green,  pixelHead[j].blue); 
      }//j
      showStrip();
  
      delay_of_x(50);
      colCount++;
    }
  
    //change colour
    colCount=0;
    col++;
    if (col>6) col=0;
  
    currentMillis = millis();
    if (currentMillis - previousMillis > timeout) finished=1;  
  
    }
  return;
}


//*******************************************
//*             fadeTheArm
//*******************************************
void fadeTheArm(struct pixel* myArray, uint8_t noElements, uint8_t fadeRate, double fadeRateDec){

//  myArray->blue=255;
//  (myArray+2)->blue=100;
//  myArray->red=0;
//  (myArray+2)->red=0;
//  myArray->green=0;
//  (myArray+2)->green=0;  

double redVal;
double greenVal;
double blueVal;

  if (fadeRate>0){
    for (uint8_t pixelNum=0; pixelNum<noElements; pixelNum++){ 
      if (myArray->red > fadeRate) myArray->red = myArray->red -fadeRate;
      else myArray->red = 0;  
      if (myArray->green > fadeRate) myArray->green = myArray->green -fadeRate; 
      else myArray->green = 0; 
      if (myArray->blue > fadeRate) myArray->blue = myArray->blue -fadeRate;  
      else myArray->blue = 0;
      myArray++;     
    }
  }

  else {
    for (uint8_t pixelNum=0; pixelNum<noElements; pixelNum++){ 
      redVal=(double)myArray->red;
      greenVal=(double)myArray->green;
      blueVal=(double)myArray->blue;
      
      redVal=redVal*fadeRateDec;
      greenVal=greenVal*fadeRateDec;
      blueVal=blueVal*fadeRateDec;
        
      myArray->red = (uint8_t)redVal;
      myArray->green= (uint8_t)greenVal;
      myArray->blue= (uint8_t)blueVal;     

      myArray++;                       
  }
}
}

//*******************************************
//*             lovinTheHeart
//*******************************************
void lovinTheHeart(void){
uint8_t arm=0;

  for (arm=0; arm<7; arm=arm+6){
    pixelHead[((arm*10)+0)]={0,0,0};  
    pixelHead[((arm*10)+1)]={0,0,0};
    pixelHead[((arm*10)+2)]={0,0,0};
    pixelHead[((arm*10)+3)]={0,0,0};
    pixelHead[((arm*10)+4)]={0,0,0};
    pixelHead[((arm*10)+5)]={0,0,0};
    pixelHead[((arm*10)+6)]={255,0,0};
    pixelHead[((arm*10)+7)]={255,0,0};
    pixelHead[((arm*10)+8)]={255,0,0};
    pixelHead[((arm*10)+9)]={255,0,0};
  }

  for (arm=7; arm<10; arm=arm+2){
    pixelHead[((arm*10)+0)]={0,0,0};  
    pixelHead[((arm*10)+1)]={0,0,0};
    pixelHead[((arm*10)+2)]={0,0,0};
    pixelHead[((arm*10)+3)]={0,0,0};
    pixelHead[((arm*10)+4)]={0,0,0};
    pixelHead[((arm*10)+5)]={0,0,0};
    pixelHead[((arm*10)+6)]={0,0,0};
    pixelHead[((arm*10)+7)]={255,0,0};
    pixelHead[((arm*10)+8)]={255,0,0};
    pixelHead[((arm*10)+9)]={255,0,0};
  }

  for (arm=1; arm<6; arm=arm+4){
    pixelHead[((arm*10)+0)]={0,0,0};  
    pixelHead[((arm*10)+1)]={0,0,0};
    pixelHead[((arm*10)+2)]={0,0,0};
    pixelHead[((arm*10)+3)]={0,0,0};
    pixelHead[((arm*10)+4)]={0,0,0};
    pixelHead[((arm*10)+5)]={255,0,0};
    pixelHead[((arm*10)+6)]={255,0,0};
    pixelHead[((arm*10)+7)]={255,0,0};
    pixelHead[((arm*10)+8)]={255,0,0};
    pixelHead[((arm*10)+9)]={255,0,0};
  }  

  for (arm=2; arm<5; arm=arm+2){
    pixelHead[((arm*10)+1)]={0,0,0};
    pixelHead[((arm*10)+2)]={0,0,0};
    pixelHead[((arm*10)+3)]={0,0,0};
    pixelHead[((arm*10)+4)]={255,0,0};
    pixelHead[((arm*10)+5)]={255,0,0};
    pixelHead[((arm*10)+6)]={255,0,0};
    pixelHead[((arm*10)+7)]={255,0,0};
    pixelHead[((arm*10)+8)]={255,0,0};
    pixelHead[((arm*10)+9)]={255,0,0};
  } 

    arm=3;
    pixelHead[((arm*10)+1)]={0,0,0};
    pixelHead[((arm*10)+2)]={0,0,0};
    pixelHead[((arm*10)+3)]={0,0,0};
    pixelHead[((arm*10)+4)]={0,0,0};
    pixelHead[((arm*10)+5)]={0,0,0};
    pixelHead[((arm*10)+6)]={255,0,0};
    pixelHead[((arm*10)+7)]={255,0,0};
    pixelHead[((arm*10)+8)]={255,0,0};
    pixelHead[((arm*10)+9)]={255,0,0};

    arm=8;
    pixelHead[((arm*10)+1)]={0,0,0};
    pixelHead[((arm*10)+2)]={0,0,0};
    pixelHead[((arm*10)+3)]={255,0,0};
    pixelHead[((arm*10)+4)]={255,0,0};
    pixelHead[((arm*10)+5)]={255,0,0};
    pixelHead[((arm*10)+6)]={255,0,0};
    pixelHead[((arm*10)+7)]={255,0,0};
    pixelHead[((arm*10)+8)]={255,0,0};
    pixelHead[((arm*10)+9)]={255,0,0};

    //output effect: ALL LEDS: tail + 10 * arms in a single push of data
    for (uint8_t j=0; j<(numInArm*numArms); j++){
      setPixel(j, pixelHead[j].red,  pixelHead[j].green,  pixelHead[j].blue); 
    }//j
    strip.show();
}

//*******************************************
//*            Running Lights
//*******************************************
void RunningLights(byte red, byte green, byte blue, int WaveDelay) {
  int Position=0;

  for(int j=0; j<NUM_LEDS*2; j++)
  {
      Position++; // = 0; //Position + Rate;
      for(int i=0; i<NUM_LEDS; i++) {
        // sine wave, 3 offset waves make a rainbow!
        //float level = sin(i+Position) * 127 + 128;
        //setPixel(i,level,0,0);
        //float level = sin(i+Position) * 127 + 128;
        setPixel(i,((sin(i+Position) * 127 + 128)/255)*red,
                   ((sin(i+Position) * 127 + 128)/255)*green,
                   ((sin(i+Position) * 127 + 128)/255)*blue);
      }
     
      showStrip();
      delay(WaveDelay);
  }
  return;
}

//*******************************************
//*             Simple_Tests
//*******************************************
void FSR_Simple(void){

  //noInterrupts ();
  #define Nnew 100
uint16_t n=Nnew;

 while(1){

      setAll(255,0,0);
      delay_of_x(n);
      
      setAll(255,255,0);
      delay_of_x(n);
      
      setAll(255,0,255);
      delay_of_x(n);


      if (n>10) n=n-10;

      if (n<=10) n=Nnew;

 }
}

 

Some of the functions were created to specifically take the effects slowly to see what made the lights flicker.

 

My final thoughts were that perhaps the timing gets corrupted by some other microcontroller interrupt routine and therefore I also added code to try and mitigate against that but to no avail.

 

This led me onto another path. In the past I had good success with the WS2812 and a PIC Microcontroller that utilised an inbuilt Configurable Logic Cell (CLC). This is a small amount of very simple onboard logic/GLUE logic and is luckily just enough to cleanly generate the WS2812 signals accurately. I therefore decided to fire up Microchip's MPLAB X IDE and wire up a PIC microcontroller as a test. I would also be able to debug my code far more easily using MPLAB than the Arduino setup.

 

I'll write another update when I hopefully get that code working.

 

13.    Code Changes [dated: 13 Jan 2020]

This competition closes tomorrow so I feel it is time for a final update of the blog. In the past I utilised the AN1606 technical memo from Microchip to generate the precise timing for the WS2812 RGB LEDs. However as software IDEs have moved on along with devices I was unable to get it to work from the old file source and have therefore spent the two+ weeks since the New Year trying to build a new project that compiles. The project uses a combination of C and the assembler to create the precise timing that the WS2812 LEDs require. I have been able to get the project to build now and to push test data to the required Data Out line but the actual WS2812 data is not being generated still.

 

13.1    Microchip Code Configurator (MCC) and Configurable Logic Cells (CLC)

The PIC microcontroller creates the WS2812 protocol using the Configurable Logic Cell (CLC) which is a small amount of digital circuit that is added to the chip, it is similar to GLUE logic. The CLC can be configured either directly by consulting the relevant datasheet and setting individual registers (which can be error prone) or by flashing up the Microchip Code Configurator (MCC) add-on to MPLAB X IDE and using that. The latter providing a far more user friendly GUI experience. Below are a couple of screenshots from the MCC where I am setting up the CLC2 and PWM6 peripherals. The Easy-Setup allows the user to select drop downs and the "Registers" tab just presents all the relevant registers for that peripheral for the user to adjust as required.

 

13.2    The PIC Microcontroller Circuit

The circuit I have used was one left over from a previous project. This used the PIC16F18857 and a simple NAND gate IC to buffer and change the voltage levels: I used this to setup the PIC and lights from the output of a Raspberry Pi where the PI received the UDP packets from the Vixen 3 software and the Microcontroller grabbed those and displayed the corresponding LED patterns. I am currently not using that and only the PIC microcontroller part of this circuit.

 

14.  Way Ahead

I feel I'm on the cusp of getting this device to work as expected. I will be piping some of the generated CLC terms to output pins to see in they appear correct until finally I can determine what the issue is. Additionally the debug tool within MPLAB X IDE is powerful in allowing the developer an insight into where the code flows to: that might also allow me to see if the code gets stuck in a loop etc.

 

Once I have managed to control the LEDs directly from the Microchip PIC16F18857 I will be able to amalgamate the MPLAB code and my Catherine Wheel code from the Arduino, adjust it and hopefully get it working. I am quite confident at that point it will not flicker either: i it does then it is down to switching currents and voltage drops/spikes on the data lines and perhaps I will have to cut into my protective epoxy layer to fix them.

 

Whatever happens I'll post an update when I get there .

 

Thank you for reading.