Haptic Array Controller, 0102.io

Haptic Array Controller, 0102.io

Tags
KiCad
C++
LTSpice

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 electro-magnetic actuators and communicates with upstream applications via bluetooth.
notion image
notion image
The actuators are arranged in a matrix array, with rows and columns connected to tri-state pins on the controller. The actuators “tap” on your skin in quick succession to create patterns that you can feel. The idea was to use this in VR systems so you could feel effects like rain or static on your skin, and also to create an assistive device for deaf-blind people who use touch as their primary form of communication.
Wearing the controller with no battery pack on the Patch prototype.
Wearing the controller with no battery pack on the Patch prototype.
Wearing the controller and battery pack on the Glove prototype.
Wearing the controller and battery pack on the Glove prototype.
Here’s the high-level electrical block diagram and some labelled photos, with more detailed descriptions below:
 
Electrical block diagram for the controller PCB.
Electrical block diagram for the controller PCB.
 
Labelled diagram of the top side of the controller PCB. Note: this was the previous revision, which had a slightly different layout.
Labelled diagram of the top side of the controller PCB. Note: this was the previous revision, which had a slightly different layout.
 
Labelled diagram of the bottom side of the controller PCB.
Labelled diagram of the bottom side of the controller PCB.
Interactive bill of materials.

Schematic Capture and Component Selection

notion image

Microcontroller

notion image
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’s cheap. The only major drawback is high power consumption. A future iteration of this board would likely use a Nordic nRF52 or nRF53-series chip for much better Bluetooth efficiency.

Power Regulation

notion image
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 convenient testing. Since I included both, I had to make sure the USB port doesn’t back-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 sensors on the controller. I used a buck-boost converter here because the LiPo cell voltage can drop as low as 2.4V under certain conditions, and the ESP32 requires 3V as a minimum for the BLE radio. In the future, if I could use an nRF52/53-series chip I could potentially swap the buck-boost for an LDO to save board space and reduce EMI.
The 12V boost regulator is the power supply for the H-bridge driver chips. Our voice coil actuators draw 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 then performed some startup and transient load simulations. 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.
 
notion image
WEBENCH load transient simulation.
WEBENCH load transient simulation.
LTSpice load transient simulation.
LTSpice load transient simulation.

Actuator Power

An individual actuator in the matrix array is powered using H-bridge driver chips, where a high-side mosfet connects a row of actuator anodes to the 12V rail, and a low-side mosfet connects a column of actuator cathodes to ground. Current then flows through the actuator at the intersection of the row and column, causing a “tap”.
notion image
Block diagram showing the high side and low side switches for each output of an H-bridge chip. Enabling a high side switch makes that output pin an open source connection to the 12V rail, and enabling the low side switch makes that pin an open drain connection to ground.
Block diagram showing the high side and low side switches for each output of an H-bridge chip. Enabling a high side switch makes that output pin an open source connection to the 12V rail, and enabling the low side switch makes that pin an open drain connection to ground.

ESD Protection

I put transient voltage suppression diodes on the USB data pins (D+/D-), power input pins, and captouch pin (T8). The captouch pin 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.
notion image

Fail Safe Protection

This board has two methods of fail safe protection to force the actuators to turn off if they get stuck on. 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 off the current 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 a 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 return to room temperature.
The current through a 1ohm shunt resistor after passing through a PTC fuse and a 6mm coil actuator. You can see how the sustained current through the fuse causes a state change around 250ms from the start of the pulse, dropping the current from 1A to 100mA. During this time, I measured the temperature of the coil rising from 27C to 34C.
The current through a 1ohm shunt resistor after passing through a PTC fuse and a 6mm coil actuator. You can see how the sustained current through the fuse causes a state change around 250ms from the start of the pulse, dropping the current from 1A to 100mA. During this time, I measured the temperature of the coil rising from 27C to 34C.
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, using this circuit:
notion image
 
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 measures the voltage across a 0.1ohm resistor, and outputs a proportional current.
  • This charges a capacitor, 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.
  • Once the capacitor 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. 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.
LTSpice circuit and simulation, with a 500ms 12V pulse causing the output to be shut off in 180ms.
LTSpice circuit and simulation, with a 500ms 12V pulse causing the output to be shut off in 180ms.
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 the microcontroller tells them to actuate.
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 to ~50 degrees C, 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.:
This 1ms on / 3ms off pattern doesn’t trigger the safety cut-off.
This 1ms on / 3ms off pattern doesn’t trigger the safety cut-off.
This circuit doesn’t protect against a single actuator being pulsed with a very high duty cycle, but since that requires active control by the microcontroller, I handled protection in the firmware library.
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 method (below) for 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.
notion image
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.
 
notion image

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.
 
notion image

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 on the microcontroller, which is used to check if the flex PCB is connected.
 
notion image
 

Layout

Screenshot of the signal layers. Every other layer is a ground reference plane, creating tightly-coupled return paths to minimize EMI.
Screenshot of the signal layers. Every other layer is a ground reference plane, creating tightly-coupled return paths to minimize EMI.
 
The major goal for this layout was to make it as small as reasonably possible so that it isn’t cumbersome to wear.
Other constraints:
  • All of the components taller than 1mm needed to be on the top side to make the complete assembly as thin as possible.
  • The reset and interact buttons, LEDs, and USB port needed to be on the board edge (since the battery pack would cover the top of the board), and the FPC needed to be in the centre of one edge for symmetry.
  • A couple of zones needed to be kept clear of components to accomodate the mounting system.
    • notion image
       

Stackup, Signal Integrity, and EMI

This board uses an 8 layer stackup as follows:
Layer 1
Components, Signal, Power
Layer 2
Ground
Layer 3
Signal, Power
Layer 4
Ground
Layer 5
Signal, Power
Layer 6
Ground
Layer 7
Ground
Layer 8
Components, Signal, Power
I used a ground reference plane close to every signal layer to ensure that every signal has a tightly coupled return to 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.
L1 in red (components, signal, power), and its closest reference plane, L2 (ground) in green.
L1 in red (components, signal, power), and its closest reference plane, L2 (ground) in green.
L3 in orange (signal, power), and its closest reference plane, L4 (ground) in blue.
L3 in orange (signal, power), and its closest reference plane, L4 (ground) in blue.
L5 in pink (signal, power), and its closest reference plane, L6 (ground) in grey.
L5 in pink (signal, power), and its closest reference plane, L6 (ground) in grey.
L8 in blue (components, signal, power), and its closest reference plane, L7 (ground) in purple.
L8 in blue (components, signal, power), and its closest reference plane, L7 (ground) in purple.

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.
notion image
 
The application notes for the microcontroller module recommend a board cutout below the antenna, which I included although my cutout was smaller than the recommendation due to overall size constraints.
Substrate cutout recommended dimensions for the ESP32-S3 Mini 1 microcontroller module.
Substrate cutout recommended dimensions for the ESP32-S3 Mini 1 microcontroller module.
I would have liked to put the H-bridge drivers on the same layer as the FPC connector so that at least some of the traces between them wouldn’t need vias. But the connector had to be on the top side with the tall components, and there wasn’t room for the H-bridge drivers on the top.
notion image

Regulator Layout

I researched and implemented best practices for the regulator layout to reduce EMI. The primary contributing factor for creating EMI in a switching regulator is the size of the high-frequency loop.
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.
 
12V regulator layout.
12V regulator layout.
 
The 3.3V buck/boost has a more compact layout which makes for a tighter loop.
 
notion image
 
 

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.
notion image
 

Firmware

The controller's primary job is to receive an actuation sequence via Bluetooth and execute it with precise timing, while consuming as little power as possible. The firmware is summarized by this flow chart:
Firmware flow chart; double click to expand.
Firmware flow chart; double click to expand.
The ESP32-S3 uses a FreeRTOS kernel, so I took advantage of its built-in task scheduling features to precisely control actuation timing while minimizing CPU power consumption. With multiple independent tasks that required shared resources, I also took advantage of mutexes to prevent race conditions (for example when receiving new actuation data and adding it to the queue, while also continuing to execute the existing actuation sequence).
I also took advantage of some power-saving features of the ESP32-S3 to reduce its somewhat abysmal efficiency. For example I:
  • Enabled the auto light sleep mode to reduce power consumption while the CPU was idle
  • Reduced the 240MHz CPU clock to 80MHz (the lowest speed that still works with BLE)
  • Enabled the WiFi modem sleep to prevent the power-hungry WiFi radio from drawing power
  • Disabled unused peripherals
  • Disabled the 12V boost regulator in between actuation sequences to minimize switching losses
These measures dramatically improved the battery life.
One other feature that I’m pretty happy with is the overheating attenuation I added to the pre-actuation checks. In the schematic section above I already described the two hardware fail-safes that prevent actuator overheating, but those only protect against the failure condition where a coil is stuck on continuously. I also wanted to prevent overheating caused by a high duty-cycle on a single actuator.
To do this, I modelled the heating and cooling of each actuator object by adjusting a property called “heat”. The “heat” of each element in the actuator array is increased each time it receives a new actuation pulse, and decreased based on how long it’s been since the last pulse.
If too much “heat” builds up, the next pulse’s on-time is reduced. The remainder of the intended on-time is then added to the cooldown before the next tap so that the actuation sequence’s cadence isn’t affected.
Example diagram of overheating attenuation, description below.
Example diagram of overheating attenuation, description below.
  • 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 t3:t4 changes.