---

Monday, April 1, 2024

ESP32 Programmable Output Current - LEDs without resistors?

I'm currently in the final stages of a new book 'Programming the ESP32' and while browsing the MicroPython documentation for the ESP32, I came across a section describing setting the output current of an ESP32 GPIO pin. This implied that by including this in your code: 

p6 = Pin(32, Pin.OUT, drive=Pin.DRIVE_3

you could set the drive current of a GPIO pin to 40mA - perhaps enough to directly drive an 8Ω speaker to quite a high volume. 

Or, by doing this: 

p6 = Pin(32, Pin.OUT, drive=Pin.DRIVE_0

you could limit the current to just 5mA and drive an LED directly without the need for a current limiting resistor. 


Background

The GPIO (General Purpose Input Output) pins on a modern microcontroller are little marvels of configurability. In software, you can configure them in various ways:
  • as an input or output
  • if acting as an input enable pull-up or pull-down resistors to bias the input high or low respectively
  • route a pin to an ADC (analog to digital converter) to act as an analog input and measure voltage
  • route a pin to a capacitative touch detector
  • route pins to special bus interfaces I2C, SPI and UART
When acting as outputs, GPIO pins have what are called complimentary outputs. That is, current can flow from the pin through something (say an LED and resistor) to GND -- called 'sourcing' or, you can connect one end of the load to the +3.3V supply and 'sink' current through the GPIO pin.

It looks like the ESP32 takes this a step further and allows you to set the maximum source or sink current for a GPIO pin in your code.

Goals of the experiment

Note that there is a big difference between good professional design practice and hobbyists messing around. A proper electronics engineer would never try to 'get away with things' in the way that I am here.

So, anyway, here's what I hoped to find out, using a low-cost ESP32 Lite board. After all there's a good chance this testing will end in the destruction of the board, so it might as well be a cheap one.
  1. Do these drive current settings actually control the DC current limiting of the GPIO pins?
  2. Does this current limiting also amount to short-circuit protection for GPIO pins?
  3. How much current can I draw from an ESP32's GPIO pins before I kill it?
  4. Can I use an LED without a current limiting resistor?

Method

Rather than jump straight to potentially destructive short-circuit testing. I began with the setup below, using a 100Ω resistor load to provide current limiting to I=V/R = 3.3/100 = 33mA. 

With the DMM set to a current range, the resistance across its leads will be close enough to 0Ω to ignore in these tests. From the MicroPython documentation here are the different drive currents. Our 100Ω resistor won't allow us to go to the maximum 40mA, but we can at least get an initial view of any current limitting.

  • Pin.DRIVE_0: 5mA / 130 ohm

  • Pin.DRIVE_1: 10mA / 60 ohm

  • Pin.DRIVE_2: 20mA / 30 ohm (default strength if not configured)

  • Pin.DRIVE_3: 40mA / 15 ohm

Results

GPIO 32


100Ω resistor load




Setting in Code

Nominal current (mA)

Measured Current (mA)

Pin.DRIVE_0

5

10.5

Pin.DRIVE_1

10

18.3

Pin.DRIVE_2

20

24.4

Pin.DRIVE_3

40

28


So, the current is about twice what was expected - at this point, I thought I might have an 'out-by-1' problem with my code (see the end of this post) but the code looks ok.

Nothing got hot, so I dispensed with the resistor and jumped to effectively short circuit testing, with the DMM ammeter connected directly between the GPIO and GND as shown below




Here are the results for this short circuit test.

GPIO 32

short cct





Setting in Code

Measured Current (mA)


Pin.DRIVE_0

10.9


Pin.DRIVE_1

22.6


Pin.DRIVE_2

44.6


Pin.DRIVE_3

84.9



Wow, so nearly 85mA from a single GPIO pin shorted to ground. I checked the ESP32 chip and using the finger test scale of: ambient, warm, hot, ouch, we were definitely 'warm'. Leaving the setup at DRIVE_3 for 90 minutes, there was minimal change in current or temperature of the ESP32.

Deciding to push my luck, I next tried 2 GPIOs in parallel at DRIVE_3, and modified my code to set the drive mode for all 2. Using pins 32 and 33, the current rose to 152mA and the ESP32 chip was still only warm.

Next, I tried adding GPIO 35. This made no difference to the current, nor for that matter did GPIO 34. However when I used GPIO 27 instead the current leapt to 220mA. I left this running for 30 mins and while the temperature increased to 'hot' it didn't get anywhere near 'ouch'.

I didn't push it any further, but instead tried some LEDs using the lowest current setting DRIVE_0. 

Red 8.4mA, Green 4.6mA, Blue 4.1mA. All lit nicely.

What Does the Datasheet Say?

The datasheet for the ESP32 is pretty vague about the GPIO pins. I couldn't find anything about the programability of current limiting. Searching for 'current' I came across this implying that 40mA was fine:


and also this:


The note 1. is staggering! 1.2A of GPIO power! 

Conclusion

Q1. Do these drive current settings actually control the DC current limiting of the GPIO pins? 
A. Yes - and it seems to work pretty well, even with a GPIO short-circuited.

Q2. Does this current limiting also amount to short-circuit protection for GPIO pins?
A. Yes.

Q3. How much current can I draw from an ESP32's GPIO pins before I kill it?
A. Well, it coped with 220mA from 3 GPIO pins without a problem.

Q4. Can I connect an LED without series resistor.
A. At the lowest current setting, I don't see any problem with connecting an LED directly to GPIO pins.

Also, the current limiting features of the GPIO pins means that it's relatively safe to parallel up multiple GPIO pins. If you were designing a product, then I wouldn't do this, but I'd be really tempted for a home project.

The caveat in all this is that without something documented in the datasheet there are no guarantees about how stressing the GPIO pins hard will affect the lifespan of the ESP32.

Test Program

Here's the test program I used for a single GPIO pin.


from machine import Pin

pin = 32

currents = [
    {'drive': Pin.DRIVE_0, 'mA': 5},
    {'drive': Pin.DRIVE_1, 'mA': 10},
    {'drive': Pin.DRIVE_2, 'mA': 20},
    {'drive': Pin.DRIVE_3, 'mA': 40},
    ]

while True:
    print('(0) 5mA, (1) 10mA, (2) 20mA, (3) 40mA')
    index_str = input('enter number 0 to 3: ')
    try:
        index = int(index_str)
        p = Pin(pin, Pin.OUT, drive=currents[index]['drive'])
        p.value(1)
        print('current set to mA: ' + str(currents[index]['mA']))
        print()
    except:
        pass

And here's the code for a lit of GPIO pins in parallel

from machine import Pin

pins = [32, 33, 27]

currents = [
    {'drive': Pin.DRIVE_0, 'mA': 5},
    {'drive': Pin.DRIVE_1, 'mA': 10},
    {'drive': Pin.DRIVE_2, 'mA': 20},
    {'drive': Pin.DRIVE_3, 'mA': 40},
    ]

while True:
    print('(0) 5mA, (1) 10mA, (2) 20mA, (3) 40mA')
    index_str = input('enter number 0 to 3: ')
    try:
        index = int(index_str)
        for pin in pins:
            p = Pin(pin, Pin.OUT, drive=currents[index]['drive'])
            p.value(1)
        print('current set to mA: ' + str(currents[index]['mA']))
        print()
    except:
        pass


No comments: