While investigating problems with the GPIOZero unit-tests, I eventually managed to slim the problem down to this simple single test-script:
import RPi.GPIO as GPIO import time # GPIOs 22 and 27 need to be connected with a jumper input_gpio = 27 output_gpio = 22 GPIO.setmode(GPIO.BCM) GPIO.setup(input_gpio, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(output_gpio, GPIO.OUT) p = GPIO.PWM(output_gpio, 100) p.start(0) do_start_stop = False def test_duty_cycles(i): print(" test_duty_cycles iteration %d" % i) if do_start_stop: p.stop() p.start(0) for dc in (40, 60): p.ChangeDutyCycle(dc) total = sum(GPIO.input(input_gpio) for i in range(20000)) avg = (total / 20000.0) * 100 print(" testing %.1f Hz, measured %.2f" % (dc, avg)) if abs(avg - dc) > 10: print("* over 10 Hz difference!") try: for i in range(5): test_duty_cycles(i+1) finally: GPIO.cleanup()
With do_start_stop set to False, then the script always runs fine (i.e. the measured frequency is always within 10Hz of the expected frequency. (Tested on up-to-date Raspbian Jessie on a RPi3 using both python and python3)
However when do_start_stop is set to True, then it never runs completely successfully e.g.
test_duty_cycles iteration 1 testing 40.0 Hz, measured 44.27 testing 60.0 Hz, measured 59.58 test_duty_cycles iteration 2 testing 40.0 Hz, measured 40.81 testing 60.0 Hz, measured 20.71 * over 10 Hz difference! test_duty_cycles iteration 3 testing 40.0 Hz, measured 60.60 * over 10 Hz difference! testing 60.0 Hz, measured 64.00 test_duty_cycles iteration 4 testing 40.0 Hz, measured 42.34 testing 60.0 Hz, measured 40.79 * over 10 Hz difference! test_duty_cycles iteration 5 testing 40.0 Hz, measured 24.90 * over 10 Hz difference! testing 60.0 Hz, measured 27.54 * over 10 Hz difference!
and it frequently errors with "Segmentation fault" at the end of the script (which seems to be more common with python3 than python2).
The GPIOZero unit-tests involve lots of starting and stopping of PWM, and these segmentation faults mean the PWM unit-tests all have to be disabled :-(
I can confirm this behavoir as well, the work around was to use ChangeDutyCycle(0),ChangeDutyCycle(X) instead of stop()/start(X). From looking in gdb, start/stop create and destroy threads and should only be called once on a PWM instance. Calling multiple triggers some thread-unsafe logic that in my case lead to segmentation faults as well.