The Big Ugly, part 4: (im)perfect present

Here we are, and this is Now. So far, I have written about the two generations of problematic and/or quasi-successful color enlarger light sources I’ve made and the lessons I’ve learned along the way. But all that is in the past. This blog is about the current, 3rd generation of the LED color light source for my (t)rusty Durst 138. It incorporates most of the lessons I learned along the way – and brought me some new ones, no doubt.

Read the other parts in this series here:

First, a little recap. Here’s what I set out to do, what I learned along the way, and what I had to keep in mind when building the 3rd version of the color enlarger project:

  • I like this old Durst 138, if only because I promised the man I got it from to take care of it. So it’s here to stay, and I better use it. I do like it a lot for what it is, though.
  • Discrete red, green and blue LEDs are currently really the only way to go for printing color, unless going back to dichroic filters and tungsten heat generators. Thanks, but no thanks.
  • SMD (surface mount device) technology is inevitable. Better embrace it instead of fight it.
  • Don’t skimp on red. Don’t skimp on green, either. There’s no such thing as too much power, anyway.
  • Much power brings much responsibility for getting rid of the heat. Thermal design remains important. OK, so maybe there is such a thing as too much power.
  • Speaking of which: don’t burn down the house.
  • Condensors are nice, but a diffusor will also remain necessary. It’s OK, but it does increase the power requirement.
  • PWM (pulse width modulation) resolutions of 12 bits and upward are a necessity; it’s just too hard to try and solve filtration linearization problems in hardware.
  • Pay half a mind to software architecture to begin with, because version 2 was a royal mess in this respect. Seriously, I dreaded going into that code to try and fix anything for fear of the entire thing collapsing like a house of cards.
  • Linear voltage regulators aren’t LED drivers, you dummy! You got sort of lucky once, now don’t push it.
  • Finally, the original requirements also still stood: enlarge any format from 35mm to 4×5, in B&W or color (RA4), with good usability/user-friendliness, and preferably built from Cheap Junk from China. (I seriously don’t think the stuff we get these days from China is junk. Most of it is actually pretty good, especially electronics components. Even if they’re counterfeit or quasi-copies of Western designs they often still aren’t half bad. After all, your expensive iPhone if you’re silly enough to have one is mostly manufactured there as well, and that’s supposed to be quality too, isn’t it?)

This time, I really took my time. I don’t know exactly how long I worked on the current version, but I reckon something like 18 months. Sure, work got in the way of the fun stuff, and the project was inactive for periods of time due to other priorities (or just a lack of interest on my part). And of course, there was no pressing need for an improved version, as version 2 worked quite alright as it was. Actually, that was a very good reason to take relatively long, because why build something if it’s not better in pretty much every way than the thing it replaces? So that’s where the bar was. It needed to beat the existing technology and preferably by a mile or so.

The light source

The light source itself evidently was at the very top of the list. I did some experimentation, quite a bit in fact, and not just on paper. This time I sourced LEDs from AliExpress instead of eBay, as supply had caught up there and it was now also (somewhat…) possible to actually find something on there. This, by the way, remains the weakest link of the AliExpress thing. It gives me lots of results whenever I search something – just not in any way related to anything I’d actually want to buy. I started with the same kind of LEDs I used in version 2, but this time decided the gluing them onto a piece of aluminium wasn’t the optimal solution. This time, I went for actual SMD technology, and that also meant I had to (1) use an aluminium PCB instead of common FR4 and (2) I actually had to learn to make PCB’s – because up to now, I just perfboarded and point-to-point wired my way through projects. So cue the first test version of the Next Generation Light Source:

Experimental light source. Service hours: probably 0.1 or so. Actual purpose: throw off visiting aliens on the weirdness of human technology.

The odd arrangement of LEDs was something I mocked up digitally to get somewhat even lighting combined with high power density. The size of the PCB is 100x160mm (a common PCB size), so I also decided along the way to abandon the idea of fitting the light source into the original space intended for it in the 138 chassis. Instead, I decided to mount it right on top of the condensor stack. The mirror easily lifts out of the way on this enlarger anyway, so there’s ample space there. And ample space means room for more power and better heat dissipation.

Well, that’s enough said about that initial test; it lit up alright with the necessary drivers, but I found it lacking in several ways. In particular, it wasn’t that more powerful than version 2, so why bother with it? What it did prove, was that it was feasible for me to design, layout and etch my own PCB’s at home, even metal-core ones. That’s a nice trick to have up my sleeve!

Freshly etched metal-core PCB of my own design. The first I ever made (metal-core, that is). Nice proof of concept, although nothing much more.
The same PCB hooked up to a 3-channel experimental current sink.

The main issue here was the disappointing power density. I needed to make a major step to really get ahead with this, so I went back to do a parts search, and this time I managed to scout down some 3535 form factor 3W LED beads. 3535 stands for 3.5mm x 3.5mm that’s not exactly big, I can tell you. Heating that up with 3W (even if that’s a liberal estimate and in reality it’s maybe half to two-thirds that power) is pretty ridiculous, but hey, with that new metal-core PCB trick, I should be good, right? So the next rendition looked like this:

