LED PWM without resistor

domingo

Well-known member
Hello,
I noticed that it is possible to control the intensity of a LED using PWM: https://www.pjrc.com/teensy/tutorial2.html

Is it safe to power an LED without resistor if intensity is set to low? Or this could eventually burn anyways the LED, consume more power than its equivalent brightness with resistor, or maybe damage the Teensy for sending too much current even if it is for small fractions of time?

I'm just wondering about the consequences. I know a resistor doesn't take much efforts to solder or space, but when using RGB resistors things start to add up 😋
 
No, use a resistor, or you'll fry the LED or the Teensy. The absolute maximum ratings are just that. Exceed them and degradation and failure can happen.
Why not consider a neopixel which takes one control signal and does the PWM for you?
 
Thanks MarkT to clarify that! I didn‘t know about neopixels, very interesting. I‘m looking for something in the traditional LED format though, it‘s easier to mount for me and smaller. Ideally in 3mm, but all RGB‘s in traditional ‚light bulb’ format I’ve found are 5mm 4-pin 🧐. The idea is to build a single LED VUmeter that changes color depending on audio peak.
I‘ll surely use resistors then! Don‘t want to fry any eggs here.

—Domingo
 
If just 2 colors will do, like green for normal and red for peak, maybe this sort of part will do?


If that complicated link doesn't work, go to Mouser and search for part number SSL-LX3054IGW.

These 2-pin bi-color parts have 2 LEDs inside, each connected the opposite way. Normally you would connect 1 resistor in series, and connect the LED+resistor to 2 pins on Teensy. Configure both pins as OUTPUT. To show one color, drive the 1st pin HIGH and the 2nd pin LOW. To show the other color, drive the 1st pin LOW and 2nd pin HIGH. Turn turn the LED off, drive both pins the same way.

This part isn't unique. Many other bi-color LEDs exist. Just search for LEDs, in the list of colors click the options that have 2 colors, and select the 3mm size if you want. Then look at the pictures to quickly confirm which have 2 wires rather than 3. Double check the datasheet before buying, as sometimes the pictures "are for reference only" which really means they just blindly copied the same image for many parts without really checking.
 
If you want the LED to show different intensity for both colors, you can achieve it using pinMode, digitalWrite, and analogWrite. For the 1st pin to be low, use pinMode to configure output mode, and then digitalWrite to drive it low. Then use analogWrite on the 2nd pin to be driven high. To show the other color with variable intensity, use pinMode output and digitalWrite low on the 2nd pin. The tricky part is you must use pinMode every time you want to switch color, since analogWrite puts the pin into a special mode. Using pinMode puts it back into the mode where digitalWrite can take control. Then configure the pin you want high with analogWrite, so you can get the intensity level you want.
 
Amazing. Thank you Paul. Checking my boxes I found some 3-pin red/green in 3mm. But they are common anode.
Can I use them with PWM intensity control (analogWrite) anyways?

I‘m thinking I could connect it to three outputs in the Teensy, with the anode always set to HIGH and the red/green set to LOW when I want them ON, otherwise all outputs HIGH to turn red/green OFF. I guess intensity values for PWM would work in reverse?
This is the part I have (picture incorrect as you predicted).
 
For the record. I managed a simple VUmeter with the mentioned 3-pin common anode red/green LED.
Each pin connected to an analog pin output of the T4, with series resistor. Red-led with series 2K, green-led with 200ohm. Total current across the anode measures around 7mA, not sure why, neither if this is safe to replicate. But it works.

Here the relevant code (it seems to consume considerable CPU).


Code:
int anodeLED = 12;
int redLED = 15;
int greenLED = 14;

elapsedMillis msecs;

void setup() {
  AudioMemory(120);

  pinMode(anodeLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);

void loop() {
 
  if (msecs > 250) {
    if (peak1.available() && peak2.available()) {
      msecs = 0;
      float leftNumber = peak1.read();
      float rightNumber = peak2.read();


      //-20dB
      if (leftNumber < 0.10) {
      analogWrite(redLED, 255);
      analogWrite(greenLED, 255);
      delay(250);
     }
 
     //20-12dB
      if ((leftNumber > 0.10) && (leftNumber < 0.25)) {
      analogWrite(redLED, 255);
      analogWrite(greenLED, 180);
      delay(250);
     }
 
      //12-6dB
      if ((leftNumber > 0.25) && (leftNumber < 0.5)) {
      analogWrite(redLED, 255);
      analogWrite(greenLED, 0);
      delay(250);
      }


      //6-3dB
      if ((leftNumber > 0.5) && (leftNumber < 0.7)) {
      analogWrite(redLED, 0);
      analogWrite(greenLED, 0);
      delay(250);
      }
 
      //3-0dB
      if ((leftNumber > 0.7) && (leftNumber < 0.98)) {
      analogWrite(redLED, 150);
      analogWrite(greenLED, 255);
      delay(250);
      }


      //>0dB
      if (leftNumber > 1) {
          analogWrite(greenLED, 255);
          analogWrite(redLED, 255);
          delay(300);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 0);
          delay(300);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 255);
          delay(300);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 0);
          delay(300);
          }
}
 
Last edited:
Regarding Domingo's recommendation to use neopixels, I'd also highly recommend them, especially if you have one of the pins that Paul's WS2812Serial library utilizes. It allows you to control n RGB leds using a single serial tx pin. So if you want to create a 20 led VU meter that goes from blue to green to yellow to orange to red, it's doable using a single pin on your teensy. I think the only wall you might run into would be related to the power the neopixels can use, but there are a few ways to limit them.

