Low-Cost Programmable Power Supply

Chinese "LM2596 DC/DC buck converter with voltmeter" + "some wires" + "Forth" = "programmable power supply"

Similar projects worth following
Programmable power supplies are important for test automation. This project turns a $1.60 Chinese DC/DC converter with µC-based voltmeter into hobby lab equipment. The price per unit is less than $2, and the conversion can be done in about 15 minutes!

So far, the following features have been implemented:
* modification instructions for a cheap DC/DC converter
* programmable output voltage and ON/OFF
* input and output voltage measurement (e.g. for display, safety, or closed loop control)
* Full programmability with an embedded scripting language
* U=f(t) time sequencing,
* character I/O with 2 keys and 3 digit 7S-LED display for user interface
* remote control, and interactive scripting through RS232

Code from the Hackaday "eForth for cheap STM8S Value Line gadgets" project is used, mastering of which will give you bragging rights. It's also possible to use this board for automation, battery charging, or lighting.

Hackable DC/DC Converters

A quick search for "LM2596 DC 4.0-40 to 1.3-37V Adjustable Step-Down" on AliExpress presents a range of cheap DC/DC converters with voltmeter from $1.50. I discovered that the voltmeter uses a STM8S003F3P6, the low-cost µC that's the target of my first Hackaday project. The first specimen I hacked didn't have the most promising hardware design: it was necessary to solder patch wires to pins of a 0.65mm TSSOP package *).

The second one I ordered looked like this:

The first obvious thing, already visible on the vendor's pictures, is that the 7S-LED display nicely covers the voltmeter part including all the passive components.

*) Of course, it's still possible to remove the 7S-LED display, and use the pin as a breakout for no less than 11 GPIOs!

Under the Hood

Underneath the LED display we find this nice arrangement:

The LM317 in TO92 package is directly connected to the voltage divider R4/R3 (the LM317 lacks the recommended capacitor Cout, Cin is shared with the LM2596). C13 is connected to Vcap (STM8 1.8V core supply circuit). R1/R2-C4 and R8/R9-C5 are the "voltmeter inputs" (PC4 Vin, and PD3 Vout). R5 is connected to the green power LED.

PC3 is connected to LED "in", and PB4 to LED "out" without current limiting resistor (the same as the 7S-LED display). Like in the first variant, the "design" relies on the limited current driving capability, and on the surprising robustness of the STM8S outputs. The LED power dissipation is limited by a 4% duty cycle (there is a small risk of accelerated aging).

Isn't there anything else missing? Yes, a bypass capacitor for the STM8S003F3P6! A 100nF capacitor should be added to prevent communication errors (see instructions). Any doubts about the robustness of the STM8S are unfounded: it will tolerate circuit bending practices on a mass production scale ;-)

I first assumed that the GPIOs used for the 7S-LED display are the same as in the first variant, but they're not (the STM8S003F3P6 is rotated by 90º).

The LEDs "in" and "out" are now connected to dedicated GPIOs (which is good for us), but the keys still share a GPIO with LED segments like in a 70s pocket calculator (PC5/SegE and PD2/SegG). It would have been really easy to use PB5 for reading the keys but well ...

As mentioned in one of my previous posts there is a thing I learned about the STM8 ICP interface: connecting the NRST pin isn't needed unless SWIM has been disabled in the device configuration, and if the port pin is in input mode at least sometimes. Since PD1/SWIM is connected to pin4 of the display, iIt's possible to get a new firmware onto the µC without removing the 7S-LED display! Programing usually fails on the first attempt but that's OK for our needs: once there is a Forth console we can use IAP (in application programming).

Which variant to buy

I only plan to provide full board support for the variant in the pictures above. Other variants are either more expensive, or they don't have GPIOs on the LED pads that can be re-used easily. If you want to buy a unit, on AliExpress search for "LM2596 DC 4.0-40 to 1.3-37V Adjustable Step-Down" or simply post a comment here. I've seen a green and a blue variant, and both are easy to identify ("CN2596-2" is written on the back, LM317 is in TO92 package, it has only few passive components near the 7S-LED display). The green variant is better, since the ICP pins (NRST & SWIM) are broken out to pads.

Modding Options