same PCB size: 100x160mm. But with a 3535 form factor you can cram a lot more LEDs onto it!
No, my fingers aren’t gigantic. Those LEDs are just damn small. Those are 3W devices, would you believe it?

These tiny LEDs did present a bit of a challenge, though: they don’t have leads. That’s right: the only way to solder them is to get tin underneath them where the contact pads are and mount them that way. Would you believe I hand-soldered that PCB above? Keep in mind this is an aluminium PCB, so it dissipates heat like it was intended to do so. If you hadn’t realized: this makes soldering a nightmare. You just can’t get anything to heat up to the melting point of solder!

This is also why ultimately, I abandoned this particular PCB, even though conceptually it seemed pretty sound. The main problem was the poor soldering, which resulted in problems when the light source ran at high power levels. The LEDs would heat up, change slightly in size and already poor connections would break. I also think I overheated a couple of LEDs because they made a poor thermal contact (but a borderline OK-ish electrical contact) and hence burned out. Finally, I was experimenting with different LED drivers as well, the failure of one of those board being the final blow to this particular light source PCB. So apart from the manufacturing problem and some user abuse, I was onto something.

The same light source PCB, now with heat sink mounted (underside), connectors added to it and note the two DS18B20 temperature sensors stuck onto it with heat-conductive adhesive (which failed). Also note how battered and bruised it is due to repairs, which I eventually gave up on.

I caved and bought more equipment. If you don’t know what to do, equipment is usually the answer. Actually, it isn’t, and I usually postpone buying stuff until the point I have to admit there’s no other solution. This was one of those times, so I got myself one of those SMD soldering hot plates. Yes, the very same one I use when pouring carbon tissues! I also made some minor changes to the light source PCB layout, replacing the glued-on temperature sensors with decent SMD types and adding SMT connectors for manufacturing ease. Hey, if you’re going the surface mount route, might as well do it right.

The ultimate Version 3 of the light source. This is the one I currently use and that appears to work very well indeed. Note the SMD-mount DS18B20 in the center. Also note the other change compared to the version before it: yes, indeed – more LEDs. There was some space left, you see.

So by about the third round I had something that I was happy with. This is the version that I’m actually using at present. It houses 132 red LEDs (650/660nm), 66 blue ones (450nm) and also 66 green (525nm) ones. Hey, a 2:1:1 ratio, again? Didn’t you say you weren’t going to skimp on the green channel? Yes, I did, but these are 3W LEDs (nominal), so that would make 198 Watts of green power alone. Or a total of 792 Watts across all three color channels.

Well, in reality, it’s not anywhere close that number. As noted in the previous installment, LEDs sold as ‘3W’ devices usually aren’t really rated for 3 Watts. It’s more of a convenience rounding up – or marketing optimism. Moreover, I don’t run them at their rated capacity. In reality, the actual (measured) power consumption of the light source at full power is around 350W or so. But that’s still a truckload of power…remember, the previous version pushed to about 68Watts and that was only on paper, and it was for most purposes adequate.

Yeah, like a Christmas tree. If you can squint, you can make out the neat little connector cables that slot right into those fancy SMT connectors. I was pretty happy with that little detail.

So with a couple of tries and some serious learning on my part, especially on the manufacturing front, I passed the hurdle of an improved light source. But a LED light source is pretty much nothing at all without proper drivers. And that was also a bit of journey.

You drive me…!

LEDs are current devices. This means that you generally drive them with a constant current and then the voltage that drops across them just happens to be what it is. You do it the other way round, and you get slapped across the face by thermal drift issues and if you’re unfortunate enough, smoke signals. (In fact, LEDs die mostly in a binary and very polite fashion. They just pop out without a sound. I know this by now, you see.)

In version 2, I used voltage regulators in lieu of constant current sinks because this was easy for me. I’m no electrical engineer and this was a solution that I could wrap my head around and implement at the time. It was probably also a contributing cause in the cyan drift issue I discussed towards the end of the previous blog, so it was out of the window for this version. Finally, I disliked the inefficient nature of the solution. It dissipated something like 20% of the total power draw of the light source, and with the present light source, this would have computed at 70W or thereabouts. That’s just silly. Let’s not go there.

Well, truth be told, initially, I did go there, of sorts. I came across the schematic of some guy’s LED drivers and he essentially used sense resistors in series with the LEDs along with a MOSFET shunt and some opamps to amplify the sense signal and drive the opamps. A servo control, basically.

Led current sink servo topology. Simplified and probably with an error or two.

The idea is that as the current through the LEDs rises, the voltage across R3 increases. The opamp picks this up and compares it against the voltage set by R4 & R5; if it exceeds this level, its output will drop, turning off the gate of Q2, which will make the current drop. And vice versa. Effectively, it keeps the current constant – provided it’s well-behaved and doesn’t oscillate. Opamps these days are good boys and girls, fortunately.

