Float voltage regulation for DIY programming power supply

Nexus665

New Member
Dec 2, 2024
5
6
0
To not clutter the DIY Charging Supply for ISTA + BMW Programming thread I decided to post this in its own thread, may be useful for other DIY PSU retrofits as well.

Why do this?
Lots of people have turned to converting server power supply bricks into PSUs for high-power charging their Lipos, for spot welding, for flashing/coding cars, ...

In the thread linked above, forum members put together a very well executed DIY car coding/flashing PSU that supplies 13.8V at high amperage while working on their cars, but found that the output voltage was not really stable, dropping .3-.6V from the 13.8V the BMW docs say should be supplied.

Just turning the no-load voltage up higher so it can hold 13.8V under load is not a great solution. This means 14.4V or more without load, and that's pretty much the upper end of the scale for the chosen PSU, as well as most other server PSUs without hardware mods.

How to make it work
Server PSUs usually have some sort of voltage feedback system, meaning they can be adjusted on the fly. This works by either connecting the sense and 3.3V with a resistor matched to the desired output voltage or directly feeding a voltage to the sense pin - you can use your multimeter to measure the required voltage on both sides of the resistor. That means it is trivial to control via a microcontroller, in this case an Arduino, since they're cheap and widely available.

Required hardware (needs to be 3.3V capable!)
Arduino board - choose one that has a USB port unless you have a serial programmer
DAC breakout board or chip
Operational amplifier (OpAmp) that is single supply 15V input capable, doesn't have to be rail-to-rail, almost anything will work
2.2K and 470 Ohm resistors
Jumper cables
2.54mm/.1 inch solderable pins
Prototype stripboard PCB
Optional: output filtering (coming soon, works fine without so far)

Chosen hardware
Arduino Pro Micro (Leonardo clone in a small format) - available from 5-20 USD, depending on where you buy them
MCP4725 DAC breakout board
LM324-N quad rail-to-rail OpAmp

Software
Arduino IDE (open source and free, Windows, Linux, Mac)
Sparkfun MCP4725 library (click on the library icon on the left and type mcp4725 in the search field, click install)

Connect Arduino to a PC via USB, install drivers for USB serial port if necessary
Be sure to select the board you chose and the correct communications port in the IDE!

Code
This is just a simple hysteresis control loop, no PID. Will do load testing and update code if needed.
It gets a voltage feedback from the PSU output via a voltage divider that scales the voltage to <3.3V for the Arduino. While the output voltage is lower than the setpoint, it will increase it, and lower it if it overshoots (i.e. when the load drops) within a few microseconds (millionths of a second), should be good enough.
Code is documented, upload and test, adjust volt_setpoint if needed. Use quality resistors (1%) if possible, accuracy depends on it.

C:
#include <Wire.h>
#include <Adafruit_MCP4725.h>

Adafruit_MCP4725 dac;
unsigned int volt_psu = 0; // 0-1023, 10 Bit resolution on the Arduino
unsigned int dac_out = 0; // 0-4095, 12 Bit resolution on the MCP4725
// Adjust the value of volt_setpoint below to change the output voltage
// Lower number = lower voltage : valid values: 0-1023 -> 750-752 measured for 13.85V output
unsigned int volt_setpoint = 752;

void setup()
{ //I2C address of the DAC - could be 0x60, 0x61 or 0x62, 0x60 is default for MCP4725
  if (dac.begin(0x60))
  {
    // output 0V from the DAC to the 12V_RTN_SENSE pin, no voltage increase for safe startup
    dac.setVoltage(0, false);
  }
  // setup our analog PSU voltage feedback pin
  pinMode(A0,INPUT);
  // wait a bit for PSU and display to start up
  delay(3500);
}

