I’ve been working on a project that I think is one of my stupider ones, and it’s time to share it so that I can move onto something that is slightly less stupid.
I build a little board that I sell on Tindie that allows you to use the WS2812 addressable LED protocol to drive bigger loads; so instead of having a tiny LED you could use spotlights in three colors.
It has a WS2811 IC, some transistors to act as inverters, and then three hefty power MOSFETs to handle the load. It’s a pretty hefty circuit, but to be conservative I rate the the boards at 4 amps per channel with all three channels driven – which would be 50 watts per channel if running on 12V – or 8 amps with one channel driven.
I therefore need a test circuit to validate that these are working okay, and that’s simple an ESP8266 doing some dimming across the loads.
And for the loads, I have a series of four 12V 50 watt light bulbs – these would be used in RVs before LEDs became common. They’re nice and hefty and it’s easy to see if they are working. Here’s a video that shows the text fixture and the bulbs working:
If you pay attention carefully – perhaps turn up your volume a bit – you will hear that the light bulbs are making a whining noise. That is because the WS2811 is running PWM at 2 Khz, smack-dab in the middle of the audible frequency range, and the light bulb filaments are vibrating at that frequency.
That was just a curiousity that I filed away, until I was testing a new set of boards and I had a thought:
What if I could change the frequency of the PWM?
And that little idea trigger a bunch of stupidity…
I dug into my supplies and found I had a bunch of unused ESP-32 boards leftover from when I ordered the 36-pin version instead of the 30-pin version.
I had sample code that did 16-channel PWM on the ESP-32 – the ESP-32 is almost ridiculously capable in some areas – and I ported that over to a new version, took one of my driver boards and pulled off the WS2811, wired them together, and got… nothing.
Played around some more, got more nothing. I just could not get it to work. The board would work fine when I drove it directly, but the ESP could not drive it. I also had trouble getting the LED on the board to work; it barely lit up at all. That should have been enough, but it took me an embarrassingly long time to figure out what I think is the problem…
Basically, these boards have a 3.3v regulator on it that is not up to the task. It claims to be an AMS1117, which should be a fine choice; that regulator has nice specs and can put out up to an amp of current; plenty for the ESP32. AFAICT, what it actually has is a cheap counterfeit that just barely puts out enough power for the chip to run but not to do anything useful. I could measure the outputs of the ESP and they looked fine as long as I didn’t put any load on them. I dredged a memory out of my brain that others had run into this issue, so now I’m left with a bunch of boards that don’t work, though I’m considering replacing the regulator to see if that helps.
I dug out one of my other ESP-32 boards, moved the code over, and it worked great on the first try. So don’t buy the other boards…
From concept to “music”
This section also took much longer than it should have, so I’m just going to mention the highlights…
The midicsv program is your friend if you want to convert songs into arrays of note values.
Midieditor was quite useful to simplify arrangements and fix up some of the midi notes, though the documentation is mostly absent.
Note quantization can be very useful if the midi file you download ends up with a new note starting quite a bit earlier than the note that it is replacing.
Excel is your friend when manipulating CSV files.
Midi tempo is complex. Basically, the note timing is expressed in terms of “ticks”. The header in the midi file specifies how many ticks there are per quarter note, sometimes expressed as “parts per quarter note” or PPQ. It will be something like 256 or 480.
Tempo is expressed in the number of microseconds per quarter note, and will be something like 500,000.
So, with a bit of math, you can determine:
tick time in seconds = (tempo / 1000000) * (1 / PPQ).
Then, based on the difference in ticks between when a note starts and when it ends, you can figure out the delay.
ESP-32 LEDC Fun
Conceptually, I wanted to have different PWM channels running at different frequencies. This turnout out to be far more problematic than I thought.
My first mistake was using the ESP-32 arduino HAL (hardware extraction layer) functions for the LED control subsystem. Conceptually, all I should need to do is:
And that’s what I did. And it sorta-kinda worked in some cases, didn’t work in others. I finally called ledcReadFreq() and looked at the values I was getting back. Channels 0 and 1 were always the same frequency, but channel 2 was a different frequency. Hmm…
After reading the non-arduino docs for the led control system, I came across the docs for ledc_set_freq(), which had a parameter named “timer_num”, and the description said “LEDC timer index (0-3), select from ledc_timer_t”.
Ahh… There are only 4 timers that you can use, not 16. Unfortunately, the HAL abstraction gives the impression that you can set frequency individually on each control channel. You can, it just affects other channels as well. Digging into the source, I found this table:
* LEDC Chan to Group/Channel/Timer Mapping
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
Ah. I’m not sure why the mapping works the way it does, but that’s the way it does, and channels 0 and 1 always use the same timer. And I think that’s a decent reason not the use the HAL, but since my code was written and I was lazy, I just mapped my channels 0, 1, 2 to 0, 2, and 4, and things were fine.
Which finally led to me declaring success, and I can therefore offer up the following videos.
Here’s a project video that shows the result. The light bulbs are terribly inefficient; with 40-50 watts of input they produce a very tiny amount of sound.
So that’s the stupid project. The software supports 3 channels but I burned out the third channel on my board when I was having issues with the crappy ESP, and since two channels is barely discernable, there’s no reason to go farther. If you would like to see the stupid code, it is here.