Pulsing one LED, flickering two others, off and on

Darn this subject heading character limit. >:(

I am new to Arduino, microcontrollers, and programming C in general, so please bear with me.

I am making a double-bladed lightsaber (Darth Maul style) that will use two LedEngin RGBA “10-Watt” LEDs. (I put “10-Watt” in quotes, because I don’t know what it means when there are actually four LEDs that are roughly equivalent to 3-Watt leds. 10 watts when all four are running simultaneously? I dunno.)

These LEDs need about 1000 mA, and I plan on powering the whole thing with two 4aaa (1000mA Ni-MH) battery packs. (How I am going to separate the two battery packs is another problem I need to work out; I’m thinking relay, but not sure if that would work.)

With the help of an ATMega328, I plan to use a single tactile switch to have these two LEDs cycle through four (or possibly two) modes:

  • red/blue
  • blue/blue
  • red/red
  • blue/red

I actually had a working sketch for this, but accidentally overwrote it with another, so I have to rewrite it from scratch. :’(

Apart from separating power supplies, that is the easy part.

The hard part is that I want to have these two main LEDs flicker when they are on, like this:

// flickering LED
int ledPin = 11; // asign LED to pin 11
void setup() // setting things up
{
pinMode(ledPin, OUTPUT); // sets pin 11 to output
}
void loop() // starting loop
{
analogWrite(ledPin, random(50)+205); // outputs a random brightness between 206 and 255 on a scale of 255
delay(random(100)); // maintains that brightness for a random interval between 1 and 100 milliseconds
}

To make things more complicated, I would also like to have a 5mm “accent LED” (or better yet, a bar-graph) doing something interesting (more interesting than simply blinking) on the hilt of the lightsaber while the main LEDs do their thing. For example, pulsing, like this:

// Project 7 - Pulsating lamp
int ledPin = 11;
float sinVal;
int ledVal;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
for (int x=0; x<180; x++) {
// convert degrees to radians
// then obtain sin value
sinVal = (sin(x*(3.1412/180)));
ledVal = int(sinVal*255);
analogWrite(ledPin, ledVal);
delay(25);
}
}

So my question is, how can I make a single sketch (on a single ATMega328) do all these things simultaneously? I’m thinking “interrupt” or “while,” but I’ve never actually used either of these and have only a vague understanding of them.

To summarize:

  • I need to use a tactile switch to switch two LEDs on and off in certain patterns
  • I need to have those LEDs flickering
  • I need to have a third LED pulsing (or doing some other interesting thing)

…all at the same time. :stuck_out_tongue:

And to get back to the power supply issue, if the two main LEDs were simply “HIGH” or “LOW”, a relay would suffice to keep two battery packs separate, but I also want those two LEDs to be flickering, and I’m not sure if a relay could convey that effect properly.

Any advice will be greatly appreciated!

The EventFuse library (which you can find in the playground) would be an easy way to do both tasks.

You would set up one fuse with a length of 25 to handle the pulsating lamp led and a second fuse to reset the analog output. in the second fuse you'd reset the fuse length each time.

Then call the burn() method once per millisecond and both tasks will be done.

Thanks, Dave! I have no idea what that means, but I'll start studying right away.

It might make a little more sense after you check out the code samples for EventFuse:

http://www.arduino.cc/playground/Code/EventFuse

Well, I'm going to admit frankly that my head is swimming. Fuses, POV, Multiplexing, MAX7219 chips....

It doesn't help that my client can't make up his mind exactly what he wants this saber to do.

So I'm going to throw my n00bish self on the mercy of the forum and ask for more explicit hints on how to make my sketch.

Let's set aside the flickering of the two main LEDs (which are actually a pair of four LEDs under a single dome). I would love to have the flickering, but right now I'll settle for just having the right colors on (full brightness) at the right time.

Also, the client decided he didn't need "blue-blue" and "red-red", so I just need the two main LEDs to switch from "red-blue" to "blue-red". That is, they will swap colors each time the switch is hit. (And actually, the "switch" is going to be a shock sensor, so the color-swapping will occur when the lightsaber hits something).

As for the accent LEDs, he wants flashy. For this particular project, I'm thinking of maybe two 5mm LEDs pulsing (maybe once a second), and a 10-segment LED bargraph in which the LEDs pulse in wave-like fashion. In other words, rather than a simple chase effect, in which only LED is HIGH at a time, and all the other LEDs are LOW, each LED fades in and out, like that "wave" thing fans sometimes do at sports events.

Does this make sense? I thinking that relying on persistence of vision and having each LED refreshed ever 50ms or so is the way to go, but I haven't got a clue as to how to write this into a sketch.

Would a MAX7219 chip makes this easier, or would that just be more programming language I have to learn...?

After my initial excitement at writing a few sketches that worked, I'm now frustrated at my inability to chew gum and walk at the same time, to use an Anglophone expression. But, hey, I learned to juggle (just three balls), so I should be able to get my ATMega chip to do more than two things at once, right?

Right? :-/

Again, thanks in advance for any help or advice.

I don't think I've quite understood what your after, but I quickly wrote this:

// LED pins
#define LEDONE 9
#define LEDTWO 10
#define LEDTHREE 11

// LED blink intervals
#define LEDONEINTERVAL 1000
#define LEDTWOINTERVAL 500
#define LEDTHREEINTERVAL 250

// Current LED states
boolean ledOneSTATE = LOW;
boolean ledTwoSTATE = LOW;
boolean ledThreeSTATE = LOW;

// Storeage for the last time the LED was blinked
long previousBlinkONE = 0;
long previousBlinkTWO = 0;
long previousBlinkTHREE = 0;




void setup()
{
  pinMode(LEDONE, OUTPUT);
  pinMode(LEDTWO, OUTPUT);
  pinMode(LEDTHREE, OUTPUT);
}

void loop()
{

  // LED one blink controller

  if (millis() - previousBlinkONE > LEDONEINTERVAL) {
    previousBlinkONE = millis();

    if (ledOneSTATE == LOW)
      ledOneSTATE = HIGH;
    else
      ledOneSTATE = LOW;

    digitalWrite(LEDONE, ledOneSTATE);
  }



  // LED two blink controller

  if (millis() - previousBlinkTWO > LEDTWOINTERVAL) {
    previousBlinkTWO = millis();

    if (ledTwoSTATE == LOW)
      ledTwoSTATE = HIGH;
    else
      ledTwoSTATE = LOW;

    digitalWrite(LEDTWO, ledTwoSTATE);
  }




  // LED three blink controller

  if (millis() - previousBlinkTHREE > LEDTHREEINTERVAL) {
    previousBlinkTHREE = millis();

    if (ledThreeSTATE == LOW)
      ledThreeSTATE = HIGH;
    else
      ledThreeSTATE = LOW;

    digitalWrite(LEDTHREE, ledThreeSTATE);
  }

}

I've not tested it, but it should be correct :slight_smile:

See if that points you in a useful direction

Wow, beige, in terms of showing me how to have three things going on at once without interfering with each other, that is a tremendous help. Now I'll see if I can build on that, adding the pulsing, wave, and flickering I'm hoping to create. Many thanks! :slight_smile:

So, I'm still soliciting help on those wave and pulse issues. Particularly the wave, since it involves the ten segments of my bargraph. :stuck_out_tongue:

Here is a concept version of your described device using the EventFuse and MsTimer2 libraries. It demonstrates how to set up multiple tasks that occur at different rates in a way that should be easy to understand and maintain.

There are six separate things happening here. The main loop is handling software PWM on pins 2 through 13. This happens at a fairly high frequency and is independent of the timing of the other tasks.

Independent of the main loop the MsTimer2 library generates an interrupt once every millisecond and calls the burn() function of the eventFuse object. The eventFuse object reduces the length of each active fuse by 1 and then calls the handler for each fuse that has reached zero.

Five fuses are active. They represent the tasks that handle the ‘A’ and ‘B’ blade LEDs, the 10-segment LED bargraph and the two blinky LEDs.

The ‘A’ and ‘B’ blades are handled by two fuses that call a single fuse handler. The fuses execute the handler at random intervals (accomplished by assigning a new random value to the fuse length each time the fuse burns down) and setting the associated blade brightness to a random value. While the code is set up to handle any mixture of red and blue active at the same time, there is currently no code to switch the blade colors.

The LED bargraph executes at a regular interval and simply adjusts the display offset of the pattern that is displayed on the bargraph.

I wasn’t sure how you were planning to handle the red/blue colors of the blades, so I set it up with the assumption that there are a total of four independent LEDs, one each of red and blue for each of the two blades. All the LEDs are independently controlled, which allows for red, blue, or any mixture in between.

I did not include code for a switch. You could add support for it by connecting a switch to digital pin 18, setting it up as an input and then creating a new fuse and handler for it. The handler would increment a counter if the button was pressed or reset the counter to zero if it was not. If the counter reached a high value the handler would trigger a ‘button pressed’ routine and then lock out the counter (set it to a special value to prevent calling the button pressed routine again) until the button was released. The button press routine would adjust the blade state variables as desired. The purpose of the counter is to debounce the switch. An impact switch may not need debouncing (I’d recommend putting a one-shot on it to ensure a clean signal with a minimum duration).

I tested most of this, the random flickering works, the blinky leds work, and the bar graph appears to animate, but I was testing it with just one led, so it might need tweaking. There is one bug that seems to jam up the flicker feature after a few minutes.

Hope it’s useful:

/* This sketch is free software; you can use, redistribute and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */#include <DigitalToggle.h>
#include <MsTimer2.h>
#include <EventFuse.h>

// Map bar graph LEDs to output pins. Output
// pins need not be mapped in sequence.
byte GraphPins[] = {2,3,4,5,6,7,8,9,10,11};

// blinky LEDs
#define BlinkyLED1Pin 12
#define BlinkyLED2Pin 13

// Lookup table for blade LED pins and values.
// Assumes that each blade has a set of two
// independent LEDs, one red and one blue. All
// four LEDs can be controlled independently,
// allowing any mixture of red and blue values
// on either blade.
#define BladeA 0
#define BladeB 1
#define RedPin 0
#define BluePin 1
#define BladePin 0
#define BladeBrightness 1
byte Blades[2][2][2] = { //  Red     Blue
                         //pin,val   pin,val
                          {{14, 0}, {15, 0}},  // Blade A
                          {{16, 0}, {17, 0}}}; // Blade B

// State indicator constants for blades
#define RandomRed 0
#define RandomBlue 1
#define AllRandom 2

// Blade state variables
byte BladeColor[] = {RandomRed,RandomBlue};

// Intensity map to be scrolled across bar graph.
// Can be any reasonable length, just add more
// values here, but only 10 values will be visible
// on the graph LEDs at one time.
byte GraphPattern[] = {0,50,100,150,200,250,200,150,100,50};

// Offset values for positioning the graph pattern
// on the graph. 
int GOffsetStart = -sizeof(GraphPattern);
int GOffsetEnd = sizeof(GraphPattern);
int GraphOffset = GOffsetStart;

// Software pwm value, all outputs with a value less than
// or equal to the value of pwmVal will be off, all outputs 
// with a value equal to the value of pwmVal will be on.
byte pwmVal = 0;

// Taking into consideration the current offset, return the
// pattern value for the specified index. This represents
// the LED intensity that should be displayed on the bar graph
// LED identifed by idx.
byte getPatternVal( byte idx )
{
  int ofs = idx + GraphOffset;
  return ((ofs<0)||(ofs>=sizeof(GraphPattern)))?0:GraphPattern[ofs];
}

// Pattern debug output vars
//int a, lastVal = 0;


void pwmHandler()
{
  pwmVal++;

  // Pattern debug output
  //if (lastVal!=(a=getPatternVal(0)))Serial.println(lastVal=a,HEX);
    
  // Bar graph LEDs
  for( byte i=0; i<sizeof(GraphPins); i++ )
    digitalWrite( GraphPins[i], getPatternVal(i) > pwmVal );
  
  // Blade LEDs
  for( byte i=0; i<2; i++)
    for( byte j=0; j<2; j++)
      digitalWrite( Blades[i][j][BladePin], Blades[i][j][BladeBrightness] > pwmVal );
}

// Animate the pattern by scrolling
void patternAnimationHandler(FuseID fuse, int userData)
{
  // Adjust the graph offset, resetting to the start as necessary
  if (++GraphOffset > GOffsetEnd)
    GraphOffset = GOffsetStart;
}

void bladeHandler(FuseID fuse, int whichBlade)
{
  // Set the value to a random brightness
  switch (BladeColor[whichBlade]) 
  {
    case RandomRed:
      Blades[whichBlade][RedPin][BladeBrightness] = random(255);
      Blades[whichBlade][BluePin][BladeBrightness] = 0;
      break;
      
    case RandomBlue:
      Blades[whichBlade][RedPin][BladeBrightness] = 0;
      Blades[whichBlade][BluePin][BladeBrightness] = random(255);
      break;
      
    case AllRandom:
      Blades[whichBlade][RedPin][BladeBrightness] = random(255);
      Blades[whichBlade][BluePin][BladeBrightness] = random(255);
      break;
  }
  
  // Hold the value for a random but short period.
  eventFuse[fuse].fuseLen = random(10,20);
}

// This handles the calls from several different fuses.
// The digital pin that it is to operate on is passed
// in the blinkyPin parameter.
// The way this routine works to blink two separate LEDs
// may not be obvious. It's a little tricky, but simple
// once you work out how it is using the fuses.
void BlinkyLEDHandler(FuseID fuse, int blinkyPin)
{
  // Toggle the BlinkyLED (should be going on)
  digitalToggle( blinkyPin );
  
  // If the output is on, schedule a call to toggle it
  if (digitalRead( blinkyPin ))
    eventFuse.newFuse( blinkyPin, 20, 1, BlinkyLEDHandler );
}

void tick()
{
  // Burn the fuses. Note that since MsTimer2 calls
  // tick inside an interrupt and because burn() will
  // call the fuse handlers, the code in the handlers
  // will be executing inside the interrupt as well.
  // Therefore, it is best to keep them quick.
  eventFuse.burn(1);
}

void setup()
{
  // For debugging
  // Serial.begin(115200);
  
  // Set up all the relevant pins for output
  for(byte i=2; i<17; i++)
    pinMode(i, OUTPUT);
        
  // Set up fuses to handle the bar graph animation, the two blades and the two blinky LEDs
  // Notice that some of these use the same handler. The first parameter
  // is passed into the handler so that it can access different data.
  eventFuse.newFuse( 50, INF_REPEAT, patternAnimationHandler );
  eventFuse.newFuse( BladeA, 1, INF_REPEAT, bladeHandler );
  eventFuse.newFuse( BladeB, 1, INF_REPEAT, bladeHandler );
  eventFuse.newFuse( BlinkyLED1Pin, 1000, INF_REPEAT, BlinkyLEDHandler );
  eventFuse.newFuse( BlinkyLED2Pin,  750, INF_REPEAT, BlinkyLEDHandler );

  // Set up Timer2 to call tick once per millisecond
  MsTimer2::set( 1, tick );
  MsTimer2::start();
}

void loop()
{
  // The main loop handles calling the 
  // pwmHandler at the desired frequency.
  // It will be interrupted periodically 
  // to update the target duty cycle (brightness)
  // of the various LEDs.
  pwmHandler();
  delayMicroseconds(20);
}

Wow! Dave, you are too kind. Now I feel like a bad student for not figuring this out on my own. But I’ll pore over the code until I understand it completely, and do my own fine tuning. I think I can figure out how to use the switch to do the color swapping on my own.

Yes, Each blade will use one red LED and one blue LED, so I need to control four positive leads. But I couldn’t figure out from your code where on the Arduino board I should plug those in. I figured out that one of them is ANALOG IN 0, but I don’t know about the other three. :-/

You can treat analog pins 0-5 as digital pins 14-19. One of the LEDs I have on pin 17 is dim though, not sure what's going on there, maybe it needs a pull-up turned on or something.

I tried to comment things so it would not be difficult to figure out what everything does. You should probably also look at the code in EventFuse.cpp.

I figured that if you are pretty new to Arduino programming it might be useful to start with a well-executed (IMHO) but incomplete version of what you are working on. That way you can see how keeping the architecture neat with a few concepts encapsulated into libraries makes the code much easier to read and debug than if you dump everything into the sketch.

On the other hand, sometimes doing that means that it's pretty, but the bugs are harder to find :smiley:

Yes, your comments are very easy to understand. And having that basic framework to build on is definitely helpful for a beginner like me. I really appreciate the help. :slight_smile:

I've often been criticized for having more comments than code (a ratio of 2:1 isn't unusual for me, and in a couple of extreme cases I've gone beyond 10:1 documenting various arcane incantations), but at least I know what the code was /supposed/ to do :slight_smile:

When you finish the project don't forget to tell us about it in Exhibition, preferably with some pictures!

I heart extensive comments. ;D

One quick (?) question. As it is, each LED on the bargraph is dark for half the cycle. Is there a simple way to have each LED immediately start brightening again as soon as it reaches the dimmest value? In other words, I would like it to be a continuous wave of light, not interrupted by darkness. BTW, I changed the values to approximate a sine wave:

// Intensity map to be scrolled across bar graph.
// Can be any reasonable length, just add more
// values here, but only 10 values will be visible
// on the graph LEDs at one time.
byte GraphPattern = {
55,80,135,211,241,255,241,211,135,80};

I will definitely post a video and heavily-commented sketch when I’m finished. :sunglasses:

You’ll need to modify getPatternVal(). As I wrote it it returns a zero if the pattern offset results in no value. To wrap the pattern around so that it is seamless you would just need to replace the zero with a lookup to GraphPattern with an offset that is calculated to wrap around to the correct value.

For clarity it would probably be best to turn it into an if statement so you can comment what it is doing in more detail.

Um....

O-ka-a-a-y-y-y...

I'll get right on that. :o

Thanks!

On second thought, after tweaking the brightness values again, I decided I’m happy with it as it is. Here’s a video:

Dave, is there some kind of time limit on the scrolling effect? I've been letting it run for 30 minutes (?) or so, and all of a sudden it just stopped, precisely halfway through the cycle, so that the 10th LED is at maximum brightness, and they get dimmer down to LED #6, which is flickering dimly. LEDs #1 through 5 are dark.

Sometimes the simplest solution is the best.

I was looking back and forth between the sketch Dave wrote for me and my breadboard, wondering how to get the red and blue accent LEDs to do what I want, when it hit me.

I just put the red LED on the same circuit as the first LED of the bargraph, and the blue LED on the same circuit as the last LED on the bargraph.

So that takes care of the accent lights. Now I need to work out the code for the main blade LEDs.

I noticed the problem with the bar graph scroll too. Not sure what the problem there is. Try starting the sketch and timing how long it takes before it fails. I suspect it always stops after the same amount of time.

It does seem to be the same length of time each time, more than 30 minutes but less than an hour. (I don’t have the patience to watch it continuously for more than half an hour. ;)) But this isn’t really a problem, since it’s almost inconceivable that the user would run the lightsaber continuously for such a long time. (I don’t know if the batteries would last that long!) So that is a low priority right now. I was just wondering if Arduino put a limit on the number of times a function can be executed. Basically, I’m thrilled with the way the bargraph turned out and am more than satisfied with the two accent LEDs.