That was the basic concept I came across, and I figured (like so often), “why leave good alone?” So I tried to improve on it. Specifically, I kind of fancied the idea of being able to set the current through each LED channel in software. After all, if you have the combination of R4/R5, which is pretty much the same as a potmeter if you stick a shaft onto it, you could replace that with something like a digipot, right? And then you could control that with, let’s say, an Arduino of some sort. You could even clamp the reference voltage from the digipots to GND to modulate the current and thereby create a PWM interface on the thing. Nice! So that’s what I did.

Microcontroller-controlled power LED current sink. Highly experimental. Moderately functional. Kind of cute, too.

I must have the schematic of that thing somewhere, but I don’t even know where to start looking, but the concept is explained above in general terms. The microcontroller has a rotary encoder and a little OLED display so I could set things like target current and overcurrent protection levels, and it would also display actual current. The microcontroller interfaced with a couple of X9C103 digital pot meters to set the reference voltage for a couple of (can’t remember…TL741?) opamps. These in turn drove the gates of some IRF840 MOSFETs, which were housed together with some sense resistors on a separate PCB. Together with a hefty heat sink, evidently. Because this was still the brute force approach, and the MOSFETs would burn any excess power the LEDs couldn’t handle.

Interestingly, it sort of worked, actually. Sort of. There were minor issues that I couldn’t be bothered to properly figure out. A ground lift issue was one of them, with the LEDs failing to turn off completely due to differences in analog ground level between different PCB’s. The kind of thing you run into when doing relatively high-current things, I suppose. And there were other concerns that led me away from this path. I was concerned with the response time in case something would go dramatically wrong somewhere. The setup wasn’t really optimized for that and I think at best it would have taken a couple of milliseconds to shut a channel off if it went haywire. More than enough time for some LEDs to go to semiconductor heaven. Not only that: it turned out to be tricky to get the current level of the LEDs as stable as they needed to be. They tended to drift by up to a few percent as I recall, and that’s just not acceptable. It would result in color mismatches between prints.

Finally, I had concerns about the stability of the thing when a PWM signal was applied to inputs on the opamps. How much time exactly would it take for them and the MOSFETs to shut off, and after releasing the pulldown on the gates, how quickly would the current settle at its intended value? Would this even keep up with the kind of PWM resolution and frequency I had in mind for this? Too many doubts, not enough confidence (truth be told: I think that this concept can work perfectly fine. It just takes some thorough engineering and selecting the right parts to make it stable, responsive and consistent. But it could very well be done, and it would be an interesting exercise). So I abandoned this concept and decided to do it properly. And easier, too.

Confession: it just took me exceptionally long to realize that I was trying to solve a problem that had been solved many times over already. Of course, I was aware of the existence of all sorts of ready-made LED driver modules (e.g. the popular BuckBlock series), but none were to my liking, mostly due to their black-box nature and/or lack of flexibility in tailoring LED currents to my needs. At some point, I stumbled across actual LED driver IC’s of various kinds. Like a kid in a candy store, I started rooting around. Ultimately, I ended up with the MP24894 buck driver from Monolithic Systems. It had a couple of things going for it:

  • Wide input voltage range. I was planning to use 48V for the LEDs and many drivers didn’t go beyond 32V or so. The MP24894 goes up to 60V.
  • Flexibility; you select the inductor, sense resistor and switching MOSFET yourself, so it’s easy to accommodate just about any conceivable LED configuration.
  • PWM dimming input; it’s kind of obvious and common, but nonetheless crucial.
  • All kinds of protection against common issues, such as short & open circuit and thermal shutdown.
  • High switching frequency up to 1MHz. I liked this because it helps to keep component footprint small, but also because it allows the PWM frequency to be set fairly high. Indeed, the device accepts PWM frequencies up to 20kHz.
  • Excellent datasheet. Especially Chinese manufacturers often have obscure, grossly incomplete or even entirely missing datasheets. That’s no good for me; I need to know what I’m working with and how it’ll behave under conditions that are sometimes outside the normal envelope, because my application isn’t an automotive interior lighting LED or a stage light, and probably more demanding in a couple of ways. If you look at the datasheet, it includes all the relevant plots of IC behavior under various conditions, including PWM dimming, startup, shutdown etc., but also an applications section that details the selection of auxiliary components. Generally it’s only the big, Western manufacturers like TexasInstruments and AnalogDevices that publish this kind of expansive information. Kudos to Monolithic for actually getting it right!
  • Cheaply available from AliExpress. Relevant, because I didn’t need just one or two and I always want a couple of toys to play and experiment with. We’re talking dozens of chips, not a handful, and then costs tend to add up rather quickly.

I decided I wanted to run the LEDs as modular as possible and also to keep the different channels as identical as possible. So unlike in the second generation, I wasn’t going to put multiple strings of e.g. red LEDs in parallel on a single current sink. Initially, I used the first version of the 3535 SMD light source, which happened to have 13 arrays of LEDs, so I also needed 13 driver circuits. The first MP24894-based driver board I made included all 13 drivers on a single PCB.

First ‘proper’ driver board, 13-channels, fed with 48V, ca. 500mA per channel. I had to iron out some bugs on the PCB…This side shows the driver MP24894 IC’s, sense resistors and MOSFETs
13-channel driver board, top side, showing power inductors and buck diodes as well as a 48-12V converter and further step-down to 3.3V and 5V for onboard logic.

