Showing posts with label case. Show all posts
Showing posts with label case. Show all posts

Friday, February 13, 2015

Remember the SmartiPi kickstarter?

Video

Or perhaps not. So here's a video reminder



Post kickstarter


http://www.smarticase.com/ is taking pre orders, if you missed the original kickstarter campaign. I was a backer of the campaign, and I'm expecting mine real soon now.

If you are getting this to use with the Raspberry Pi camera module, check out this pdf:

http://www.raspberrypi.org/wp-content/uploads/2013/07/RaspiCam-Documentation.pdf



Francois
@f_dion

Monday, November 19, 2012

"Fab"oard Deux (2)

On continue avec le thème de la planche a découper. Je vous présente le "Fab"oard Deux.

Comme je l'indiquais pour le "Fab"oard Un, c'est parfait pour les démo en public, a l’école, pour les conférences, les groupes d'usagers, les hackerspaces etc


boitier 3d et vis en nylon
Je vous présente plein d'images, dans le désordre.

On a plein d'espace

boitier avec couvercle et cable fait maison
Module d'expansion fait main, sur support
Le gertboard sur support


Friday, November 16, 2012

LuzBox

Bluetooth?


Desde el verano, fue a buscar una manera de conectar sin hilos a mi LuzBox lo mas barato posible. Finalmente, he recibido un módulo Bluetooth que opera con el Arduino Uno. O, al menos, eso es lo que pensé.


Todo bien de este lado...
Sólo he recibido una placa sin chips...

!Ay!

Por eso, estoy de vuelta a tratar de encontrar un módulo bluetooth muy barato que conecta a un Arduino. ¿Porque? Para hacer la programación y ajustar los horarios de mi LuzBox.

LuzBox


El nombre es muy obvio cuando uno ve la LuzBox en operación. Es un prototipo para experimentar con control de aparatos eléctricos. La LuzBox puede hacer igual que un ser humano, pero miles de veces a cada día, sin olvidar, sin descansar, y mucho mas rápido.

Por ejemplo, mi LuzBox esta siguiendo un programa en secuencia, demostrado aquí con lámparas de noche:


La LuzBox tiene cierta independencia por que el horario se ajusta con el tiempo y con sensores (temperatura, sonido y voy a añadir otros), pero, a cada vez que hay de cambiar el horario, la secuencia o el modo de operación, es necesario conectar el Raspberry Pi a la LuzBox (Arduino) por medio de USB:

Funciona, pero no tan conveniente...

Y es la razón por que estoy buscando un modulo Bluetooth que opera con el Arduino. Pero debe ser muy barato. Sino, un Raspberry Pi modelo A o B con dongle wifi es mucho mas barato que un Arduino + Bluetooth (o wifi).

Mientras tanto, pensé que les gustaría ver las entrañas de la LuzBox.

Componentes


