7

Is there a way to read a PWM signal from the pins on Raspberry Pi?

I tried googling it, but I only found PWM output, and not input.

3

The short answer: You CANNOT reliably read PWM on Raspberry Pi.

Reading PWM requires microsecond precision (unless you're reading a very-very slow PWM), and that is not available on Raspberry Pi for userland software without tinkering with kernel modules.

The easiest way to capture PWM would be to get any cheap (< $0.5) microcontroller with serial or I2C output and hook it to your Raspberry Pi and read the actual values from the microcontroller. This will work very reliably and is quite precise.

  • 1
    Any idea where to get such an IC? – Pepijn May 27 '15 at 16:05
  • @Pepijn what you're looking for is an analog-to-digital converter (ADC). A very common one which I use very frequently is the MCP3008 8 channel, 10-bit unit, and another that I have permanently on my unit testing Pi for my Perl software is the ADS1115 4 channel, 16-bit unit. The latter needs a bit of soldering work, the former does not. There are many code examples available for using both of these units (C, Python, Perl etc), so if you don't want/can't write your own, it should be trivial to get going. – stevieb Feb 3 '17 at 15:42
  • Any simple microcontroller will do. I used to use Microchip products for this sort of thing. The advent of the Arduino has brought the AVR chips into popularity again. Now I just use a small breakout board containing a microcontroller and whatever life-support the chip needs. My favorite is the Teensy 3.x series. – NomadMaker Apr 7 '18 at 15:08
2

This is an interesting question and one that your are correct in saying Google Search does not provide an obvious solution! (I miss the days when Google could answer anything I wanted to know for my education / assignments within seconds.)

I'm assuming you understand the principles of PWM. Therefore, I will not go into that. However, I believe you could in theory read a PWM value on a regular digital input pin with some clever coding.

I will admit I haven't tried this myself, but you should be able to measure the time that the pin is high and the time for which it is low (giving you your PWM reading) and then use whatever mathematical formula the sensor's supplier provided to convert this to the actual reading.

This method works for me on a similar problem where I needed to read the pulse length from an ultrasonic module and then convert it to distance. The problems I can envisage involve ensuring reliable readings!

If you think it will help and want to see the code I used for the ultrasonic module just say so, and I'll copy it on when I get home.

I started copying the code but for some reason the website only lets me copy it a small section at a time (and i'm too lazy to get my pi out of the garage) so here is the link to it. ignore most of the functions at the bottom as they are related to using the module as a proximity sensor. http://pibot.webnode.com/products/ultrasonic-range-sensor/

  • That will be good to see the code, so I can get a base to start my own code, if you can paste here I'll be very gratefull ^^ – Caio Keto Oct 1 '13 at 18:13
  • The problem with this suggestion is that the Linux kernel doesn't give you sufficient timing to read typical RC PWM duty cycles (which are typically 1000-2000 microseconds every 16 milliseconds) with sufficient precision. If we could install an interrupt handler for a GPIO pin change, and capture timestamp on high/low transitions, in a kernel module, then this might be a useful solution. – Jon Watte Aug 14 '17 at 18:12
2

I can do fairly accurate pulse width measurement using the piGpio C library: http://abyz.me.uk/rpi/pigpio/index.html

This library enables you to install a callback function that will trigger on any edge transition on a gpio pin and gives you a microsecond level timestamp for each transition. Don't think you can count on this for microsecond accuracy - but my testing suggests that accuracy is at least +/- 10us, maybe better.

Lot better than running a busy loop polling a gpio for the level change yourself.

  • +1 It may be worth noting that the accuracy may vary some based on CPU load (especially the single cores), but otherwise a good answer. :) – Jacobm001 Feb 3 '17 at 16:32
1

The long answer: You actually can! (well with a little help from our friends resistor and capacitor)

You can convert a PWM output to an analog voltage level, (DAC) and read it with ADC pin on your raspberry pi.

What you need is a 4k7 resistor and 0.1uF capacitor:

schematic

simulate this circuit – Schematic created using CircuitLab

The simple RC low-pass filter above converts the PWM signal to a voltage proportional to the duty cycle which can be read by your raspberry pi as an analog value.

  • 3
    The ADC pin? And which would that be? – Ghanima Aug 9 '16 at 10:05
  • @Ghanima Well, new Microsoft Lightning driver is in development but serve this purpose quite well ( it is documented on their pages ). For what I've found for BCM2837 it might have a hardware ADC which is on GPIO0 (sadly this one isn't linked to any header pin).. at least they linked hardware pwm channels – Quest Aug 9 '16 at 15:56
