PCB Hotplate Continued

The hot plating continues! And with the boards actually in hand, I can compare my estimates to some real world measurements.

In the end, I made both FR4 and aluminum heaters. These heaters ended up having slightly different properties. Oddly, the aluminum PCB has a higher resistance (4 ohms) than the copper pcb, 2.9 ohms). Read on to figure out what this did to the design.

Designed Vs Measured Resistance:

The design resistance of the heater track was 3.3 ohms. Clearly, this was not “nailed” for either of the boards, but it was close. I was surprised that both boards didn’t overshoot the resistance, due to the addition of a few extra squares of conductor on the connector island- however the FR4 heater seems to have a more generous allotment of copper, and the aluminum PCB may have slightly less copper.

Unfortunately, this means that at higher temperatures, the power delivered to the aluminum board is much lower than desired. The calculated value for the resistance at 200C is about 6.7 ohms, and the measured value is 7-8 ohms. This means that the maximum power available at 20V is 50-60W, or less. This is pretty close to the theoretical minimum power needed to reach 200C, which is why this plate struggles to achieve 200C. As you can see, the heater is on for the entire time (until I pulled the plug). Time units are in 10ms intervals. Fortunately, this validates the power delivery assumptions I made in the past post.

To recap, Qdot is the amount of heating power needed to sustain a particular delta T, given some convection parameters. I assumed a delta T of 175k, with an area of about .01m2, and h being 25 W/m2k, resulting in a power delivery at the heater of 43W. while this is not totally spot on, its in the right ballpark. It suggests that lowering the resistance and actually delivering 100w would be very helpful to removing this 200c limit. I think 10-20 is a reasonable conservative estimate of convective cooling, but its conservative in the wrong direction if you are designing a heater.

On the other hand, the resistance of the FR4 heater is low. At low temperatures, running at 100% PWM (all on), will cause the power supply to brown out. This means that the power delivery actually needs to be cut back (PWM’ed) when the heater is cooler, so that the overall current draw is lower. Once the heater is in the vicinity of 100C, it can be run “full blast”, aka more or less shorted across the power supply.

Does It Blend?

Both the aluminum and FR4 heaters were able to reach 200c depending on the conditions. Both were capable of melting solder directly on the plate, as you can see. The aluminum heater would benefit from an improved (lower resistance) heater trace.

The remote probe feature allows for the sensing of the temperature of the PCB being soldered. This is good, since that is where the temperature actually matters. This can cause the heater to get much hotter (20c or so) than the top of the target board, which in this photo, resulted in some discoloration of the solder mask of the heater.

Temperature Gradients and Overheating:

The hot plate design needs to achieve two tasks: be cold at the connector (so as not to de-solder it), and it needs to be hot (and ideally, evenly hot) across the surface.

The worst case for de-soldering the connector is with the aluminum heater, since the aluminum substrate should conduct heat pretty well. Up at peak temperatures, the connector area only got to about 85C. Certainly warm, but not nearly hot enough to damage the connector.

In terms of heater gradients, the aluminum PCB had about a 10C difference from the center to the corner at 200c. The gradient to the “neck” of the connector island was much worse (13+ C), since the thick traces there (and the substrate) act like a heat sink. The corners are cooler since there is some radiation and extra convection out of the sides of the heater.

The FR4 board has a even higher gradient, in excess of 50C during heat up, and staying steady at about 50C after several minutes. This is annoying, since it makes the outer areas of the PCB too cold to solder on. The center can be overdriven to compensate, but that leads to overheating and discoloring the solder mask.

New Design Goals + Limitations:

Its clear that hot plate soldering is not the ideal way to solder- something like a toaster seems a lot better and faster, since it can more evenly heat the target PCB. The main issue is that in order to get the top of the PCB hot enough to reflow, the bottom has to be quite a bit hotter. This is worse with traditional lead-free solders, since they need to hit about 220C. Lead-Tin eutectic solders need to hit 185 or so, and bismuth lead-free solders only need to reach 140 C (there is a medium caveat here).

You can see the results of this heating on an adafruit board (above), that I gently reflowed. The left board is new from adafruit, and the right one is reflowed. It didn’t really damage the board, but it certainly would be an unacceptable outcome if a manufacturer overheated a board like this, and the lead free solder (I assume) on top barely reached the liquidus point, and would re-solidify as soon as I moved a component.

The medium sized caveat is that any lead in the process can poison your bismuth solders forming a bismuth-lead crystal that has a very low melting point (95C!). I think the jury is still out on using bismuth solder for stuff that gets hot, but for most prototyping it seems fine. For low temperature solders, the hotplate is fine because your whole board stays under typical soldering temperatures.

For leaded solders the hot plate will likely also work ok, since the heater can be at about 200c and the surface of the target PCB can be at 180. However, for lead-free SAC (Sn Ag Cu) alloys, I think the hot plate will likely damage the solder mask of the target PCB. However, using the PCB hotplate as a preheater could work fine, with the addition of a hot air gun assist.

I would like to use this hot plate to solder and test complete prototype designs, with packages that are hard to hand solder, particularly leadless stuff like QFNs. I find solder fumes to be irritating (and there are more at higher temperatures), and so I am going to try using the bismuth solder, at least for prototyping. For leaded work, I’m pretty sure the hot plate would work fine, and it will be a good tool (combined with hot air) for reworking SAC alloy lead-free boards. I’d also like to take up less closet space than a toaster oven, so I think the project does have some value. It also fits in nicely with the USB-C powered soldering iron ecosystem.

Electrical Stuff:

In the previous posts I shared some choices around the electrical system. The two concerns I had were not shorting out the power supply, and preventing inductive spikes from exceeding Vdsmax.

The Vgs, shown above, was totally fine since it was limited by the free-wheeling diode. Even with a 1ms charge time, the inductor failed to exceed 25V and there was very little ringing. The Vdsmax is about 30V, so there is plenty of margin.

To test power integrity, I wrote some code to turn the heater on different power levels for 10ms, starting at 100% power. As you can see, 100% power reduces the output voltage just a bit, to 19.5V. This is well within the PD specification. When the PWM begins, the power supply seems to tolerate some relatively large spikes/dips as the heater (inductive load) is switched at 25kHz.

Electrical + Control Improvements:

To get a truly performant hotplate, the resistance at 200C needs to be less than 4 ohms, so the full 100W (or close to it) can be used to maintain the target operating temperature. Doing this is tricky, because it means that at 25C the heater can’t be operated at 100% duty cycle. Basically, power is not a function of duty cycle, but of duty cycle, temperature, and board characteristics.

There are two approaches to solving this problem. One is a per-heater lookup table, that lists a reasonable safe duty cycle per temperature. This is limiting since it would need to be generated per-heater, but its not unreasonable to do that given that I will likely stick with a single heater, and most heaters should be pretty similar.

Another approach would be to add a current monitoring to the USB PD supply, and cut the heater power off once you get close to the limit. This requires a relatively high bulk capacitance to provide current, even at 25kHz, but its certainly achievable. Overall power could be modulated by adding a dead time in between heating cycles, in order to follow a temperature profile. Higher frequencies could be considered, taking into account the switching time of the low side FETs. This is basically a current-controlled buck converter.