Empecé con un Arduino Uno (eventualmente voy a utilizar mi propio diseño para reducir el costo.

El Arduino Uno, opera independientemente del Pi
Una modificación: alimentación re-ubicada atrás

Fue necesario a re-ubicar la alimentación atrás, porque solo la conexión USB es accesible afuera de la LuzBox:

montaje empotrado
Mucho espacio en una caja eléctrica
con un "shield" de control de 4 canales
Al punto de poner todo en la caja


En la caja, hay el Arduino, el "shield", los sensores, una alimentación de 12V, receptáculos eléctricos 110V, un disyuntor y una cantidad de cable eléctrico.


Casi finalizado

Falta 6 tornillos

En cuanto a hacer la programmacion de un Arduino con un Raspberry Pi, es necessario hacer ciertas operaciones. Al minimo:


pi@raspberrypi ~ $ sudo usermod -a -G dialout fdion
pi@raspberrypi ~ $ sudo apt-get install arduino
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  arduino-core avr-libc avrdude binutils-avr ca-certificates-java default-jre
  default-jre-headless extra-xdg-menus gcc-avr icedtea-6-jre-cacao
  icedtea-netx icedtea-netx-common java-common libatk-wrapper-java
  libatk-wrapper-java-jni libftdi1 libjna-java libnspr4 libnss3 libnss3-1d
  librxtx-java openjdk-6-jre openjdk-6-jre-headless openjdk-6-jre-lib
  ttf-dejavu-extra tzdata-java
Suggested packages:
  arduino-mk avrdude-doc task-c-devel gcc-doc gcc-4.2 equivs libjna-java-doc
  icedtea-plugin libnss-mdns sun-java6-fonts fonts-ipafont-gothic
  fonts-ipafont-mincho ttf-wqy-microhei ttf-wqy-zenhei ttf-indic-fonts
Recommended packages:
  icedtea-6-jre-jamvm
The following NEW packages will be installed:
  arduino arduino-core avr-libc avrdude binutils-avr ca-certificates-java
  default-jre default-jre-headless extra-xdg-menus gcc-avr icedtea-6-jre-cacao
  icedtea-netx icedtea-netx-common java-common libatk-wrapper-java
  libatk-wrapper-java-jni libftdi1 libjna-java libnspr4 libnss3 libnss3-1d
  librxtx-java openjdk-6-jre openjdk-6-jre-headless openjdk-6-jre-lib
  ttf-dejavu-extra tzdata-java
0 upgraded, 27 newly installed, 0 to remove and 0 not upgraded.
Need to get 58.3 MB of archives.
After this operation, 173 MB of additional disk space will be used.
Do you want to continue [Y/n]? Y 


Después de eso, es lo mismo que hacer la programación inicial de un Arduino con Windows o una machina Linux.

Tengo un poco mas en este articulo en frances: framboise a la mode arduino.

Monday, November 12, 2012

PiQuizMachine

This article continues documenting one of the PYPTUG Workshops: PyHack Workshop #01, and goes into writing the PiQuizMachine code.

The machine

The PiQuizMachine

The Circuit


Each button controller is made from 1/2" PVC parts and a momentary mini push button, connected by a wire to a board.

On the board, one 10K Ohm resistor pulls up the GPIO to high, while the push button is connected to the GPIO on one end and to ground through a 1K Ohm resistor on the other end.

 This last resistor is optional if you are certain you can avoid pressing the buttons whenever the GPIOs are configured as outputs instead of inputs...


This circuit is repeated for all 4 buttons

Source Code


Make sure you've installed Mercurial and pulled the code from bitbucket (as instructed in the previous article), and go into:

    $ cd fablocker/PyHack/workshop01
    $ cd trivia
    $ ls
    load.py  piquiz.py  piquizmachine.py  questions.txt  README.md short.txt

The load.py is the first piece of code we will review.

We have a text file (question.txt) with all the questions and answers for the quiz game. We generated this file using a python script to web-scrape the data from a few pages of triviachamp.com (you can also do this by hand, selecting all the questions on a screen, and copy-pasting into a text file).

There is also a shorter version, with 2 questions / answers. We will use that first to figure out how we will load them into memory in our program. Here is the content of short.txt:

 Louis Leterrier - This film was released in 2010.Who directed Clash of the Titans?  
   
     a. Rodrigo Garcia  
     b. Louis Leterrier  
     c. Joe Carnahan  
     d. Iain Softley  
   
   
 Chicago - This team is part of the NFL.If you wanted to see the Bears play football, which city would you need to visit?  
   
     a. Chicago  
     b. Houston  
     c. San Diego  
     d. Denver  
   

Wow... So what do we have here? We do not have the data nicely separated by a special delimiter or by a new line. We will have to figure out a way to ignore the blank lines, handle the slight variations and extract the data into various fields:

answer - trivia blah blah.question blah blah?
multiple choices of answers

The thing is, the trivia and question can have all kinds of punctuation marks such as comma, period, dash and varying amount of white spaces. Doing this by hand coding a function to do it would be a lot of work and quite boring.

There is something that can deal with this fairly easily. I'm referring again to Python's "batteries included", a library called re, for Regular Expressions.

Regular Expressions


Regular Expressions ( abbreviated as re, regex or regexp), is a language designed to create simple to complex matching against data ( a string or a file). It is about as much the opposite to Python as can be, as it is dense, hard to read and hard to debug.

But sometimes, it is the perfect solution to a problem, and it is fairly easy to use in the Python implementation (a full implementation on Python, just like in Perl - in some other languages, it is a partial implementation, and can be hard to use or at least way more involved).

At any rate, if you ever have a career in IT, chances are you will have to become familiar with them, from system administrators wanting to parse log files, to programmers doing EDI, to web developers doing URL matching (Django, Web.py, even config files for certain web servers), to loading and extracting data from a file that is not 100% structured to be read by a computer (as we will do here).

The official documentation of the re module can be found at docs.python.org

A quick cheat sheet on regular expressions can be found here at tartley.com

And for those who like to follow video tutorials, check out this google video tutorial by Nick Parlante, on youtube.

Let's now look at the code in load.py:

1:  #!/usr/bin/env python  
2:  # -*- coding: utf-8 -*-  
3:  """  
4:  load.py - just loading the question file  
5:  Loads questions and answers from quiz data file. It follows the format  
6:  from triviachamp.com  
7:  """  
8:  # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4  
9:    
10:  import re  
11:    
12:  with open('short.txt', 'r') as f:  
13:    for line in f:  
14:      if len(line) > 1:  
15:        match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
16:        if match:  
17:          answer = match.group(1).strip()  
18:          trivia = match.group(2).strip()  
19:          question = match.group(3).strip()  
20:          choices = []  
21:        else:  
22:          match = re.search(r'\s+(\w)\.([\w ,-]+)', line)  
23:          if match:  
24:            choices.append(match.group().strip())  
25:            choice = match.group(1)  
26:            description = match.group(2).strip()  
27:            if description == answer:  
28:              correct_response = choice  
29:            if choice == 'd':  
30:              print(question, choices[0], choices[1],  
31:                   choices[2], choices[3],  
32:                   correct_response, trivia)  
33:    


Line 1 through 7 are typical of what we've done for the past several scripts, with the exception of line 2. On that line we specify that this document or script is following not ASCII, not ISO8859-1 but UTF-8 for the encoding of the characters. In this particular case we do not need it, but if we had to use accented letters, or special glyphs (for a card game, the heart, spade, diamond and clover glyphs, for example) then we would need this. Python 3 defaults to UTF-8, so it is a good idea to start learning about unicode and UTF-8, even though we are writing Python 2.x code right now. Line 8 is simply some instructions for an editor named vim.

Line 10 is where we import the regular expression module we just discussed. This is part of the Python library, so no need to download anything. This follows the "batteries included" pattern of Python. For general programming, you typically dont need to download anything else. For domain specific applications however, you will need to download and install other modules (like web.py, matplotlib, pygame, scipy etc)

Line 12, we are opening a file named short.txt, in the 'r' (or read) mode.

12:  with open('short.txt', 'r') as f:  

This is quite alien looking for some having a background in another language, such as C. In fact, it is also possible to open a file this way:

f = open('short.txt', 'r')

However, by using the statement with, we get exception handling and graceful housekeeping for free. We dont have to use try, except and finally, it is done implicitly. So just use this form.

Line 13, we have a for loop, and just like we did with the PINS in the previous article, we are getting items from an object. For PINS it was either a tuple or a list, both of which can be iterated by a for loop. In this case, we use a file object named f, which we obtained by calling open(). This is different than, say, a file descriptor in C/C++ (which is what fopen() in these languages would return) where it is only a reference to be used by other functions to do stuff. In Python, it is an object that, when an iteration is requested, will give us one item. For files, it will be one line. We could have also used a different variable name: for blah in file would get me a whole line in variable blah.

Line 14, we use a built-in, len() to tell us if we are dealing with an actual line with some data, or just a blank line, based on the length of the line.

Let me isolate the next 5 lines so we can focus on them:

15:        match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
16:        if match:  
17:          answer = match.group(1).strip()  
18:          trivia = match.group(2).strip()  
19:          question = match.group(3).strip()

Now, the scary part, line 15. The regular expression. You'll just have to trust me that it works as intended. In the workshop I couldn't go into all the details. Similarly, this article would be way too long if I did, but I'll try to do it anyway... The actual regular expression is this:

^([\w ]+)-([\w ,-]+)\.(.+)

The caret (^) is to start the match at the beginning of the line. The first group in parenthesis will match an alphanumeric character (\w) or a space (anything listed between the two square braces), while the + says to repeat it for as long as you can:

([\w ]+)

But we also follow this with a dash (-) and so it will stop the first group just before the dash. This separates the answer, from the trivia about the answer. The square brackets delimit a set of what characters should match (think of it as a Python list with no commas). This is convenient, because in the second group, we want to get the trivia, which includes not only alphanumeric or 'word characters', but also spaces commas and dashes:

([\w ,-]+)

The + repeats the match until the next rule, the period. It has to be escaped, because a period means match any character. We want it to match an actual period, so we escape it with a backslash (\). The last group uses the period to match any character and wont stop until the end of the line.

To actually use this whole regular expression in our Python code, we have to put it in a string. In python we can use the single or double quote mark for strings ( 'a_string' , "also_a_string" ).

In this case however, not just any string, a raw string. We do this by adding a r at the beginning: r'a_string'. That way we do not have to escape the backslash. And we pass that, along with our line to the search() function of the re module.

This returns a match object only if there is an actual match, so we have to test for existence of such on line 16, before we can use it on lines 17-19. There, we get the data from the groups we defined (defined in the regular expression by the parenthesis pairs) and assign them to variables: answer, trivia and question.

Wow, lots of explanation for only 5 lines of code! But that is the nature of the regular expression beast. As I said, often, it is not the answer, but when it is, we just have to live with its denseness...

Line 20 is just setting up (or clearing) a list to store the multiple choice answers that will follow. Yes, that is right, we haven't dealt with those yet...

Line 21 is the else tied to the existence of a match. Basically, if we couldn't match the first regular expression we wrote, that means we are probably dealing with one of the multiple choice answers. This next part could have been done without regular expressions, but since we spent all this time explaining them, let's use them again on line 22, this time with only 2 groups defined. The first will have the a-d letter and the second the description.

Line 23, we test again for a match. Line 24, we get the match.group() without specifying which of the group we want. By doing this, we get the whole match. We further use the strip() function to remove any leading or trailing white spaces, and we then append this to our choices list. Initially it is empty, and we add to it the multiple choices until the last choice (d).

Let me put the last piece of code repeated here:
25:            choice = match.group(1)  
26:            description = match.group(2).strip()  
27:            if description == answer:  
28:              correct_response = choice  
29:            if choice == 'd':  
30:              print(question, choices[0], choices[1],  
31:                   choices[2], choices[3],  
32:                   correct_response, trivia)  

Here, on line 25 we get the letter (a-d) assigned to choice. We then get the description on line 26 (using strip() again to remove leading and trailing white spaces). We can then use this description to compare it  (line 27) to the answer we picked up earlier. If the answer is on that line, we then assign to correct_response that letter.

Furthermore, if we are on the last line of the multiple choice answers, we are now ready to either store the whole group of question, choices, correct response and trivia, or in this case (lines 30-32) to print it.

We could also have passed the file to the regular expression and write a single regular expression getting all the data we wanted on multiple lines at once, but the sheer complexity of it would have rendered this tutorial useless.

Simple Quiz


In piquiz.py, we keep things simple (relatively... !) again. It is a basis that can be evolved into something a bit more interesting. Just a straight script, a trivia game in 50 lines of code (a little bare bone obviously with no scoring):


1:  #!/usr/bin/env python  
2:  # -*- coding: utf-8 -*-  
3:  """  
4:  PiQuizMachine - A quiz game for the Raspberry Pi.  
5:  Loads questions and answers from quiz data file. It follows the format  
6:  from triviachamp.com  
7:  """  
8:  # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4  
9:    
10:  import re  
11:  import random  
12:    
13:  data = []  
14:  with open('questions.txt', 'r') as f:  
15:    for line in f:  
16:      if len(line) > 1:  
17:        match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
18:        if match:  
19:          answer = match.group(1).strip()  
20:          trivia = match.group(2).strip()  
21:          question = match.group(3).strip()  
22:          choices = []  
23:        else:  
24:          match = re.search(r'\s+(\w)\.([\w ,-]+)', line)  
25:          if match:  
26:            choices.append(match.group().strip())  
27:            choice = match.group(1)  
28:            description = match.group(2).strip()  
29:            if description == answer:  
30:              correct_response = choice  
31:            if choice == 'd':  
32:              entry = (question, choices[0], choices[1],  
33:                   choices[2], choices[3],  
34:                   correct_response, trivia)  
35:              data.append(entry)  
36:    
37:  random.shuffle(data)  
38:  for question,choice_a,choice_b,choice_c,choice_d,\  
39:      correct_response,trivia in data:  
40:    print question  
41:    print choice_a  
42:    print choice_b  
43:    print choice_c  
44:    print choice_d  
45:    team_answer = raw_input("Your answer:")  
46:    if team_answer == correct_response:  
47:      print "That is correct, the answer is",correct_response  
48:      print trivia  
49:    else:  
50:      print "Not correct."

Up to line 31, it is almost the same as we already discussed (but using the full size questions.txt file). 32-34, instead of printing, we now store this entry into a list named data, which we initialized empty on line 13.

We are now going to randomize or shuffle the list of questions on line 37. On line 11 we imported the random module which includes a shuffle function. This is very convenient for games, not just for a quiz, but really interesting for a card game. We could define a card deck as a list, then shuffle it.

On line 38 and 39 (the backslash makes it as if it was a single line), we now loop through all the questions in the data list using a for loop. We then print the question and multiple choices on lines 40 through 44. I'm using a print syntax of a statement, but make note that starting with Python 3, print is a function. The next code example uses the print() function syntax (works in python 2.7 and 3). Lines 38-44: This can all be coded in a prettier way, but in order to keep this as simple as I can, I just did it by specifying all the fields so it is very obvious what we are doing.

Line 45 allows us to get an answer from a keyboard, with a prompt of "Your answer:". We check if the answer is correct on line 46 and if so print a message and the trivia tied to the answer on lines 47 and 48. If the answer was wrong (else) we print a different message on line 50.

So that is the basic core of a quiz program.

The Real Deal

Combining the code we've done in the button/quiz*.py scripts in the previous article, with the code above, we have all the ingredients to make an interactive quiz machine, one where each of the four teams or players gets a game controller, and will be able to "buzz" in first to answer, much as in TV games, such as Jeopardy or Family Feud.

This code was designed to teach about Python and GPIOs for a workshop and it is what I would call "squeaky clean". It is kept on purpose simple, yet demoes several key features of Python and the GPIOs.

The code was run through pep8 and pylint and is properly documented and formatted (even having 2 blank lines between functions, no use of ; etc), is quite verbose (several things were done in multiple lines but in normal use I would probably do as one) and yet, is less than 100 physical lines of code.

1:  #!/usr/bin/env python  
2:  # -*- coding: utf-8 -*-  
3:  """  
4:  PiQuizMachine - A quiz game for the Raspberry Pi.  
5:  Loads questions and answers from quiz data file. It follows the format  
6:  from triviachamp.com. Lock out through pushbutton controllers  
7:  """  
8:    
9:  __author__ = "Francois Dion"  
10:  __email__ = "[email protected]"  
11:    
12:  import re  
13:  import random  
14:  import RPi.GPIO as gpio  
15:    
16:  PINS = (22, 23, 24, 25) # list of pins as tuple  
17:  OFFSET = 21 # team number to GPIO pin offset  
18:    
19:    
20:  def loadtrivia(filename):  
21:    """ Load the trivia into a list, after extracting the fields """  
22:    data = []  
23:    with open(filename, 'r') as f:  
24:      for line in f:  
25:        if len(line) > 1:  
26:          match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
27:          if match:  
28:            answer = match.group(1).strip()  
29:            trivia = match.group(2).strip()  
30:            question = match.group(3).strip()  
31:            choices = []  
32:          else:  
33:            match = re.search(r'\s+(\w)\.([\w ,-]+)', line)  
34:            if match:  
35:              choices.append(match.group().strip())  
36:              choice = match.group(1)  
37:              description = match.group(2).strip()  
38:              if description == answer:  
39:                correct_response = choice  
40:              if choice == 'd':  
41:                entry = (question, choices[0], choices[1],  
42:                     choices[2], choices[3],  
43:                     correct_response, trivia)  
44:                data.append(entry)  
45:    return data  
46:    
47:    
48:  def getteam(lockedout):  
49:    """ figure out which team presses their button first """  
50:    poll = [pin for pin in PINS if pin - OFFSET not in lockedout]  
51:    while True:  
52:      buttons = [gpio.input(pin) for pin in poll] # list comprehension  
53:      if False in buttons: # at least one button was pressed  
54:        if buttons.count(False) == 1:  
55:          return buttons.index(False) + 1  
56:        else: # trouble, multiple buttons  
57:          teams = [i for i, b in enumerate(buttons) if b is False]  
58:          return random.choice(teams)  
59:    
60:    
61:  def main():  
62:    """ our main program """  
63:    data = loadtrivia('questions.txt')  
64:    
65:    gpio.setmode(gpio.BCM) # broadcom mode, by GPIO  
66:    for pin in PINS:  
67:      gpio.setup(pin, gpio.IN) # set pins as INput  
68:    random.shuffle(data)  
69:    for question, choice_a, choice_b, choice_c, choice_d, \  
70:        correct_response, trivia in data:  
71:      # if we wanted to make a graphical game using pygame  
72:      # we would replace the print statements below  
73:      print(question)  
74:      print(choice_a)  
75:      print(choice_b)  
76:      print(choice_c)  
77:      print(choice_d)  
78:      lockedout = [] # we start with no team locked out  
79:      while len(lockedout) < 4:  
80:        team = getteam(lockedout)  
81:        prompt = "Your answer, team {0}? ".format(team)  
82:        team_answer = raw_input(prompt) # get an answer  
83:        if team_answer == correct_response:  
84:          print("That is correct, the answer is:")  
85:          print(correct_response)  
86:          print(trivia)  
87:          print("")  
88:          lockedout = [1, 2, 3, 4]  
89:        else:  
90:          print("That is not the answer.")  
91:          lockedout.append(team)  
92:    
93:    
94:  if __name__ == "__main__":  
95:    try:  
96:      main()  
97:    except KeyboardInterrupt:  
98:      print("Goodbye")  
99:      gpio.cleanup()

Lines 1 to 16 should be familiar. I did add two variables for author and email. This is just a convention some people do in their code. Line 17 I'm defining an offset between the team number (1,2,3,4) and the GPIOs (22,23,24,25).

Lines 20 through 45 is the code from the previous example, but put into a function that accepts a file name for the quiz data and that upon execution, will return a list containing all our questions.

lines 48 through 58 is our quiz4.py code from the previous article, put into a function, but with a twist:

On line 52, instead of using directly the PINS tuple, we filter it first on line 50 to see which pins we should really poll. When getteam() is called, a list of teams that have been locked out is provided. We are not even going to check these teams button controllers, because they answered this question already, with a wrong answer.

50:    poll = [pin for pin in PINS if pin - OFFSET not in lockedout]  

So, what is happening here merits an explanation. You've probably recognized this as a list comprehension (which we've introduced in the previous article). But it looks strange... Let's read it. We will add to this list a pin, from an iteration of the PINS tuple (containing 22,23,24,25), but we will do this only if the pin minus the offset (21) is not in the list of locked out teams. So, if we take 22 - 21, that is 1. If team 1 is locked out, the list stays empty and continues on to the next value and test it. So on and so forth.

