Projects

EZ-ATX Workbench Power Supply Part 2

Assembly and Testing

Liam Davies

Issue 67, February 2023

This article includes additional downloadable resources.
Please log in to access.

Log in

Transform any old computer power supply into an absolute workbench beast with this project.

Welcome back to Part 2 of the EZ-ATX project. After a bit of troubleshooting from last month (Part 1 Issue 66), we’re assembling our final design, uploading our code and giving everything a test drive. Let’s dig in!

Redesigning the PCB

While we talked mainly about the design and theory of the project last month, we did do some testing behind the scenes of our Version 1 PCB and were forced to make some adjustments. This is not an uncommon occurrence. While PCBs are incredibly reliable, they can be nigh-on impossible to modify once they have been manufactured and almost always need a new revision to fix outstanding issues. Any good engineer will allow time for PCB revisions - as the complexity of a certain product increases, so too does its chance of failure.

In one of our prototype assemblies, we had a phantom short circuit that was due to a terminal screw hole internally shorting out to the PCB. Upon applying power to the 5V rail, there would be a full short circuit to ground. Don’t even ask us how long it took to find the short-circuit!

That wasn’t the end of our electrical gremlins, though. Upon further inspection, it turns out the method we were using to drive our MOSFETs wasn’t exactly correct. While it did work, it wasn’t reliable and depending on the gate threshold of the MOSFET used, it could only partially turn on the MOSFET. For those who don’t work with MOSFETs often, this is a bad idea because the MOSFET acts as a resistor, outputting an unsafe amount of heat.

Additionally, the transistors used to drive the MOSFETs weren’t correctly configured either. The way we had them configured meant that whether the base was 0V or 5V from the Arduino, it would still conduct anyway thanks to the voltage between the Base and Emitter. While this issue could be picked up straight from the schematic, it took a bit of physical debugging to realise why.

Old Drive Circuit
New Drive Circuit

We used the -12V rail to switch the MOSFET on the 5V and 3.3V rails fully, which is important given that they are P-channel and need a sufficiently large Vgs (Gate-to-Source voltage). Pulling them to ground would not have been enough to fully turn on the gates of the MOSFETs.

The way we opted to redesign the circuit was to use an optocoupler on each channel. Not only does this simplify the circuit with regards to working with the MOSFETs, but it also provides some protection to the ATmega328’s digital outputs.

Before committing to a full PCB rework, we created a small test jig that would ensure our new design would work. It was complex, but it successfully tested the design and showed that the optocoupler system works.

While we were at it, we improved some small gripes we had with the Version 1 PCB. The footprints for the resistors and transistors are now bigger to make soldering easier, and there are more silkscreen labels to clarify what goes where and why. Some components have also been moved to more logical places so the entire project can be assembled without any weird quirks or specific instructions. We’ve still included Gerber files and schematics for both versions, however, we suggest ordering the latest version. It’s worth noting that the PCB design instructions from last month aren’t invalid whatsoever - our design process is exactly the same, just with physical changes to the circuit layout.

Parts Required:IDJaycarAltronicsElement14
8x 10kΩ Resistors*R15,R13,R10,R16,R4,R6,R1,R8RR0596R05821126840
7x 200Ω Resistors*R22,R21,R9,R20,R23,R3,R2RR0555R05411126865
4x 75Ω Resistors*R5,R19,R14,R17RR0545R05311127008
1x 39kΩ Resistors*R11RR0610R05961126981
2x 3.3kΩ Resistors*R7,R12RR0584R05702330130
1x 16x2 LCD ScreenDS1QP5521-3769973
1x 6x6mm THT Tactile PushbuttonS1SP0601S1120-
3x IRF4905PbF MOSFETQ6,Q5,Q9--8648190
4x XC4610 30A Current MonitorU4,U2,U6,U5XC4610Z6429-
3x BC547 NPN TransistorsQ1,Q4,Q2ZT2152Z10401574381
4x TLP785 OptocouplersU7,U8,U10,U9--2524357
1x 6-pin Male Header*J1HM3212P53902802334
1x ATmega328P-PU3ZZ8727Z51261972087
1x LM4040LP-4.1 Voltage Reference^U1--3124413
4x 4-pin Male Header*J6,J2,J3,J4HM3212P53902802334
1x IRFZ44N MOSFETQ10ZT2466 #Z1537 #8650225
1x 10kΩ 25-Turn Vertical PotentiometerRV1RT4650R2382A4014484
2x 22pF Ceramic CapacitorsC1,C3RC5316R28142860009
1x 1uF Electrolytic CapacitorC2RE6032R47182112947
1x Axial Power Resistor (Optional)R18RR3360R0431-
1x 16MHz Oscillator CrystalY1ZZ8727V1289A-
1x 24-pin ATX Connector (Molex Mini-Fit Jr 5569-24A1)J5--9734260
1x 16-pin Female Header-HM3230P53901834975
2x Red Binding Posts-PT0453P92522783482
2x Yellow Binding Posts-PT0451P92551234237
2x White Binding Posts-PT0452P92561815823
2x Blue Binding Posts-PT0450--
4x Black Binding Posts-PT0454P92542783481
4x Green Illuminated Pushbuttons-SP0804-1815820
ATX Computer Power Supply----