void loop()
{
  // read PSU voltage feedback - ~15mV resolution, i.e. 0.015V - seems good enough
  volt_psu = analogRead(A0);
  // If voltage is less than desired
  if (volt_psu < volt_setpoint ) // Hysteresis of +-1 => ~0.03V
  {
    // limit the max value output to the DAC - should never really reach this value anyway, usually around 2xxx for 13.85V
    if (dac_out < 4095)
    {
      // increase trim/output voltage
      dac_out = dac_out + 1;
    }
    // output the voltage
    dac.setVoltage(dac_out, false);
  }  // if voltage is higher than the setpoint
  else if (volt_psu > volt_setpoint) // Hysteresis of +-1
  {
    // limit to 0 as minimum
    if (dac_out > 0)
    {
      // decrease trim/output voltage
      dac_out = dac_out - 1;
    }
    dac.setVoltage(dac_out, false);
  }
  // delay a short time for the PSU to react - load regulation still needs testing
  delayMicroseconds(10);
}

Circuit board and connections
I used some spare stripboard I had lying around, easy to work with. Everything in gold print is supposed to be connected where names match even if you don't see a direct trace.
Arduino-VReg-Schema.png Boards-and-cabling.jpgBoards-plain.jpgBoards-annotated.jpgBoard_reverse_annotated.jpgCase_overview.jpgelectronics_and_display.jpg


Other uses
You can use the same design to drive some custom gauges for your simracing setup, simulate sensors, you could even output sound via the DAC. The pro micro board could also easily drive an LCD. It can very probably also be used for other server power supplies with trim pins.

Will post updates as I make them here. In the meantime, merry Christmas and a happy New Year everyone!
 

wheela

Captain
Jun 4, 2021
1,409
821
0
Twin Cities, MN
Ride
2015 e84 X1 35i Msport
To not clutter the DIY Charging Supply for ISTA + BMW Programming thread I decided to post this in its own thread, may be useful for other DIY PSU retrofits as well.

Why do this?
Lots of people have turned to converting server power supply bricks into PSUs for high-power charging their Lipos, for spot welding, for flashing/coding cars, ...

In the thread linked above, forum members put together a very well executed DIY car coding/flashing PSU that supplies 13.8V at high amperage while working on their cars, but found that the output voltage was not really stable, dropping .3-.6V from the 13.8V the BMW docs say should be supplied.

Just turning the no-load voltage up higher so it can hold 13.8V under load is not a great solution. This means 14.4V or more without load, and that's pretty much the upper end of the scale for the chosen PSU, as well as most other server PSUs without hardware mods.

How to make it work
Server PSUs usually have some sort of voltage feedback system, meaning they can be adjusted on the fly. This works by either connecting the sense and 3.3V with a resistor matched to the desired output voltage or directly feeding a voltage to the sense pin - you can use your multimeter to measure the required voltage on both sides of the resistor. That means it is trivial to control via a microcontroller, in this case an Arduino, since they're cheap and widely available.

Required hardware (needs to be 3.3V capable!)
Arduino board - choose one that has a USB port unless you have a serial programmer
DAC breakout board or chip
Operational amplifier (OpAmp) that is single supply 15V input capable, doesn't have to be rail-to-rail, almost anything will work
2.2K and 470 Ohm resistors
Jumper cables
2.54mm/.1 inch solderable pins
Prototype stripboard PCB
Optional: output filtering (coming soon, works fine without so far)

Chosen hardware
Arduino Pro Micro (Leonardo clone in a small format) - available from 5-20 USD, depending on where you buy them
MCP4725 DAC breakout board
LM324-N quad rail-to-rail OpAmp

Software
Arduino IDE (open source and free, Windows, Linux, Mac)
Sparkfun MCP4725 library (click on the library icon on the left and type mcp4725 in the search field, click install)

Connect Arduino to a PC via USB, install drivers for USB serial port if necessary
Be sure to select the board you chose and the correct communications port in the IDE!

Code
This is just a simple hysteresis control loop, no PID. Will do load testing and update code if needed.
It gets a voltage feedback from the PSU output via a voltage divider that scales the voltage to <3.3V for the Arduino. While the output voltage is lower than the setpoint, it will increase it, and lower it if it overshoots (i.e. when the load drops) within a few microseconds (millionths of a second), should be good enough.
Code is documented, upload and test, adjust volt_setpoint if needed. Use quality resistors (1%) if possible, accuracy depends on it.

