Now that we know what pneumatic components and microcontroller we are using, let's go over how to interface the two.
The solenoid valves are controlled individually: apply 12V to power them on; open the circuit to shut them off (or vice-versa, for a normally-open valve). So I needed one digital output pin and one transistor per valve, as well as a 12V power supply.
Given that the ESP32 only has around 20 usable GPIO pins, I also needed an I/O expander. The best option I found was the PCA9698. It is controlled over I2C, has 40 I/O pins and, to make my life extra-easy, there is an Arduino library to use it, thanks to Iowa Scaled Engineering. Not too bad for 4$.
So, with just the SDA, SCL and a couple other pins, the ESP32 can easily control a ton of outputs.
Next up are the transistors. ULN2803s are perfect for this: they have 8 darlington pairs which can sink up to 500mA each (at up to 50V), and can switch inductive loads such as solenoids thanks to the included flyback diode. I was hoping to find large darlington arrays controlled over I2C or SPI, instead of using separate components for this, but I was unable to find anything good. Still, the PCA9698 + ULN2803 combination is fairly compact and works great.
The ULN2803 is also useful for switching the pumps: although they may require more than 500mA, one can simply use several pins of the ULN in parallel to sink more current.
So far, we have one PCA9698 and five ULN2803s (four to drive the 32 solenoid valves, and one for the two pumps).
Next up are the pressure regulators. They are powered by 24V, and have an analog input and output (see previous log). Unfortunately, the analog voltages are between 0 and 5V, and the ESP32 functions on 3.3V. Another minor inconvenience is that the ESP has 2 DAC's, and I needed 3 pressure regulators.
The 5V -> 3.3V conversion is very straightforward: a simple voltage divider does the trick here. For the 3.3V -> 5V, I settled on using op-amps to make simple non-inverting amplifiers. This way, the two DAC pins can be used to control two of the pressure regulators. For the third, a PWM output of the ESP32 combined with an RC low-pass filter does a fairly good job of outputting an analog 0->3.3V voltage, which is then amplified to 0-5V just like the other two.
Finally, power. The ESP32 requires 3.3V, but the development board (which I decided to use for this PCB, which is basically a shield for the dev board) has a regulator to power itself from USB. So we already have 5V and 3.3V. In order not to require USB (since bluetooth support is planned at some point), I decided to also include a 5V regulator.
The solenoid valves and pumps, which require by far the most power of all the components here, require 12V. Therefore I decided to have a 12V power input (with a standard barrel jack) and use regulators for the other voltages I needed (5V, as mentioned, and 24V to power the pressure regulators).
The 12->24V part was actually a little problematic. I couldn't find a compact, board-mount regulator capable of delivering enough power -- up to 28W or so -- so in the interest of getting a working version done as soon as possible, I just went with an external regulator.
This version of the control system isn't intended to be final: hence the use of the ESP32 development board, external pressure regulators, etc. It is a bit of a platform for experimentation, and I hope to build on this to make a more polished version.