I mentioned earlier we had just touched the tip of the list comprehensions. here we went a little deeper, but it goes on.

Lines 51-58, we loop until a button is pressed. If only one button was pressed at the exact same time, we are good to go, and return which of the teams (1 - 4) pressed the button first. But trouble is looming on the horizon. It is possible for 2 or more buttons to be pressed at the exact same moment.

We thus have to introduce some random process to select one of those that have been captured as pressed. We do that on lines 57 and 58. 58 uses the choice() function of the random module (imported on line 13), but 57 requires some explanation, for those who are just starting with Python:

57:          teams = [i + 1 for i, b in enumerate(buttons) if b is False]  

We want to generate a list of all the teams that had pressed their buttons. buttons is a list containing something like [False, True, True, False], indicating here that team 1 and 4 pressed their button at the same time.

What I now want is a list containing [1, 4] to select randomly from it. So what we do is to use a built in function called enumerate() on that buttons list. This returns the index (starting at zero) and the value, so we capture this with a for i, b. We will assign i (the zero based index) + 1 (to get a team number) to the list, but only if b (the value) is False. There, that wasn't so bad, after all!

We are now ready for our main() function, lines 61 to 91 (only 30 lines).

On line 63 we load the data from the file questions.txt

Lines 65-67, we set up the GPIOs, as we've done in quiz4.py.

