Educational DIY Synth Thing – Part 2

In the first part I described the concept and basic design parameters I was looking for. In this part I go into detail about how I’m going about implementing them with an ESP32 WROOM board.

  • Part 1 – This introduction and high-level design principles.
  • Part 2 – Detailed design of an ESP32 based PCB.
  • Part 3 – Software design.
  • Part 4 – Mechanical assembly and final use.
  • Part 5 – Six simple experiments to try.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

The ESP32 EduMod Hacked Synth Thing

The principles and code I’m using are taken from these previous posts:

From the experience gained so far, I’ve gone for the following:

  • Use the ESP32 DACs for the output for two envelope generators, each driven by four pots, a trigger and a gate signal.
  • Use the ESP32 PWM outputs for two VCOs, each with four waveforms (sine, triangle, saw, square) and an analog input (pot or CV) for frequency and amplitude.
  • Use another ESP32 PWM output for a LFO with rate and depth analog controls.
  • Use the ESP32 UART for MIDI in.
  • Use a TDA7052A as the final VCA/audio output stage.

Adding all this up, and taking the view that the VCOs will be supporting simultaneous waveforms on four pins each (rather than any kind of waveform selection input), I’m going to need some kind of IO expander. The easiest thing is probably to use an analog multiplexer for the twin ADSR and LFO controls. I’m designing this around a CD4067 analog multiplexer which provides 16 input channels and will require 4 IO pins for control and 1 IO pin for the analog value to read.

This leads me to the following pin usage, using the same cheap ESP32 WROOM module, I’ve used for my other experiments so far:

ENGPIO23PWM OUTVCO 2 Square
EG 2 GateDig InputGPIO36GPIO22PWM OUTVCO 2 Saw
EG 2 TriggerDig InputGPIO39GPIO1PWM OUTVCO 2 Triangle
EG 1 GateDig InputGPIO34GPIO3RXD 0MIDI IN
EG 1 TriggerDig InputGPIO35GPIO21PWM OUTVCO 2 Sine
ALG Mux InputAlg InputGPIO32GPIO19PWM OUTVCO 1 Square
ALG Mux S0Dig OutputGPIO33GPIO18PWM OUTVCO 1 Saw
EG 1 OUTDAC 1GPIO25GPIO5PWM OUTVCO 1 Triangle
EG 2 OUTDAC 2GPIO26GPIO17PWM OUTVCO 1 Sine
ALG Mux S1Dig OutputGPIO27GPIO16PWM OUTLFO Triangle
ALG Mux S2Dig OutputGPIO14GPIO4Alg InputVCO Amp
ALG Mux S3Dig OutputGPIO12GPIO2Alg InputVCO 2 CV
LFO SawPWM OUTGPIO13GPIO15Alg InputVCO 1 CV
GNDGND
VIN3V3

One thing I hadn’t considered when defining the above pinout (and subsequently designing a circuit around it) is the ESP32’s strapping pins. There are six pins that are used as configuration pins on boot to set the various mode and operation of the device and of course it assumes the use of the UART. Once booted, they can usually be used as normal GPIO pins, but if connected circuitry changes the state on boot, then unexpected things may occur.

See the discussion below for further details, implications, and things I’d wish I’d realised before putting a design together 🙂

The analog inputs for the Mux are defined as follows:

1 (I0)Not used
2 (I1)LFO Rate
3 (I2)LFO Depth
4 (I3)EG 1 Attack
5 (I4)EG 1 Decay
6 (I5)VCO 1 Pitch CV
7 (I6)VCO 1 Amplitude CV
8 (I7)VCO 2 Pitch CV
9 (I8)EG 1 Release
10 (I9)EG 1 Sustain
11-12 (I10-I11)Not used
13 (I12)EG 2 Release
14 (I13)EG 2 Sustain
15 (I14)EG 2 Decay
16 (I15)EG 2 Attack

Basic electrical properties and design principles

  • All internal control signals (CV, triggers or gates) will be using 3V3 levels. All internal audio signals will be DC biased to the 0-3V3 range.
  • Internal patch points will be using jumper wires to make it clear that these are not “proper” modular signals for general patching outside of the unit.
  • A special input will be provided for a MIDI IN socket.
  • CV, trigger and gate signals will include basic protection to limit them to the 0-3V3 levels for use within the system.
  • An output stage will be provided that will support either a directly connected small (8Ω) speaker or a mono line out jack.
  • Power will be duplicated to a header on the side of the unit to allow for easy connections to breadboards.
  • The PCBs will fit in a 100x100mm footprint to keep costs down, but I might end up with several per unit.
  • All code will run on a single ESP32 WROOM dev module that seems easily, and cheaply, available.