I’ll likely stick to the first approach for now, since doing current limited control requires more hardware, and it is unlikely to really improve the outcome of my soldering.

Mistakes + Takeaways

There were a few minor circuit goofs, and some things that went surprisingly well. Here is a small list, that might help you or me in the future:

GPIO9 and GPIO8 on the ESP32 C3 are bootstrapping pins– they control what the ESP does when it boots. I connected one of them to a switch, as an input for the program. This meant that the ESP would not boot into my program, but it would boot back into bootloader mode (and accept new programs). Toggling the switch made the ESP behave normally. Note to self: use these pins carefully.

The pinecil is a pretty beefy iron. It had trouble soldering to the aluminum PCB, but with boost mode and patience it had enough power to solder directly to a 3mm wide trace on an aluminum substrate. It had NO trouble soldering XT60 connectors to large pours. Don’t let the small size fool you- it is a real iron.

It is a good idea to put indicator LEDs on things like the actual power rail and the actual HEAT signal. It makes it unambiguous that something is really happening, and that the heater is getting hot.

I added a solder jumper to the overtemperature cutoff circuit. I accidentally swapped the inputs on the monitor chip, so it didn’t work. Cutting the jumper made it really easy to test with this protection turned off.

All my temperature sensors gave wildly different readings, even when co-located. I’m not sure why, but I do trust that the delta temperature measurements were ok since they were from the same instrument with two of the same probes, and they had the same readings when physically connected.

USBC-PD can deliver serious power, and its easily obtained with USB PD trigger boards.

Next Steps:

During testing, I learned what this type of soldering setup is really appropriate for, and what the capabilities of the heater are. I want to try some of this low temp solder, and I want to do some hot plate soldering to figure out how it feels, before going back to take another pass at the heater element. I suspect targeting an even lower heater resistance will allow me to hit 200C easily with the aluminum plate.

I also have some work to do on the UI (there is a screen- I just didn’t plug it in), and on the controls. If I find it useful, I will probably build an enclosure for it to protect the electronics and to make it easier to use.

PCB Hotplate Controller

The PCB heater for the hotplate obviously needs to be driven by something. I could have put more circuits on the heater board itself, but separating them has some advantages:

  • if the heater element is bad, or needs reconfiguration, it can be replaced
  • better opportunity for a heat break between the boards
  • opportunity to try different heater substrates or even different heaters/heater shapes

My goal for the hotplate is to keep it simple. I decided that I really probably need one profile at most, and a manual-temperature mode. I’d rather make that profile on the computer, instead of on the device, and be able to play it back from the device. I’d also rather get information back from the plate on the computer instead of a tiny display. Hopefully these simplifications will allow a hotplate to be up and running sooner rather than later.

Here is the architecture I came up with:

There are two usb C connections- one for programming the micro, and one for power, equipped with a USB PD “trigger” chip. My new favorite controller, the ESP32-C3 is in charge of the show (and native USB C). A single thermocouple is used for feedback, and an RTD is used as a safety cutoff- basically if it goes above some target temperature, the micro will no longer be able to drive the power FET. This won’t prevent the board from getting super hot, but it does provide a hardware safety against exceeding some set temperature if the controller spaces out…assuming the thermistor does not fall off.

Additionally there are indicators for PD power, DATA power, data transmission (heartbeat), heater power, and safety over temp, hot surface, mode, and (finally) a temperature readout. These are diagnostically useful, and they should satisfy my desire for a cool looking annunciator panel.

One annoying thing is that the ESP needs to be driven at 3v3 while the rest of the stuff is happy at 20V. While a cheap, integrated buck would be a neat solution, an LDO is so much cheaper. I decided to go with the buck, since I wanted to avoid burning extra power when operating near the 100W limit of the power supply.

The heater itself will be driven by a gate driver like the NCP81071B, which is 20V tolerant. 20V will just barely allow us to use something like the AOSD32334C, which is a dual N FET. Paralleling the FETS will reduce the RDSon to almost nothing- close to 10 mOhms.

The heater element here is represented by a resistor and an inductor- I expect the trace to have some very real inductance (100s of nH?), which will cause a large voltage spike when the FET turns off. This could easily exceed the 30VDS of the FET, and cause it to break down. The flyback diode will hopefully prevent this. Additionally, a capacitor Cbulk will be chosen to prevent over-drawing current from the power supply. At 20V, the 3.3 ohm heater will draw 6A, while the maximum allowed current is 5A. By PWMing this, the average current will be below or at 5A, but when the heater is on that current needs to come from somewhere- the bulk capacitance C.

To connect to the heater itself, I am using an XT60 connector. These are low-resistance and they seem easy to get, due to their use in drones.

Safety + Temperature Monitoring

There are a lot of ways making a big heater that is capable of destroying itself can go wrong. Ultimately this device falls under the category of things you should unplug if its not in use, and definitely not something to leave running unattended. However there are a few details I added as a nod to safety.

The first is a thermal trip based on the MIC841N The HTH input is fed by a NTC resistor divider that exceeds the 1.24V reference at about 200C. This SETs the latch, so the inverting output goes low, pulling down the enable pin of the fet driver. This is the case until either the whole circuit is reset (and the thermistor has cooled off) or until the reset line is pulled down by a button. Resetting while hot should result in an illegal state where ~Q is still 0.

Note: this circuit totally fails if Rtherm is removed, and it wont work if Rtherm gets disconnected from “the hot part”. If I wanted to add more logic, this could be resolved, e.g. inverting the logic of the resistor bridge. However, this is currently just a secondary protection. If it works nicely it can be refined later.

Temperature monitoring (for feedback and control) will be done with a thermistor dropped either onto the heater, or onto the target PCB. This allows for point control, near the stuff that you care about. For the time being, this will be made out of a MAX31850 thermocouple breakout that I have lying around from years ago.

Next Steps

With the heater and the controller designed, its time to get them made and then patiently wait for them to arrive!

PCB Hot Plate State of the Art

I have started seeing a whole bunch of PCB hot plate builds, of varying success. I kind of want one now, since they seem very practical for one-off small PCBs, and most off the shelf solutions are quite large. However, I also want to improve on the state of the art of PCB heaters. These are useful for all kinds of stuff, from PCR machines to 3d printer hot plates.

Builds I researched:

CarlBujejas V1/V2 hotplate videos, files

AfterEarthLTD Solder-Reflow-Plate git, video coverage

electronoobs video

There are a lot of challenges to overcome to make something that does not suck. Ideally, it could even be USB powered. Below are some calculations and considerations for making a pcb hot plate. You can see the WIP (scripts, pcbs, etc) on github.

PCBs are not a good material:

Using a PCB as a heater is not a new idea- however, in some ways, they are not good material candidates for high soldering temperatures:

  • The board will be at/above TG for a long time
  • solder mask/silk can discolor
  • could de-solder itself/the controller
  • repeated stress on copper tracks from board warping while heating

That said, several people have made them and they seem super-good-enough for this process.

Thermal Expansion