Lines 68-77 is the logic we had in our previous example: piquiz.py

At line 78, we initialize the list of locked out teams to be empty, and start a loop on line 79 that will go on until we have 4 teams locked out by 4 wrong answers, or a right answer (which forces all 4 teams to be locked out).

Line 80 is where we poll the button controllers until we have somebody pressing a button.

81:        prompt = "Your answer, team {0}? ".format(team)  
82:        team_answer = raw_input(prompt) # get an answer  

Lines 81 and 82 are asking for an answer from the team that pushed their button first. This answer is given using the keyboard.  Line 81 could also have been written as:

prompt = "Your answer, team " + str(team) + "?"

This is closer to what is done in other languages, but each concatenation with + creates a new resulting object. As such, it is a less efficient way of doing it, and if used in a loop with lots of data, will consume a lot of memory and be slow.

lines 83-88 handle a correct answer (concluding by locking out all teams to force a new question), while lines 89-91handle a wrong one, adding that one team to the list of locked out teams (passed to the function on line 80)

94:  if __name__ == "__main__":  
95:    try:  
96:      main()  
97:    except KeyboardInterrupt:  
98:      print("Goodbye")  
99:      gpio.cleanup() 

Lines 94-99 repeat the safeguard pattern we established in our previous article, in quiz4.py on lines 21-25

There you go, a complete game with Raspberry Pi GPIO hardware integration, that can be played with friends, in less than 100 lines of python code.

Team 2 is smoking! Two correct answers in a row

Conclusion


I've avoided classes and methods on purpose. These would have complicated things too much for the workshop audience (which ranged from teenagers to adults and from some who had never programmed in python, to some who had written a good bit).

Several also had a background in basic and shell scripts, so I did a good bit as straight scripts, without functions, waiting to the end to introduce these concepts. I've also added a bit more code than in the workshop in order to provide a better reference after the fact.

I hope it is useful to others, and if you are in the area, make sure to keep an eye for our next PyHack Workshop. Let me know if I've provided enough details, what things I could have explained more etc.

Sunday, November 11, 2012

PyHack Workshop #01

Yesterday, PYPTUG held its first PyHack workshop at Fablocker in Winston Salem, NC. The Raspberry Pi was briefly introduced along with some background information on the foundation.


Environments

David Mitchell presented iPython, the iPython notebook (uber cool, look into it if you dont know what it is) and the Adafruit WebIDE, along with how to install this (which some set it up during the workshop) and some demo. This was quite interesting, and David's presentation is available here.

"Fab"oard Deux (2)

You might have seen already "Fab"oard Un (1). This version features a larger cutting board, a raspberry pi in a "Fab"ulous Pi Case, with a GertBoard (not used in this workshop) and a homebrew board.