There will be some, but limited protections on internal jumper connections. I’m working on the basis that it should be fairly cheap to build, all chips should be relatively cheap and easy to replace (and therefore socketed), so educated experimentation is encouraged, but uneducated plugging in will probably break something.

As long as everything stays within the 0-3V3 range, it should probably be ok, though.

ESP32 Strapping Pins

Section 2.4 of the ESP32 datasheet (“Strapping Pins”) has all the details. Essentially a number of pins are pulled high or low using the internal pullup or pulldown resistors. These define the boot configuration for the board and can be overridden by external pullup or pulldown resistors.

One or two other pins have additional circuitry too on a typical DevBoard.

The key GPIO pins used as Strapping pins are:

  • GPIO 0 is the boot button, so I’m not worried about that. However to enter boot mode both GPIO 0 and GPIO 2 must be LOW. If my use of GPIO 2 is an issue, then I’ll have to flash the board before plugging it in. Once plugged in and booting from flash, then the state of GPIO 2 is irrelevant to the ESP32. GPIO 2 is pulled LOW via an external 10K resistor though on my dev board too.
  • GPIO 2 on my DevKit board is also connected to the onboard LED with a 1K resistor to ground. This means that it’s use for an analog input was probably a mistake! It is possible to remove the onboard LED if required, but otherwise this means that the analog input range will be somewhat decreased to around 2.3V.
  • GPIO 4 is pulled LOW. I don’t know what the significance of this is, but as this is connected to a CV input stage, when nothing is plugged in, this will be default pulled LOW externally too. I don’t know what will happen if a CV is present on boot…
  • GPIO 5 is internally pulled HIGH. It is connected to a PWM output circuit, so I might be ok. To be determined…
  • GPIO 12 (MTDI) is pulled LOW. It is connected to the MUX address pin, which is a digital input pin. If that works like a microcontrollers input pin then it will have a high impedance in which case, this should be fine.
  • GPIO 15 (MTDO) is pulled HIGH. This determines if the boot is “silent” or not – i.e. if startup messages are sent to the UART. As I’m using the TX pin (GPIO 1) as a PWM output, it would be advantageous actually to have this LOW to stop any output, but either way it shouldn’t be a big deal. This pin is connected to a CV input stage with a default pull LOW resistor if nothing is plugged in, but I suspect the internal pull-up to be the stronger of the two and most likely to “win” out.
  • GPIO 1 and 3 are TX and RX respectively. RX is connected to the MIDI IN circuit, so this means that board can’t be programmed while connected anyway. TX is configured for a PWM output circuit so there may be spurious output signals on startup for a short period of time on this output.

There is a good summary of some of the implications of attempting to use the strapping pins on this page here.

From the above discussion, I think I might get away with it for the most part (although GPIO 2 is annoying), but I’ll have to see once the boards are made up and do some proper tests. At present, I suspect the most significant ramification of the above is that I won’t be able to program the ESP32 module whilst plugged in, but that is likely to be as a result of the MIDI circuit on RX rather than anything else.

Update after testing:

  • GPIO 0: not relevant.
  • GPIO 2: as mentioned above, GPIO 2 will not present the full range as the analog input. I believe this is related to the voltage drop across the LED so one option is simply to desolder the LED or its resistor from the board. Also, when not connected the minimum reading is equivalent to 400-600mV rather than zero.
  • GPIO 4: this largely works, but when not connected does seem to not quite read full zero, but that could just be noise.
  • GPIO 5: no noticeable impact.
  • GPIO 12: no noticeable impact.
  • GPIO 15: this works fine with an analog input connected, but like GPIO 4 doesn’t quite get to zero when not connected.

ESP32 VCOs and LFO

These will be based on the ESP32 and PWM experiments – I’m planning to include two VCOs and a LFO.

The VCOs will take an analog input – a control voltage for frequency which can come from a pot or an external signal. One of the VCOs will also include a CV for amplitude, to allow some simple shaping (e.g. from the LFO). Both will output four PWM signals: sine, triangle, saw, square – each on its own output pin. I’ll use the built-in tone() function for the square wave – that won’t need a DDS approach.

The CV input will be using a 1V/oct scale, so the input range will be relatively narrow – just over three octaves. I do plan to hook up a MIDI input to the ESP32’s UART so that can feed a frequency directly into one of the VCOs without going via the analog stage, to give a wider range over MIDI only.

Of course, I need to decide what note/frequency 0V will start at.

Control Voltages

