dbA -> RGB LED colour change = choppy transitions

I have a DFRobot Gravity: Analog Sound Level Meter and a 144 LED/M APA102 Digital RGB LED Light Strip wired to an Adafruit Feather HUZZAH with ESP8266. The sound level meter outputs 0.6 - 2.6 V but the ESP8266' ADC only takes 1 V maximum, so I made a voltage divider from a 220 Ohm and 330 Ohm resistor, which is all I have in the field, so to speak. With my set-up, I obtain dbA values from 37 to around 87 near a runway, which I put through a running median filter with a window of 11 samples, the output from that translated to a HSV colour transition from green to red via yellow. This works, but the visual impression from the RGB LEDs is somewhat choppy, see the raw and running median output in the image below. I'm not using delay() or for-loops in loop(), but it seems something is running too slow here, or I am doing something wrong in code. I can't use the FastLED library because that interferes with the ESP8266' WiFi TX capability.

Any ideas how I can do this better? At least, a more gentle fall-off back from red via yellow to green after a noise-peak would be good.

#include <RunningMedian.h>
#include <SPI.h>
#include <Adafruit_DotStar.h>
#include "config.h"

#define NUMLEDS 144
#define DATAPIN 13
#define CLOCKPIN 14
const byte pinSEN0232 = A0;
const int intervalSaveSEN0232 = 2000;

unsigned long timeNowSaveSEN0232 = 0;
long dbValue = 0;
long dbValueMedian = 0;

RunningMedian samples = RunningMedian(11);
AdafruitIO_Feed *dezibel = io.feed("Dezibel");
Adafruit_DotStar myLEDStrip(NUMLEDS, DATAPIN, CLOCKPIN, DOTSTAR_BRG);

void setup()
{
  Serial.begin(115200); while (! Serial);

  io.connect();

  while (io.status() < AIO_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }

  Serial.println(); Serial.println(io.statusText());

  myLEDStrip.begin();
  myLEDStrip.show();
}

void loop()
{
  io.run();

  readSEN0232();

  if (millis() - timeNowSaveSEN0232 >= intervalSaveSEN0232)
  {
    timeNowSaveSEN0232 = millis();

    dezibel->save(dbValue);
  }

  int hueLED = constrain(map(dbValueMedian, 35, 80, 0, 21845), 0, 21845);

  uint32_t valColour = myLEDStrip.gamma32(myLEDStrip.ColorHSV(hueLED));

  myLEDStrip.fill(valColour, 0, 11);

  myLEDStrip.show();
}

void readSEN0232()
{
  float voltageValue;

  voltageValue = analogRead(pinSEN0232) / 1024.0 * 3.0;

  dbValue = voltageValue * 50.0;

  samples.add(dbValue);

  dbValueMedian = samples.getMedian();

  Serial.print(dbValue); Serial.print(" "); Serial.println(dbValueMedian);
}

You could try avoiding use of float calculations. These are not fast on most MCU because almost none of them have any hardware floating point acceleration. Try changing

 float voltageValue;

  voltageValue = analogRead(pinSEN0232) / 1024.0 * 3.0;

  dbValue = voltageValue * 50.0;

to

 dbValue = analogRead(pinSEN0232) * 3 * 50 / 1024;

Next, try reducing the frequency you update the strip. No point going any faster than about 20 updates per second. Even though you are only using the first few LEDs, the entire 144 get updated each time .show() is called, and that is probably slow. So use the same millis() technique you used for the AdafruitIO_Feed to update the strip only once every 50ms.

Every time loop() runs, you print values to the serial plotter. Once you have limited the strip update frequency, this printing of values probably slows loop down more than anything else, so try commenting out those prints to see if that makes a difference to the led visualisation.

When you take a sample you get a reading of the instantaneous value of the voltage at that point in time. What you are trying to measure is the peak value of the input waveform. What you are doing will produce choppy results, as you call them.

What you need to do is to feed your signals through an Envelope detector first before sending it into the Arduino.

Note this simple one requires the signal is at least 0.7V before it will show anything. There are other more complex detectors using an operation amplifier to get detection at lower voltage levels.

As to holding on to a peak longer, that can be done by increasing the resistor value across the capacitor.

Mike, the OP does appear to be using a "sound level meter" which I think may include an envelope follower circuit, it's not simply an amplified microphone. Have a look at the link in the original post and see what you think.

Thanks PaulRB, I did not think of the floating point issue and I will remove the serial printing, which the final set-up does not need anyway. Updating the strip "manually" sounds like a good idea, too.

Compared to the €200 Testo device, Testo usually being a quality company (excellent data-loggers and K-probes for example), the fairly involved €40 DFROBOT PCB holds up pretty well (aimed both in the same direction).

But, yes, Grumpy_Mike, what you mention about peaks rings a bell, it reminds me of peak meters on HiFi equipment from the 80s, where there was a peak-LED that sort of lingered a bit and then decayed.

I think Mike's concern was that you were using a simple amplified microphone module, and the signal being fed to the Arduino was the audio frequency waveform (AC or perhaps with an offset to prevent the signal going negative) and your samples could be captured at random points in that audio waveform, rather than representing the overall sound volume level.

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