Parts Required:

* Quantity shown, may be sold in packs. You’ll also need a breadboard and prototyping hardware. #This item is a substitution, but it should work equally well. % May need some modification to work. ^Optional, but better measuring precision is gained if using. Be sure to set the Arduino code to EXTERNAL reference if using.

Assembling the PCB

The Version 2 PCBs arrived from JLCPCB in the same pleasingly-short timeframe that the Version 1 boards did.

Soldering PCB

With the latest PCB version finished and on our desks, we can begin soldering all of the required components onto the board.

We suggest starting with the resistors. There are quite a few of them to do on this board, so take it slow and ensure that you’re using the right values for each value. We have a full parts list available for easy reference.

We can then solder in the Arduino chip. We’re using the ATmega328 chip, which is exactly the same chip used in the Arduino Uno, so you can either pick one up from your local electronics store, or pull one out of an Arduino board you already have.

Image credit Altronics Z6240

Note: ATmega328 chips can be “borrowed” from any Arduino Uno-compatible board.

To make our lives easier if we ever decide to move the chip to a new version or a rebuild, we used a DIP socket for the chip. We didn’t have any 28-pin sockets, so we used two 14-pin sockets instead. We then slotted in the chip over top, double and triple checking that the notch faces towards the left side (when viewed from above).

While we’re at it, we soldered in some other components nearby the Arduino chip that helps it run properly. This includes a 16MHz quartz crystal for running the chip’s clock - although this isn’t absolutely necessary. If configured correctly in your programming software, you can actually use the internal 8MHz clock instead. To go along with the crystal, we added two 22pF ceramic capacitors in the C1 and C3 slots.

Image Credit ChipSun technology

We additionally added three BC547 transistors around the top of the board, which are essential for controlling the power supply and LCD backlight. Finally, we popped in a 6x6mm push button that can be used to reset the Arduino whenever necessary.

At this point, we started soldering in any headers necessary for everything to connect to. We started with the headers responsible for connecting the four illuminated pushbuttons.

All headers are mounted to the rear side of the board, with the exception of the LCD mount! We forgot about this, of course, and had to spend half an hour desoldering a bunch of header pins on the wrong side of the board. We also added a 10-turn potentiometer to the LCD contrast slot. You can use a regular potentiometer for this, but we’d advise against this as they can move over time thanks to knocks and vibrations, eventually making the display impossible to read.

We can now solder in the optocouplers. These are quite small 4-pin packages, so you may need to bend the pins if they don’t comfortably fit into the sockets. The small dot corresponds to Pin 1, and is on the same end as the notch on the PCB’s silkscreen.

Next, we soldered on the 24-pin ATX connector on the bottom side of the board. You must absolutely ensure that it’s orientated correctly - with the latch facing forwards on the bottom side of the board. If you don’t, you may let out quite a lot of magic smoke!

At this point, the PCB is almost ready for an initial test to make sure the Arduino is running properly. We soldered in the top MOSFET - an IRF4905 - to test if 12V can be turned on and off on the top terminals. A power supply can be connected to the board, and as soon as it’s turned on, the LCD should light up. If text appears on the screen, and 12V can be turned on and off, it’s good to go!