Each VCO analog input stage will consist of a pot which can act as a manual control voltage or as some kind of control for an external control voltage. But should the pot add to the external voltage or limit (attenuate) it? If it adds, then it would provide the ability to set an offset to the external signal. If attenuating then it affects the range of the external signal.

I originally went with the following circuit, which is largely based on the input stage of the MiniMo (ATtiny85 based) synth.

But the operation of the pot wasn’t quite what I wanted. With no CV, then the pot allows adjustments between 3V3 and 0V. But when a 3V3 CV is applied to the input, then there is effectively now a voltage divider happening which reduces the peak-to-peak range of the input signal by half and then moves the DC bias between 0-1.5V through to 1.5-3V (or there abouts). Given the limited voltage range anyway, I really wanted to be able to pick up the full 0-3V3 range of the CV if possible.

If all I want is attenuation then one way to achieve this would be to use a switched jack, where the switch will link the pot to the CV when a jack is inserted and to 3V3 when it isn’t, but I wanted to be using jumper wires with no switches.

I’ve gone for summation of the signals, so in the end I decided I’d just do it in software and wire the pots in directly to an analog input then construct a pot-less CV input stage.

As the CV input will be limited at 0-3V3 it will need to be protected against over and under voltage. I’ve seen a range of approaches to achieving this, but in the end I took the basic principles from the modules designed by Hagiwo, resulting in the following circuit:

As I understand things, for the CV input, the BAT43 zener diodes will “clamp” the input CV to GND or 3V3 in the case of under or over voltage; whilst the values of the resistors (1K/680K) mean that there is a sensible impedance but little voltage change. In some cases, I’ve seen the use of a voltage divider here to change a 5V signal into a 3V3 signal. That might be ok if the microcontroller then still treats the 3V3 signal as a “full range” (5 octave) signal, but in my case I want to preserve any 1V/Oct input on any intermediate signal, even if that then leaves me with a smaller three octave+ (0 to 3V3) range.

PWM Output Filtering

Each PWM output will be in the 0-3V3 range, at typical audio frequencies, so a simple low-pass filter will be added as follows:

This is a simple one-stage low-pass filter with a 3db point of just under 5kHz which hopefully gives a nice smooth output. I tried 470, 680 and 1K resistors and 68nF and 100nF capacitors and couldn’t see a significant difference at my test 440 Hz signal, so went with the lower values for a higher cutoff to support more audio frequencies of notes.

The LFO will have two pot controls – rate and depth (amplitude) and no external control. It will provide a triangle and saw PWM output.

Note: If the capacitor is left off the square wave signal then a sharper square wave is possible, so I’ll leave these as optional on the circuit board itself, but will probably leave them out in my build. This makes sense as the sharpest square wave would be a continuing series of odd harmonics, so if I’m filtering out any above 5kHz that will take away quite a few!

In fact, as already mentioned, in code it probably makes sense for the square wave to be a tone() signal rather than a PWM output anyway, in which case the sharpest signal would be with no resistor either, but I suspect some resistance is necessary to protect against whatever the pin ends up driving. Keeping with 470 will match the other outputs, but an even sharper signal is possible with 220 instead. I’ll stick with 470 to keep everything matched. I will be using tone() rather than PWM in the code.

Envelope Generator

The gate and trigger connections for the two envelope generators will allow external connections. This could be a button or an external active HIGH signal. To get these into the microcontroller, requires a circuit like the following (I’ve found various versions of this online, but I think the definitive version came from Rich Holmes – at least on the forums I was looking at anyway):

Again, as I understand things, the use of a diode and transistor provides over and under voltage protection, the resistors provide current protection, and the 1M to GND resistor ensures the input isn’t floating if unconnected.

One issue with this circuit is that the signal to the microcontroller is inverted – i.e. it will be default HIGH until the gate is activated in which case it will become active LOW. The code in the microcontroller has to take this into account.

There will be four of these circuits, one for each of the gate and trigger inputs for each of the two envelope generators.

A manual gate or trigger can be built from a switch that connects the EGn_EXT_yyyy signal to 3V3, with a suitable capacitor for debouncing.

Other External Signals

An external connection will be provided for MIDI IN which will be my standard 3V3 compatible, H11L1 optoisolator based MIDI circuit, so no surprises there. I’m not providing MIDI out.

This will go directly into the ESP32’s RX pin for UART 0 and processed in software to generate an internal CV/gate/trigger signal. This CV/gate/trigger won’t be visible outside the microcontroller – it will be the one part of the system that remains entirely within software. This means that MIDI should extend to a full range of pitches, not just those within the 0-3V3 1V/oct range.

There will be no special hardware for USB, so if USB MIDI is supported in the end, then it will be via the ESP32’s built-in USB port, but that might complicate the power circuitry.