"Fab"oard Deux (2)

GPIOs


Many wanted to know how to use the GPIOs with Python. After providing a quick reference card (in Raspberry Pi appropriate colors...), we started reviewing code.





The workshop source code is hosted on bitbucket.org, using mercurial (hg). You will need to install it on your Raspberry Pi before you can get the code and use it:

$ sudo apt-get install mercurial

To get the code for the workshops, go into the directory where you will want to store your python code and then issue the following command:

$ hg clone https://bitbucket.org/fdion/fablocker

Let's review what we did for Workshop #01. Armed with the GPIO quickref:




Flash 

And we went on to talk about GPIO outputs. The simplest, LEDs:
$ cd fablocker/PyHack/workshop01
$ cd flash
$ ls
flash.py  okled.py

The okled.py is about the simplest way to get something going with GPIOs on the Raspberry Pi. It will turn on the OK LED for 5 seconds, then turn it off. I would suggest disabling the OS process that is already using it before using this code. I review it and explain how to disable the OS process on the RPi.GPIO dot dash article.

The code for flash.py:

1:  #!/usr/bin/env python  
2:  """ Setting up two pins for output """  
3:  import RPi.GPIO as gpio  
4:  import time  
5:  PINR = 0 # note, this should be 2 on a V2 RPi  
6:  PING = 1 # note, this should be 3 on a V2 RPi  
7:  gpio.setmode(gpio.BCM) # broadcom mode  
8:  gpio.setup(PINR, gpio.OUT) # set up red LED pin to OUTput  
9:  gpio.setup(PING, gpio.OUT) # set up green LED pin to OUTput  
10:  #Make the two LED flash on and off forever  
11:  while True:  
12:    gpio.output(PINR, gpio.HIGH)  
13:    gpio.output(PING, gpio.LOW)  
14:    time.sleep(1)  
15:    gpio.output(PINR, gpio.LOW)  
16:    gpio.output(PING, gpio.HIGH)  
17:    time.sleep(1)  

flash.py goes a little further, in that it will flash 2 LEDs, in alternance. One red LED and one green LED, on GPIO0 and GPIO1 (pins 3 and 5 on the P1 header on the Raspberry Pi). This is for version 1.0 of the Pi. For version 2.0, use flash2.py which uses GPIO2 and GPIO3 instead.

To run these, you will need to have RPi.GPIO installed. Again, I'll refer you to the RPi.GPIO dot dash article, to install RPi.GPIO. Then to execute:

$ sudo ./flash.py

There is no need to do sudo python flash.py, because the scripts are executable (chmod +x) and the first line is #!/usr/bin/env python.

The code is by no means optimal. It was designed to provide a gentle introduction to Python and GPIOs. I brought up lists as a way to make the code tighter, and this is demonstrated in the next section.

Buttons


From output to LEDs, we graduated to input with buttons.

$ cd ../button
$ ls
button.py  quiz.py  quiz2.py  quiz3.py    quiz4.py

If we look at the code of button.py (line numbers added to help following):