While this driver board worked, it was plagued with a couple of issues, some of which proved catastrophic. The onboard logic, which did the final stage of PWM signal conditioning as well as run a cooling fan, wasn’t particularly reliable. Moreover, the LED drivers themselves suffered from issues, the nature of which I frankly can’t recall exactly, but I do remember at one point mistakenly shorting two LED channels, resulting in a cascading failure of several LED drivers and also several LEDs. There was much weeping and gnashing of teeth, after which I swallowed hard and completely redesigned the light source and the drivers. The story on the light source, you already know about.

As to the drivers, I went a more modular approach, so any failures would hopefully involve a little less rework. I arranged the LED drivers into four separate board: two for red and one each for blue and green. Each board has six driver circuits, each circuit with its own fuse to protect the LED arrays in case things go really pear-shaped. In the process, the number of LEDs increased substantially, hence the increased number of drivers. The total of 24 drivers are rated at 500mA each and take 48V as well as a PWM signal and use those to drive the LED strings. The theoretical maximum power is thus in the vicinity of 575W, but things run quite a bit cooler because the forward voltage of the LED strings is significantly below the 48V supply voltage.

Schematic for single-color channel driver board with 6 identical driver circuits. I made 4 of these boards; two for red and one each for blue and green.
LED driver board PCB artwork. It’s not super EMI friendly, but it works…and it’s compact. Each of these could theoretically drive over 140W of LEDs, and the boards are only 75x50mm. Double-sided design, DIY manufacturing.
LED driver boards in-situ. I did the solder mask in the color of the LED channel for easy identification. Two red channels to the left, green is top right, blue bottom right.

The drivers work very nicely; I scoped the output current during testing and the PWM transitions are nice and perfectly square with little to no ringing. What ringing there is, is lost in the normal noise of the buck frequency. I didn’t filter this out with a capacitor across the LEDs because this would degrade PWM performance. You trade one problem for another. I don’t have the scope traces, because I made them with the old 20MHz Philips CRT scope I used to have. Once you get down to very low duty cycles, everything does break down. As I recall, below about 20/4095 (that’s less than 0.5%), linearity broke down badly and the LEDs would either turn off, or flicker a bit. But running at a 10kHz PWM frequency, we’re talking about <500ns (nanosecond!) pulses here.

I also separated out the little fan driver and PWM conditioning MOSFETs into a small board, added a driver-to-light-source interface panel that recombines the driver outputs (which are grouped in 2xR, G and B) into the correct order of the light source (which alternates in R, G and B strips for even illumination across the colors). And there’s a little filter board that adds a bit of capacitance near the LED drivers to reduce EMI noise on the power supply cable.

Head assembly. The actual light source is hidden behind the supporting electronics.

In the image above at the top is the yellow interface board that regroups the LED driver connections into the right order for the LED head; the 10 grey flat cables go to the light source itself. The light source actually sits at a decent distance above the condensor stack for reasons of even illumination across the entire field. There’s also a ground glass (as opposed to frosted plexiglass) diffusor at the bottom of the head assembly. Ground glass passes a lot more light than the plexi I used before, but it also diffuses somewhat less effectively, so there’s a tradeoff between light source distance to the condensor stack, diffusor material and available light.

The LED driver boards are visible in the center. To the left is a little power distributor board that also adds a bit of capacitance to reduce EMI emissions of the cable from the power supply to the head. This is a fairly long cable (1.5m or so), which tends to act as an antenna. HAM enthusiasts in the area probably don’t like it if I turn this thing on as it is; let’s not make their lives unnecessarily miserable. Due to the efficiency of the drivers, the supporting electronics don’t get particularly hot; no active cooling is necessary.

To the right is a small PWM distribution board that passes the 3 PWM signals to the appropriate driver boards, and it also drives two 12V fans on the light source. This driver board actually houses an ATtiny85 microcontroller that drives the fan and that interfaces over a OneWire interface with the main controller (we’ll get to that later). I had to write the OneWire slave firmware myself, as nothing I found on GitHub would actually work as advertised. The little controller board also extends the OneWire bus and necessary power supply to the DS18B20 temperature sensors that are embedded into the actual light source.

I haven’t gotten around yet to making a proper housing for the head assembly. For now, it’s mounted on an MDF box that’s held together with urethane glue and duct tape. Let’s face it, with the possible exception of the Palestinian situation, there’s no problem in the world we couldn’t solve with a sufficient quantity of duct tape.

Another view on the head assembly, showing the cooling arrangement at the top of the light source.

For thermal management of the LEDs, the contraption relies on the aluminium-core PCB on which they’re mounted, which in turn is bolted onto a COB LED heat sink with two 12V fans. The fans aren’t particularly loud, so instead of PWM-ing them, I just turn them on as soon as the light source reaches 35C. This happens within a few seconds when running at full power, and the head is explicitly NOT intended to run at full power for any extended period of time. I might at some point improve the heatsinking situation, but since the light source is never full on for more than a minute or so, it just hasn’t been much of a necessity. It’s not like this is an interior lighting fixture, or anything. During regular printing, i.e. focus & composition followed by one or more brief exposures, the head cycles between 30C and 40C or thereabouts, depending on room temperature. This is the actual LED PCB surface temperature, so the LEDs won’t be all that much hotter.