A lot of people worry about thermal expansion with pcb heaters- it makes sense because at the length and temperature scales that the heater will experience (100s mm, 200 C), real growth/shrinkage of the pcb will happen. With those temperatures and lengths, a FR-4 PCB will grow about .28 mm! An aluminum one will grow even more- .4mm! copper has a similar coefficient of linear expansion (alpha) as FR4, which means that the copper will need to stretch about .1mm/100mm of length. That is a strain of about .1%. That should be in the plastic deformation range for copper according to some stress strain curves I found, but it could cause cracking in more rigid parts of the assembly.

To further mitigate any expansion issues, I meandered the heaters on both sides, which should hopefully prevent pulling “straight” on any piece of copper. hopefully having a longer track (albeit, glued to the substrate) will help distribute the strain along the track- in the same way that a meandered track in a strain gauge rosette works.

In addition, I cut a slot between the hot part and the cold part, and put the connector thermally far away from the heater, while adding a lot of area for cooling. This should keep the connector from desoldering.

Low Voltage Efficiency Challenges:

A lot of these builds use lowish DC voltages to power the plate. This is not a great idea, because these things take a good chunk of power. Since the voltage is low, this drives up the current in order to deliver enough power. This results in losses in all the cables and connectors between the power supply and the heater.

As the impedance of your load approaches the impedance of the source of power, the power supply starts to eat a lot of power. I am considering all parasitic resistances here to be part of the “source” driving just the heating coil.

n is efficiency- as Rload gets to be about the same as Rsource, the efficiency will be about 50%- that means 50% of the power is going to get burned in whatever is supplying the power. This means that the rest of the circuit- connectors, fets, etc. need to be very low impedance compared to the load.

For example- to get a 100W heater at 12V, the Rload needs to be about 1.2 ohms This means that even a 100mOhm RDSon will drop the efficiency about 8%!

What would be ideal would to have the Rload to be greater than the R of everything else, by a lot. This is possible, but it comes with a catch- the current will be lower, and therefore, the power delivered to the heater will be low. Lets compare two cases, RL = 1 and RL=9, for Rs=1, and a source voltage of 10V that can output up to 5A.

Casen, efficiencyCurrent (A)PtotalPheater
RL = 150%550W25W
RL = 990%110W9 W

An interesting (and not very important) aside is that for a particular heater power, and an infinitely powerful supply, there are usually two operating points for a given heater power. Check this out:

This gets turned into a very ulgy polynomial:

Lets look at a case where we claim Rsource = .5 ohms, V = 24v, Pload = 80 w.

If you crank this through the quadratic equation, (with some rounding) you will get R load of about 6 or .04 ohms. two very different resistances, but if you look at the same table you will see:

Casen, efficiencyCurrent (A)PtotalPheater
RL= 692%3.788W80
RL = .047%44A1000W80
yes- I rounded a lot but this is about right

Obviously, a heater with .04 ohms of resistance is impractical – the heater would be tiny (since the resistance has to be low), it would get SUPER hot because of the low thermal mass, and we would need to draw a kilowatt to use it.

Low Voltage Workarounds:

The good news is that the voltage does not need to be very high. Even a voltage of about 20-24 V (commonly available) is much better at delivering power than 12v.

Boosting to a higher voltage can help too. This won’t help as much as not having a low voltage at all, because the loss in the wiring/connectors in the low voltage section will still exist. However it could give more flexibility in the design or choice of heater elements. e.g. a 12W element that runs at 12V will have a resistance of 12 ohms (and a current of 1A). running this at 24V will result in a quadrupling of output power to 48W.

Ok, how much power do we need?

If we look at the existing hot plate examples, one figure of merit seems to be the steady state temperature at a given input voltage, and therefore power. My suspicion remains that connectors are eating a lot of power here, because the numbers do not add up. For example, at 3:10 in the hot plate V2 video we see the power supply is at 12V 7A, for a total input power of 84W. The temperature is 196C, with a room temp of about 25C. The heater diameter is .01 m2.

If the temperature is stable, that tells us energy in = energy out:

Using these numbers to back-solve for h, the convection coefficient, gives us about 44W/(m2k), which seems high. Given a similar delta T and the 70*50 area of the after earth board, the coefficient is about 10 W/m2k. These numbers seem high- I do not think all this power is getting delivered to the board, since a typical number for h is closer to 10-20 W/m2k for a pcb application. Its possible I am missing a few mm2 of area, or a lot of radiation or conduction, but these pcbs seem reasonably thermally isolated and appear to be in still air.

These designs come out to 5-10W/in2, or about 10kW/m2. I have seen a lot of heaters that claim to be 5-10W/in2, so that seems like a reasonable power to target.

Doing the math forwards (delta T of 175k, A= .01m2, h=25W/m2k), gives a desired wattage of 44W. I will aim to overshoot that, because it will reduce the heat-up time.

How fast does it need to get hot?

A good rate seems to be about 3C/sec, based on some reflow profiles. however, this rate needs to be done not only at the lower temperature, but near the top end of the reflow temperature- lets say 200c. This is important because if the heater is designed so that the max temperature is the reflow temperature, the heating rate will be slow as it approaches the max temp asymptotically.

drawing of typical newtonian heating

This is semi easy to estimate- we can make some assumptions, like that our heater is infinitely thin, massless, and is heating a plate that it is in very good contact with. the plate is also thin, so we don’t care about the gradient across it.

This basically says Temperature is equal to thermal mass * energy in an object. Taking the derivative with respect to time gives us a heating rate that depends on the heat into/out of the object. If we assume mostly convective cooling, and choose Qin, we can get a rate of temperature change. The delta T chosen tells us what the rate will be at a given temperature above ambient.

assuming a 1mm aluminum platen, with .01m2 of area, the volume is 1e-5 m3. with a density of 2.7e3 kg/m3, and a specific heat of 900 j/kgk, the platen has a thermal mass of about 73j/k.

for h=25 w/m2k a= .01 m2 dt=175, the convection term comes out to 43.75W (this is the same as the power needed to heat to this dt)

Tdot should be 3c/s,

so solving for Qin gives an power (at the heater) of about 100W. This is a rough SWAG, and it comes with the caveat that it will likely be even slower than that, due to delays in conducting that heat to the PCB.

Using USB C PD:

A USB C power delivery brick can, in theory, supply 100W. Really getting 100W out of a USB PD charger seems to be a bit of a stretch. There are tolerances on the actual voltage a given supply must provide, plus an allowable voltage drop (and power loss) in a to-spec USB C cable. Per the usb C R2.0 spec:

This Vmax and Vmin represent +/-5% of the allowable voltage. At 0A, there is no drop on the cable, but at 5A there is an allowable 750 mV drop. Interestingly, this drop is not symmetrical- only a 250 mV drop is allowed on the ground connection (even though it should have the same current flowing through it). This voltage drop at 5A means that a USB cable is allowed to burn about 3.75W.

This means the worst case voltage to come out of a 100W PD supply and cable at 5A is really 18.25 V, which is really closer to 91W. However, as much as 20.25V can be delivered at 5A, for 101.25W (with an ideal 0 ohm cable).

