Using the frequency analyzer on sensor expansion board

Hi!

I am trying to use the sensor expansion board to run “sound - spectrum analyzer 1D/2D” by ChrisNZ on a custom 2d matrix bikini top I created. I have uploaded the coordinates of my matrix into the mapper (confirmed matrix is correct by testing other 2D codes) and confirmed the expansion board is detected but the spectrum analyzer is not correctly analyzing the frequency of sound and is giving a very random output even in dead silence. I updated the width to March roughly the number of rows of pixels I have in my irregular matrix. I tried using a tone generator to illustrate my point. When a single tone is played only one bar should rise but it’s just appears to be random output of frequencies. When I look at the vars watch output it is giving random values even in dead silence.

I have attached a video showing the output from the ton generator and in silence.

My output looks nothing like the youtube video example.
Here is the code for reference:
/*
Sound - spectrum analyser 1D/2D

Output demo: https://youtu.be/sZIZiAt9l4o
(You can connect multiple Pixelblaze to a single sensor expansion board.)

This pattern uses the sensor expansion board and a 2D LED matrix. It displays
a spectrum analyser based on the frequency data from the microphone. This is a
real time graph where the low frequencies are plotted on the left hand side,
and higher frequencies are on the right.

This pattern is meant to be displayed on an LED matrix or other 2D surface
defined in the Mapper tab. Using the computer graphics convention, (x, y) =
(0, 0) is the top left (positive y advances downwards). You will need to set
the ‘width’ variable below to match the width of your matrix.

There’s also a 1D fallback and a spectrum simulator used when the sensor board
is not detected.

Generously contributed by ChrisNZ (Chris) from the Pixelblaze forums.
Profile - ChrisNZ - ElectroMage Forum
*/

// Set this to the width of your 2D display, or number of frequency bars to plot
width = 18
height = pixelCount / width

// Set the hue, saturation, and value for peak value indicators.
// E.g. For white peaks, set peakHSV[1] = 0. No peaks, set peakHSV[2] = 0
peakHSV = array(3) // [h, s, v]
peakHSV[0] = 0; peakHSV[1] = 1; peakHSV[2] = 1

// Get frequency information from the sensor expansion board
export var frequencyData = array(32)
// Start with an impossible value to detect if the sensor board is connected
export var light = -1

// Peak values for each bar, in the range 0…height
peaks = array(width)
// Current frequency values for each bar, in the range 0…height
fy = array(width)
peakDropMs = 0 // This will accumulate delta to drop our peaks by a pixel

// Automatic gain / PI controller. See comments in “sound - blinkfade”.
targetMax = .9 // Aim for a maximum bar of 90% full
// Approx rolling average of the maximum bar, for feedback into the PIController
averageMax = 0
pic = makePIController(.25, 1.8, 30, 0, 100)

function makePIController(kp, ki, start, min, max) {
var pic = array(5)
pic[0] = kp
pic[1] = ki
pic[2] = start
pic[3] = min
pic[4] = max
return pic
}

function calcPIController(pic, err) {
pic[2] = clamp(pic[2] + err, pic[3], pic[4])
return pic[0] * err + pic[1] * pic[2]
}

export function beforeRender(delta) {
// Calculate sensitivity based on how far away we are from our target maximum
sensitivity = max(1, calcPIController(pic, targetMax - averageMax))

hueT = time(1 / 65.536) // 1 second hue rotation

peakDropMs += delta

// Drop all the peaks every 100ms
if (peakDropMs > 100) {
peakDropMs = 0
for (i = 0; i < width; i++) peaks[i] -= 1
}

if (light == -1) simulateSound() // light is >= 0 if the SB is connected

currentMax = 0
for (i = 0; i < width; i++) {
logy = log(i / width + 1) // Plot lower bins (log of 2 = bottom 30%)
// Determine the portion of the bar filled based on the current sound level.
// We use the PIController sensitivity to try and keep this at the targetMax
powerLevel = frequencyData[logy * 32] * sensitivity
fy[i] = floor(min(1, powerLevel) * height)
peaks[i] = max(peaks[i], fy[i] - 1)

currentMax = max(currentMax, powerLevel)

}
averageMax = averageMax - (averageMax / 50) + (currentMax / 50)
}

export function render2D(index, x, y) {
xPixel = floor(x * width) // Converts 0…1 ‘world units’ x into pixel width
yPixel = height - 1 - floor(y * height) // Invert so baseline is yPixel == 0

h = hueT + x // Cycle the bar color through the rainbow. hsv() ‘wraps’ h.
s = 1
v = fy[xPixel] > yPixel // Fill bars from 0…fy[xPixel]

// If this is a peak pixel, apply the peakHSV color
if (peaks[xPixel] == yPixel) {
h = peakHSV[0]; s = peakHSV[1]; v = peakHSV[2]
}

hsv(h, s, v)
}

// The 1D fallback plots the raw 32-bin spectrum across all pixels in a strip
export function render(index) {
h = hueT + index/pixelCount // Cycle bar color. Remember, hsv() ‘wraps’ h.

// Spread all 32 bins across the strip and interpolate
binPixelWidth = pixelCount / 31
LBin = floor(index / binPixelWidth)
RBinPct = (index % binPixelWidth) / binPixelWidth
v = (1 - RBinPct) * frequencyData[LBin] + RBinPct * frequencyData[LBin + 1]
v *= sensitivity // Scale by PI controller’s sensitivity

hsv(h, 1, v * v)
}