C:
#include <Wire.h>
#include <Adafruit_MCP4725.h>

Adafruit_MCP4725 dac;
unsigned int volt_psu = 0; // 0-1023, 10 Bit resolution on the Arduino
unsigned int dac_out = 0; // 0-4095, 12 Bit resolution on the MCP4725
// Adjust the value of volt_setpoint below to change the output voltage
// Lower number = lower voltage : valid values: 0-1023 -> 750-752 measured for 13.85V output
unsigned int volt_setpoint = 752;

void setup()
{ //I2C address of the DAC - could be 0x60, 0x61 or 0x62, 0x60 is default for MCP4725
  if (dac.begin(0x60))
  {
    // output 0V from the DAC to the 12V_RTN_SENSE pin, no voltage increase for safe startup
    dac.setVoltage(0, false);
  }
  // setup our analog PSU voltage feedback pin
  pinMode(A0,INPUT);
  // wait a bit for PSU and display to start up
  delay(3500);
}

void loop()
{
  // read PSU voltage feedback - ~15mV resolution, i.e. 0.015V - seems good enough
  volt_psu = analogRead(A0);
  // If voltage is less than desired
  if (volt_psu < volt_setpoint ) // Hysteresis of +-1 => ~0.03V
  {
    // limit the max value output to the DAC - should never really reach this value anyway, usually around 2xxx for 13.85V
    if (dac_out < 4095)
    {
      // increase trim/output voltage
      dac_out = dac_out + 1;
    }
    // output the voltage
    dac.setVoltage(dac_out, false);
  }  // if voltage is higher than the setpoint
  else if (volt_psu > volt_setpoint) // Hysteresis of +-1
  {
    // limit to 0 as minimum
    if (dac_out > 0)
    {
      // decrease trim/output voltage
      dac_out = dac_out - 1;
    }
    dac.setVoltage(dac_out, false);
  }
  // delay a short time for the PSU to react - load regulation still needs testing
  delayMicroseconds(10);
}

Circuit board and connections
I used some spare stripboard I had lying around, easy to work with. Everything in gold print is supposed to be connected where names match even if you don't see a direct trace.
View attachment 104049 View attachment 104055View attachment 104056View attachment 104057View attachment 104062View attachment 104058View attachment 104059


Other uses
You can use the same design to drive some custom gauges for your simracing setup, simulate sensors, you could even output sound via the DAC. The pro micro board could also easily drive an LCD. It can very probably also be used for other server power supplies with trim pins.

Will post updates as I make them here. In the meantime, merry Christmas and a happy New Year everyone!
Very nice, thanks for sharing this!!
 

Nexus665

New Member
Dec 2, 2024
5
6
0
Hey, you're welcome! Since I'm doing it anyway, might as well give something back and share it since the PSU case design and component choice in the original thread was pretty well thought out and saved me some headache :)

So far, I have tested it up to 20A load using my lipo chargers, next up will be tests with the car while trying to code something.

Results have shown that load regulation is fine - max voltage drop was .02V, from 13.83V to 13.81V, which seems fine. This was measured on the PSU side, not at the end of the cables. Just using a resistor, the voltage dropped around .4V, so more than an order of magnitude better. No visible overshoot, just went back to the initial voltage instantly when I stopped the load.

On the cable/connector ends, I noticed about a .08V drop at 20A using the suggested AWG8 cables - that was pretty much expected. Will raise my volt_setpoint so it sits around 13.95V without load, giving about .15V margin to 13.8V and see what it does on the car.

Could also add a sense wire that connects to the cable end clamps that connect to the car/load, which would eliminate the cable voltage drop. Might be the cleanest way to do it vs. just turning it up further. Will decide what to do after more testing.
 
  • Like
Reactions: wheela