Half-decent LED-PWM and capacitive-sense GPIO coroutines

Discussion and questions about boards that can run MicroPython but don't have a dedicated forum.
Target audience: Everyone interested in running MicroPython on other hardware.
Post Reply
Posts: 29
Joined: Sun Jul 10, 2016 7:57 am

Half-decent LED-PWM and capacitive-sense GPIO coroutines

Post by nmz787 » Sun Jul 10, 2016 10:15 pm

(edited because I figured out how to get PWM working for the LED, and results are marginally better) (and again to enable BBCode)

The idea is to set a GPIO pin LOW, then as an INPUT, and see how long (based on capacitive field of your body moving to and from) the pin registers as HIGH. I try to self-calibrate by finding the min and max readings for how long it takes (iterations/cycles of a loop) to detect the INPUT change... so train the MCU by moving toward and away from the MCU a few times. It is not a linear response, it is most apparent within an inch or so of the MCU. The LED intensity(value) method didn't work for me, so I had to try and do a fake-PWM on the LED... any improvements for linearizing the response and making the LED nicer would be appreciated!

Code: Select all

import pyb
from pyb import Timer

# timer 2 will be created with a frequency of 10 kHz
# attach the timer to the LED GPIO, turning the brightness OFF to begin
tchannel = tim.channel(1, Timer.PWM, pin=pyb.Pin.board.LED_GREEN, pulse_width_percent=0)

# setup comm channel, for debug prints
# uart = pyb.UART(2, 9600)                         # init with given baudrate
# uart.init(9600, bits=8, parity=None, stop=1)

glob_max = glob_min = glob_span = 0

max_num_check_cycles = 50000

update_func = None

def later_update_func(current):
    use the min and max to calculate a range of sensor readings, then normalize the current reading
    finally scale the normalized reading to a 0-100 percentage value, cast to Integer
    then finally update the Timer PWM percentage
    global glob_min
    global glob_max
    global glob_span
    global tchannel
    glob_max = max(current, glob_max)
    glob_min = min(current, glob_min)
    glob_span = glob_max - glob_min
    if (glob_span) > 0:
        glob_dly = (((current-glob_min)/(glob_span))*100)
        glob_dly = int(glob_dly)
    # uart.write('LATER UPDATE {}\n'.format(glob_dly))

def first_update_func(i):
    set up the initial values for min and max of sensor range, 
    then switch the update func to "later_update_func" (I think this avoids an "if" branch code)
    global glob_min
    global glob_max
    global update_func
    glob_max = i
    glob_min = i
    update_func = later_update_func
    # uart.write('FIRST UPDATE\n')

update_func = first_update_func

def gpio_cap_sense():
    GPIO capacitive sense
    global update_func
    while True:
        # Discharge the pin first by setting it low and output
        g = pyb.Pin(pyb.Pin.board.A0, pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)

        # Make the pin an input WITHOUT the internal pull-up on
        g = pyb.Pin(pyb.Pin.board.A0, pyb.Pin.IN, pyb.Pin.PULL_NONE)

        # Now see how many cycles it takes to get the pin pulled up
        while i<max_num_check_cycles:
            # apparently there is some inverter,
            # such that I need to check when the pin goes apparently-low???
            if not g.value():

# set up co-tasks
tasks = [gpio_cap_sense()]

# loop through each task forever
while 1: 
    for t in tasks:
Last edited by nmz787 on Mon Jul 11, 2016 11:58 pm, edited 4 times in total.

Posts: 29
Joined: Sun Jul 10, 2016 7:57 am

Re: Half-decent fake-LED-PWM and capacitive-sense GPIO coroutines

Post by nmz787 » Sun Jul 10, 2016 10:43 pm

Since BBCode isn't enabled for my user, here is the code on github for readability:
https://gist.github.com/nmz787/afc7fea4 ... ce5da0c52d

User avatar
Posts: 4335
Joined: Fri Jul 18, 2014 8:01 am
Location: UK

Re: Half-decent LED-PWM and capacitive-sense GPIO coroutines

Post by pythoncoder » Mon Jul 11, 2016 7:12 am

I wonder if capacitive sensing would work better using an ADC? Link an ADC input to a DAC output. Set the DAC to half scale to charge the capacitance to 1.65V, then turn the DAC pin to a digital input to make it high impedance. Then take periodic readings from the ADC. This would allow for the situation where the capacitance change causes the voltage to fall - as far as I can see there's no guarantee which way it will move so you need to measure the absolute value of any change from the initial reading.

In my youth I experimented with capacitive sensing using a different sensing technique. I found sensitivity was improved by using a metal plate as the sensor - of course this needs to be well insulated and I mounted it behind a perspex sheet.

Nonlinearity is inevitable as the relationship between distance and capacitance is nonlinear. The capacitance between a pair of conductive plates is proportional to area/distance.
Peter Hinch

Post Reply