Taken during one of the early torture tests of the light source. The display shows a LED PCB temperature of 69C, which in normal use it never reaches, by far. The ‘344’ in the bottom right corner is the total power consumption on the 48V rail as reported by the power supply. Yes, that’s 344W going to the LEDs. It’s kind of evident that things might get a little warm.

Power it all up

The power supply is a pretty straightforward affair. Err…well, it could have been, but it turned out not to be. There are in fact two power supplies: a 500W 48V SMPS (switch-mode power supply) and an auxiliary 12V SMPS. The latter only powers the controller in the power supply and the main controller unit. This brings me to the reason why the power supply isn’t so straightforward – it actually houses a bit of intelligence. My main concern here was that if you power a project at 48V and up to 9A or so, the potential for actually setting something on fire down the line is pretty real. Also, even though most of the components are pretty cheap, I spent quite a lot of time engineering and building everything, so I’d rather not have this turn into a particularly labor-intensive firework.

Power supply innards. At the top, a 48V/500W SMPS. Bottom right, an exhaust fan. Bottom center is a small 12V SMPS that supplies the logic with power even if the big 48V unit is turned off. To the left a relay is visible, which switches the 48V unit and that’s part of the current monitoring module (see text below). The actual power supply controller is to the left and mostly obscured by the Wago connectors.

For this reason, and also because, you know, “we can”, I included some brains in the power box. Firstly, there’s the actual power supply controller itself, which is based on an ATMega328P. This monitors basic parameters such as temperature (using another DS18B20) and the 48V and 12V voltages. It can autonomously switch the 48V unit off if something goes out of bounds, and it reports system parameters to the main controller over I2C. The controller also runs a PWM-modulated exhaust fan depending on local temperature.

There’s also a separate little current monitor board that uses an ACS713 Hall-effect current sensor to continuously monitor the current delivered by the 48V supply, switching it off as soon as it rises above a critical level. On this board another ATtiny85 lives that reports current measurements to the power supply controller (this is also a OneWire slave), so these are available in real-time through the user interface (see example image above). Well, they should be, because truth be told this module has been acting up in the last week and it has been reporting bogus values. I still need to dig into this, but there are other lines of protection that make this less of a pressing matter. Finally, there’s some PWM signal conditioning in the power supply as well.

I chose to keep cabling to a minimum (insofar reasonably possible), so there’s only one cable (a standard UTP network cable) connecting the main controller unit to the power supply, and there are two cables connecting the power supply to the head: one for 48V power, and a lighter-duty one for PWM and OneWire communications. There is no direct connection between the user interface controller and the head, which is deliberate decision for two reasons: (1) the new controller unit is in principle backward compatible with the generation 2 power supply and light source, and (2) not having a direct connection means there is less risk of cables snagging behind something when the baseboard/table or the head are dropped or lifted. In practice, this arrangement works very well and the cable mess is kept to a minimum that never really gets in the way of anything.

What does this button do?

Now for the controller. Remember the requirement that the system should be as easy to use as a regular dichroic head, but at the same time, it is of course inherently far more complex. Not to mention that the potential for user configuration and different use cases is there, so I wanted to use at least some of that potential.

Controller box. I mounted this to the right-hand side of the baseboard, which turns out to be extremely convenient. Note the yellow UTP cable at the top; this goes to the power supply unit and carries power for the controller and the PWM and OneWire signals to the rest of the system. The white cable with the DIN connector goes to the footswitch.

Let’s start with the hardware. I took some inspiration from the Model-View-Controller concept and essentially kept the three parts separate. The View was the easy bit; this is mostly a 20×4 LCD display and a couple of blinking LEDs on the front panel of the user interface. Because the display can ‘only’ show 80 characters, I wrote software that uses a virtual memory map combined with scrolling to be able to display much larger pieces of information, such as lists of system status variables or configuration parameters.

The LCD is a standard unit which I soldered one of those I2C piggyback boards to, so it’s more or less self-contained. The only odd thing about is that I hacked it so that it has an orange as well as a red LED backlight in addition to the factory-fitted white one. This was also the main reason I chose an LCD instead of the 7-segment LED displays I used previously. 7-segment displays cannot change color, and with this system, I want to do color as well as B&W. For B&W, a red backlight is ideal, because it doesn’t fog the paper. For color, a dim orange backlight is more suitable, as it exploits the reduced sensitivity around the 600nm region in RA4 paper. And with the normal white light on, it’s nice to have a white backlight for legibility.

To switch between white backlight and orange or red, there is a little light sensor mounted to the display; the controller automatically switches between white light and ‘safe’ backlight based on its input. The level of the red and the orange backlights are user-configurable through the controller itself, as they are PWM driven. The same is true for the four red indicator LEDs to the right of the vertical row of yellow buttons; their maximum intensity can be programmed so that they are visible, but don’t fog the paper.

The controller unit in different modes and dim light: a B&W mode on the left puts the display in red backlight mode, a color mode makes the backlight amber. The red backlight appears almost illegible, but this is just a poor phone snap; in reality, it’s actually red and very legible indeed.

The controller-part of the MVC approach is the actual user inputs, in this case 8 tactile switches and 4 rotary encoders (each with a tactile switch as well). Because of my experiences with poor responsiveness of the user interface in the second generation system, I decided to settle this once and for all. Perhaps the solution is massive overkill (it actually is, trust me), but boy, does it work beautifully…the button/rotary controller revolves around an ATMega328P (again! I told you I like these) and a Texas Instruments TCA8418 keypad scanner. The ATMega monitors the rotary encoders, the TCA8418 monitors the 8 + 4 buttons (+ footswitch, +2 additional buttons that can be attached to an external module). The nice thing of the TCA8418 is that it is pretty much self-contained and buffers any events in its own RAM, which can be queried over its I2C interface. This means that the ATMega has its hands free to poll the rotary encoders regularly and to talk to the main controller over I2C whenever it’s being queried. This means that the ATMega runs two I2C interfaces. As a slave, I used the hardware I2C interface and to talk to the TCA8418, I used a SoftWire interface.

User input module PCB. Top side to the left with 4 rotary encoders, 8 buttons and 4 3mm LEDs, bottom side of the same PCB to the right with the ATMega328P controller in the middle of the board and the TCA8418 about 1/3 from the top (it’s a tiny 4x4mm QFN24 package, so easy to miss). Note a couple of bugfixes I had to do on the finished PCB. There’s usually a couple of beauty marks on boards like these if I make them.

I liked the TCA8418 approach of input event buffering so much that I replicated it in the ATMega, so it autonomously monitors all inputs (rotaries directly, buttons via the TCA8418), collects any events, distinguishes between slow or rapid rotary movements, short, long and double presses of buttons, and stores all of these events in a FIFO-buffer. The main controller can periodically query the ATMega for new events, which can then be handled at leisure. I generally poll the input controller every 20 or 50 milliseconds or so (I noticed that anything up to around 75ms is experienced as pretty much instantaneous), which results in no noticeable delay for the user, but it keeps the hands of the main controller free most of the time to run more time-critical tasks. As I said, it’s massive overkill, but it works beautifully. The user interface is extremely responsive and it never drops a beat. Every single click of any of the rotary encoders is correctly registered and seemingly instantaneously processed. Yeah, I’m pretty pleased with how this part panned out, if you hadn’t noticed.

Inside the controller box. The 20×4 display is the green PCB top right, seen from behind. The blue PCB below it is the underside of the user input module and houses the rotary encoders and 8 tactile buttons (on the other side of the PCB), and also the ATMega328P controller and a few cm to the left of it (barely visible) the tiny TCA8418 keypad controller. The red PCB to the left is the central brain box, with its ESP32WROOM CPU module in the center.

Now for the Model-part of the MVC topology. The main controller board is based on an ESP32WROOM controller module with integrated flash memory, WiFi and whatnot. These little things cram a lot of functionality and performance in a small and very cheap package. Yes, that’s actually a dual-core CPU running at 240MHz in there, running its own real-time operating system (RTOS) with my custom firmware running on top of that. I initially thought that this kind of brainpower would be massive overkill, but it really is true that software tends to expand to the size of the envelope that the hardware creates. Hardware-wise, the controller module isn’t particularly interesting. There’s a lot of connectors, evidently, to interface with the input controller and the display, the footswitch, the power supply, and there’s an internal expansion connector that carries I2C and logic power (5V + 3.3V) and also an external expansion connector (RJ45) that has the same, plus connections for two external buttons. Presently both expansion connectors are unused.

The really complex and also interesting (in my view) part is the software hidden inside all this. I chose the Arduino platform because it was for me the quickest way to get the job done, and Espressif has fairly decent (albeit with some hiccups here and there) support for it. Since Arduino is basically C++ with some bells and whistles, and I was thoroughly dissatisfied with the spaghetti code I made for the previous generation, I went full-out object oriented this time and I even experimented with some automatically-generated code.

In terms of object oriented approach, let me illustrate: the main loop() of this controller contains only a dozen or so lines of code. It’s all just calls to update functions of various objects. I’ve got objects that run the display, the GPIO outputs (status LEDs, buzzer, main light source LEDs), the countdown timers, persistent flash storage of user variables, the backlight, the linearization curve calculator, and most importantly a state machine object. The latter is really the core of the system, as I designed this as a finite-state machine (FSM). Here’s an Arduino example, but I did it my way, which turned out to be far more expansive due to the requirements on the system.