/*
Simulate the sensor board variables used in this pattern, if no sensor board
is detected. The values and waveforms were chosen to approximate the look when
real sound is sensed for a basic 4-on-the-floor loop.
*/
BPM = 120
var measurePeriod = 4 * 60 / BPM / 65.536

function simulateSound() {
tM = time(measurePeriod) // 2 seconds per measure @120 BPM
tP = time(8 * measurePeriod) // 8 measures per phrase
for (i = 0; i < 32; i++) frequencyData[i] = 0

beat = (-4 * tM + 5) % 1 // 4 attacks per measure
beat *= .02 * pow(beat, 4) // Scale magnitude and make concave-up
// Splay energy out, most energy at lowest frequency bins
for (i = 0; i < 10; i++) frequencyData[i] += beat * (10 - i) / 10

claps = .01 * square(2 * tM - .5, .10) // “&” of every beat
for (i = 9; i < 14 + random(10); i++)
frequencyData[i] += claps * (.7 + .6 * random(i % 2))

highHat = .003 * square(4 * tM - .5, .05) // Beats 2 and 4
for (i = 20; i < 30; i++) {
frequencyData[i] += highHat * (.8 + random(.4)) * (i % 3 < 2)
}

lead = 4 + floor(16 * wander(tP)) // Wandering fundamental synth’s freq bin
for (i = 4; i < 20; i++)
// Excite the fundamental and, 20% of the time, 4 bins up
frequencyData[i] += .005 * (lead == i || lead == (i - 4) * r(.2))
}

// Random-ish perlin-esque walk for t in 0…1, outputs 0…1
// Pixelblaze wander | Desmos
function wander(t) {
t *= 49.261 // Selected so t’s wraparound will have continuous output
return (wave(t / 2) * wave(t / 3) * wave(t / 5) + wave(t / 7)) / 2
}

function r(p) { return random(1) < p } // Randomly true with probability p

This can be caused by power quality issues. If you LEDs are drawing a lot of current it can cause the voltage to fluctuate wildly on weak power supplies and that gets picked up just like sound in the sensor board. Wiring from the power supply can also come in to play as it can cause voltage drop as well.

Try dropping brightness way down or disconnecting LED. If that fixes it and it starts behaving normally you know it’s power related.

Some power supplies might just have very noisy power even at low power too. Try powering it from a computer usb with no LEDs, see if the live preview and vars are sane.

Sometimes adding large caps can help, like 470uF.

Thank you so much for the reply!

So this would explain why I had this working on my test matrix that was only 50 LEDs but now that I am attempting a 322 LED matrix it is not working correctly.

So I tried turning the brightness way down and no difference BUT then I tried turning half the LEDs off so only 161 were on and it works correctly (looks just like the youtube video example)!!! So as you said it seems to be an issue with too many LEDs drawing too much current. Unfortunately I can’t lower the number of LEDs by more than 50 maybe but its already installed so I would rather not start over.

So I have the LEDs wired directly to the power supply which I was using an adafruit Lithium Ion Polymer Battery - 3.7v 2500mAh for testing. I switched to a 3.7v 4400mAh battery and the reactivity improved! It is still not perfect (is not as accurate as the youtube video or when I only had 161 LEDs) but it is much better. I can’t do much better than the 4400 on the tops because I don’t have space for a larger power supply.

If I were to try adding the 470uF in line with the power supply would this potentially make the power less noisy while running 322 LEDs?

Yes, so you are probably fighting resistance of the battery. A capacitor can help smooth things out but it will still have voltage drop but hopefully the fluctuations are lower frequency and won’t show up in audio as much. It’s a cheap and easy thing to try.

Some batteries have higher C ratings even for the same size/capacity. Hobby RC batteries used for quadcopters for example can push out dozens to hundreds of amps with extremely low resistance for even super tiny batteries.

I would also caution having a fuse so an accidental short doesn’t turn into a fire. Many of those won’t have short protection built in.

1 Like

I will give the cap a try.

Regarding the hobby RC batteries is this what you were referring to: https://hobbyking.com/en_us/turnigy-heavy-duty-5200mah-3s-60c-lipo-battery-pack-w-xt90.html
If not, would you mind linking a battery you have used with the pixel blaze?

Does the configuration matter: 6S vs 4S vs 3S?

So these hobby RC batteries don’t have any protection circuitry? That is an advantage of the current LiPos I am using. Definitely want to avoid a fire as I am wearing the piece.

You probably want 1S. The number is how many 3.7v cells are in Series. More than one “S” would fry 5v LEDs. However 3S can power 12v LEDs. Otherwise you need to add a power converter, which adds bulk, cost, and complications.

Right, most of the really high “C” lipos don’t because that protection circuitry would trip for the high current loads of the aircraft. Many cheap low end ones don’t either. When in doubt a fuse is your friend.

These might work

https://a.co/d/gsLrFIF

You could also run lower rated protected cells in parallel, just be careful to always make sure they are around the same charge level when connecting.

Worth trying the cap first.

Update: the cap worked!! The frequency feedback is much smoother and more accurate using the same battery.

Thank you so much for the tips!

2 Likes

So you wouldn’t need to boost the voltage of these drone batteries to power a 5v pixelblaze project? If there is no protection circuit then you can get by with less than 5v?