Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
869 lines (808 sloc) 27.7 KB
<html lang="en">
<head>
<meta charset="utf-8">
<title>GPIO Zero</title>
<meta name="description" content="Designing for education is hard">
<meta name="author" content="Ben Nuttall">
<meta name="author" content="Dave Jones">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="../reveal.js/css/reveal.css">
<link rel="stylesheet" href="../reveal.js/css/theme/serif.css" id="theme">
<link rel="stylesheet" href="../reveal.js/lib/css/zenburn.css">
<style type="text/css">
.reveal .fragment.replace.visible:not(.current-fragment) {
display: none;
height: 0;
line-height: 0;
font-size: 0;
}
.reveal section img {
border: 0 none;
box-shadow: 0 0 8px #220;
vertical-align: middle;
}
.reveal section img.diagram {
box-shadow: none;
background: transparent;
}
.reveal table td {
border-bottom: 0 none;
}
</style>
<script>
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = '../reveal.js/' + (window.location.search.match(/print-pdf/gi) ? 'css/print/pdf.css' : 'css/print/paper.css');
document.getElementsByTagName('head')[0].appendChild(link);
</script>
<!--[if lt IE 9]>
<script src="../reveal.js/lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>GPIO Zero</h1>
<h2>or: Designing for Education is <em>Hard</em></h2>
</section>
<section>
<h2>Vital Statistics</h2>
<dl>
<dt>Name</dt><dd>Ben Nuttall</dd>
<dt>Occupation</dt><dd>The Pi'd Piper of Cambridge</dd>
<dt>Twitter</dt><dd><a href="https://twitter.com/ben_nuttall">@ben_nuttall</a></dd>
<dt>GitHub</dt><dd><a href="http://github.com/bennuttall">github.com/bennuttall</a></dd>
</dl>
</section>
<section>
<h2>Vitalstatistix</h2>
<dl>
<dt>Name</dt><dd>Dave Jones</dd>
<dt>Occupation</dt><dd>Making scary code, so you don't have to!</dd>
<dt>Twitter</dt><dd><a href="https://twitter.com/waveform80">@waveform80</a></dd>
<dt>GitHub</dt><dd><a href="https://github.com/waveform80">github.com/waveform80</a></dd>
</dl>
</section>
<section>
<h2>So, what is it?</h2>
<p>A library that makes physical computing easy</p>
</section>
<section>
<h2>&ldquo;Physical&rdquo; computing?</h2>
</section>
<section>
<img src="physical_computing.jpg" />
</section>
<section>
<img src="asimo.jpg" />
</section>
<section>
<img src="robot-wars.jpg" />
</section>
<section>
<h2>LED blink</h2>
</section>
<section>
<img class="diagram" src="led_wiring.svg" />
</section>
<section>
<pre><code class="python">
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(17, GPIO.OUT)
while True:
GPIO.output(17, GPIO.HIGH)
sleep(1)
GPIO.output(17, GPIO.LOW)
sleep(1)
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import LED
from time import sleep
led = LED(17)
while True:
led.on()
sleep(1)
led.off()
sleep(1)
</code></pre>
</section>
<section>
<img class="diagram" src="button_wiring.svg" />
</section>
<section>
<pre><code class="python">
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GIPO.setwarnings(False)
GPIO.setup(18, GPIO.IN, GPIO.PUD_UP)
GPIO.wait_for_edge(18, GPIO.FALLING)
print("Don't push the button!")
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import Button
from time import sleep
button = Button(18)
button.wait_for_press()
print("Don't push the button!")
</code></pre>
</section>
<section>
<h2>Why?</h2>
<blockquote>You see things; and you say "Why?" But I dream things
that never were; and I say "Why not?"</blockquote>
<p>&mdash; George Bernard Shaw, Back to Methuselah</p>
</section>
<section>
<h2>Why not?</h2>
<blockquote>Because you're making it too easy!</blockquote>
<p>&mdash; John Q. Public, The Internet</p>
</section>
<section>
<h1>Lesson 1:</h1>
<h2>There's no such thing as &ldquo;too easy&rdquo;</h2>
</section>
<section>
<h2>Query a web-page</h2>
<p class="fragment">requests</p>
<p class="fragment">urllib</p>
<p class="fragment">socket</p>
</section>
<section>
<blockquote>But that's work, we're talking about
<strong>education</strong>. It's meant to be harder!</blockquote>
<p>&mdash; John Q. Public, The Internet</p>
</section>
<section>
<img src="classroom_in_your_head.jpg" />
</section>
<section>
<img src="classroom_reality.jpg" />
</section>
<section>
<img src="classroom_simpsons.jpg" />
</section>
<section>
<img src="smart_kid.jpg" />
</section>
<section>
<pre><code class="python">
from gpiozero import LED
from time import sleep
led = LED(17)
while True:
led.on()
sleep(1)
led.off()
sleep(1)
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import LED
from time import sleep
led = LED(17)
led.blink()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import LED
from time import sleep
from signal import pause
led = LED(17)
led.blink()
pause()
</code></pre>
</section>
<section>
<img src="bad_kid.jpg" />
</section>
<section>
<img src="achievement.jpg" />
</section>
<section>
<img class="diagram" src="simplicity.svg" />
</section>
<section>
<h1>Lesson 2:</h1>
<h2>&ldquo;Easy&rdquo; is a continuum</h2>
</section>
<section>
<h2>Why is Python great for teaching?</h2>
</section>
<section>
<pre><code class="python">
do_this()
do_that()
if this:
do_one_thing()
else:
do_another()
</code></pre>
</section>
<section>
<pre><code class="python">
def do_this():
return that
while True:
that = do_this()
if not that:
break
</code></pre>
</section>
<section>
<pre><code class="python">
class Animal(Eukaryote):
def eat(self, food): # ...
def excrete(self, where): # ...
class Fungi(Eukaryote):
def sit_around(self): # ...
def release_spores(self, n): # ...
</code></pre>
</section>
<section>
<h2>And GPIO Zero?</h2>
</section>
<section>
<img class="diagram" src="led_button_wiring.svg" />
</section>
<section>
<pre><code class="python">
from gpiozero import LED, Button
led = LED(17)
btn = Button(2)
while True:
if btn.is_pressed:
led.on()
else:
led.off()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import LED, Button
from signal import pause
led = LED(17)
btn = Button(2)
btn.when_pressed = led.on
btn.when_released = led.off
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import LED, Button
from signal import pause
led = LED(17)
btn = Button(2)
led.source = btn.values
pause()
</code></pre>
</section>
<section>
<h1>Lesson 3:</h1>
<h2>Making things easy is <em>hard</em></h2>
</section>
<section>
<h2>Dive Dive Dive!</h2>
</section>
<section>
<pre><code class="python">
class LED(DigitalOutputDevice):
pass
</code></pre>
</section>
<section>
<pre><code class="python">
class DigitalOutputDevice(OutputDevice):
def __init__(
self, pin=None, active_high=True,
initial_value=False): # ...
def close(self): # ...
def on(self): # ...
def off(self): # ...
def blink(
self, on_time=1, off_time=1, n=None,
background=True): # ...
</code></pre>
</section>
<section>
<pre><code class="python">
class OutputDevice(SourceMixin, GPIODevice):
def __init__(
self, pin=None, active_high=True,
initial_value=False): # ...
def on(self): # ...
def off(self): # ...
def toggle(self): # ...
@property
def value(self): # ...
@property
def active_high(self): # ...
</code></pre>
</section>
<section>
<pre><code class="python">
class GPIODevice(Device):
def __init__(self, pin=None): # ...
def close(self): # ...
@property
def closed(self): # ...
@property
def pin(self): # ...
@property
def value(self): # ...
</code></pre>
</section>
<section>
<pre><code class="python">
class Device(ValuesMixin, GPIOBase):
@property
def value(self): # ...
@property
def is_active(self): # ...
</code></pre>
</section>
<section>
<pre><code class="python">
class GPIOBase(GPIOMeta(nstr('GPIOBase'), (), {})):
def __setattr__(self, name, value): # ...
def __del__(self): # ...
def close(self): # ...
@property
def closed(self): # ...
def __enter__(self): # ...
def __exit__(self, exc_type, exc_value, exc_tb): # ...
</code></pre>
</section>
<section>
<img src="wat1.jpg" />
</section>
<section>
<pre><code class="python">
class GPIOBase(GPIOMeta(nstr('GPIOBase'), (), {})):
def __setattr__(self, name, value): # ...
def __del__(self): # ...
def close(self): # ...
@property
def closed(self): # ...
def __enter__(self): # ...
def __exit__(self, exc_type, exc_value, exc_tb): # ...
</code></pre>
</section>
<section>
<h2>Meta-classes: <em>why?!</em></h2>
</section>
<section>
<h1>Lesson 4:</h2>
<h2>Debugging is a <em>hard</em> skill</h2>
</section>
<section>
<h1>Challenge:</h1>
<h2>What went wrong&hellip;?</h2>
</section>
<section>
<pre><code class="python">
from gpiozero import Button
from signal import pause
def button_was_pressed():
print("Don't push the button!")
b = Button(21)
b.pressed = button_was_pressed
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
Traceback (most recent call last):
File "dont_push.py", line 8, in <module>
File "gpiozero/devices.py", line 158, in __setattr__
self.__class__.__name__, name))
AttributeError: 'Button' object has no attribute 'pressed'
</code></pre>
</section>
<section>
<h2>More Horror!</h2>
</section>
<section>
<pre><code class="python">
import inspect # OH NOES!
import weakref
from functools import wraps
from threading import Event
from collections import deque
from time import time
try:
from statistics import median
except ImportError:
from .compat import median
</code></pre>
</section>
<section>
<pre><code class="python">
class EventsMixin(object):
# ...
@when_activated.setter
def when_activated(self, value):
# That doesn't sound good ...
self._when_activated = self._wrap_callback(value)
</code></pre>
</section>
<section>
<pre><code class="python">
def _wrap_callback(self, fn):
if fn is None:
return None
elif inspect.isbuiltin(fn):
return fn
else:
try:
inspect.getcallargs(fn)
return fn
except TypeError:
inspect.getcallargs(fn, self)
@wraps(fn)
def wrapper():
return fn(self)
return wrapper
</code></pre>
</section>
<section>
<img src="wat2.jpg" />
</section>
<section>
<h2>No boilerplate!</h2>
</section>
<section>
<pre><code class="python">
from gpiozero import Button
from signal import pause
def button_was_pressed(btn): # &lt;--- mandatory parameter
print("Don't push the button!")
b = Button(21)
b.when_pressed = button_was_pressed
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import Button
from signal import pause
def button_was_pressed(): # &lt;--- not so mandatory parameter
print("Don't push the button!")
b = Button(21)
b.when_pressed = button_was_pressed
pause()
</code></pre>
</section>
<section>
<h2>More WAT?</h2>
</section>
<section>
<img src="say_wat_again.jpg" />
</section>
<section>
<h2>So that declarative thing &hellip;</h2>
</section>
<section>
<table>
<tr>
<th class="fragment" data-fragment-index="2">
Outputs have <code>source</code>
</th>
<th class="fragment" data-fragment-index="3">
</th>
<th class="fragment" data-fragment-index="1">
All devices have <code>values</code>
</th>
</tr>
<tr>
<td class="fragment" data-fragment-index="2">
LED<br/>
Buzzer<br/>
Energenie<br/>
</td>
<td class="fragment" data-fragment-index="3">
<img style="position: fixed; top: 34%; left: 20%; width: 7em" class="diagram" src="arrows.svg" />
</td>
<td class="fragment" data-fragment-index="1">
LED<br/>
Button<br/>
LightSensor<br/>
DistanceSensor<br/>
</td>
</tr>
</table>
</section>
<section>
<pre><code class="python">
from gpiozero import LED, Button
from signal import pause
led = LED(17)
btn = Button(2)
led.source = btn.values
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import LED, Button
from gpiozero.tools import all_values
from signal import pause
led = LED(17)
btn1 = Button(2)
btn2 = Button(3)
led.source = all_values(btn1.values, btn2.values)
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import Energenie, Button
from signal import pause
lamp = Energenie(1)
btn = Button(2)
lamp.source = btn.values
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import Robot, MCP3008
from signal import pause
robot = Robot(left=(17, 18), right=(3, 4))
left_pot = MCP3008(channel=0)
right_pot = MCP3008(channel=1)
# TANK!!!
robot.source = zip(left_pot.values, right_pot.values)
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import Energenie, MCP3008
from gpiozero.tools import booleanized
from signal import pause
heater = Energenie(1)
thermometer = MCP3008(channel=0)
heater.source = booleanized(thermometer.values, 0.0, 0.5)
pause()
</code></pre>
</section>
<section>
<pre><code class="python">
from gpiozero import Energenie, MCP3008
from gpiozero.tools import booleanized
from signal import pause
heater = Energenie(1)
thermometer = MCP3008(channel=0)
heater.source = booleanized(thermometer.values, 0.0, 0.5,
hysteresis=0.1)
pause()
</code></pre>
</section>
<section>
<img src="traffic_light.jpg" />
</section>
<section>
<pre><code class="python">
from travispy import TravisPy
from gpiozero import LED
from gpiozero.tools import negated
from time import sleep
from signal import pause
def status(repo, delay=3600):
t = TravisPy()
r = t.repo(repo)
while True:
yield r.last_build_state == 'passed'
sleep(delay) # don't hit Travis constantly
tl = TrafficLights(21, 20, 16)
tl.red.source = negated(tl.green.values)
tl.green.source = status('RPi-Distro/python-gpiozero')
pause()
</code></pre>
</section>
<section>
<h2>Pin implementations</h2>
<table>
<tbody>
<tr><td><strong>RPi.GPIO</strong></td><td>C, software PWM, default</td></tr>
<tr><td><strong>RPIO</strong></td><td>C, DMA-based PWM</td></tr>
<tr><td><strong>PiGPIO</strong></td><td>C, DMA-based PWM, remote pins</td></tr>
<tr><td><strong>Native</strong></td><td>Pure python, fallback</td></tr>
</tbody>
</table>
</section>
<section>
<h2><em>Remote</em> pins?!</h2>
</section>
<section>
<pre><code class="bash">
$ export GPIOZERO_PIN_FACTORY=PiGPIOPin
$ export PIGPIO_ADDR=192.168.1.5
$ python
</code></pre>
</section>
<section>
<h2>Runs on</h2>
<table>
<tbody>
<td><img class="diagram" src="linux.png" /></td>
<td><img class="diagram" src="macosx.png" /></td>
<td><img class="diagram" src="windows.png" /></td>
<td><img class="diagram" src="raspberry_pi.png" /></td>
</tbody>
</table>
</section>
<section>
<h2>Cool toys!</h2>
<table>
<tr>
<td><img class="fragment diagram" src="tactile-push-button.png" /></td>
<td><img class="fragment diagram" src="pir.png" /></td>
<td><img class="fragment diagram" src="ldr.png" /></td>
</tr>
<tr>
<td><img class="fragment diagram" src="ultrasonic-distance-sensor.png" /></td>
<td><img class="fragment diagram" src="motor.png" /></td>
<td><img class="fragment diagram" src="servo.png" /></td>
</tr>
</table>
</section>
<section>
<h2>Timeline</h2>
<table>
<tbody>
<tr><td><strong>14th Sep 2015</strong></td><td>Initial commit on GitHub</td></tr>
<tr><td><strong>15th Sep 2015</strong></td><td>First PR, first alpha release</td></tr>
<tr><td><strong>23rd Sep 2015</strong></td><td>Sprints at PyConUK</td></tr>
<tr><td><strong>28th Sep 2015</strong></td><td>v0.6 beta</td></tr>
<tr><td><strong>16th Nov 2015</strong></td><td>v1.0 released</td></tr>
<tr><td><strong>31st Aug 2016</strong></td><td>v1.3 released</td></tr>
<tr><td><strong>31st Aug 2016</strong></td><td>v1.3.1 released (oops)</td></tr>
</tbody>
</table>
</section>
<section>
<h2>Community growth</h2>
<table>
<thead>
<tr><th>v1.0</th><th>v1.3</th></tr>
</thead>
<tbody>
<tr><td>200 commits</td><td>581 commits</td></tr>
<tr><td>6 contributors (2 major)</td><td>13 contributors (3 major)</td></tr>
<tr><td>103 issues (50 PRs)</td><td>439 issues (235 PRs)</td></tr>
<tr><td>0.51 issues/commit</td><td>0.75 issues/commit</td></tr>
</tbody>
</table>
</section>
<section>
<h2>Issue driven development</h2>
</section>
<section>
<h2>Future Development</h2>
<ul>
<li>Support more devices</li>
<li>Move default pins to PiGPIO</li>
<li>Remote SPI and I2C support</li>
<li>Promote zero boilerplate philosophy in other projects</li>
</ul>
</section>
<section>
<h2>GPIO Zero</h2>
<dl>
<dt>Documents</dt><dd><a href="https://gpiozero.readthedocs.io/">gpiozero.readthedocs.io</a></dd>
<dt>GitHub</dt><dd><a href="https://github.com/RPi-Distro/python-gpiozero">github.com/RPi-Distro/python-gpiozero</a></dd>
<dt>Join us!</dt><dd>at the sprints tomorrow</dd>
</dl>
</section>
<section>
<h2>Thank You</h2>
<p>Questions?</p>
</section>
</div>
</div>
<script src="../reveal.js/lib/js/head.min.js"></script>
<script src="../reveal.js/js/reveal.js"></script>
<script>
var forEach = function (array, callback, scope) {
for (var i = 0; i < array.length; i++) {
callback.call(scope, i, array[i]);
}
};
// Strip the indentation from the pre-code sections
forEach(document.querySelectorAll('pre code'), function(i, el) {
var lines = el.textContent.split('\n');
if (lines[0] === '') lines.shift();
var matches;
var indentation =
(matches = /^[\s\t]+/.exec(lines[0])) !== null ?
matches[0] :
null;
if (!!indentation) {
lines = lines.map(function (line) {
return line.replace(indentation, '').replace(/\t/g, ' ');
});
el.textContent = lines.join('\n').trim();
}
});
</script>
<script>
Reveal.initialize({
// The "normal" size of the presentation, scaled here for the notebook
width: 800,
height: 600,
// Factor of display size that should remain empty around the content
margin: 0.1,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 1.5,
controls: false, // Display controls in the bottom right corner
progress: true, // Display a presentation progress bar
slideNumber: false, // Display the page number of the current slide
history: true, // Push each slide change to the browser history
keyboard: true, // Enable keyboard shortcuts for navigation
overview: true, // Enable the slide overview mode
center: true, // Vertical centering of slides
touch: true, // Enables touch navigation on devices with touch input
loop: false, // Loop the presentation
rtl: false, // Change the presentation direction to be RTL
fragments: true, // Turns fragments on and off globally
// Flags if the presentation is running in an embedded mode,
// i.e. contained within a limited portion of the screen
embedded: false,
// Flags if we should show a help overlay when the questionmark
// key is pressed
help: true,
// Number of milliseconds between automatically proceeding to the
// next slide, disabled when set to 0, this value can be overwritten
// by using a data-autoslide attribute on your slides
autoSlide: 0,
// Stop auto-sliding after user input
autoSlideStoppable: true,
mouseWheel: false, // Enable slide navigation via mouse wheel
hideAddressBar: true, // Hides the address bar on mobile devices
previewLinks: false, // Opens links in an iframe preview overlay
transition: 'none', // Transition style (none/fade/slide/convex/concave/zoom)
transitionSpeed: 'default', // Transition speed (default/fast/slow)
backgroundTransition: 'default', // Transition style for full page slide backgrounds (none/fade/slide/convex/concave/zoom)
viewDistance: 3, // Number of slides away from the current that are visible
// Parallax background image
parallaxBackgroundImage: '', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
// Parallax background size
parallaxBackgroundSize: '', // CSS syntax, e.g. "2100px 900px"
// Amount to move parallax background (horizontal and vertical) on slide change
// Number, e.g. 100
parallaxBackgroundHorizontal: '',
parallaxBackgroundVertical: '',
// Optional dependencies
dependencies: [
// Cross-browser shim that fully implements classList - https://github.com/eligrey/classList.js/
{ src: '../reveal.js/lib/js/classList.js', condition: function() { return !document.body.classList; } },
// Syntax highlight for <code> elements
{ src: '../reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
// Speaker notes
{ src: '../reveal.js/plugin/notes/notes.js', async: true },
// Remote control your reveal.js presentation using a touch device
//{ src: '../reveal.js/plugin/remotes/remotes.js', async: true },
// MathJax
//{ src: '../reveal.js/plugin/math/math.js', async: true }
]
});
</script>
</body>
</html>