WRT your format requirements, something like the reverse mount neopixels may fit the bill. If not, there are tons of other sizes/formats as well, so perhaps spend some time giving them a look.

Only mentioning this because I wish I had used them instead of legacy leds on some projects when I first started playing around with MCUs.
 
Thank you @sndsgd, I'll take a deeper eye into neopixels but now I want to keep it really simple and I'm struggling with the code.

I need some 'delays' because otherwise blinking goes too fast. I want the LED combination to stay ON for a while if the peak goes a higher step, but the delay function blocks all other inputs and I need to push rec/stop buttons meanwhile. (I thought the code was CPU consuming because buttons where sometimes unresponsive, but actually it was the delays fault :p).

How can I implement these delays ie. with elapsedMillis? Notice that 'delay time' increases as peak steps increase and the wait should be interrupted if a higher peak follows.

Code:
int anodeLED = 12;
int redLED = 15;
int greenLED = 14;

elapsedMillis msecs;
float peak;

void setup() {
  AudioMemory(120);

  pinMode(anodeLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);

 
void loop() {
    
  if (msecs > 250) {
    if (peak1.available() && peak2.available()) {
      msecs = 0;
      float leftNumber = peak1.read();
      float rightNumber = peak2.read();
    
      if (leftNumber > rightNumber) {
      peak = leftNumber;
      } else {
      peak = rightNumber;
      }
      
      //-20dB
      if (peak < 0.10) {
      analogWrite(redLED, 255);
      analogWrite(greenLED, 255);
      delay(400);
     }
  
     //-12dB
      if ((peak > 0.10) && (peak < 0.25)) {
      analogWrite(redLED, 255);
      analogWrite(greenLED, 180);
      delay(400);
     }
  
      //-6dB
      if ((peak > 0.25) && (peak < 0.5)) {
      analogWrite(redLED, 255);
      analogWrite(greenLED, 0);
      delay(400);
      }

      //-3dB
      if ((peak > 0.5) && (peak < 0.7)) {
      analogWrite(redLED, 0);
      analogWrite(greenLED, 0);
      delay(600);
      }
    
      //0dB
      if ((peak > 0.7) && (peak < 0.98)) {
      analogWrite(redLED, 150);
      analogWrite(greenLED, 255);
      delay(800);
      }

      //>0dB
      if (peak > 1) {
          analogWrite(greenLED, 255);
          analogWrite(redLED, 255);
          delay(200);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 0);
          delay(200);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 255);
          delay(200);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 0);
          delay(200);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 255);
          delay(200);
          analogWrite(greenLED, 255);
          analogWrite(redLED, 0);
          delay(1000);
    }
  }
}
 
Last edited:
I can't say exactly how your program should be written, but here is some general advice which may (or may not) help.

I would add a variable which keeps track of the intensity level you're currently displaying. Maybe this would be a single integer where 0 means off, a certain range of numbers means green, and another high range means red. Or perhaps it would be 2 variables, maybe one for intensity and another for color. It's really a matter of style. If the red display is a natural extension of the green, I would probably prefer to keep the variable simple even if more code is needed. If they really are disjoint meaning, I'd probably go with 2 variables. But whatever way makes most sense to you is probably best.

Before restructuring the program, I would probably create a function which sets the LED color & intensity. The function should take a simple input which intuitively makes sense, even if that means more work inside the function to turn it into the analogWrite() numbers. For example, the function could take 0 to 2.0, where 0 mean off, 0.0001 to 1.0 means green, and 1.00001 to 2.0 means red. For maybe the whole range is just 0 to 1.0, where the top portion from 0.8 to 1.0 means red? The idea is to get the details of analogWrite away from your code which makes the decisions, so don't use a scale of 255 in the rest of your program, unless that truly is the way you think about the intensity. The smaller and simpler your decision code can be, the easier it will be to understand and troubleshoot. I'd make that function the *only* place which writes to that current state variable(s). Then everywhere else in your program can read it, but the code inside that function which deals with conversion to the analogWrite numbers ought to be the only place that updates the variable indicating the currently showing colors/intensity.

In loop(), I would delete all delay() and commit to never adding another delay() call.

For a first attempt, I would try use the 1 elapsedMillis variable you already have. I would try to structure the code where you process new peak info separately from the passage of time. For example:

Code:
float current_intensity = 0.0;
elapsedMillis msecs;

void loop() {
  if (peak1.available() && peak2.available()) {
    float leftNumber = peak1.read();
    if (leftNumber > current_intensity) {
      // new peak is higher
      show_on_LED(leftNumber);
    } else {
      // new peak is lower, do nothing
    }
  }
  if (msecs > 20) {
    msecs = 0;
    if (current_intensity > 0) {
      show_on_LED(current_intensity - 0.05); // decrease over time - code inside show_on_LED() to treat negative as zero
    }
  }
}

This program structure will let you immediately react to new peaks. By putting the time response as separate code, you'll also be able to write code to make the display change over time.

Again, the key to success (at least for me) on this sort of code is representing the state in a way that makes intuitive sense. I would put all the conversion of 0-255 for analogWrite and any other details of updating the hardware into a function that I don't need to view while focusing the main code which decides what to do.

You might decide to intermix the handling of new peaks and handling of time elapsing, but usually this sort of project is simpler if you can keep the immediate "live" response and code which create ongoing animation over time as separate as possible.

Not sure if this really solves your problem, but hopefully it helps.
 
Back
Top