The board has some quirks, but we can work around them:

  • for reading ADC values, the display should be dark (the anode, and the cathode outputs should be off - due to the required low duty cycle that's no problem
  • ADC Vin ADC is easily accessible on the backside of the PCB (after cutting the copper trace it can be used for measuring something else)
  • ...
Read more »


Try-It snapshot (the latest version is on )

ihx - 16.33 kB - 01/31/2017 at 19:35


  • 1 × DCDC Module (e.g. AliExpress "1pcs LM2596 DC 4.0~40 LED Voltmeter")
  • 1 × 100nF ceramics capacitor 0603 or 0805 (47nF to 10µF should work, too)
  • 1 × 10µF ceramics capacitor 0603 or 0805 (4.7µF*47R should work, too)
  • 1 × 47R 0805, replaces LED "IN" (33R*10µF should work, too)
  • 1 × 1K8 0805, replaces LED "OUT" (any value from 220R to 4k7 should work)

View all 6 components

  • Script it: Forth Intro

    Thomas06/01/2017 at 17:05 0 comments

    Forth is a very simple language, ideal for scripting, and therefore the Forth Intro in the GitHub Wiki also was simplified.

    Check it out - feedback is welcome!


    Thomas05/19/2017 at 17:29 0 comments

    Nice, this little project seems to catch the attention of many of you!

    It's really easy and cheap to hack the CN2596 DC/DC modules, and I can't wait to see on of these boards running your code, to do something useful, or to confuse a cat, I don't care :-) In any case, if you have questions just ask!

  • A Bug!

    Thomas05/06/2017 at 08:40 0 comments

    @RigTig found a bug in tg9541/stm8ef that also affects this project: emiting a "Z" to the 7S-LED display shows an "S" while he's doing fancy things with the W1209. My bad. Bug fixed, but right now only in the develop branch. In other words, the base software is otherwise rather stable.

    It's a bit quiet here because I'm currently bringing Forth to the HC12 wireless RS232 bridge. Robust communication to "smart things" in-house, on-premises or at > 500m in free space for about $3 and with very low power-consumption in stand-by mode.

  • Wiki Page on GitHub Updated

    Thomas04/19/2017 at 18:22 0 comments

    The eForth for STM8 Wiki page on the CN2596-2 board has been updated. On this page I try to consolidate information dispersed here over comments and log entries.

    If you think that something important is missing, please let me know!


    Thomas03/31/2017 at 05:38 0 comments

    Nice, 100 people show interest in what was meant as a showcase project for the STM8 Value Line eForth project!

    It's true, Forth differs a lot from procedural programing languages like Java, C++, C, Pascal, or Fortran. If you put the religious wars about programing languages aside, you'll see properties of scripting (Bash, BASIC, Perl, Python), REPL (LISP, Clojure), and ... assembly. It's not a panacea, but for some things it's very good.

    I'd like to encourage you to get one or two of the green DCDC boards, an ST-LINK V2 adapter, and a serial interface adapter. With budget delivery, this will cost you about $4. It would be really nice to some companions on this journey, and to get some feedback about which features are needed most, or which points need better docs :-)

    EDIT: 102 people now. Thanks guys! I forgot to mention the two capacitors and the two resistors. If you don't have an assortment in your lab anyway, here and here are examples for what you'd need for this, and for other projects (you may also ask a friend for some of these half-a-penny items).

    Does someone know of a Hackaday page with recommendations for an "electronics starter kit"?

  • Cold Cranking (automotive ECU testing, homebrew style)

    Thomas03/26/2017 at 11:24 0 comments

    Programmable power supplies are a necessary (and very expensive) test equipment in the automotive industry. The price tag for a single test channel can be several $1000 (and sometimes more). You'll never hear me say that one can fully replace such a piece of equipment on-the-cheap, but it's always possible to use cheap equipment for doing a tests that support the development, and thus to reduce the risk that the thing you develop won't pass tests. Also it's sufficient for "explorative testing", a method that's used in validating requirements (i.e. find out if known requirements are sufficient for higher level goals, like robustness or functional safety).

    One of the common power supply tests for networked ECUs (electronic control units, e.g. motor control, body controller, brake control) is the cold cranking test. The "test pulse" (i.e. the effect on the power supply) looks like this:

    Please note that the time scale in the first part of the function is in the order of miliseconds, and later in the order of seconds! The first part often induces a reset of an ECU (even if the requirements say that it shouldn't). Since no ECU I know of can power valves (or other actuators) from 3V, the 50 ms slope that follows stresses diagnostic routines. The 10s plateau then requires the ECU to rest in a degraded mode from which it has to recover somewhere in the 10s slope leading to normal operating conditions. Of course, the test pulse is just an acceptance test. For the supplier, developing a robust ECU is the main objective, not meeting the exact requirement.

    The pulse can be generated by simply defining the output voltage as a function of time. I took the values from the chart and made a table for @inter which maps time in ms to voltage in mV.

    Creating a test pulse is very simple, all it takes is a background task, that maps TIM value (ms ticks) through the mapping function and sets the output voltage:

    CREATE cctab 6 , 12000 0 , , 3000 5 , , 3000 20 , , 6000 70 , , 6000 10070 , ,12000 20070 , ,
    : coldcrank TIM cctab @inter mV ; ' coldcrank BG ! HAND

    CREATE defines the new word cctab, which returns the address of the following cells. In the following lines, the , take numbers from the data stack, and create a table by storing them in the dictionary memory (i.e. right after the word cctab. The word coldcrank gets the timer ticker value and the address of the table on the stack and uses @inter to get the value corresponding to the value of the 16bit ticker (in ms), and uses mV from the last log entry to set the output voltage. Negative values, and values > 20070 ms result in the saturation value 12000 mV (just like in the diagram). The pulse is thus repeated ever 65.536 s.

    On my >20 years old damaged scope, the result looks like this:

    If the picture doesn't look like the the diagram above it's because of the two time scale . It's hopeless to see a 70ms voltage drop to 3V at a 5s/DIV timescale. The rising slope looks good (it appears to be linear!), and also the two constant voltages (6V and 12V) are accurate. Discharging capacitors to 3V in just 5ms without additional effort won't work (at 330R load the output voltage drops to 5V after 60 ms from where it rises to the plateau of 6V in about 20ms). However, this result was reached with a very quick open loop control hack, and it took much longer to write this log, than to make this showcase application!

    These guys from FIG were right, Forth is very effective, especially for building test equipment. For decades I only used C and assembly for embedded programming, and I had missed out on a very good tool.

    EDIT: for discharging the output capacitor of the CN2596-2 board (220µF) from 12V to 3V in 5ms about 22R load is required. If the DuT (device under test = the test load) has a power rating of more than about 6.5W@12V that would always be the case. If the power rating is lower it depends: if the DuT has an ideal...

    Read more »

  • Linearized output

    Thomas03/26/2017 at 08:58 0 comments

    With the interpolation routine from the last log entry linearizing the DCDC converter output is simple. It doesn't really matter how the function looks like: the grid point density can be adjusted to match the second derivative. In a first example, I simply used a voltmeter and the interactive Forth console to map PMW values to the output voltage. It turned out that between 0 and 1V the function is less smooth, and I inserted an extra grid point. Also, below 20mV I couldn't reliably control the output voltage (after all, a bias to the feedback ground of an LM2596 is a hack :-) ), so I set the last grid point to 469/20mV.

    I decided to represent the voltage in units of mV, and created the following table with 14 grid points:

    CREATE vptab
    14 ,
    469 20 , , 451 500 , , 434 1010 , , 401 2000 , ,
    367 3010 , , 333 3990 , , 297 5010 , , 261 5990 , ,
    222 7010 , , 183 7990 , , 141 8990 , , 97 9990 , ,
    50 11000 , , 0 12000 , ,

    The mapping of output voltage to PWM is simple:

    : mV vptab @inter pwm ;

    By typing eg. 7500 mV I get an output voltage of 7.53V, which is more accurate than I had ever expected. I observed some drift, but as there is nothing like reference voltage source, what can you expect?

    Here are more examples:

    5000 mV -> 5.02V
    3700 mV -> 3.73V
    3300 mV -> 3.35V
    2800 mV -> 2.83V

    Through the saturation feature of @inter the following works, too:

    15000 mV -> 12.00V
    -10000 mV -> 0.03V

    Once more: level up!

  • Interpolation routine wanted (solved)

    Thomas03/23/2017 at 06:25 10 comments

    This project calls for a robust and easy to use interpolation function, preferably one that accepts a list of (Xn,Yn) points sorted for ascending value of X. The result should be (Y0) for X < X0, and Ylast for X > Xlast.

    If anybody has some code lying around or knows of some code that's been published under a permissive Open Source license, I would love to hear about it. Otherwise you'll just have to wait until a sufficient number of neurons in my brain isn't already fully satisfied by working on work related problems ;-)


    I brewed something up. @inter includes the table search and the interpolation function @K.C. Lee referred to, and a limiting function that works by comparing the pointers to value pairs. It can be used for arbitrary y=f(x) with saturation, which is what's needed for many control and linearizion tasks.

    : @dif ( a -- n )     \ delta of value pairs
      dup 2+ 2+ @ swap @ - ;
    : @inter ( x a -- y ) \ find value pairs, interpolate
      dup @ 1- >R 2+ dup begin 
        3 pick over @ < not while 
        nip dup 2+ 2+ next 
          drop dup
        else R> drop then \ eForth LEAVE idiom
      over = if           \ pointers equal: saturation 
        2+ @ nip 
        dup rot over @ - over 2+ @dif 
        rot @dif */ swap 2+ @ + then ;

    @inter does a table lookup with interpolation. The input is the value and the address of a table with n value pairs (Xn, Yn) ordered by ascending values of X, and preceded by the number of n. @dif looks up two values returns the difference (X1-X0 or Y1-Y0 depending on the offset). A C program would be a bit longer, I guess. Compiled this code requires 177 bytes.

    Here is an example:

    500 1000 400 800 300 600 200 400 100 200 50 100 6 ok
    create pairs , , , , , , , , , , , , , ok
    : p pairs @inter . ; ok
    450 p 225 ok
    90 p 50 ok
    1500 p 500 ok

    pairs is an arbitraty list of 6 x/y value pairs: [6, [100, 50], [200, 100], [400, 200], [600, 300], [800, 400], [1000, 500] ]

    Test values:

    450 is between (400/200) and (600/300) -> result 225
    90 is below X0, and following the requirements above Y0 is returned -> 50
    1500 is above X(n-1) and Y(n-1) is returned -> 500

  • Instructions updated

    Thomas03/18/2017 at 20:24 0 comments

    Release v2.2.8 on the TG9541/STM8EF on GitHub now has "official" support for the CN2596-2 DCDC converter as a Programmable Power Supply, and the instructions in this Hackaday project provide a (hopefully complete) step-by step guide.

    In any case, if you need help, or if anything should be clarified, please let me know. I'm curious if anybody dares to invest $1.60 in this hack ;-)

  • CN2596-2 Board keys work (Forth 101)

    Thomas03/14/2017 at 06:32 0 comments

    The CN2596-2 board keys (labeled "IN" and "OUT") are now supported.

    I removed the LED "IN" from the word OUT!. With the B4-controls-LM2596-ONOFF mod, the DCDC converter can be switched on and off easily.

    I'll describe the next steps in an update to this log entry later on. The code in TG9541/STM8EF develop branch is up-to-date.

    EDIT: the development branch now contains ready-to-use DCDC code (i.e. with timer initialization, and the pwm output word).

    I also added ON/OFF control for the LM2596 through port PB4 (former LED "OUT"). The led got replaced by a 1.8k 0804 resistor as a pull-up, pin5 of the LM2596 was cut in the middle, and connected to PB4 (red wire).

    The following code toggles the LM2596 output voltage on and off when pressing the "IN" key:

    : b ?key if 65 = if out @ 1 + out! then then ; 
    ' b bg !

    A little explanation for C programmers: in Forth most of the dataflow happens on the data stack (it's maybe easier to understand for JavaScript programmers: the blank between Forth words is a bit like the . between jQuery filter words, and the stack corresponds to the $ object).

    ": b" defines word b. ?key puts "<key code>" and "-1" " on the stack if a key is pressed, and "0" otherwise. if takes a number from the stack, and goes to the next then if the value is "0". Inside the condition structure, = tests if the key code equals 65 and in the next if the value of the outputs register out is incremented to toggle bit0, and then written to PB4 with out!. Note that 65 is of course the ASCII code of "A" (key "OUT" corresponds to "B", and both keys pressed together is "C").

    The operator ' (tick) put the address of b on the data stack. which is assigned to the background task vector BG with the store operator !. From there on b gets executed in the background in a 5 ms interval.

    The while the code above runs in the background, the output voltage can be changed on the console interactively:

    \ set maximum voltage (e.g. 12V, use trimmer to adjust)
    0 pwm
    \ set low output voltage (about 0.5 V)
    450 pwm

    As you may have guessed, \ is the comment operator for single line comments. pwm pops a value from the stack and sets the TIM1_CC3 compare register (the duty cycle of PC3 controls the ground level of the LM2596 feedback loop). Since the board init routine sets the Timer1 reload register to 1000, the compare value 450 corresponds to 45.0% duty cycle.

View all 20 project logs

  • 1
    Step 1

    Note: the picture in this log entry shows a fully modified board.

    Prepare the board:

    1. remove LED "IN"
    2. remove LED "OUT"
    3. interrupt the connection between LM2596 pin 5 (!ON/OFF) and GND (i.e. cut the pin in the middle, and de-solder the lower half)
    4. carefully cut the copper trace between the LM2596 heat sink GND and the 330R next to the trimmer.
    5. cut the copper trace between LM317 pin2 and anode pad of LED "IN". Expose some of the copper of the trace near said pad.
  • 2
    Step 2

    Add new components:

    1. solder a C 100nF between LM317 pin 2 and GND on the backside of the PCB
    2. solder a 10µF ceramics capacitor 0603 or 0805 over the now cut copper trace between the LM2596 heat sink GND and the adjacent pad of the 330R next to the trimmer
    3. solder a 47R 0805 resistor to the pads of LED "IN"
    4. solder a 1k8 0805 resistor to the pads of LED "OUT"
  • 3
    Step 3

    Make new connections for controlling the LM2596:

    1. add a wire between the cathode pad of LED "OUT" (PB4) and LM2596 pin 5
    2. add a wire between the copper trace near the anode pad of LED "IN" (PC3) to the 330R pad, where also the 10µF capacitor is connected
    3. connect (pin 2 || pin 3) of the trimmer to pin 1 of the voltage adjust trimmer on the board

View all 6 instructions

Enjoy this project?



Elliot Williams wrote 04/10/2017 at 19:10 point

I love the idea of a simple/cheap programmable power supply as a demo project for your Fortheries, so I ordered two of these things a few weeks ago.  As luck would have it, I got the "first variant" versions of the boards (or maybe something else, who knows?).  

To anyone following along: the big difference in board appearance is that the "first variant" board has more discrete components visible -- three resistors next to the TO92 package and two on either side of the display.  Don't get that board if you want to retain the display.

So I popped the display off and soldered some wires on.  Of the SWIM and UART lines, the only one that's not broken out into a through-hole is the NRST, which you may not need.  I gently lifted the pin and fly-wired it anyway. 

On my board, the chip needed unlocking, so I ran stm8flash with the -u flag first, and then uploaded a version of Thomas' stm8ef that didn't need to use the half-duplex hack, because without the LED you have clear access to both the TX and RX lines.  

So far, so easy.  Next I'm going to hack on Thomas' control routines for the  LM25596.  I'm half tempted to see if some minor code changes can turn this into a solar-cell MPPT device.  We'll see.

  Are you sure? yes | no

Thomas wrote 04/11/2017 at 05:36 point

Hi Elliot, the MPPT idea sounds great!

If you want to continue with the "first DC/DC PCB type", most of what I know about it is described in the log entries below. I also have half-baked code to control the LED display. If necessary I can refactor it to meet the improved "board framework" conventions but as you live in Germany I can also send you one of my a "green" DC/DC boards by mail.

If no LED display is required, an MPPT might also be feasible using plain DC/DC boards, and a STM8S breakout board. So far I didn't find out whether measuring the current is necessary for an optimal MPPT.

Here are log entries on the first DC/DC board variants:

Here is a bit on getting a PWM out of TIM1:

  Are you sure? yes | no

Elliot Williams wrote 04/11/2017 at 08:26 point

Thanks!  I was looking through those links late last night.  And no need to send me a green one -- ebay is already on the case. My impatience costs me only 2€.

Summarizing the voltage control routine to see if I've got it right: you inject an offset voltage into the voltage divider between the output and feedback pins by disconnecting a resistor to ground and replacing ground with a filtered-PWM voltage.  

You then look the PWM up in a table?  Why not close the loop on output and vary the PWM until it produces the desired output?  Is it unstable?  Have you tried?  Some combo of the lookup table PWM plus fine-adjustment should get the voltage "accurate" down to the resolution of the ADC.  (Of course, no voltage reference, etc.)

For MPPT: yeah, current is also necessary. But without the LED, I've got a lot of free pins. :)    

  Are you sure? yes | no

Thomas wrote 04/11/2017 at 17:05 point

You're of course right in all points:
* injecting an offset lowers the output voltage, but it also reduces the DC/DC control loop gain by a factor of (1-offset/1.25V)

* the lookup table acts as a open-loop-control component for the DC/DC converter's closed-loop-control set-point, and it greatly improves stability and responsiveness of this hack. However, a feedback component can help to reduce the residual control error. I guess that a PID controller would be a good addition, even though the voltmeter resolution is just about 50mV. Also, the lookup-table can be "self-learning".

* Removing the LED-display from any variant of the DC/DC converter turns the display's solder pads into a STM8S003F3 breakout. Who needs a display if the power supply is networked and automated, and the visualization (if needed) can be done with a central "dashboard"? Really a smart idea!

EDIT: the Wiki page on the CN2596 board now contains some info on STM8S003F3 and LED display connections:

  Are you sure? yes | no

K.C. Lee wrote 02/04/2017 at 12:24 point

>it was necessary to solder patch wires to pins of a 0.5mm TSSOP package.\

It is actually 0.65mm pitch.

  Are you sure? yes | no

Thomas wrote 02/04/2017 at 13:34 point

Thanks! Fixed.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates