Getting I2C regulated 0-10Volt out of an Arduino or ESP8266

The max Output of an Arduino or ESP8266 is roughly 5, respectively 3.3 Volt. The Arduino has the possibility to output a varying voltage on its Analog output pins, whereas the ESP8266 can emulate a varying voltage on a PWM addressed pin, with the help of a low pass filter (basically an RC filter [4k7/10uF]). There are instances where it would be handy/necessary to have a 10 Volt regulated output: Some dimmable lights require a control signal of 0-10Volt as do some commercial Power-motor controllers.
It is not hard to get such a signal from the mentioned microcontrollers, by amplifying the output.

The circuit shows such an amplifier. It is a simple Opamp non-inverting amplifier. When the Op Amp receives an input on the non-inverting output,  it will raise or lower it’s output until the level on the inverting output is matched.
As we are using a voltage divider to feedback the output voltage, we can make the opamp to drive the output higher than the input on the non-inverting pin.
The output voltage is thus defined as:
Vo=Vin x (1+(Rtop/Rbottom)), in which Rtop is R1+the top fraction of  P2 and Rbottom is R2+the bottom fraction of P2.
the amplification thus can vary between 1+(10/20) and 1+(20/10) or: between 1.5 and 3.
Ideally, with the variable resistor in the exact middle, the amplification equals 2 and the Output -when driven by the Arduino- should be 10 Volt, however, the Arduino may not output exactly 5 Volt, in which case the output can be trimmed with the variable resistor.
As the LM358 is not a rail to rail opamp, the Vcc needs to be higher than the required output. As the datasheet is a bit unclear on this, I have chosen 12 Volt, which turns out to work fine.
When using the ESP8266, the maximum output of the circuit will be 3×2.2=9.9Volt. In practice however the output on the ESP8266 pins will probably not reach 3v3, also, making an analog output with PWM and a low passfilter is not ideal.

Enter, the MCP4725
The ‘problems’ mentioned above, can largely be remedied by using  a proper DAC (Digital Analog Converter). The MCP4725 is a cheap, easy to use DAC that is available on break out boards at various chinese webshops.
The MCP4725 is a 12 bit, rail-to-rail DAC, which means that if you feed it with 5 or 3V3, that will come out of it (well…almost). It also has on board EEPROM for storage of setup, but for this build that is not really necessary. There is a library available in the Arduino IDE library manager. The Vcc pin requires an appropriate bypass capacitor of about 0.1 µF (ceramic) to ground. An additional 10 µF capacitor (tantalum) in parallel is also recommended to further attenuate high frequency noise present in application boards.

As the MCP4725 is a 12 bit DAC, the range goes from 0-4095. Writing 4095 to the MCP 4725 to the DAC therefore would give the max output. A program for the MCP4725 thus would look like something like this:

#include <Wire.h>
#include <Adafruit_MCP4725.h>
Adafruit_MCP4725 dac;

void setup(void) {
dac.begin(0x60); // This may vary between 0x60 and 0x65 for your board

void loop() {
// 1V Out from MC4725 -> 2V Out from LM358
dac.setVoltage(819, false); // false meaning "do not write to EEPROM"
// 2V Out from MC4725 -> 4V Out from LM358
dac.setVoltage(1638, false);
// 2.5V Out from MC4725 -> 5V out from LM358
dac.setVoltage(2043, false);
// 3V Out from MC4725 -> 6V Out from LM358
dac.setVoltage(2457, false);
// 4V Out from MC4725 -> 8V Out from LM358
dac.setVoltage(3276, false);
// 5V Out from MC4725 -> 10V Out from LM358
dac.setVoltage(4095, false);

The max output current of the LM358 is a bit obscure from the datasheet, it is probably around 20-40mA which is sufficient for most control signals. If you need more current (maybe you directly want to drive a motor), consider the LM675 that has a max output of 3 Amps. Be careful though as the LM675 can easily oscillate at amplification <10. Oscillation will lead to output going to (almost) rail even at no input and the chip warming up). Use decoupling capacitors – preferably a ceramic (100nF) and tantalum (10uF) type in parallel- and keep the leads short. A a small capacitor (on the order of 50 pF to 500 pF) across the circuit input, helps preventing the input leads to function as antenna.
A better choice might be the OPA544T, though that only has a 2 amp output, but is more stable at low amplifications. It is also more expensive.

Another way to get more current is with a power transistor, such as a BD137 (1A) or even a good old 2N3055/TIP3055 (15A).  However,the hFE starts to play an important role in this circuit. take for instance the BD137 that has a worst case hFE of 40. When it needs to deliver 1A, it will require  25mA as its base current. That is on the edge of what an LM358 seems to be able to deliver. If we add a BC547 that has a hFE of 110-800, a much smaller current will be required from the 358.
Other possible transistors in this range are 2SD882 (2Amp hFE 60), BD135, BD137, BD139 (1.5 A, hFE 40)
In case of the 2N3055, that has a worst case hFE of 20. If 15A is required, a base current of 750mA is required. Adding a BC547 would bring that down substantially, but it can’t deliver 750mA. A 2N2222 could, but it’s hFE is only around 30 at that current so you would need a BC547 AND a 2N2222  AND a 2N3055

In this light a TIP120 or TIP122  is a better choice as those already are powerdarlingtons with an hFE of about 1000 and a max current of 5A


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: