Dreamachine #3 : Progress
- Documentation Home
- GitHub Repository
- Dreamachine #1 : Background
- Dreamachine #2 : Pink Noise
- Dreamachine #4 : Squeeze!
Let me get one thing out of the way first. Arguably this thing is massively overengineered for a flashy-lights thing. What it will actually do is given in Requirements (that and the rest of the documentation is still in-progress).
Chatting with my brother about it, he pointed out that the original was made with a light bulb, record player turntable and (probably) cardboard. “Couldn’t you just use a 555..?”. Well yes, but…
I have two justifications. The first is that I think it would be useful to have a machine with which you could do genuine scientific research. Being able to accurately control the parameters and including the audio generation adds another dimension.
My second justification is that the machine has a topology I want to use on other projects (mostly music synths). I have dabbled with microcontrollers and DSP a little in the past, with Chatterbox being the latest effort. But with that, being new to the ESP32 and pretty hopeless at C++, I have wound up with spaghetti code and stalled before I got it to be properly usable. So it made sense to take a step back, build something new from scratch and try to be a bit more systematic.
Progress : Documentation
Because I want to reuse this basic system I do want to get everything properly written up. I’ve got that well on the way (this post is a key bit I was missing).
Progress : Hardware
So I have now finished the hardware on a breadboard. As soon as I get a couple more ESP32 modules I’ll solder up a more robust version on stripboard.
The hardware comprises :
- ESP32 module
- 2 x rotary encoders (with built-in push switches)
- OLED display
- minimal driver circuitry to power LEDs
- minimal driver circuitry for earpieces
A full schematic isn’t really necessary (though I’ll probably do one, I’d very much like to put the system on a PCB), right now I’ve only got a list of the connections in Wiring.
The signals for the LEDs I’m taking as PWM from two of the GPIO pins. A simple transistor amplifier acts as a driver for each channel:
As the signal is PWM, the transistors are either off or driven to saturation, so small-signal transistors can easily handle the kind of load the LEDs offer. (The PWM modulation is in the kHz range, so optical persistence of vision will comfortably filter it).
The audio out I’ve got coming from the ESP32’s DACs. They’re only 8 bit, but that’s fine for the white/pink noise. I had initially planned to include a little amplifier module but the signals seem just loud enough going straight to earpieces (through a 270R resistor and 22u capacitor in series). I’ve not played with Bluetooth, which is supported by the ESP32, but a future option might be to send the signals to Bluetooth speakers.
I’ve got it basically working and more significantly I believe I’ve done the difficult bits in a way that should make the rest straightforward.
The main difficulty with a system like this is it’s all very realtime. It needs the continuous generation of waveforms – slow for the flashing lights, fast for the noise generator for audio. It needs to respond to adjustments on the rotary encoders and update the OLED display.
Fortunately the ESP32 is quite a wonderful critter. It’s fast, includes a load of really useful peripheral interfaces, timing, interrupts etc. and has two cores. The obvious way to implement the system was to have the UI on Core 0, the signal generators on Core 1.
I’m using the Arduino Wiring-based Framework, (it’s a convenient wrapper around RTOS etc), using PlatformIO on VSCode on Ubuntu on a cheap desktop computer.
I’ve also got an ESP Prog board hooked in (on the left in the photo above) which allows proper debugging. I’ve not actually used it very much, but it has been really good a couple of times when I got stuck, couldn’t see the problem in my code.
I’ll tidy up this diagram when the machine is more like finished. But I think the key architecture is pretty well finished, if not entirely correctly shown here…
So…the ESPMachine class is the entry point. This spawns two Tasks, UI and Waves, (on cores 0 and 1 respectively) and what they do is determined by classes ESPMachineUI and ESPMachineWaves. A queue (called intercoreQueue in the code) is created to safely pass messages from the UI core to the generator core.
When one of the rotary controllers is adjusted, this triggers an interrupt (I). This is picked up (through a semaphore) and passed through a Dispatcher to callback functions. which updates the Mode and hence the display.
The Mode holds the details of the current user interface mode and has an associated modeMessage. This message is simply the number of the current mode and the latest change to the corresponding value (eg. frequency).
The modeMessage is passed through the queue over onto the ESPMachineWaves class.
Over on the ESPMachineWaves side, a low frequency oscillator LFO is continuous running, triggered by a timer (T). Another timer is used to generate a rapidly changing value for the white/pink Noise.
When a modeMessage is received the parameters of LFO and Noise are changed as appropriate (this is also done via semaphores and dispatchers/callbacks, not shown on the diagram).
Progress : TODO
The trickiest thing I have yet to do is sort out the rotary encoder handling. It basically works, but isn’t set up very well. The existing library I found didn’t really work as I wanted, so I’m in the process of modifying that.
I’ve only looked at a couple of the modes so far, but I’ve got skeleton code in place for all but one the others, the implementation will be pretty easy once I’ve nailed down the encoder bit.
The mode I’m missing is to include a timer, so the flashing will automatically stop after say 1 minute. This should be straightforward, just hang it off another hardware timer.
My original plan for the UI was to have the left rotary encoder just handling the brightness, for safety’s sake. But after playing with things a bit I think I’ll instead have the left encoder for light settings, the right one for sound.
Other tweaks are needed all over the place, but nothing demanding.
I’ll probably solder up this thing while I’m still working on the software. I very much want to put together a generalish-purpose PCB that’ll cover this application and synth stuff with external ADC, DAC and MIDI. But that’ll be a while yet.