This means to get the full power out of the lower allowable voltage chargers, (18.25V), the heater resistance will need to be 3.65 ohms. however, that will draw too much current on the high side. the solution seems to be to cater to the lowest voltage, and then PWM the heater (which I will do anyway) so that it does not exceed the average current draw for higher voltage/lower resistance supplies. Designing around the higher voltage supplies will result in a under-powered design, since we will not get the full 5A at lower resistances.

Additionally, there needs to be some allowance for other resistive circuit elements, e.g. the switching FET, solder joints etc. Estimating a few 300 milliohms for those seems ok, so so the real target heater resistance should be about 3.3 ohms at room temperature.

Resistance Temperature Sensitivity:

Resistance of materials increases with temperature. Copper increases about .0039 ohms per degree ohm. With a delta T of 175K, and a target resistance of 3.3 ohms, this means that the resistance will increase by 2.2 ohms! This means that at higher temperatures the maximum output will be closer to 72W.

It is easy to calculate what original resistance would give us 3.3 ohms at 200C- this turns out to be about 2 ohms. However, the bulk capacitance required to smooth out the ripple at 10kHz is about 1000uF, which is expensive to implement. Using an electrolytic capacitors incurs a pretty steep power loss in parasitic resistance, and ceramic capacitors are too expensive for the design. I will stick with 3.3 ohms for the design, and just loose some power generation at higher temperatures.

Heater Design Math:

What follows is a detailed design of the heater traces. An important concept is sheet resistance- this is the concept that the resistance of any square of material in a homogeneous sheet has the same resistance. 1 oz copper has a sheet resistance of about .5 mOhms, per square.

As we know from our previous calculations, the resistance of the heater should be about 3.3 ohms. We can use this equation to get the number of squares:

From this I calculated that the number of squares needed is 6600. This means the trace for the heater (if its made of 1 oz copper) to be 6600x longer than the width.

I want the heater to be about 100×100 mm, or less. This will give us an area of about .01m2, which was used in the calculations above for heat transfer. Ideally, the heater would be a little smaller, in order to hedge on the side of performance. Another practical consideration is putting the power terminals next to each other- this will minimize the length change between them when hot, and it will make the wires going to the heater short (preventing a wire run from another edge or corner of the board).

In order to meet these requirements, I wanted a rectangular pattern that would fill in the heater rectangle. I opted for what I decided would be called a double zig-zag, which can be cut anywhere on the perimeter so that the power terminals are next to each other. The extra long trace should also help prevent from stretching the copper under thermal expansion, since the whole pattern should act like a spring.

This drawing shows the units broken down- two long bars Ltarg long of width W, and several units that would each be connected to each other. Each unit has these dimensions:

The ideal distance between the tracks, H, should be very small, because it will result in more coverage of the board in heater tracks, and therefore more even heating. I chose to start H at about 6 mil/.16mm. H needs to be varied so that the total number of units, N, is an integer. having a half-unit means that the spacing will be off or that it will not be possible to terminate the pattern properly. The next thing to do is to come up with a couple relationships for the geometry of the pattern. The first is that the pattern must be a certain number of squares long. This sum is the top and bottom width full length bars plus N units * squares per unit. The Number of units, N, also has to be equal to the target height of the units (Htarg – 2w) divided by the unit height. Here h is the spacing between units, and H is Htarget.

These first two expressions combine, after some work, into this ugly polynomial. we can now input an h, spacing between traces, and get out w. w allows for computation of N, the total number of units, which must be an integer to be valid. see plot below to compare red dots (N) to track width (mm) vs spacing, h (mm), as the spacing is increased.

With this computed, it is easy to choose the best outcome, where the track thickness is maximized, and spacing is minimized, to create an evenly heated area.

Final design:

I decided that the heater dimension should be 100×76, since it is a little smaller than .01m2, which should give better performance at 100W. It is also a reasonable size for most PCBs I would make and under the price break dimensions for aluminum PCB.

Since the pattern can’t go all the way to the edge, and because the mounting is somewhat TBD, I decided to subtract a 1mm border on the long (100mm) dimension and add 4mm border on the short dimension. This makes the final dimensions for the board 100×100 (including connector), and the pattern dimensions 98×78 mm.

For a target track resistance of 3.3 ohms, the calculator reports that the width should be .996 mm, with .267 mm spacing, and 38 repeats.

Kicad Plugin

Of course drawing this trace is tedious and difficult to get right. A plugin was designed to draw the trace meander. Details can be found on git.

Next Steps:

The next thing to do is to ship the design off to be fabbed. I’m planning on trying both FR4 and aluminum substrate PCBs. FR4 has the advantage of having a similar linear expansion coefficient as copper, and being an ok insulator. Aluminum on the other hand, is possibly more rigid than FR4, and is a good conductor/heat spreader- this would be good to prevent heat buildup in any one trace. I will also need a controller for the heater, with a micro, a sensor for feedback, etc (post coming soon).

Color Temperature Tunable, High-CRI LED Lamp

In my last post, I discussed the basic requirements and layout of my lamp – I wanted it to be able to control the color temperature and brightness, and I wanted it to be really bright. I experimented with a single strip, and detailed how the control board would work. If you want to try to build your own, the code/boards are here, although it is not very well documented.

Here is the control board all wired up. It packs an ESP32-C3, because I had one on hand, and because connectivity is important for this project. In addition to manual over-the-air control, I want to be able to put the lamp onto a simulated daytime color temperature loop- a real-life version of f.lux, which turns up the warm temperature light on your display as the day progresses. I also would like to add a way to sync it to some kind of live color temperature data.

This wiring leaves a lot to be desired- this is because the boards and mechanical assembly were developed at different times, and the mechanical side of things ended up going to “plan b” aka laser cutting. It might be worth revisiting in the future, especially because a lot of ports/connectors ended up unused. For now, I am just happy to have a lamp.

Here is the control page that the lamp serves up. It lets you choose a mode: SYNC, MAX, or NORM.

Norm is a smooth light intensity with changing color temperature- the output never exceeds the brightness of having all warm or all cool lights on, so when the temperature is “balanced” the output is 50% brightness warm, 50% brightness cool.

Max allows for the overall maximum light intensity to vary, but keeps the mix “smooth”. So at full warm, 100% intensity, the brightness of the warm strip is 100%, and at “balanced” temperature, 100% intensity, the output is 100% warm 100% cool.

SYNC will take over both intensity and temperature and to create a profile that mirrors a day, which is still TBD- to be developed. There are many ways this could be implemented, from having the lamp look up the weather/sunrise/sunset, to building a weather station, to having a server on a main home automation controller that tells the lamp what to do. Its also not totally clear to me what the ideal profile would be- for example I would like to have nice bright light, even in the early evening, so I don’t want it to totally follow the real “day” profile.

Mechanical Notes + Details

This lamp, despite not achieving one of my crazy tensegrity ideas, fancy woodworking concepts, or brass-pipe dreams, does use one of my favorite tricks: using orings as precision rubber bands. The LED channels are very thin, and therefore fairly hard to attach to with screws. They are meant to be mounted with supplied clips, but I didn’t feel great with those overhead, since they are not closed on the bottom.

Instead of clips, I used the aforementioned fancy rubber bands, with two per side (four total) per LED channel. This means that if one breaks, I will have time to notice and replace it. Since they are flexible, the overall alignment of the strips matters less and the hole placement does not need to be spot on. They are also very thin, so they don’t block light.

Another interesting fabrication note is how I removed the scorching from the laser cut cross-bars. These were completely covered in soot as-cut. Usually cleanup of parts like this is annoying and time consuming because the soot is ingrained in the plys of the wood. This means the parts need to be sanded back significantly in order to remove the soot, causing the overall shape of the part to change. In this case I had access to a media blasting cabinet- a light blast with glass media removed most of the scorched material, saving time and preserving the shape (and sanity).

Done?

This is the kind of project that could go on forever, but for now I will enjoy having a nice lamp. Here is a short list of improvements for “someday”:

  • use websockets for more responsive control of the lamp
  • make lamp status persistent
  • find a nicer looking power supply
  • add a setup page/wifi STAtion mode as a fallback in case it can’t get on the network
  • finish daylight color tracking/sync mode
  • redo the PCB +other components to make it less of a noodly mess
  • add a display (of some sort) to show the IP address
  • use the neopixel on the dev board to indicate connection status
  • etc.

LED Strip Dimming Driver

After listening to my Make Your Own Gnome, I decided I wanted to make my own led lamp. Specifically, I want a lamp that:

-is dimmable from “nightlight” to “worklight”

-is color-temperature adjustable from warm to cool light

-has some kind of smarts- daytime color temperature tracking, live outdoor color temperature matching, remote control

LED Choice

I decided the “hybrid” LED strips from waveform would be nice based on having a high CRI, and having a mix of warm and cool LEDS on separate channels. These LEDS strip lights are “DC constant voltage” strips meaning that you put 24 volts across the power/ground of each to light them. Each color temperature has a separate ground, for low side switching. Each rail (one per color temperature) has in parallel several sets of 6 LEDs in series, with current limiting resistors. The number of LED units in parallel is controlled by the length of the strip, with 6 LEDs per color per 4″.

Since I want to have worklight-bright intensity from either a warm or cool color, I need about twice as many feet of LEDs than I would with only one brightness. Empirically, this is about 4.5 feet of LED strip (per color), or about 2000 lumens, or a total of 9 feet and 4000 lm combined.

System Architecture

I decided to have a main control board and several strip driver boards. Each control board has switching supplies for 3v3 and 12V, and an ESP32-C3. The control board has enough connectors for three channels of output consisting of warm white PWM, cool white PWM, and driver enable, as well as a connector to distribute 12V. 12V is used to run the FET gate drivers.

At higher currents (single- digit amps), the inductance of the wires from the power supply (30 cm or so) and the switching can start to cause some ground bounce, so having local decoupling is needed- and reducing the overall strip length also reduces the total inductance per-strip. Above is the drain of the fet switching the warm channel- as you can see switching what is essentially two inductive loads causes some voltage spikes, which fortunately do not exceed the VDS of the FET.

This modular architecture also lends itself to reuse for a two or one strip lamp.

Switcher Board Detail

A FET driver is way better than driving the FETs directly from the microcontroller, because it drives the gates at a high current, higher voltage, and without the inductance of long wires coming from the controller board (which could be several feet away). It also reduces the requirements for the FETS, because they do not need to have low RDSon at logic level drives.

Driving the FETs fast is important, because the longer switching takes, the longer the LEDS will be operating with additional current limiting, and therefore far from the target current. This change in current can cause the leds to shift their output spectra, which defeats the purpose of having really nice high CRI LEDs. Additionally, the longer switching time can lead to frequency limitations e.g. if the switching time starts taking up most of (or exceeding) the period.

I want to switch at 25kHz- this is above the audio threshold, so any components vibrating due to switching (capacitors) won’t be audible. This is also well above what a person should be able to notice for flicker (120 Hz) and it seems like its also well above the frequency where it would affect a phone camera with a rolling shutter. 25 kHz means that the period is 40uS. I estimated that I want at least 10-100% duty cycle, so a minimum on or off time of .4uS. I estimated that the total switching time should be no more than 10% of this minimum time, which gave a rise/fall time of 200nS.

The FET has a gate charge of about 9nC, and the driver can source on the order of 1A, so the switching time should be a on the order of 10 nS- well under the 200nS limit. This means that the minimum on time, allowing for 10% of the waveform to be rise/fall time, is 200nS. This would allow dimming to .5% full brightness, which is very dim indeed.

As you can see here, the real rise time is pretty much the predicted 10ns, with a symmetrical falling time, so the driver should be suitable for this application.

Concluding Thoughts

The switching board works fine, and I’ll certainly use them. The light is GREAT and being able to control the color output is awesome! Of course, in the process of this design I have come up with some improvements for a next iteration.

First of all, the efficiency of these lighting strips is Not Great. Per-color channel per-section (4″) they consume almost a watt (.96 W). The problem is that the LEDs consume only 70% of that energy, and they are not 100% efficient. 30% of the energy is burned off as heat in the resistors, which are wisely distributed along the strip. While this is much better than an incandescent bulb, it makes the strips get pretty warm, with the outer aluminum shell reaching about 40C in 20C ambient temperatures.

The reason for this voltage design decision seems to be wanting to run the strip off a higher voltage to reduce IR drop along the length of the strip, and 24V being a ubiquitous supply voltage. Since my strips are relatively short for this type of application, IR drop is not a huge issue. Given 6 LEDs in series, dropping down to even 20V would result in an improvement to about 85% efficiency.

Going a step further, a current-controlled switching supply could be used per-section of LEDS. While this is more expensive, I expect it to be much more efficient (close to 90/95%). Given that I’m not sensitive to a small increase in BOM cost, this could be just fine.

Second, I found the WAGO connectors to be annoying to hand solder, and a bit expensive. They are really nice, but ultimately I don’t think they are worth it. Standard screw-terminal headers would be just fine.

Next up: building the lamp+controller.

ADC Linear Gain/Offset Correction

In my last post I explored the errors of unprocessed ADC data. In this post, I will show what happens if you add some simple gain + offset correction.

Arduino INL

Gain error is caused by DNL. The ideal code width is 1, but lets say that there is a predictable DNL error of +.1 LSB- that means that for every code (and there could be a lot of codes!), the measured value strays from the ideal value by +.1 LSB. At the end of the range, the difference will be +.1LSB * number of codes. This (in the example) is equal to INL. Take a look at the INL of the arduino above- the average code width is 1.08, so the INL increases by .08 LSBs/code. Therefore the INL looks like a straight line when plotted against LSB.

Corrected INL plot for Arduino

If we just assume each LSB is a little bigger, we will get a much lower INL. This will cause us to loose a little voltage resolution (.08 LSBs), but we will end up with a MUCH lower INL. This is the same data, but with gain correction. INL was reduced from 85 LSBs to about 6 LSBs.

However, the INL can still be improved- at low voltages, the most ADCs dont work very well. This causes a large DNL at the first code, as seen here in the corrected plot. This is where the line shoots straight up from 0 to about 6 at the first ADC code.

Arduino uno adc near code 0. orange line is the end of adc code 0

On a plot of DAC input-ADC output, it looks like the above – basically code 0 has a huge width. We can null this out by just adding a small offset to all the data. That means code 0 will still be wrong, but the rest of the codes will be right. because only the first code is messed up, we can usually ignore this error because we it only exists for a single code (code 0).

MUCH better

Here is the DNL for the fully corrected ADC- the max DNL across the whole ADC is closer to .8 now, except at code 0, which is off by about 6LSBs. This is equivalent to saying we cant measure less than 6 LSBs, but that if we make these corrections we will have an accuracy that is about .00084 mV (with 100x oversampling). That is pretty darn good.

What about the other ADCs?

How much can the other ADCs be improved? The ADS already has a pretty perfect gain and very little offset, so I skipped trying to fix it. Note the new LSB size and standard deviation are different than the previous values – to avoid the influence of the extremes of the ADCs, I only used the middle 90% of the ADC range to calculate the average LSB size.

ADC NAMELost Range
(millivolts)
New LSB size
(LSBs)
Compensated INL
(LSB)
INL Improvement
(LSB)
Compensated INL
(Volts)
ADS101501.160.00008
ESP (COMP)1971.00±.7123.8*129.2.006
ATMEGA328P61.08±.070.8683.62.0009
SAMD21231.01±.5011.263.85.002
*this is actually much worse

Surprisingly, the ESP and the SAMD are much closer in this comparison. SAMD still wins out, but at least the ESP32 has recovered slightly from having a three-digit INL. The one caveat here is that my calculation of code widths does not account for missing codes, which produce -1 DNL each. This data is correct if the missing codes on the ESP (of which there are 15) need to be ignored for this to be valid.

Arduino, ESP32, SAMD21, ADS1015 ADC Comparison

Hopefully my catchy title helps people find this post. After spending some time building a tools to compare A/D converters, I wanted to share some results from popular micros and previously popular (but now out of stock) ADCs.

Single Reading Accuracy/ENOB

ENOB, effective resolution, noise free resolution – these are all ways people describe an adc. I’ll provide the ENOB based on IEEE 1057, which is described as:

ENOB = log2 [full-scale input voltage range/(ADC RMS noise × √12)]

I will also list how good a typical measurement is from 5-95% of full scale. This comes from the average standard deviation across all codes. In theory, this is equal to the ADC RMS noise. I like this number because it tells me the typical LSBs of noise from the converter. However, because I didn’t test per-code the type of distribution, it does not necessarily mean that the noise distribution is gaussian.

If the noise distribution does not behave in a gaussian way, the predicted oversampling will not be accurate, as you can see here. The orange is data oversampled at 16x, and the dark purple color is the predicted standard deviation.

Here is a histogram for a random DAC input code from the ADS1015. As you can see most of the codes are code 400, so oversampling wont help much- the average will not improve much because most of the time code 400 will be returned.

Compare this to the SAMD standard deviation plot:

Here the predicted and actual 16x measurements start to line up pretty well round code 40000. If we look at code 42000:

The distribution looks a lot more gaussian, and covers many codes- in this case averaging can help, and the predicted error lies up nicely with the measured error.

ADC NameENOBSTD.DEV. (LSB)STD.DEV. (Ideal volts)
ADS101510.60.39.00019
ESP326.216.00429
SAMD218.24.00097
SAMD21 16x10.4.89.00021
Atmega328p9.70.36.00038

Unsurprisingly, the ADS1015 is the best in terms of ENOB. Since the ADS is actually an 11 bit ADC (it reserves a bit for the sign, but it never uses it in single ended mode). It costs as much as any of the other chips, but it has only one job: being an ADC. The ESP32 really is pretty bad, as it loses about half its effective bits to noise for a given reading. The atmega comes in at a respectable 9.7 bits! The resolution is lower than the ADS, but much better than the ESP.

The SAMD21 ADC is what I would like to use. looking at the standard deviation chart, it looks like 16x oversampling is reasonable to do. This would give similar performance to the ADS1015 in terms of noise.

DNL/INL

a plot of a wide code

The ideal code width for an ADC is 1, and the DNL is the error from the code. If we have consistent code sizes, it will be easy to compensate for, but codes that represent an extra-wide or extra narrow voltage range are more difficult to compensate for. If the DNL is -1, the code will be missing (the narrowest width a code can be is 0). Missing codes are very bad, because its hard to tell if they are missing or just really really narrow. If you map the adc and compensate by ignoring that code, there will be an LSB of offset when it reappears.

The most interesting stats here are the average code (this better be about 1) width and the standard deviation. If code widths are all about the same (even if they are say, 1.2 on average), it’s good, because each step is a predictable size. If the widths are all different, its hard to tell how much the measured voltage changed with 1 LSB difference.

plot of code widths- outliers are in orange

I flagged any codes that were larger than 2 standard deviations from the mean. For most ADCs this only catches high (wider) codes since if the standard deviation is >.3 LSB, 3 standard deviations below the mean is a missing code (DNL = -1).

INL was calculated as the sum of the DNL. Max INL is the maximum error you expect to see from a straight line. Positive and negative DNL cancel out, but very high DNL mid-range is hard to compensate for because you need a lot of small negative codes to get rid of it- and while positive DNL can go to any number, INL can only go to -1 before the code disappears.

SAMD M0 INL

High DNL at the start or end can be compensated for- basically, you just add a DC offset and dont use those codes. If the LSB error is consistent, or if steps are mostly slightly wide/short you get a gain error, which can also be compensated. However in the middle, it is hard to compensate for because there is a big jump. In theory you can just note that measuring a specific code maps to a wide range of voltages, but that makes the ADC kind of nonlinear and bad, plus you need a huge lookup table.

I took measurements with and without the gain/dc offsets applied on the ESP32 and SAMD21 ADCs. Both these micros have built-in compensation values. I also manually compensated the Arduino ADC for fun.

For Worst DNL, I only included values from 5-95% of the range, even for uncompensated ADCs. I feel those numbers are a better metric for how good an adc is mid-range. If no wide codes were reported, I gave the highest code width measured between 5-95% of the range.

ADC NameAverage Width
± std dev (LSB)
Worst DNL (LSB)Wide Codes (#)Max INL (LSB)Max INL (volts)Missing Codes (#)
ADS10151.00±.091.376.16.000080
ESP320.91±2.064.470*153.03.04114
SAMD211.01±.613.94674.75.0180
ATMEGA328p1.08±.191.36084.48.0900
*the standard deviation is so absurd that this does not even matter

As you can see, the ESP32 ADC really is terrible (as promised by the datasheet). The standard deviation, DNL, and INL are all high, and there are missing codes! On the other hand, the dedicated ADS1015 is really good. The thing that surprised me here is how bad the SAMD21 is in terms of worst case DNL. It does not add up quite as high as the ESP, but its still not great, especially with all the wide codes that it has mid-range.

arduino adc INL

(Un?)surprisingly the Arduino ADC did well in overall INL, even uncompensated, compared to the ESP32. This is pretty cool, since a lot of people use it. The INL error chart shows it should be pretty easy to compensate. this is a “good” plot because it shows that on average the code width is a little wider than it should be, since INL increases in an apparently linear and monotonic fashion, with some offset near 0.

Compensation:

I will look at this in my next post, using the collected data. Aside from oversampling, the next adc-error correction technique is to change the assumption that 1 LSB = 1/2^n volts. If we have measured all the code widths as 1.1 LSB (in volts), then every code we are (on average) adding .1 LSB INL! This causes gain error in the ADC.

By assuming that each adc code is on average wider/thinner than 1 LSB, we can correct this gain error. The tradeoff is a change in range and in resolution- if the resolution goes up ( average LSB<1) the range will go down- we wont have enough bits to measure the whole range. if resolution goes down ( average LSB >1) the resolution will go down but the range should go up (although it may not, because ADCs are poorly behaved at high and low values).

ADC Comparison – What and How I am Testing

I built a tool to measure DC performance of an ADC. Since I have it, I figured I may as well use it to investigate the four ADCs I have lying around- an esp32, and arduino uno (atmega328), SAMD21 based feather board, and the ADS1015. This post is about the data I want to collect and analyze.

Key Performance Indicators:

The things I look for in an ADC for DC performance are a high effective number of bits (including with oversampling), good monotonicity, low DNL and INL. All of these characteristics are related, but each one gives me a feel for the ADC, and only some of these are listed in datasheets.

Here is the data I intend to capture.

Standard Deviation + ENOB

The standard deviation per DAC output code is a good metric to start with. It is important to have resolution, but if the lower bits are twiddling back and forth seemingly randomly, they are not really useful. We can measure this twiddle and find out how many bits are useful- the effective number of bits (ENOB), for a single read. Even better, that twiddle might not be random- if we look at 100 codes and average them, we should get closer to the “real” value (oversampling).

I took 100 readings with the ADC per output code. The “true” value of the ADC is probably close to the mean of these readings. I calculated several standard deviations based on this mean- the standard deviation for the population of all readings, and then the standard deviation of oversampling 4x, 8x, and 16x.

As the oversampling increases, the standard deviation of the population of oversampled results decreases. Basically since we are averaging a bunch of samples, the random error cancels out and we end up closer to the mean. The standard deviation should be reduced by a factor of 1/sqrt(n) where n is the number of samples (standard error). Here you can see the results of a run with the SAMD21. I plotted the 1x and 16x sampling, as well as the predicted 16x sampling (dark blue over orange). It is pretty cool to see the theoretical value line up with the measured value. If these don’t line up, its a sign that the errors are not random- and that means that oversampling wont actually help get close to the “true” value.

One artifact of calculating ENOB this way is that at the edge of a code, the standard deviation is going to be really high compared to mid-code. You can see this effect here- the xticks are set to be the width of an ADC code, but they are not aligned. Usually the standard deviation is pretty small, but it gets really big near what are likely code transitions. Since we usually don’t have a map of the whole ADC when we are measuring something, we have to take the worst case scenario (our measurement could be on a code). Still, averaging helps improve ENOB.

Monotonicity

Monotonicity is related to ENOB- the output codes should be monotonic at the effective number of bits. Since my reading from the ADC is in terms of averages of 100 readings, I will use the standard deviation/10, since the standard deviation should be reduced by sqrt(100 readings). The worst case standard deviation is about 14.5 LSB, so I chose to look at the output code every 1.5 LSB ADCs of input voltage.

In theory, this should always be a positive difference- the output code should increase by at least 1 every 1.5 lsbs. However, this is not the case. There are 39 non monotonic jumps, and each of them is only a little bit bad- about 1 LSB. In terms of the code/code plot. these correspond to flat spots, or places where the codes actually decrease, or are flat, as seen in the next section.

Max DNL

There are two DNL errors to look for, DNL<-1, which causes missing codes, and DNL>1, which causes non-monotonic (not always increasing) behavior. Looking for missing codes is easy and is done more or less by brute force – fortunately in this in this example, there were no missing codes.

It is not surprising to have codes be wider or skinnier than 1 LSB of the ADC. However, DNL>1 causes a the ADC output to be flat for a while- this means that we have extra error that we is hard to account for. I looked for codes that were more than 3 standard deviations from the mean width, and then plotted the errors. The widest code (aside from the expected nonlinearity near 0) was an astounding 7LSBs!

output from tool showing outliers and zoomed in DNL

Max INL Measurement

To get the INL, I just took the sum of the DNL’s. Since I didn’t correct for offset in this case, there is a large jump in DNL at the start.

Conclusion:

I now have a tool for measuring real world DC performance of ADCs. I have a few ADCs lying around, and I want to figure out what kind of defects they have and compare them!

Testing An ADC for DC Characteristics

The tester!

With the ADSXXXX series out of stock for the foreseeable future, I needed to evaluate the ADC’s on several micros to understand if they would be good enough for my application – reading oxygen sensors. Mostly I care about DC characteristics, because my signal should be changing very slowly- on the order of many seconds.

I’ve seen a lot of interesting work done on ADC characterization and calibration, but only rarely do the posts explain where the reference voltage is coming from. If the voltage source is noisy or has some nonlinearity or bias, it will throw off the calibration.

really really excellent series on adc error/calibration from thea.codes

demo showing how the esp32 adc is bad

auto-cal of the esp32 adc*

The most obvious way to do this is to generate a really nice (precise + accurate) voltage, put it into the ADC, and then figure out what the expected error is for a sample or for multiple samples. With enough resolution on this voltage, I can look for missing codes (high DNL) and for error in value from code-to-code (INL), and find really good gain and offset correction factors, and come up with an idea of how good the ADC is (at DC, for some ADC settings, etc).

The trick, of course is generating the nice voltage. Since I don’t have a sub-mv programmable reference voltage, I had to buy one. Since I didn’t want to spend hundreds of dollars on a non-programmable benchtop equipment, I decided to buy a 16-Bit DAC. These chips are pricey, but on the order of a pastry and a coffee, not a nice bicycle or oscilloscope. Instead of spinning my own board, I bought an eval kit to speed things up.

The DAC8050X comes in a one and two channel flavor. I got the two channel flavor in case I wanted to make differential measurements at some point in the future. These are really nice, and offer <1LSB nonlinearity. This is really pretty impressive to me, and looking at the typical characteristics, the INL and DNL are way way lower than 1 LSB.

The DAC80502 is also pretty sweet in that it has programmable gains for both the reference and the outputs- this gives it a really wide range of outputs, without any external circuitry. It also has an external VREF in, so you can supply a voltage range you care about. Combining the two outputs with some creative-op amp-ery could give you a really wide range of voltages to play with, but I really care most ab out the sub-3v range, which this is perfect for.

*this seems somewhat sketchy given the really bad stated INL/DNL for the adc, and the fact that the DAC on the esp32 seems to be 8bit. it might make the output look linear, but I wouldnt use it for anything important

Who tests the Tester?:

When making measurements against a standard, its important to understand how good your reference is. In this case the DAC is being used as a reference, which means it needs to be very “nice”. There are many charts in the datasheet, showing that all kinds of systematic and thermal drift, referred noise, error under load, etc. The total unadjusted error appears to be around .02% typically, and the INL/DNL per code is <<1 bit, usually around .2 LSB. While these charts are all very reassuring, its nice to verify them.

To do this I connected the DAC to a nice 6.5 digit multimeter (Agilent 34465a) and captured a 5 reads at each output code, and then calculated the error per code, to verify. This turned out to be a slow process, because 2^16 (65.5k) times even a few milliseconds is a LONG time. In order for the instrument to have a high enough resolution/accuracy, I had to wait a long time for it to collect data, so the test ended up taking ~4 hours.

N.B. the 34465A is a lot faster at resolving lower voltages than the lower spec models- those would have taken even longer!

very nice!

This chart shows the measured voltage and the difference in voltage from output code-to-code in LSB. The ideal difference code-to-code should be 1LSB of voltage – 1 LSB is Vref/(2^16). A difference of 0 indicates no change in output (a missing output code) and the distance from 1LSB indicates how far from the ideal each step is (INL). A different-sign difference between codes, which we don’t see here, indicates that the output is not monotonic (increasing or decreasing constantly). Fortunately, the worst thing seen are some missing codes at the start.

NB: this happens even without auto-zero on, and the meter can read negative voltages, so these really are missing codes (DNL>1). This seems wrong compared to the datasheet, but my measurements should be good enough to capture this. That said, I wont totally rule out some small dc offset or error in measurement.

That means this DAC is pretty awesome. The standard deviation of the errors is about .073 LSbs. Its not quite gaussian (kurtosis 1.17) because it has basically no tails, but its close enough for me. As you can see the LSB error code to code (DNL) is usually less than .2, which matches with the datasheet.

NB: this result was obtained under ideal circumstances. When I worked at my computer/charged my phone right next to it and generally jostled the setup, I got non-monotonic readings an more LSB error. With short wires to the DMM and good test practices it performed much better.

Preliminary Data:

Here is a read of the SAMD21 ADC with the internal 1VREF and no gain, compared to the DAC with 1.25VREF. The total error/code is shown below in LSB. This is a lot more than the stated 15 bit total unadjusted error, but I haven’t used the built in gain/offset correction, and I’m not sure if this incorporates the samd21 adc arduino bug. As you can see, a simple offset could reduce this total error by about 40%. Interestingly, there were a couple very odd codes near the 1/3 and 2/3 points where the error quickly jumps.

Dactyl Manuform Flex PCB

My flex pcbs showed up and they are just lovely. Unlike a rigid PCB, they are a kind of coppery gold translucent color with shiny copper underneath. And unlike a rigid PCB, I had to spend hours and hours carefully routing every trace in smooth, even curves, so the whole thing just has a delightful aesthetic. They came in sheets of two (right and left) with the flexes being retained in the sheet by a few small tabs. This is a great way for them to come because once they are free of their nest, they become very flexy and floppy (as planned).

Time Savings vs Handwiring:

Wiring up a dactyl with these flexes is dead simple and fast. I estimate that it takes about an hour to put on all the diodes and to solder all the switches. I’ll time myself next time when I am not taking photos (and running to microcenter), but it would be easy for someone to do in an evening, provided you are used to surface mount soldering. My first dactyl took me multiple evenings of careful snipping, bending, soldering, stripping, and checking, I would estimate that took 8++ hours.

Assembly Steps+ Notes:

Soldering to a flex pcb is a little different than soldering to a regular pcb. The big differences are that it is very floppy, and that the pcb coverlay (kind of like solder mask) has very low thermal conductivity, and it is very thin. I did all my soldering on a heat proof silicone mat, with a normal sized chisel tip on a hakko FX888D with tweezers and no magnification. You don’t need a fancy iron, but magnification can help if you are not used to components of this size. The small size components (and orientation) are important to preventing stress on the solder joints when the flexes are flexed. Below are the steps I took to solder this thing. These steps assume you already have a keyboard with the keys installed (look at the flex to make sure you install them in the right orientation- pins should be close to the bottom of the keyboard).

A note on safety: unlike rigid boards, flex boards are springy and if they release at the right time, I’m sure they could shoot some molten solder somewhere bad- say your eyes. It seems like a very good idea to wear some eye protection while working with these.

I’m free!

1: Remove the flex from the backing sheet. This should be done by carefully pulling the flex apart instead of tearing the flex or using a knife. find each tab and pull perpendicular to the tab take your time. Once the flex is fully free, double check to make sure there are no tabs left between the flexes.

Diodes on parade!

2: Diode soldering. The cathode (marked with a line) goes on the “cup” side of the solder mask. Solder the diodes on, or if you want super detailed instructions, continue on. First, I deposited a small blob of solder on one side of every diode pad. I put the solder on the pad on my dominant hand side. Then I laid out a bunch of diodes, and lined them up so all the cathodes were facing one direction. on this board, most of the diodes are cathode-on-left so that is how I lined them up.

Just a tiny dab of solder

Once I had all my diodes ready to go, I started tacking them to the board, working towards my dominant hand. that way the iron/iron wielding hand does not have to cross over already soldered components. Once the diodes were soldered, I rotated the mat that the flex was on and tacked on the other side. if a diode didn’t look flat, I took it off and reworked it.

Like a slinkie! Note tack soldering on top and bottom buttons

3: The next step is to start to install the flex. I started on the outermost row (outside the pinky row). I simply pulled the pcb up and into the shell- it was happy to extend out like a slinkie so that some of it was outside the shell while I worked. First, I made sure that the pins from the switches went through all the holes on the pcb, then I tacked down the first and last pins with solder. Once the flex was tacked, I went through and soldered each pin to its pad, making sure to get a good connection. Once the row was done I would start the next row.

thumb cluster detail

4: Thumb cluster buttons are a little different- each one lives on its own little mini flex connection. It worked well for me to tack them down one at a time. NB the “L” peninsula buttons do not change orientation- the bottom side of the PCB should always face the switches.

5: Solder the micro. NB the island that the micro sits on is meant to be folded over, so that the micro sits on top of it. There is some text that says “THIS SIDE UP” to indicate the right side. If your micro came with headers on all the pins, and you don’t want to remove it, you can snip off the extra flap of material. The USB connector should point “down” towards the thumb cluster. The micro orientation might seem strange to some- its mean for a USB bulkhead like this one, so the “wrong” orientation is meant to let the cable have a nice service loop in the shell.

6: Program the micro. Plenty of tutorials on that, and I will have some files up soon to fix a few pin order mishaps that happened on these boards (one header is flipped). NB some kapton tape should be placed under the micro to prevent shorts.

What went wrong:

one of the pin ones is not like the other one…

Inexplicably, I flipped a single header, so there may need to be separate firmwares for the left and right hands. This is annoying, but not nearly as annoying as wiring up a whole dactyl or screwing up in some way that is not a small matter of programming (SMOP).

What’s next?

I need to test the right side and finish my second keyboard (for the office).

How do I get one?

Twoards the end of the week I will be putting the extra prototypes up for sale. If you are interested, you can submit some info here to be notified.

Hey! where are the design files?

At the moment, I have decided not to share the design files. Unlike many projects there is little to be learned from them for repair or use. The boards are literally transparent, and the schematic is the same one thats been used on pretty much every dactyl. I have decided I want a bit of a head start selling these to recoup some of my costs before I make it easy for anyone to just go and buy a grip of them and sell them and put them up for sale (however unlikely that is).