"Smooth" or "Fade" values received from a Serial Input

I'm adjusting the brightness for a LED strip using a MAX Patch, which sends values from 0 to 255, effectively regulating it. However, I would like to know if there is a way I can smooth out or fade the values, so when it changes, it's not a sudden change, but a subtle one.

Almost certainly, yes.

Yes.

definitely yes

I assume the problem is at low levels? You can see the difference between 1 & 2, but you can't see a difference between 254 & 255, right?

I don't know what that is.

I'm not sure if there's a way of increasing the PWM resolution (16-bits would give you values between 0 and 65,536 bytes).

There are add-on chips higher resolution, or if you aren't doing anything else with the Arduino you make PWM "manually", which is simply a modification-variation of the Blink Example. The advantage of analogWrite() is that it runs in the background so the processor can do other things at the same time.

Yes, adapt the following pseudocode to your project.

  current = read "of MAX Patch";
  if (current - previous >= 0) {
    for (int x = (current - previous); x <= current; x++) {
      digitalWrite (pin_led, x);
      delay ( ? ) // adapt to see the effect according to taste
    }
  }
  else {
    for (int x = (previous - current); x >= current; x--) {
      digitalWrite (pin_led, x);
      delay ( ? ) // adapt to see the effect according to taste
    }
    previous = current ;

Very useful, guys.

You could use some kind of low-pass filter, e.g. an exponentially weighted moving average would work well, it's the digital equivalent of a first-order RC filter.

#include <Arduino_Helpers.h>
#include <AH/Filters/EMA.hpp> // EMA<>
#include <AH/Timing/MillisMicrosTimer.hpp> // Timer<>

Timer<millis> timer {10}; // Fires at 100 Hz
uint8_t target_pwm_value = 0;
EMA<5, uint16_t> pwm_filter {target_pwm_value};
// https://tttapa.github.io/Arduino-Helpers/Doxygen/d7/d40/classEMA.html
static_assert(pwm_filter.supports_range(0u, 255u), "");

void setup() {}
void loop() {
  // Simulate serial input
  static Timer<millis> fake_serial_timer {3000};
  if (fake_serial_timer)
    target_pwm_value = 255 - target_pwm_value;

  // The actual code to update the LED  
  if (timer) { // every 10 ms 
    uint8_t filtered_pwm_value = pwm_filter(target_pwm_value);
    analogWrite(LED_BUILTIN, filtered_pwm_value);
  }
}

In this example, the filter output y at iteration n is given by y[n] = α x[n] + (1-α) y[n-1], with α = 0.5⁵ = 0.03125. You can change how quickly the filtered output goes to the target duty cycle by either changing the exponent (5 in the example) or the period of the timer (10 ms in the example).

More details about the filter and the implementation in the documentation.

With this example, you'll see that the LED fades nicely every 3 seconds, but you'll also notice that the intensity is not linear in the PWM duty cycle. Small changes are much more perceptible at low duty cycles.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.