Now that we know the Arduino is running, we can finish soldering the remaining components in. We suggest doing the MOSFETs - you’ll need three IRF4905s and one IRFZ44N. The IRF4905s are used for the 12V, 5V and 3.3V while the IRFZ44N is used for the -12V.

The current sensors can now be added. They are mostly unmodified Duinotech modules from Jaycar (XC4610) - the only change we made was to desolder the three-pin header and resolder it on the underside of each of the four modules. From here, it’s just a simple matter of soldering them in! Be sure to keep them flat and perpendicular to the PCB.

Next up, we’re connecting the buttons to the headers. The buttons we’re using are high-quality stainless steel which have a nice tactile feel to them. They have illuminated green rings, which are connected to the channel outputs so you know immediately whether a channel is enabled or not. They have six button contacts (two poles with Normally Open and Normally Closed pins) and two contacts for the LED. The following diagram shows the wiring for the buttons - all four are the same.

We chopped off the ends of female headers and soldered the other ends to the buttons. Be sure to use heat shrink as once everything is shoved into the enclosure, there is a much higher chance of wires fouling against contacts. There are four connections, which are in this order (top-to-bottom) when plugged into the male headers on the PCB:

Connection on PCB

Connection on Button

Button Sense

NO

GND

C

GND

LED -

Illuminated LED Output

LED

Final Assembly

It’s time to put everything together! We’ve tried to make assembly as simple as possible, so it only requires a few nuts and bolts to make everything work. The PCBs sit on top of each other like a sandwich, separated by M3 spacers. You can assemble everything with a screwdriver, connecting the layers together.

The binding posts on the front panel have been designed to protrude through to the rear PCB. You’ll need 12 binding posts, all of which can be threaded through the holes and clamped down from the underside of the front board. Use an adjustable wrench to really tighten them down - one of the most frustrating experiences with any binding post is having it come loose during use.

While we re-ordered a version 2 of the rear panel, we didn’t reorder the front panel. The first version we’re using uses a copper backplane, which we removed in Version 2. We noticed the binding posts digging into the copper - while this wasn’t an issue with our plastic binding posts, metal binding posts will short out against the PCB.

We used some thicker wire, heatshrink and eyelets to create small wire bridges. These go between the terminal output points and the current sensors. While you can use wire by itself, it isn’t as secure or reliable as using eyelet or spade connectors.

