Overview
While working at 0102.io, one of my main projects was designing the controller PCB for our haptic fabrics, and writing the firmware for it. This board manages arrays of up to 400 voice coil actuators and communicates with upstream applications via bluetooth. The actuators are arranged in an LED-matrix array, with rows and columns connected to tri-state pins on the controller. The actuators have small magnets in them that flip up to hit your skin, which we called a “tap”. We use these taps to create patterns that you feel on your skin, which we think will have applications in VR and assistive devices.
Here’s a high level electrical block diagram and some labelled photos, with more detailed descriptions below:
Schematic Capture and Component Selection
Microcontroller
I used an ESP32-S3 Mini 1 module for the microcontroller on this board. It has a Bluetooth/WiFi module with a PCB antenna, lots of storage and memory, a lot of flexible GPIOs, touch sensors, FCC/CE certifications for RF emissions, and it is very inexpensive. The only major drawbacks are the high power consumption and the size. This board is intended to be an open source prototype that demonstrates a proof of concept for this technology, so we were okay with its size and power consumption in return for how easy it was to integrate and use. A future iteration of this board would more likely use a Nordic chip since they are so much more power efficient with BLE.
Power Regulation
This board has two potential power inputs and two regulators. Power would typically come through the battery pack pins, but I also included a USB port for easier program upload and testing. Since I included both, I had to make sure the USB port doesn’t power an upstream device from the battery pack. So I added a pair of “ideal diode” (very low forward voltage drop) ICs which are arranged in the configuration shown in section 9.2.1 of the chip’s datasheet. In this arrangement, the higher voltage source (USB) will be used when both are connected. The output of the diodes is connected to the 3.3V and 12V regulators.
The 3.3V regulator is the power source for the microcontroller and other logic level chips on the controller, so it is always enabled. It has a buck-boost topology to keep the output at 3.3V even when the battery voltage drops below that (which happens briefly while actuating when the battery is almost drained). I considered using an LDO which would be simpler and a much smaller footprint, but the battery voltage can drop as low as 2.4V momentarily under certain conditions, and the min recommended input for the ESP32 module is 3V so I didn’t want to cause any unexpected behaviour.
The 12V boost regulator is the power supply for the H-bridge driver chips. The 6mm voice coil actuators we used draw approximately 0.5-0.6A, but the duty cycle is typically very low (<10%). I didn’t want a huge boost power stage which would be inefficient and have a large footprint, so I picked the LM2735X, which has an internal compensation network and can just barely hit that output current within its recommended specifications with a LiPo cell as the input.
I used TI’s WEBENCH Power Designer to get recommended component values and do some startup and transient load simulations, and I also took their PSpice model and put it into an LTSpice simulation to do my own verification with the actual component characteristics I was using.
An actuator is powered on by enabling one H-bridge high-side switch, and one low side switch. The two switches can both be on the same H-bridge chip, or they can be on different ones.
ESD Protection
I put transient voltage suppression diodes on the USB data pins (D+/D-) and touch pin GPIO8/T8. GPIO8 also has a current limiting resistor as recommended by these application notes, noting that the touch sensor on the ESP32 S3 seems to be a “C2D” (charge transfer) type sensor. This size of the resistor was also partially informed by this forum thread.
These TVS diodes have a low capacitance (less than 1pF) so they don’t slow the rise and fall times of the USB bus down too much. They have a 3.6V standoff voltage, and a low clamping voltage (15V at 16A). I think that is enough to provide some protection from ESD events, especially since the ESP seems to have internal ESD protection as well. I should have also included a diode for the VBAT pin since it is exposed.
Fail Safe Protection
This board has two methods of fail safe protection to force the actuators to turn off if they are left on for too long. These actuators can get very hot in just 1-2 seconds, and surprisingly the LED on the actuator PCB does not fail fast enough to break the circuit.
Both fail safe circuits use open-drain outputs to pull the H-bridge enable line low when they trigger, which means that they can have their outputs connected to each other with no issue.
Overcurrent Detection Method
The most effective protection against an actuator getting too hot is to directly monitor the current passing through it, and to cut the current off if it is applied for too long. I originally considered using positive-temperature-coefficient (PTC) fuses in series with the actuators to accomplish this. They are made of a polymer that physically changes states at specific temperature threshold, and in their high temperature state they have a much larger resistance, effectively cutting off the current to the rest of the circuit. Unlike a normal fuse though, they reset themselves when they drop back down to room temperature.
Unfortunately I couldn’t find one that would work well enough and not waste too much power. So instead, I decided to monitor the total current passing through the H-bridges with a current sensor, with this circuit:
This method disables the H-bridge drivers if they are drawing current for too long (a couple hundred milliseconds). It works like this:
- The current sensor IC measures the voltage across a 0.1ohm resistor, and outputs a proportional voltage (labelled “ISENSE” below)
- ISENSE is then connected to an op-amp configured as a voltage follower so that the rest of the circuit doesn’t load the sensor output.
- The op-amp charges a capacitor through a resistor, which causes the voltage across the capacitor to rise (RC low pass filter).
- The capacitor voltage is compared to a reference voltage with a comparator IC.
- Once the RC voltage is higher than the reference voltage, the comparator’s open-drain output is enabled, which pulls the H-bridge enable pin low.
- This immediately disables the outputs, which stops the current flow. Once the RC voltage drops below the reference, the enable pin is pulled high, but the H-bridge outputs remain low until the microcontroller enables them again.
To verify that this circuit would work, I simulated this circuit in LTSpice, using TI’s PSpice model for their INA169 current sensor, and other basic components for the buffer and comparator.
In this simulation, I am pulsing the 12V source across the 0.1ohm shunt resistor and 20ohm load (the actuator’s DC impedance) for 500ms (red line). The output of the low pass filter (blue line) rises until it hits the DC bias point, causing the output (green line) to be pulled low. At this point the H-bridge fets would be shut off and the current would stop flowing (though that isn’t modelled here). When the filter’s output voltage drops below the bias point, the H-bridges are enabled again and will actuate again the next time they are written to.
This doesn’t work as well as an individual fuse for every actuator because the low pass filter can’t differentiate between a single actuator being stuck on, and a very-high duty cycle pattern across all of the actuators. So I wanted a “cut-off time” of 150-200ms; much lower than the 1-2 seconds it takes a coil to get hot, but much longer than a typical actuation pulse of 1-3ms. This still allows for fairly high duty cycles without disabling the outputs, e.g.:
So this circuit doesn’t protect against a single actuator being pulsed with a very high duty cycle, but since that takes some working code-execution on the controller’s part, I figured that would be fine to protect with the firmware.
The other downside to this circuit is that there are a lot of components in this chain, so there are several opportunities for failure. That is why I also added the watchdog for some redundancy.
Watchdog Method
The second method uses a watchdog timer IC to disable the H-bridge enable line if the microcontroller stops sending pings. Once enabled, the watchdog needs to either be disabled (through the enable pin, WD_EN) or pinged (through the input pin, WDI) within the ~100ms timeout period. If it isn’t disabled or pinged within its timeout window, it pulls the output pin (WDO) low. This is connected to the H-bridge enable pin.
This method only has one chip, so there are fewer failure points, but it only protects against some microcontroller failure conditions. If for example, the firmware was messed up and kept pinging the watchdog while keeping an output on for too long, there’s nothing the watchdog could do to shut off the output - which is why the overcurrent circuit is also used.
I added normally-closed jumpers so that both circuits can be configured to disable either the watchdog or overcurrent protection circuits, just in case either one of them didn’t work well. But since they do seem to work well, I would remove them from the next iteration. This board is meant to be a public prototype anyways so it may even be helpful to another developer.
IMU Sensor
The inertial measurement unit IC measures acceleration in 3 linear axes and 3 rotational axes, and has a temperature sensor and two configurable interrupt pins connected to the microcontroller. We use this data for demo applications where you e.g. balance a virtual ball on your hand with your eyes closed, where you feel the ball as taps on your skin.
Fuel Gauge
The fuel gauge IC estimates the state of charge (SOC) of the LiPo cell in the battery pack. It generates interrupts on the ALRT pin when the SOC changes, which tells the firmware to re-check the current SOC so it can pass it to upstream applications.
FPC Connector
The flexible printed circuit (FPC) tail of the substrate PCBs connect directly to the controller through this connector. It has 41 pins with 0.25mm pitch, with the centre contact connected to a capcitive sensing pin (GPIO10) on the microcontroller, which is used to check if the flex PCB is connected.
Layout
The major goal for this layout was to make it as small as reasonably possible so that it isn’t cumbersome to wear. This goes for the x, y, and z dimensions. Cost wasn’t a major concern because this would be for a very small run of development kits, rather than a consumer product for the general public.
Other constraints:
- all the components that are taller than 1mm should be on the top side to help make the assembly with the enclosure and battery pack as thin as possible. This would save ~2mm of height.
- The reset and interact buttons, LEDs, and USB port needed to be on the board edge, and the FPC connected needed to be in the centre of one edge.
- A zone on top face next to the microcontroller needed to be free of parts, and the bottom face around the mounting holes needed a 5mm clearance.
Stackup, Signal Integrity, and EMI
This board uses a 10 layer stackup as follows:
L1 | Components, Signal, Power |
L2 | Ground |
L3 | Signal, Power |
L4 | Ground |
L5 | Signal, Power |
L6 | Ground |
L7 | Signal, Power |
L8 | Ground |
L9 | Signal, Power |
L10 | Components, Signal, Power |
It needed a lot of signal layers mostly because the 4 H-bridge drivers each have 10 outputs and need to be connected to the SPI bus.
I also wanted to have a reference plane close to every signal layer to ensure every signal has a tightly coupled return to help reduce EMI and potential impacts on signal integrity. By making all the reference planes ground specifically, I could use ground vias next to signal vias to keep the return paths tight when the layers have to switch layers. There isn’t enough current to justify dedicated power planes, so I routed power on mixed signal/power planes.
There are some places where it was too tight to have a return via for each signal via, so I tried to group nearby signal vias to be close to a shared return via. In other places it was too tight to add any return, so in hindsight I may have made the board a bit too small.
One consideration for EMC that I have not done much checking for is trace length in comparison to wavelengths / rise times.
Component Placement
We really wanted the tall components on one side of the board to make the whole assembly thinner, but this did mean I would have to put the microcontroller on the same side as the power inductors. It would have been nice to put one or the other on the bottom to add extra shielding, but at least the inductors are shielded, and I tried to place them as far away from the PCB antenna as I could.
The edge of the board of course has the USB port, FPC connector, user buttons, and indicator LEDs. The microcontroller module needed to also be on the edge with at least a copper keep out zone under the antenna. The application notes for integrating the module actually recommend a board cutout below the antenna, which I should have done, but it still works as-is. At least I did add the cutout for my more recent sculpture controller board.
On the bottom side, I placed the analog components (for the over-current protection circuit) on the opposite side of the board from the switching regulators, but they are still close to some digital signals like the I2C bus and the indicator LED lines. I only really learned more about separating analog and digital sections of the board after making this design, so this is another area for improvement. The analog circuit does still function properly at least.
I would have liked to put the Hbridge drivers on the top layer so that at least some of the traces between them and the FPC connector wouldn’t need vias. But there wasn’t room, so the drivers went on the bottom, and I tried to be very judicious with my via placement.
Regulator Layout
With the switching regulators on this board, the goal is as always to keep the high frequency loops tight and well coupled to ground.
For the 12V boost regulator, I think I could have done a better job of this. I put the input capacitor very close to the output stage, forcing me to wrap the Vin node quite far around the part. Since there is a continuous ground plane underneath this layout, I should have put the Cin cap closer to the inductor and used vias to the ground plane. I probably also could have found a part with a better pin layout. I don’t like that the feedback, analog ground, and enable pins are all in between Vin and the switch node, forcing me to bump out the inductor and input caps further from the regulator, making the loops bigger. And I ended up placing the feedback components together too tightly to squeeze in a return via, so the closest on is a little farther than I would have liked.
The 3.3V buck/boost has a more compact layout which makes for a tighter loop.
Board Outline
For fabbing this board I specified which edges to route for tighter tolerances and a cleaner edge than the V-cut lines. This helped the board fit smoothly in its enclosure.
Firmware
The controller's primary job is to receive a set of taps via bluetooth and execute the taps with precise timing, while consuming as little power as possible. The firmware is summarized by this flow chart:
This was a really interesting part of the project for me. At my last company (PBSI) we essentially treated the ESP32 in our Node Controller PCB as a more powerful arduino. For this project, I took advantage of more advanced features from the FreeRTOS kernel like task scheduling and mutexes, and dove much deeper into Espressif’s documentation and resources - especially while trying to reduce power consumption.
I also finally made the jump to the PlatformIO extension in VS Code and it was a huge game changer, it made development so much easier and less frustrating. I still used the arduino framework rather than Espressif’s IDF because I was using arduino libraries that I didn’t want to rewrite for the scope of this project. That meant I didn’t have direct access to some build configurations that I really wanted, namely the WiFi modem sleep and auto light sleep modes. But thankfully Espressif has documentation on how to use their library builder to re-compile the core libraries for the arduino framework. In addition to enabling the modem sleep and auto light sleep modes, I also lowered the core clock frequency to the lowest it could go while supporting bluetooth, disabled unused peripherals, and disabled the 12V regulator in between actuation sequences. In total this reduced power consumption by ~60% from where I started.
One other feature that I’m pretty happy with is the overheating attenuation I added to the pre-actuation checks. We have the two hardware fail-safes for leaving a coil on for an extended period of time (which could heat it up enough to burn your skin), but those only protect against a coil being stuck on continuously. I also wanted to prevent a high duty-cycle tapping on a single coil to prevent it getting toasty, and also to protect the LED on the actuator which is in the high-current path. To do this, I gave each possible intersection in the actuator matrix a “heat” property. Just before actuating, I would reduce the heat value depending on how long since that actuator was last on (i.e. it would “cool down” in between taps). If the heat was above certain thresholds still, it would be actuated for a shorter duration, and the remainder of the intended duration would be added to the cooldown before the next tap so that the pattern cadence remained unchanged. Then heat is increased based on the actual on duration.
- t1: ‘heat’ builds up from an actuation pulse.
- t2: ‘heat’ removed from ‘cooling’.
- t3: another actuation pulse; if there is too much ‘heat’, the pulse is attenuated by shutting off the output.
- t4: a delay is added to the cooldown phase between pulses so that pattern cadence is unchanged.
Depending on how much ‘heat’ the actuator has, the attenuation ratio for t4 changes.