<img style="vertical-align: top;" /> | Enter Your Holiday Season Project for a Chance to Win Over $20,000 Worth of Prizes and Gifts to Give! | Project14 Home |
Monthly Themes | ||
Monthly Theme Poll |
LED Firework Suite: Catherine Wheel
- LED Firework Suite: Catherine Wheel
- 1. Introduction
- 2. Previous Attempt at Catherine Wheel
- 3. New Design: After a Rethink
- 4. 3D Printed Hub
- 5. Manufacturer
- 6. Waterproofing
- 7. Driver Circuit
- 8. Coding It up
- 9. Final Effect
- 10. An Extra Effect
- 11. Summary
- 12. Code Changes [dated: 09 Jan 2020]
- 13. Code Changes [dated: 13 Jan 2020]
- 14. Way Ahead
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.
Top Comments
Rod,
Great work! The new effects/display look really great. Keep up the good work.
Gene
Hi Rod,
Great display, and very neat build! I think next year you should join the team that performs the display at the London Eye for New Year's Eve!!
Happy 2020 and looking forward to reading about…
Fun project and the contraption in the video link was really wild :-)