1:  #!/usr/bin/env python  
2:  """button.py - simplest testing of a gpio input"""  
3:    
4:  import time  
5:  import RPi.GPIO as gpio  
6:    
7:  BUTTON = 22 # header pin 22, GPIO25  
8:    
9:  gpio.setmode(gpio.BOARD) # reference by header pin number  
10:  gpio.setup(BUTTON, gpio.IN) # set pin as INput  
11:    
12:  not_pressed = True  
13:  print("Press button to exit")  
14:  while not_pressed:  
15:    not_pressed = gpio.input(BUTTON)  
16:    time.sleep(.2)  
17:    
18:  print("Goodbye")  
19:  gpio.cleanup() # reset pins to default  
20:    

Line 1 is to allow executing the script directly, line 2 is documentation. Line 4 imports the time module, which we'll use for the sleep function.

Line 5 is key to using GPIOs. We import this library, as gpio, so we wont have to type RPi.GPIO.function, we can use instead gpio.function.

Line 7, we decide to use GPIO25 / header pin 22 for the button and assign that to a constant named BUTTON. We are using BOARD mode (line 9), that is why we use a pin number instead of a GPIO number (like we did with the okled.py).

Line 10 sets up the pin as an input.

Line 12, we default not_pressed to True so the while loop (line 14) will be done at least once. On line 13 we are simply printing a statement to the console.

Inside the loop (that starts at line 14), we check on line 15 the status of the pin 22 and assign that to not_pressed. We chose this convenient way of loop control because gpio.input can return either True or False. True means the reading is high (3V3), which is the default, because we wired the pin with a pullup resistor (10KOhm) to 3v3, while the switch is connected between the pin and the ground (add a 1KOhm resistor in series with the switch if there is a possibility of seting the pin as an output and pressing the button at the same time, something that would fry the pin circuitry on the Pi). If the button is pressed, the pin sees 0 volts (ground) and thus has a value of False.

We conclude the loop with a delay of 0.2 seconds. If not_pressed is set to False (because we pressed the button) then the loop exits. We bid farewell by printing Goodbye to the console.

Last step, line 19, we do housekeeping of the GPIO.

Moving on to quiz.py:

1:  #!/usr/bin/env python  
2:  """ quiz.py - reading 4 different inputs """  
3:    
4:  import time  
5:  import RPi.GPIO as gpio  
6:    
7:  gpio.setmode(gpio.BCM) # broadcom mode  
8:  gpio.setup(22, gpio.IN) # set GPIO22 as INput  
9:  gpio.setup(23, gpio.IN) # set GPIO23 as INput  
10:  gpio.setup(24, gpio.IN) # set GPIO24 as INput  
11:  gpio.setup(25, gpio.IN) # set GPIO25 as INput  
12:    
13:  print("question?")  
14:  while True:  
15:    mybutton1 = gpio.input(22) # read value of GPIO22  
16:    mybutton2 = gpio.input(23) # read value of GPIO23  
17:    mybutton3 = gpio.input(24) # read value of GPIO24  
18:    mybutton4 = gpio.input(25) # read value of GPIO25  
19:    print mybutton1, mybutton2, mybutton3, mybutton4  
20:    time.sleep(.2)  
21:    

This is a similar concept, but this time we read 4 different pins, for 4 different push buttons:

PVC game controllers
Up to line 7, the code is similar. On line 7 we use BCM mode instead of board, so we can use the consecutive GPIO numbers (22 to 25).
Lines 8 to 11 set up the pins as inputs.

The while loop starts at 14 and lines 15 to 18 read the status of the 4 pins. We then print these statuses to the screen on line 19 and sleep for 0.2s on line 20 before continuing on with the while loop.

There is a lot of repetition going on here. Surely there is a better way to do this in Python? Yes.

Let's have a look at quiz2.py:

1:  #!/usr/bin/env python  
2:  """ quiz2.py - improving quiz with a tuple """  
3:    
4:  import time  
5:  import RPi.GPIO as gpio  
6:    
7:  PINS = (22, 23, 24, 25) # list of pins as tuple  
8:  gpio.setmode(gpio.BCM) # broadcom mode, by GPIO  
9:  for pin in PINS:  
10:    gpio.setup(pin, gpio.IN) # set GPIO pin as INput  
11:    
12:  print("question?")  
13:  while True:  
14:    mybuttons = [] # reset the list to be empty  
15:    for pin in PINS:  
16:      mybuttons.append(gpio.input(pin)) # read value of pin input  
17:    print(mybuttons) # print the whole list, the statuses of all pins  
18:    time.sleep(.2)  

That is both shorter and more easily read. We establish a list of pins on line 7. A list in Python is defined with []. Here we define this list with () because it is really an immutable list (a constant array in some other programming languages parlance). In Python, this is called a tuple. A list or a tuple can be iterated, and that is what we are about to do:

On line 9 we start a for loop, where the pin variable is assigned from PINS. We then use this assignment on line 10 to set up the pins as inputs.

Similarly, on lines 14 through 16 we do a for loop process and read the pin statuses and append them to a list. We then print the values at once on line 17.

Could we improve this a little further more? Sure. Are there other ways to do this? Again, yes. quiz3.py:

1:  #!/usr/bin/env python  
2:  """ quiz3.py - even better with list comprehensions """  
3:    
4:  import time  
5:  import RPi.GPIO as gpio  
6:    
7:  PINS = range(22, 26) # [22, 23, 24, 25] - same as tuple, no advantage  
8:    
9:  gpio.setmode(gpio.BCM) # broadcom mode, by GPIO  
10:  for pin in PINS:  
11:    gpio.setup(pin, gpio.IN) # set pins as INput  
12:  print("question?")  
13:  while True:  
14:    mybuttons = [gpio.input(pin) for pin in PINS] # list comprehension  
15:    print(mybuttons)  
16:    time.sleep(.2) 

Since the GPIOs are consecutives, 22, 23, 24 and 25, we can leverage some of the functional programming aspects of Python. On line 7, we assign PINS as a range of numbers that starts at 22 and ends less than 26 (25). I mention this simply for you to investigate functional programming with Python, since here, we dont gain any advantage compared to using a tuple. In fact, it is harder to read.

What about lines 14 to 16 in the previous code (quiz2.py), is that the Python way? No, not really. When it comes to Python and lists that are programmatically generated, the majority of the time, you'll want to use a list comprehension. Basically, you can generate the list in one line. If we read the new line 14, we are assigning to mybuttons a list that will comprise of the result of gpio.input(pin) where the value of pin is sequentially fetched from PINS (22,23,24 and 25).

This is barely touching the tip of the list comprehension iceberg.

We are forgetting an important safeguard in our code, a way to do the GPIO housekeeping after we exit with Control-C. Plus, as our code is growing, we want to put it inside a function.

Let me demonstrate both concepts with the next variation of quiz, quiz4.py:


1:  #!/usr/bin/env python  
2:  """ quiz4.py - added handling of ctrl-c and print only when pressed """  
3:  import time  
4:  import RPi.GPIO as gpio  
5:    
6:  PINS = (22, 23, 24, 25) # list of pins as tuple  
7:    
8:    
9:  def main():  
10:    """ our main program """  
11:    gpio.setmode(gpio.BCM) # broadcom mode, by GPIO  
12:    for pin in PINS:  
13:      gpio.setup(pin, gpio.IN) # set pins as INput  
14:    print("question?")  
15:    while True:  
16:      mybuttons = [gpio.input(pin) for pin in PINS] # list comprehension  
17:      if False in mybuttons: # at least one button was pressed  
18:        print(mybuttons)  
19:      time.sleep(.2)  
20:    
21:  if __name__ == "__main__":  
22:    try:  
23:      main()  
24:    except KeyboardInterrupt:  
25:      gpio.cleanup()  
26:    

Lines 1 to 6 should be very familiar by now.

Starting on line 9, we now define a function called main(), to hold our code. By default this is evaluated as a function definition and is not executed. In order to execute it we need lines 21 and 23 which basically state that if this script is executed as itself, and not as an import into another script, then we want to run or execute the function main(). FYI, functions in Python are first class objects, like classes, like everything else (even strings and numbers).

Going back to our definition of our main() function, on line 10 we document it. Lines 11 through 19 are the same in functionality as we had before. But if we hit Control-C to interrupt the program while in this code, something interesting happens, an exception is trapped (by the code on line 24) and execution jumps to line 25, does a cleanup and exits.

The reason the KeyboardInterrupt exception was trapped, is because we put a try: statement on like 22, before calling our main() function. Any error raised inside of main() will be caught by the except statement. In our case however, we are trapping only the KeyboardInterrupt (which is what is raised when you hit Control-C).

So, I think it is a good recap of what was discussed in the main part of the workshop. We still have to talk about the PiQuizMachine, but this is already quite long, so we will have to do that in another article, tomorrow.

Tuesday, November 6, 2012

"Fab"oard Un (1)

Que peut-on faire avec une planche a découper? Cuisiner un Raspberry Pi, bien sur. Après le boitier "Fab"ulous, je vous présente le "Fab"oard Un (de fabrication et board, de l'anglais pour planche.

C'est parfait pour les démo en public, a l’école, pour les conférences, les groupes d'usagers, les hackerspaces etc.

Comme c'est le "Fab"oard Un, cela implique qu'il y a un "Fab"oard Deux. Je vous le présente cette fin de semaine. Le Un utilise des composantes commerciales (boitier Multicomp, Pi Cobbler et cable GPIO de Adafruit). Le Deux, du fait main.

2 petits trous pour nous guider

Les vis du haut pour le boitier, du bas pour...
une surface pour prototypes

Le Pi cobbler




De l'espace en bas pour ajouter une autre surface proto

Monday, November 5, 2012

"fab"ulous pi case

Thanks to Lon from fablocker, I have a 3d printed case for the Raspberry Pi. It'll be for one of the few RPi I'll be bringing to fablocker this weekend, for PyHack.

That's how it is printed
And the other side. Much smoother since it lays flat
Good fit

With the lid on
Plenty of ventilation and GPIO access
The hole for the audio jack is a bit too wide

Sunday, November 4, 2012

PiBox

When you get your Raspberry Pi, it will come in a box.

Up to now, both RS and Element14 (the only 2 manufacturers, everybody else is a distributor) sent the Pi in an antistatic bag inside a cardboard box.
Manufactured by RS
Manufactured by element14
either way, it used to be in an antistatic bag


It looks like RS is now shipping the Pi in a plastic box, inside the cardboard box.

New plastic container

Some are making a case with it.