VCA and Audio Output

As already mentioned, the last stage will make use of the TD7052A combining a VCA and output stage amplifier in one. This is basically the circuit I ended up with from my experiments.

The TDA7052A is designed to drive an 8Ω speaker directly from its differential outputs pins 5 and 8), but I’ve also seen it used with a single output to send to a line socket, so I’ve tried to support both here.

Ideally I’d have been able to use a switched jack socket to disable the speaker when a jack is plugged in. Instead, this is another area where there is a compromise and people will have to use one or the other only.

The resistor values on the input have been chosen to attempt to match the input ranges required of the TDA7052A, so ~0-1.2V for the control voltage and ~0-300mA for the audio input. This is the point where the audio signal finally gets the DC bias removed and from a 0-3V3 audio signal becomes a +/- 150mA signal instead. But recall that the TDA7052A will then internally bias it to around 2.6V. Also recall that the VOL signal will sit at 1.25V until pulled low by the transistor and incoming control voltage. One again I’m using a BC557 (PNP) transistor here.

The output is mono and will eventually produce around a +/- 400mV output at the socket when powered from a 5V or 9V supply.

From my experiments it seems to output a 1.5-2V signal with an 8Ω loudspeaker across pins 5 and 8 when powered from 5V. It was slightly increased with a 9V, but the signal started to distort.

To be honest, I’m not convinced of the utility of the speaker output, so it may be that if a speaker is required it will be better taken from the line out and through a secondary small amplifier of some sort. Or it may be that prototyping this section on a breadboard is a little futile – I’ll have to wait until it is on a PCB and try again.

Power and Battery

This has been left about as simple as it is possible to be:

All connectors and switches have been left with pin headers. The 7-12V header could be connected up to a 9V battery if required. The power switch can be omitted by jumpering across J9. This is fed into a LM7805 to produce a steady 5V VCC supply.

Note VCC will be fed into the ESP32’s VIN pin to create the 3V3 power signal that supplies the rest of the circuit. It will also power the TDA7052A directly.

Also note that if nothing is plugged in here (i.e. neither battery or DC), then it should be possible to power the unit from the ESP32’s USB port. If this is the case, then VCC will come from VIN from the ESP32 and the jumper J2 should be removed to disconnect VCC from the LM7805. In this case the ESP32 VIN (which comes from the USB 5V) will also be powering the TDA7052A.

According to the schematics I’ve found online for my dev board (which seem to agree with what I can trace out on my board) the on-board power circuit is as follows:

I’ve found some discrepancy in the capacitors values – another schematic suggests C1 here is 100nF. More seriously, I found one schematic that shows VCCUSB connected to VIN on the NCP1117 rather than VIN. I’m pretty sure this is a mistake and it certainly doesn’t match what I have on my board.

Taking VIN on the module pinout as being the input to the NCP1117 and with a maximum dropout voltage of 1.2V (as per the NCP1117 spec) then the input range for VIN should be 4.5V up to 20V.

Powering it from the 7805 should be perfectly adequate and in fact could probably have been omitted. But in the interests of providing some stability to the PCB when in use, along with wanting a known voltage for the amplifier, I’ve opted to include the regulator. It also means I’ll be able to test parts of the board without requiring the ESP32 plugged in, and might provide an element of protection for the ESP32 should an incorrect power supply be plugged in.

But it should be quite possible to leave it out and direct VIN directly to the input power connection if required.

There is no protection for using USB and powering the system from the regulator, so this has to be decided as a build option to be one or the other, but being able to use USB during testing may be useful.

Panel Design

I’m hoping to get all this within a 100×100 footprint for ease of manufacturing. Initial efforts seem to show that it will be possible. This is the panel design I’m going for.

Design principles:

  • MIDI, power, audio output, and any external links for breadboarding will be left off the main panel – the idea being that they will probably be in the side of a case. Or they could be an additional panel element if required.
  • I’ve deliberately not included a filter stage – that will be an area where breadboard experimentation will be encouraged! It can be included by taking the output of one of the VCOs and reconnecting to the input of the VCA.
  • I might double up on some of the connection points to allow several patch leads – e.g. to make it easy to link to a scope.

PCB Design

I have a PCB design ready, further details available below:

Closing Thoughts

I’m writing this after some experimentation and initial breadboarding, so I’m hoping much of this is from a position of knowledge (although obviously not enough knowledge to have anticipating the strapping pins issue!).

The only compromise I think I’ve had to make is that I’ve left out a filter stage. At some point, if this rather mad contraption actually works, then I might consider designing a second panel with some additional modules and that might include some filter options.

Kevin

Leave a comment