This is also where Excel came in, and some computer-generated code. I actually planned the state machine and its behaviors in Excel. I created a workbook in which I defined all the individual machine states (e.g. “color mode during exposure”, “color mode with focus lights on”, etc. etc.) and the routes from one state to another (e.g. “from [state color mode lights off] to [state color mode exposure] through [short press] on [footswitch] or [exposure button]”). I also defined the system behavior in this workbook, e.g. which LEDs are on at which intensity in each machine state, which variables are visible at which positions on the display, in which states which counter counts upwards or downwards, etc. Moreover, I defined all relevant global variables in this Excel, which helped me to automatically store the contents of these variables in flash memory (without having to program this manually for each variable) and to facilitate manipulation of variables across different objects. To this end, I treated each variable as an object with a couple of properties to allow for different variables being of different types and keep track of if they needed to be stored on shutdown or not.

Snapshot of the Excel system planning system; this screen shows parts of the routes between different machine states and the actions that trigger state changes.

I then went one step further and actually had Excel generate the more boring bits of code I would otherwise have to write manually. For instance, the state change routes (how the system transitions from one state to another, and which action triggers this transition) are all defined by 3-byte arrays, which I had Excel define automatically with heavy use of the concatenate() function.

Example of auto-generated code by Excel, in this case state transition triggers. I had Excel add automatically generated comments so I could actually find my way in the automatically generated code afterwards.

I then limited the programming to the behavior of the different objects, to the effect that the actual machine behavior can be changed without actually writing any code manually, but just make some changes in the Excel planning system and export its automatically generated code into the Arduino project. This proved to be quite convenient; for instance, the other day I noticed a small bug in the split-grade printing mode where the low-grade exposure wouldn’t end (although the countdown timer did count back to 0). I could easily track down the problem in a missing state change route, add it in Excel, export the generated code to the project and upload it to the controller. To fix this bug, I did not have to actually touch one line of code. It’s not an isolated example; there have been a few instances in which I changed my mind about how the machine behaves and that I could change simply by going through the Excel workflow outlined above.

So far, I’m pretty happy with the software architecture. Yes, it’s thousands upon thousands of lines of code, and the complexity almost feels infinite at times. But at the same time, it actually proves to be far easier than with the previous generation machine to track down issues and fix them, and to modify the behavior of the system.

Where does this leave us?

Primarily, with a functioning color enlarger that covers the bases I set out initially. So there’s that, and I’m quite happy with it.

Current setup. The Frankenstein light source assembly is visible on top of the condensor stack. The power supply module is the little MDF box on which the 300/5.6 Symmar-S and my folded Toyo field camera sit. The controller is the little red box mounted to the side of the enlarger table. The footswitch on the floor was a gift; I don’t know where it came from, but it works a treat.

I’d almost forget it, but even though I evidently spent a lot of time building this thing, I’ve also already spent a lot of time printing with it, and hope to do a lot more printing with it still. Overall it’s really a joy to use, and even though it won’t suit everyone’s printing tastes, it fits mine like a glove. Here’s a list of features I included in it:

  • There are various printing/operation modes that I can cycle through at the press of a button:
    • Color mode for RA4 prints, with ‘Y’ and ‘M’ filter settings and exposure time.
    • Fixed grade B&W mode, with grade (in steps of 0.1, from 0 to 5) and time.
    • Split grade mode, with a low grade + time and a high grade + time, with exposures happening consecutively after a button/footswitch press.
    • RAW mode in which R, G and B can be set on their native 12-bit (0-4095) scale, plus an exposure time.
    • ‘Ortho’ mode for making exposures onto orthochromatic and x-ray film. It allows blue and green to be mixed (both on a scale of 0-300, so effectively 0-100% in 0.33% increments; there is the possibility for adding a linearization curve) and exposures to be timed at 10 millisecond steps as opposed 100 ms steps for the other modes.
  • Each mode features a countdown timer with a buzzer beeping every second during exposure, as well as a burn-button with a timer that counts up and beeps every second. No more metronome needed! There’s a focus function as well, evidently, that just throws lots of light through the negative and onto the baseboard.
  • Filter and time settings across the various modes are stored in persistent memory on shutdown. I can pick up where I left off last time.
  • There’s a screen with system status parameters showing temperatures of all the sensors, power supply voltages, power supply controller status, overtemperature warnings and OneWire interface status. And probably a couple more things. Handy during development and troubleshooting, but it doesn’t get in the way of normal use.
  • During exposures, light source temperature is displayed so I have a feeling for light source health. It should also display power consumption, but that module has thrown a tantrum recently. Fortunately this doesn’t interfere with the system in any way.
  • There’s a screen with system settings, including the parameters for the polynomial linearization curves for the RA4 color filters as well as the B&W paper grades. No need to reprogram the controller if for some reason I feel the filters need adjustment; I can adjust them through the user interface. Same for the ambient light level at which the display backlight switches between white and orange/red, backlight brightness, indicator led brightness and probably some more things I forgot.
  • There are provisions for a baseboard sensor module and paper calibrations (both B&W and color). I’ve done some work on this, but it’s on the backburner for now, so I might revisit this later on. My requirements on this are kind of high, so it’s tricky to get it right.
  • It’s expandable with some controller modules, for instance a radio module that could control my darkroom light system. But so far, I haven’t bothered with it.
  • Basically, there’s lots of bells and whistles, but in the normal color and B&W modes, only the actually useful information is displayed and important functions (focus, expose, burn etc.) are accessible through a simple button press or a twist of a dial (filter/grade, exposure time). No need for navigating through menus etc.