0

If you are happy with a slow response, you can read a fast PWM by undersampling. Simply read the GPIO in a loop and apply a low pass filter. The probability of reading a 1 each cycle is proportional to the pulse width. An easy to implement IIR low pass filter is:

double acc=0.5;
const double k=0.01;
for(;;) {
  bool x=GPIO.read();
  acc+=k*(x?1:0-acc);
}

As k decreases, the resolution improves but the bandwidth decreases.

0

Although my answer is not from the pins, you could use something based on a Sound Card Oscilloscope to read a pulsed input.

People have been using Sound Cards in desktop PCs for years to create oscilloscopes. It appears that with a modern internal sound card you can get usable results up to 10kHz. With a Raspberry Pi USB connected Sound Card, your max frequency might be lower.

Here's an example of one Sound Card Oscilloscope project for Linux: http://www.yann.com/en/diy-turn-your-gnulinux-computer-into-a-free-oscilloscope-29/09/2010.html

0

This python script I wrote works fine for me for reading PWM signals of a RC Receiver. High frequency PWM signals obviously won't work as it has been pointed out already.

I directly connected the ten signal out pins of the RC receiver to the Raspberry GPIO pins. The receiver is powered by the +5V and GND pins from the RPI.

I simplified the script as it does lots of other stuff, if you find any mistakes or leftovers, let me know

import RPi.GPIO as GPIO
import time
import numpy as np
inPINS = [2,3,4,14,15,18,17,27,22,23]
smoothingWindowLength=4

def getTimex():
    return time.time()

GPIO.setup(inPINS, GPIO.IN)
upTimes = [[0] for i in range(len(inPINS))]
downTimes = [[0] for i in range(len(inPINS))]
deltaTimes = [[0] for i in range(len(inPINS))]

def my_callback1(channel):
  i = inPINS.index(channel)
  v = GPIO.input(inPINS[i])
  #GPIO.output(outPINS[0], v) # mirror input state to output state directly (forward servo value only) - don't set PWM then for this pin
  if (v==0):
    downTimes[i].append(getTimex())
    if len(downTimes[i])>smoothingWindowLength: del downTimes[i][0]
  else:
    upTimes[i].append(getTimex())
    if len(upTimes[i])>smoothingWindowLength: del upTimes[i][0]
  deltaTimes[i].append( (downTimes[i][-1]-upTimes[i][-2])/(upTimes[i][-1]-downTimes[i][-1]) )
  if len(deltaTimes[i])>smoothingWindowLength: del deltaTimes[i][0]

GPIO.add_event_detect(inPINS[0], GPIO.BOTH, callback=my_callback1)
GPIO.add_event_detect(inPINS[1], GPIO.BOTH, callback=my_callback1)

try:
  while True:
    ovl = deltaTimes[0][-smoothingWindowLength:] # output first pin PWM
    ov = sorted(ovl)[len(ovl) // 2] #ov = np.mean(ovl)
    print ov
    time.sleep(0.1)
except KeyboardInterrupt:
  GPIO.cleanup()
-3

Easy Solution with high accuracy:

Using an Arduino as iic slave or UART device seems to work perfectly fine. The microcontoller is capable of reading the information via the pulseIn method.

Fore detailed information: https://www.youtube.com/watch?v=ncBDvcbY1l4

protected by Darth Vader Apr 7 '18 at 20:08

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.