Fading a LED using PWM and VirtualDelay library

Hello guys, I’d like to share with you a bit of the code I’m writing for the bike led lamp I bought on AliExpress…

While fiddling around with it, its MCU (so-8 without any label) apparently died and I couldn’t bring it back so, as I had an ATtiny85 laying around, I thought “why not use my own MCU and program it myself?”.

Long story short: I decided to add new features, new blinking modes, turn signal and horn sounds (the lamp has a piezo speaker) so as there’s a lot of things going on in the loop(), I had to mostly give up on using delay().

The simple solution was to use avdweb’s VirtualDelay library. If you are not familiar with it, you just set it up like a normal library inicialization, and when the time comes, you start the timer using a library function with the value of milliseconds you would use as if it was a normal delay(), and check if the time to proceed has come using another library function. It’s pretty useful and straightforward.

However, it’s apparently not possible (or very hard) to use this with a for() loop. (at least I couldn’t make it work, I’m not an expert myself)

I wanted to do a simple LED fade using a single for() loop as described in Arduino/Reference/For. The original code is as follows:

void loop() {
  int x = 1;
  for (int i = 0; i > -1; i = i + x) {
    analogWrite(PWMpin, i);
    if (i == 255) {
      x = -1;  // switch direction at peak
    }
    delay(10);
  }
}

Works great with a delay() but doesn’t seem to work at all with the VirtualDelay, mostly because for() apparently does not accept values returned by functions as a condition. I couldn’t find anything related on Google and had to come with my own solution.

My solution was to convert the for() loop to a if() loop, which can accept the values needed for the library to work properly. This is the code I came up with, already modified to work with the VirtualDelay.

#include <avdweb_VirtualDelay.h>

int x = 1;
int i = 0;
int ledPin = 9;

VirtualDelay ledDelay;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  FadeLEDPwm();
}

void FadeLEDPwm() {
  
  DO_ONCE (ledDelay.start(5)); // This is a library macro to prevent a deadlock. The following
                               // function needs a kickstart to work.
  
  if (i > -1 && ledDelay.elapsed()) {
    i = i + x;
    analogWrite(ledPin, i);
    if (i == 255) {
      x = -1;
    }
    else if (i == 0) {
      x = 1;
    }
    ledDelay.start(5);
  }

}

I’m sorry if I talk too much, and I hope this helps someone that is also fiddling around with this library.

github.com/avandalen/VirtualDelay

I love the VirtualDelay library. Its pretty helpful! However, I don't think it works correctly on the AtTiny 85 unfortunately!

In this test I found that I was getting a countdown of roughly 4.1 seconds:

ledDelay.start(500);

The default is millis so that is way off! If it works for small timing changes then go for it, but I wouldn't trust it on this platform! The following warning is issued during compilation which hints there might be trouble:

WARNING: library avdweb_VirtualDelay claims to run on (atmelavr, atmelsam) architecture(s) and may be incompatible with your current board which runs on (avr) architecture(s).

Here was my full test code:

#include "avdweb_VirtualDelay.h"

int ledPin = 0;
bool ledToggle = true;

VirtualDelay ledDelay;

void setup()
{
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  DO_ONCE (ledDelay.start(500));

  if (ledDelay.elapsed())
  {
    digitalWrite(ledPin, ledToggle);
    ledToggle = !ledToggle;

    ledDelay.start(500);
  }
}

I'm not so sure you tested this code on an AtTiny as written. You make use of Pin9 which is not possible on an AtTiny.

Here's an alternative version with slightly less code involved in the fade direction. Might be confusing to some though! One with delay and one with the virtual delay which I advise not to use.

int i = 1;
int ledPin = 0;
bool fadeDirection = true;

void setup()
{
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  i += fadeDirection ? 1 : -1;
  analogWrite(ledPin, i);
  if (i == 0 || i == 255)
  {
    // flip the fading direction
    fadeDirection = !fadeDirection;
  }

  delay(30);
}
#include "avdweb_VirtualDelay.h"

int i = 0;
int ledPin = 0;
bool fadeDirection = true;

VirtualDelay ledDelay;

void setup()
{
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  DO_ONCE (ledDelay.start(5));

  if (ledDelay.elapsed())
  {
    i += fadeDirection ? 1 : -1;
    analogWrite(ledPin, i);
    if (i == 0 || i == 255)
    {
      // flip the fading direction
      fadeDirection = !fadeDirection;
    }

    ledDelay.start(30);
  }
}

Sn3akyP3t3: I love the VirtualDelay library. Its pretty helpful! However, I don't think it works correctly on the AtTiny 85 unfortunately!

In this test I found that I was getting a countdown of roughly 4.1 seconds:

ledDelay.start(500);

The default is millis so that is way off! If it works for small timing changes then go for it, but I wouldn't trust it on this platform!

Hey, that's weird. I didn't notice anything strange when I started using my ATtiny with this library. The delays seemed fine and when testing a 15 minutes delay with it (using my phone's stopwatch), it seemed pretty accurate. I'm going to measure the small delays though, which I didn't.

The following warning is issued during compilation which hints there might be trouble:

WARNING: library avdweb_VirtualDelay claims to run on (atmelavr, atmelsam) architecture(s) and may be incompatible with your current board which runs on (avr) architecture(s).

Also, I'm not getting that warning (full verbose output enabled, of course). I'm using SpenceKonde's ATTinyCore, if it matters (EDIT: I'm running it at internal 16Mhz PLL. I haven't tried it with other frequencies). I actually used pin 9 in the example I posted because my actual code was a bit too messy to post here (as I'm not a native English speaker) and because of that all of my variables were named in my language. (I'm from Brazil, btw)

Thanks for the code, I'm going to try it out. I think I'm going to use a Pro Mini instead of the ATTiny, as soon as possible, I'm already reaching its memory limits.

mostly because for() apparently does not accept values returned by functions as a condition.

Yes, it does. You can use a function as a condition in a for loop no problem. Post the code that failed.

My solution was to convert the for() loop to a if() loop

if statement, not if loop. If doesn't loop. The loop function loops. Which is why you don't really need the for loop if you code right.