An important aspect of the whole thing is actually the way it’s not really there if you don’t want it to be. I can still remember the constant jet airliner fan of my Ilford 500 system (now sold). This system has a quiet fan in the power supply and two fairly quiet fans on the light source that only operate when actually necessary. There are beeps when it makes sense to have them, but overall, you can’t hear it’s on. I had to culture the discipline to turn it off after a printing session, because I tended to forget it was still on, at first. And it’s also kind of comforting that so far, it has made lots of light, and no smoke at all.

That’s all a little too good to be true…

Hmm, not really. But it isn’t perfect, if that’s what you mean. There are things I don’t like about it, or that I’d like to do differently, hopefully better. Let’s see what I can come up with.

  • Split grade mode is not really developed, yet. It works to make a perfectly fine split grade print, but to make the test strips, I still use fixed grade mode and first make the grade #0 strip and then the #5 strip. I’m also not happy with the intuitiveness (or lack thereof) of low- and high-contrast burning. I feel there should be an easier way to do this within the split grade operational mode, but I haven’t decided on it yet. Or perhaps I should just read the darn manual first. I have a feeling that would solve the burn issue, at least…
  • I’ve made lots of prints with this setup, and I’m confident that B&W mode pretty much works as intended. I do feel that the exposure equality across grades could be better, so I can hit dmax on the paper with just about the same exposure across different grades. I know this is not how most people like it, but this is my thingy here, so I’m going to do it my way, and in my style of printing, shadow differentiation is the first thing I look for. Furthermore, I have some doubts about the spacing of the grades. I have a feeling they’re not perfectly equidistant in terms of contrast, so that a change from grade 1 to grade 2 gives less of a contrast change than going from 3 to 4. I must confess I did the grade calibration job kind of haphazardly on this machine with just a cursory test and some quick and dirty curve-building.
  • Color mode really works, and works well. But what I did not do, is any work with color densitometry, control strips etc. There are no problems that I can see in the prints I make – polarized skies on Ektar come out an incredibly nice and saturated deep blue, skin tones render naturally, and all the hues materialize on paper just the way I remember them when shot – but all that is not a guarantee that everything is as perfect as it could be. At some point I may have to bite the bullet and do some proper testing on this. I’ve been putting this off because I know this brings the risk I might want to change to a different wavelength combination for the LEDs, which might trigger a rebuild of the entire light source, and…so I try to be content with what I have now, for the moment, and the beautiful (if I can be bothered to shoot some decent images) prints it gives me.
  • I’m quite proud of the job I did on the hardware, but not because the hardware itself is so great. It’s just that I learned a lot in a relatively short time, both on the electrical engineering and the manufacturing fronts. I’m painfully aware of the many little issues in the various boards across the system – and also of the presence of just too many boards and modules. The whole thing could be made far simpler, and as a result, less prone to spurious problems (which largely remain absent for now, fortunately). As soon as something crucial breaks, I think I’m going to re-engineer it. The power supply is one of those things…I don’t think I tackled that one the way I should have. That also goes for the power supply controller firmware, which is a little bloated.
  • I recently added OTA (over the air) update support so I can update the main controller’s firmware over WiFi. But that’s all I’m using in terms of WiFi at present, and it would be kind of nice to make the system status and configuration pages available through a browser. That makes so much more sense than going through all that on a 20×4 character display with a rotary encoder. I’ll need to add a € 1,50 WiFi antenna to the controller though, because the PCB design wasn’t really optimized for WiFi reception (I didn’t imagine I’d actually use it…)
  • There’s this sensor module and paper calibration subsystem that would be a nice addition. It would basically take the place of a color analyzer. Fact of the matter is, I only used analyzers briefly and didn’t particularly like them because they didn’t help me make better prints, or help me make them any quicker. So there’s very little benefit in it, apart from the technical engineering challenge to get it to work.
  • The elephant in the room is of course the, well, the elephant in the room. Because that’s kind of what it looks like, particularly the light source assembly. The mechanics / housing could do with a thorough makeover. Same for the power supply; I mean, it’s encased, but did you know that the lid of the case doesn’t even attach to the box itself? It just sits there, ready to slide off!

And there’s probably several things more I could list here. But for now, I have no plans to start a 4th generation. Writing the earlier blog about matching LED wavelength to RA4 paper does create a bit of an itch, with the alternate wavelength configuration I presented there. It’s an itch I can and will ignore, for now.

Well, that’s it, for now. If there are any future developments, I’ll be sure to let you know. I’m sure there are many things I didn’t tell and that you might be wondering about. Feel free to let me know; you can reach me through the comments (if you make it through the captcha; I’m sorry if you don’t, it’s kind of beyond my control at this point) or otherwise through my account on Photrio (guess my username there). I’ll also add these posts to the thread about this project and the associated blog posts there, so in all likelihood, if you read this, you probably got here through that thread anyway.

Leave a Reply

Your email address will not be published. Required fields are marked *