The two 3mm holes next to each voltage output allows direct access to the rail output (i.e. 12V, 5V, 3.3V and -12V, as well as a Ground terminal. You can use 5mm or 6mm bolts to attach an eyelet connector to each hole, with washers and a locking nut to ensure they do not come loose if vibration is introduced.

The screw terminals themselves will need to be screwed down firmly to avoid them coming loose randomly.

You may have to bend the eyelet connectors at 90 degree angles to squeeze them onto the posts. If you’re not using any current sensors, the eyelet connectors can be screwed into the terminal outputs directly. The binding posts have two securing nuts which will need to be tightened firmly. Higher quality binding posts may have a more robust way of mounting, but our eyelet connectors should be compatible regardless. In the photo below, you can see we haven’t fully secured our binding posts so we can check the voltages that appear on the output of the terminals when the power supply is plugged in.

Before we screw everything together through, we need to use our 3D printed enclosure we’ve designed for the project! While this is optional, it will prevent wires shorting out and will also clean up the aesthetic of the project. The enclosure allows you to friction-fit the ATX power supply and compact the connecting wires into the front of the enclosure. Note that it won’t prevent it from falling out if you lift it up by the power supply - there are no screws to affix the PSU in place.

It took about 15 hours to print the enclosure on our Guider IIs in Blue PLA, which needed almost no cleanup. It’s a very simple print, however, be aware that it has quite a significant build volume - you’ll need to accommodate a 25cm model long on at least one axis.

The front PCB can be inserted into the front, ensuring the button mounts are lined up. It should fit snugly into the front panel. Depending on what M3 bolts you have available, you may have to adjust the construction of your EZ-ATX accordingly. We used two 10mm nylon standoffs for the two bottom bolts, and four 25mm M3 bolts for the screws to affix the LCD screen.

It can get pretty squashy in the enclosure, so we suggest using a pair of tweezers to connect all of the wires for the buttons and binding posts. A screwdriver with a small socket set helps immensely with screwing everything down when there is limited room available.

Once everything is secured into the enclosure, it’s now time to insert our PSU. If you have a semi or fully modular PSU, disconnect everything aside from the 24-pin motherboard connector. If you have a non-modular PSU, try and bunch the permanently affixed wires together to save room in the enclosure.

It’s also a great idea to test and program the EZ-ATX before pushing the PSU in the enclosure. The PSU we were using last month suddenly started acting very strange, shutting down even without a short-circuit and outputting all sorts of unexpected voltages. We swapped it out with a cheaper Corsair 1000W unit, however, it works absolutely fine. Of course, the Arduino chip won’t magically know what to do without some additional help from our code. We’ve detailed this in the next section, so if you haven’t programmed the chip yet, do that first before inserting the power supply.

You may find it convenient to zip-tie the remaining cables running from the power supply to save space when packing everything in. Finally, remember to turn on the power supply with the switch at the back! This caught us out more times than we’d like to admit.

Code

The code for this project ended up approaching 1000 lines of code so we won’t delve into all of the details here. Much of it is set up to implement your own functionality if desired, however, you won’t need to make any changes to the code if you’re using our PCB.

At the top of the file, we have included a big list of configurable parameters as detailed below. Note that the default values are usually fairly logical, but if there is some specific functionality you’re looking for, feel free to delve in and customise it to your heart's content.

We also wanted to show some of the more interesting parts of the code that newer Arduino programmers may not be familiar with. With the 16x2 LCD modules, it’s actually possible to register and use up to 8 custom characters that can be used anywhere on the screen. To create them, simply make an array of bytes that correspond to a simple bitmap of which pixels are filled in and which are empty. For example, we can create a symbol for a musical quaver with an online generator like below: (omerk.github.io/lcdchargen/)

Afterwards, it’s a simple matter of copy-pasting the output code into your Arduino program. We created a bunch of characters that make our display more compact and easier to read. We’re quite constrained for space on our display so anywhere we can save some room is a must! We also made a small ‘A’ character to make current displays a little easier to read.

You can register the character onto your display like this (where SYM_XXX is the name of your byte array):

  lcd.createChar(0, SYM_12);
  lcd.createChar(1, SYM_DOT_3);
  lcd.createChar(2, SYM_A);
  lcd.createChar(3, SYM_PRESS);

To write it onto the display, you can just call this:

// Where x is the index of the custom character
lcd.write(byte(x));

While 8 is a fairly limited number of characters to make cool things with, there is a lot you can do with them. Below, YouTuber ‘upir’ shows how to make a fully animated progress bar with custom character manipulation.

Code Parameter

Default Value

Description

REF_VOLTAGE

4.096

The reference voltage if using an external reference.

CUR_SENSE_VOLTAGE

5.0

The supply voltage to the current sensor modules.

MV_PER_A

66

The gain of the current sensor modules in mV/A

DISPLAY_UPDATE_PERIOD

500

How many milliseconds between display updates?

BUTTON_SENSE_TOLERANCE

50

How many units should the analog button divider tolerate to trigger a press?

BUTTON_LONG_PRESS_DURATION

500

How long in milliseconds does a button press need to be to count as a long-press?

CURRENT_INCREMENT_AMOUNT

0.10

How many amps should the limit increment or decrement in the menu when adjusted?

TIME_INCREMENT_AMOUNT

500

How many milliseconds should the limit increment or decrement in the menu when adjusted?

MAX_TIME_AMOUNT

60000

What is the maximum time that can be set for the time limit menu in milliseconds?

SLEEP_MODE_TIME

900000

How long in milliseconds until the EZ-ATX goes to sleep?

BACKLIGHT_FADE_TIME

3000

How long in milliseconds does it take to fade the backlight when going to sleep?

TURN_OFF_IMMEDIATELY

false

Should the EZ-ATX turn off the PSU immediately when all channels are disabled? If not, it will wait until the EZ-ATX goes to sleep first.

Image Credit YouTube: upir - Smooth Arduino 16x2 Gauge

We’re getting a little off-track. After creating our special LCD characters in our setup function, we can finish booting up our EZ-ATX. Below, we’re turning on our LCD screen, and sending the Power ON signal to the PSU to turn it on. Before we change to the main channel display, we need to make sure that our PSU is okay to use. The ‘while’ loop simply waits until the POWER_OK becomes 5V, in which case the PSU has signalled that its voltage rails are stable enough to use. In a dying or misbehaving power supply, this line won’t ever go high and the EZ-ATX will safely pause on the loading screen. Sure, it’s annoying, but much better than frying one of your precious circuits with an unsafe voltage!

  //Boot screen, and checking the POWER_OK line to ensure the lines are okay.
  pinMode(BACKLIGHT_PWR, OUTPUT);
  digitalWrite(BACKLIGHT_PWR, HIGH);
  digitalWrite(POWER_ON, HIGH);
  lcd.setCursor(0,0);
  lcd.print("---- EZ-ATX ----");
  lcd.setCursor(0,1);
  lcd.print("Checking PSU...");
  delay(1000);
  while(digitalRead(POWER_OK) == 0);
  updateDisplay();

One other big part of our project is monitoring the current draw of each channel. The current modules we’re using output an analog voltage proportional to the current going through them. However, it’s not quite as cut and dried as that. When reading zero current, the modules output a voltage equal to half of their supply voltage - this is designed so that both negative and positive currents can decrease or increase the voltage accordingly.

Together with the mV/A ratio of the module, we can write some code responsible for converting a simple analog reading into a current value.

  for(uint8_t c = 0; c < sizeof(current_sense_pins) / sizeof(uint8_t); c++) {
    uint16_t raw_read = analogRead(current_sense_pins[c]);
    double voltage = (REF_VOLTAGE * raw_read) / 1023.0;
    Serial.print((String)c + ": " + (String)voltage + "V, ");
    //We need to subtract the current voltage from 
    //half of the current module's supply voltage.
    double current = (voltage - (CUR_SENSE_VOLTAGE / 2.0)) / MV_PER_A * 1000.0;
    Serial.print(current);
    last_current_readings[c] = abs(current);
   
    // (limit checking code here, 
    // shown in full code)
}

Because there are four channels, we have a for loop that iterates over each. After reading the raw ADC reading of each channel, we then need to convert it to a voltage. You may notice we’re using the ‘REF_VOLTAGE’ variable, which refers to the voltage we’re using as a reference for our Arduino. If you’re using the internal reference, this should be set to 5V, although it won’t be super accurate.

LEFT ◄ The LM4040 reference we used. It acts as a shunt diode which provides an exact 4.096V.

As we soldered in a separate external reference, we set this variable to 4.096V. After that, we convert the voltage reading to our current reading with our MV_PER_A ratio, and output it to our current reading variable so we can use it later. Although we aren’t showing it here, we’re also using these readings to decide whether to turn the channel on or off based on our current limits.

Programming the EZ-ATX

Okay, enough nerding out with code! Time to get the code onto the Arduino chip on board. To program the EZ-ATX, you’ll need an FTDI programmer like the one below.

IMAGE CREDIT: Adafruit Industries

We’ve included a 6-pin header on the left side of the rear PCB that can be used to completely reprogram the EZ-ATX. It can also be used with almost any software, such as Arduino IDE or Visual Studio Code with PlatformIO - from the computer's perspective, our project is a standard Arduino Uno. Be sure to get the correct pinout when plugging in the programmer, and do not connect the PSU while programming (or, alternatively, disconnect the 5V line). This would cause the 5V supplies of the FTDI programmer and the PSU to fight, which we definitely don’t want.

The FTDI programmer should be immediately recognised by your computer, appearing as a Serial device. If it isn’t, you may need to install prerequisite drivers for your programmer - many cheap clones include CH340G chips that need their own Windows drivers. If you’re using Arduino IDE, be sure to select the COM port and then upload the code. Other platforms like PlatformIO should automatically detect the COM port.

Testing

Throughout this project, we’ve been careful to note down any shortcomings of the PCB we made, so you’ll find that the PCB Gerber files we’ve provided may be slightly different to fix the issues we had. Despite the fact that this PCB is the second iteration of this design, it’s still not quite perfect!

When testing this design, it’s best to find a load that can handle all voltages the project can handle easily. We chose an incandescent car light bulb to test the 12V, 5V and 3.3V channels. Bear in mind that any resistive load like these light bulbs are capable of dissipating large amounts of heat, and are quite easy to burn yourself on. Upon applying the bulb to each channel and enabling the channel with the button, we verified that each worked as necessary. We also checked that no voltage was present when the channel was turned off, and that the current limit immediately shuts off the channel when the limit is reached.

Probably the biggest thing to note with the delivery of high currents is that the voltage can drop more than a proper laboratory power supply would otherwise. Applying 5A of load to the 12V channel caused a 0.5V drop. It’s up to you to decide whether that is acceptable or not - in our case it’s not perfect, but wasn’t a deal-breaker either.

For the moment, our EZ-ATX will sit happily on our workbench with power always applied. It’s not a distraction, thanks to the auto-sleep mode on the LCD. After 15 minutes of idling, the display turns off and will sit waiting until a current channel is turned on.

Troubleshooting

This is one of our more complex PCBs, so it's not out of the question that you may run into a few issues during the assembly and testing process. To avoid running into these issues, we suggest testing the PCB during the building process as often as possible. Use a multimeter to check there aren’t any shorts by putting it into continuity testing mode.

If you’ve finished building the project and something isn’t working, it’s best to disconnect the power supply and use a different power source to connect voltages. This will help isolate the problem if the problem is related to a specific supply rail. For example, if disconnecting the +3.3V rail suddenly allows the Arduino to turn on properly, the fault is likely due to how you’ve wired your MOSFET. Pay very close attention to the footprints on your PCB and ensure all components are inserted the right way.

In general, this project will suffer from two different types of problems - Arduino and PSU problems. Arduino problems are usually fairly subtle and don’t blow anything up. If a button isn’t working, or the LCD isn’t lighting up, double check your wiring and refer to the schematic if needed. PSU problems can potentially be a bit more dramatic. Because we’re dealing with many amps, a dead-short circuit from the power supply will make a small shower of sparks, potentially destroying any components that were in series with the current. For this reason, avoid connecting the power supply again until you’re absolutely sure the issue has been resolved. As we found out earlier, the power supply itself can also be at fault! We recommend heading to your local computer repair store and asking for a few old PSUs - they’ll be either very cheap or free. This will give you a backup should one not work correctly, and will allow you to swap out PSUs if you blow one up.

Where To From Here?

If you’re interested in building this project, you’ll likely already have ideas to improve it generally, or make it better at a specific task.

If you’re after more fine-tuned control, you may wish to modify the Arduino code we have provided to suit your needs. For example, you may wish to turn a certain output on or off at a certain time (such as your workshop lights), or export the data to an SD card module. Speaking of which, there is also enough space on the PCB to hot-wire additional modules onto the Arduino chip, although you’ll need to access the underside of the Atmega328 microcontroller.

IMAGE CREDIT: Jaycar

Why not upgrade your EZ-ATX with SD card logging?

The current monitors onboard aren’t the most accurate sensors in the world - they’re basic analog sensors that are susceptible to small, but not negligible, offsets in current readings. If you really want to get advanced, you could use a sensor like an INA219 to digitally measure both current and voltage! This would help bring this project into the same realm as lab bench supplies that can monitor and regulate milliamp-level supplies.

One of the most desired reasons for a lab bench power supply is the ability to supply a constant current to a load. By setting a maximum current, the power supply will decrease the supply voltage until the load draws the set current amount. This is exceptionally helpful for high-powered LEDs and Lithium-Ion batteries where constant-voltage supplies are a big no-no. Unfortunately, without further switch-mode circuitry or heavily modifying the power supply, it’s not really possible with a stock ATX PSU - it’s been designed to output a constant voltage!

As we mentioned in the beginning, we’d love to see an adjustable power supply in general for this project, together with integrated current monitoring if possible. We would love to see someone take on this challenge and see what they can get out of a standard ATX PSU.

EZ-ATX Workbench Power Supply Part 1