Extraescolar Robòtica 2016-2017

De Wikijoan
(Diferència entre revisions)
Dreceres ràpides: navegació, cerca
(Introducció. Joc del SIMON)
(Objectiu)
 
(Hi ha 17 revisions intermèdies sense mostrar fetes per un usuari)
Línia 2: Línia 2:
 
[[Fitxer:Simon-Game l.jpg|thumbnail]]
 
[[Fitxer:Simon-Game l.jpg|thumbnail]]
  
*[Extraescolar_Rob%C3%B2tica_2015-2016|Extraescolar Robòtica curs 15-16]
+
*[[Extraescolar_Rob%C3%B2tica_2015-2016|Extraescolar Robòtica curs 15-16]]
  
 
*https://en.wikipedia.org/wiki/Simon_(game)
 
*https://en.wikipedia.org/wiki/Simon_(game)
Línia 16: Línia 16:
 
==Objectiu==
 
==Objectiu==
 
L'objectiu és construir 4 prototips de Simon amb la Raspberry Pi, i un Simon finalitzat del tot.
 
L'objectiu és construir 4 prototips de Simon amb la Raspberry Pi, i un Simon finalitzat del tot.
 +
==Resultat final (juny 2017)==
 +
Youtube:
 +
*https://youtu.be/bSFUz6TXSyg
 +
Article al blog:
 +
*http://www.joanillo.org/?p=1478
 +
 
==Requisits==
 
==Requisits==
 
*Llums de colors
 
*Llums de colors
Línia 22: Línia 28:
  
 
=Resum Sessions=
 
=Resum Sessions=
14 ó 15 sessions:
+
*[[Sessions Robòtica Extraescolar. Curs 2016-2017]]
 +
13 sessions:
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_1 | '''Sessió 1''']]: 2 març. Introducció al moviment Maker. Primers passos amb la Raspberry Pi. Introducció al projecte SIMON
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_2 | '''Sessió 2''']]: 9 març. Mirem el codi original del Simon en el qual ens basem, i n'identifiquem les parts. Executem parts per separat, no cal que sigui a la Rpi, es pot executar directament a Linux/Ubuntu.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_3 | '''Sessió 3''']]: 16 març. Per fer un prototipus del joc del Simon necessitarem com a mínim poder apretar uns botons i que s'iluminin unes llumetes. En aquesta sessió començarem a treballar amb els pins GPIO per fer entrades i sortides. És la manera com la RPi es pot comunicar amb el món físic.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_4 | '''Sessió 4''']]: 23 març. En la sessió anterior vam fer les primeres proves per llegir i escriure en els pins GPIO. En aquesta sessió aprofundirem més en la programació de LEDs des de la Raspberry Pi.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_5 | '''Sessió 5''']]: 30 març.  Volem que els botons del Simon siguin ben lluminosos. En la pràctica anterior encenies un LED controlat per un pin GPIO, però aquests pins només donen 3,3V. Està clar que si volem més llum necessitarem més tensió elèctrica. L'objectiu serà encendre 6 LEDs blancs (que fan força llum), controlats per un pin GPIO de la Raspberry Pi.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_6 | '''Sessió 6''']]: 6 abril. Avui fem una classe més tranquil.la. Discutim sobre la construcció del prototipus. Materials, dimensions,... Fem pluja d'idees i els alumnes aporten reflexions interessants.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_7 | '''Sessió 7''']]: 20 abril. Hem d'anar definit aspectes tècnics del nostre joc del Simon. Parlarem de com alimentem el nostre joc del Simon, i de com produirem el so.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_8 | '''Sessió 8''']]: 27 abril. En la sessió 6 vam estar discutint sobre el disseny industrial del Simon. En especial ens preocupa el disseny dels botons grossos i el sistema de molla/retorn del botó. Avui treballarem amb LibreCAD el disseny dels botons per tal de mecanitzar-ho amb una màquina CNC. Estudiarem el flux de treball que es fa servir: disseny CAD > Generació del G-Code > Fresat CNC. És un procés similar al que utilitzaem per a imprimir amb 3D.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_9 | '''Sessió 9''']]: 4 maig. Anem a construir ja els prototipus dels alumnes. Aquesta setmana ens centrarem en la construcció, la setmana vinent ens centrarem ja en el codi. Els alumnes disposen de la RPi, 4 botons, 4 LEDs de colors, resistències, cables, grimpadora,...
 +
**Començar a posar-ho tot junt.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_10 | '''Sessió 10''']]: 11 maig. Ara que ja tenim construït el nostre prototip, anem ja a fer funcionar el Simon a partir d'un codi original de què disposem. Avui ja hem de jugar amb el Simon! Més endavant podrem fer modificacions i millores...
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_11 | '''Sessió 11''']]: 18 maig. En aquesta sessió treballem el software, fins arribar a la versió que serà definitiva, i presentem l'estat del prototipus.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_12 | '''Sessió 12''']]: 25 maig. Versió en principi definitiva del software, simon_v6.py. Implementem el reset i altres millores. Discutim l'estat actual del prototipus. Discutim possibles i futures millores, com ficar una pantalleta.
 +
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_13 | '''Sessió 13''']]: 1 juny. Presentem el moble acabat. Mirem com han quedat connectats i soldats tots els components, i n'identifiquem totes les parts. Així mateix, hi ha un últim canvi en el software. I juguem!
 +
 
 +
=Codi original. Llibreries necessàries=
 +
fitxer '''simon_original.py''':
 
<pre>
 
<pre>
23 febrer
+
##!/usr/bin/env python
2 març
+
#______________________________
9 març
+
#
16 març
+
#Simon Game prototype
23 març
+
#Piers Kennedy
30 març
+
#12-07-2012 (update 27-02-2013)
6 abril
+
#
20 abril
+
#______________________________
27 abril
+
4 maig
+
import RPi.GPIO as GPIO
11 maig
+
import random
18 maig
+
import time
25 maig
+
import numpy
1 juny
+
import wave
 +
import pygame
 +
import subprocess
 +
GPIO.setmode(GPIO.BOARD)
 +
GPIO.setwarnings(False)
 +
 +
#Set up sounds
 +
#This avoids any delay before sound starts
 +
pygame.mixer.pre_init(44100,-16,2,2048)
 +
#Initialise mixer
 +
pygame.mixer.init()
 +
subprocess.Popen('amixer cset numid=3 1',shell=True)
 +
SAMPLERATE = 44100
 +
 +
def createSignal(frequency, duration):
 +
    samples = int(duration*SAMPLERATE)
 +
    period = SAMPLERATE / frequency
 +
    omega = numpy.pi * 2 / period
 +
    xaxis = numpy.arange(samples, dtype=numpy.float) * omega
 +
    yaxis = 32800 * numpy.sin(xaxis)
 +
    return yaxis.astype('int16').tostring()
 +
 +
def createWAVFile(filename, signal):
 +
    file = wave.open(filename, 'wb')
 +
    file.setparams((1, 2, SAMPLERATE, len(signal), 'NONE',
 +
    'noncompressed'))
 +
    file.writeframes(signal)
 +
    file.close()
 +
 +
def playWAVFile(filename):
 +
    sound = pygame.mixer.Sound(filename)
 +
    sound.play()
 +
 +
#blue sound 209Hz
 +
bluesound = '/tmp/blue.wav'
 +
signal = createSignal(frequency=209, duration=.4)
 +
createWAVFile(bluesound, signal)
 +
#yellow sound 252Hz
 +
yellowsound = '/tmp/yellow.wav'
 +
signal = createSignal(frequency=252, duration=.4)
 +
createWAVFile(yellowsound, signal)
 +
#red sound 310Hz
 +
redsound = '/tmp/red.wav'
 +
signal = createSignal(frequency=310, duration=.4)
 +
createWAVFile(redsound, signal)
 +
#green sound 415Hz
 +
greensound = '/tmp/green.wav'
 +
signal = createSignal(frequency=415, duration=.4)
 +
createWAVFile(greensound, signal)
 +
 +
#losing tone 42 Hz
 +
losingtone = '/tmp/losingtone.wav'
 +
signal = createSignal(frequency=42, duration=3)
 +
createWAVFile(losingtone, signal)
 +
 +
#Variables
 +
max    = 100              #No. of rounds in game
 +
RoundNo = 1
 +
RED    = 1
 +
GREEN  = 2
 +
YELLOW  = 3
 +
BLUE    = 4
 +
correct = True
 +
 +
#Setup GPIO switches
 +
GPIOSwitch=[0,7,11,13,15]
 +
GPIO.setup(7, GPIO.IN)      #red    GPIO 7
 +
GPIO.setup(11, GPIO.IN)    #green  GPIO 0
 +
GPIO.setup(13, GPIO.IN)    #yellow GPIO 2
 +
GPIO.setup(15, GPIO.IN)    #blue  GPIO 3
 +
 +
#Setup GPIO LEDs
 +
GPIOLED=[0,12,16,18,22]
 +
GPIO.setup(12, GPIO.OUT)    #red    GPIO 1
 +
GPIO.setup(16, GPIO.OUT)    #green  GPIO 4
 +
GPIO.setup(18, GPIO.OUT)    #yellow GPIO 5
 +
GPIO.setup(22, GPIO.OUT)    #blue  GPIO 6
 +
 +
#Connect the ground to pin 6 and the positive to pin 1 (3V3)
 +
 +
#Generate a random list of LED outputs
 +
colour=[]
 +
for n in range(1,max+2):
 +
    colour.append(random.choice([RED,GREEN,YELLOW,BLUE]))
 +
 +
#Function to switch LEDs on then off
 +
def LEDout(val):
 +
    if (val==1):
 +
        playWAVFile(redsound)
 +
    elif (val==2):
 +
        playWAVFile(greensound)
 +
    elif (val==3):
 +
        playWAVFile(yellowsound)
 +
    elif (val==4):
 +
        playWAVFile(bluesound)
 +
    while pygame.mixer.get_busy():
 +
        GPIO.output(GPIOLED[val], True)
 +
    GPIO.output(GPIOLED[val],False)
 +
    time.sleep(0.15)
 +
    return[]
 +
 +
#Function to check when switch is pressed
 +
def SwitchChosen():
 +
    while True:
 +
            if (GPIO.input(GPIOSwitch[RED])):
 +
                return RED
 +
            if (GPIO.input(GPIOSwitch[GREEN])):
 +
                return GREEN
 +
            if (GPIO.input(GPIOSwitch[YELLOW])):
 +
                return YELLOW
 +
            if (GPIO.input(GPIOSwitch[BLUE])):
 +
                return BLUE
 +
 +
#Function to flash LEDs after mistake
 +
def LoserLights():
 +
    playWAVFile(losingtone)
 +
    for cycle1 in range(0,6):
 +
        for cycle2 in range(1,5):
 +
            GPIO.output(GPIOLED[cycle2], True)
 +
        time.sleep(0.5)
 +
        for cycle2 in range(1,5):
 +
            GPIO.output(GPIOLED[cycle2], False)
 +
        time.sleep(0.2)
 +
    return[]
 +
 +
#Main routine
 +
while correct:
 +
    print("Round %i" %RoundNo)
 +
    #LED cycle
 +
    for mout in range(1,RoundNo+1):
 +
        LEDout(colour[mout])
 +
    #Response
 +
    for ans in range(1,RoundNo+1):
 +
        push=SwitchChosen()
 +
        LEDout(push)
 +
        if(push!=colour[ans]):
 +
            LoserLights()
 +
            correct = False
 +
            print("Unlucky!")
 +
            print("You made it to round %i" %RoundNo)
 +
            break
 +
    RoundNo+=1
 +
    if (RoundNo==max+1):
 +
        print("WOW!! You Rock dude")
 +
        break
 +
    time.sleep(0.5)
 
</pre>
 
</pre>
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_1 | '''Sessió 1''']]: 23 febrer. Raspberry Pi. Teoria
+
Anem a executar aquest codi en una Raspberry Pi 3 on hem instal.lat 2017-02-16-raspbian-jessie-lite.img, acabada d'instal.lar.
**Cremar una imatge del Raspbian
+
**Moviment Maker
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_2 | '''Sessió 2''']]: 2 març. Sessió SSH. Començar a programar amb Python
+
**Comencem a programar amb Python. De moment no cal programar a la RPi. Programant a Ubuntu/Linux n'hi ha prou.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_3 | '''Sessió 3''']]: 9 març. Programació Python I. Control de GPIO. Llegir botons.
+
**Botons en sèrie. Jugar amb els botons: seqüència de botons que obri una porta.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_4 | '''Sessió 4''']]: 16 març. Programació Python II. Control de GPIO
+
**Encendre i apagar leds. Seqüencia de leds, fading, altres efectes.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_5 | '''Sessió 5''']]: 23 març. Amplificador bàsic amb transistors
+
**Encendre tres leds blancs.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_6 | '''Sessió 6''']]: 30 març. Alimentació de la Raspberry Pi i del Simon
+
**Diferents possibilitats. Discussió
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_7 | '''Sessió 7''']]: 6 abril. Programació Python III. So i notes musicals.
+
**Diferents possibiltiats. Qualitat del so. Buzzer. Amplificador mínim.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_8 | '''Sessió 8''']]: 20 abril. Programació Python IV
+
**Començar a posar-ho tot junt.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_9 | '''Sessió 9''']]: 27 abril. Construcció de prototipus I
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_10 | '''Sessió 10''']]: 4 maig. Disseny i construcció del moble. LibreCAD o FreeCAD.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_11 | '''Sessió 11''']]: 11 maig. Disseny dels botons. Impressió 3D?
+
**O bé mecanitzar metacril.lat de colors del Servicio Estación.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_12 | '''Sessió 12''']]: 18 maig. Construcció de prototipus I
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_13 | '''Sessió 13''']]: 25 maig. Construcció del Simon final.
+
*[[Sessions_Robòtica_Extraescolar._Curs_2016-2017#Sessi.C3.B3_14 | '''Sessió 14''']]: 1 juny. Jugar al Simon?
+
  
=Sessions=
+
Executem el codi i solucionem els errors que ens dóna, degut a què ens manquen certes llibreries.
*[[Sessions Robòtica Extraescolar. Curs 2016-2017]]
+
<pre>
 +
$ python simon_original.py
 +
Traceback (most recent call last):
 +
  File "simon_original.py", line 13, in <module>
 +
    import numpy
 +
ImportError: No module named numpy
 +
</pre>
 +
'''NumPy''' is the fundamental package for scientific computing with Python
  
 +
Hem d'instal.lar la llibreria ''numpy'':
 +
<pre>
 +
$ pip install numpy
 +
</pre>
 +
per instal.lar pip,
 +
<pre>
 +
$ sudo apt-get install python-pip
 +
</pre>
 +
o millor:
 +
<pre>
 +
$ sudo apt-get install python-numpy
 +
</pre>
 +
Tornem a executar el nostre fitxer:
 +
<pre>
 +
$ python simon_original.py
 +
Traceback (most recent call last):
 +
  File "simon_original.py", line 15, in <module>
 +
    import pygame
 +
ImportError: No module named pygame
 +
</pre>
 +
<pre>
 +
python-pygame - SDL bindings for games development in Python (Python 2)
 +
$ sudo apt-get install python-pygame
 +
</pre>
 +
i ara ja funciona:
 +
<pre>
 +
$ python simon_original.py
 +
numid=3,iface=MIXER,name='PCM Playback Route'
 +
  ; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0
 +
  : values=1
 +
Round 1
 +
</pre>
 
{{Autor}}, desembre 2016
 
{{Autor}}, desembre 2016

Revisió de 11:18, 1 juny 2017

Contingut

Introducció. Joc del SIMON

Simon-Game l.jpg

Video:

Jugar online (Javascript):

Solucions purament electrònica:

Codi Python del Simon. Punt de partida:

Objectiu

L'objectiu és construir 4 prototips de Simon amb la Raspberry Pi, i un Simon finalitzat del tot.

Resultat final (juny 2017)

Youtube:

Article al blog:

Requisits

Resum Sessions

13 sessions:

Codi original. Llibreries necessàries

fitxer simon_original.py:

##!/usr/bin/env python
#______________________________
#
#Simon Game prototype
#Piers Kennedy
#12-07-2012 (update 27-02-2013)
#
#______________________________
 
import RPi.GPIO as GPIO
import random
import time
import numpy
import wave
import pygame
import subprocess
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
 
#Set up sounds
#This avoids any delay before sound starts
pygame.mixer.pre_init(44100,-16,2,2048)
#Initialise mixer
pygame.mixer.init()
subprocess.Popen('amixer cset numid=3 1',shell=True)
SAMPLERATE = 44100
 
def createSignal(frequency, duration):
    samples = int(duration*SAMPLERATE)
    period = SAMPLERATE / frequency
    omega = numpy.pi * 2 / period
    xaxis = numpy.arange(samples, dtype=numpy.float) * omega
    yaxis = 32800 * numpy.sin(xaxis)
    return yaxis.astype('int16').tostring()
 
def createWAVFile(filename, signal):
    file = wave.open(filename, 'wb')
    file.setparams((1, 2, SAMPLERATE, len(signal), 'NONE',
    'noncompressed'))
    file.writeframes(signal)
    file.close()
 
def playWAVFile(filename):
    sound = pygame.mixer.Sound(filename)
    sound.play()
 
#blue sound 209Hz
bluesound = '/tmp/blue.wav'
signal = createSignal(frequency=209, duration=.4)
createWAVFile(bluesound, signal)
#yellow sound 252Hz
yellowsound = '/tmp/yellow.wav'
signal = createSignal(frequency=252, duration=.4)
createWAVFile(yellowsound, signal)
#red sound 310Hz
redsound = '/tmp/red.wav'
signal = createSignal(frequency=310, duration=.4)
createWAVFile(redsound, signal)
#green sound 415Hz
greensound = '/tmp/green.wav'
signal = createSignal(frequency=415, duration=.4)
createWAVFile(greensound, signal)
 
#losing tone 42 Hz
losingtone = '/tmp/losingtone.wav'
signal = createSignal(frequency=42, duration=3)
createWAVFile(losingtone, signal)
 
#Variables
max     = 100               #No. of rounds in game
RoundNo = 1
RED     = 1
GREEN   = 2
YELLOW  = 3
BLUE    = 4
correct = True
 
#Setup GPIO switches
GPIOSwitch=[0,7,11,13,15]
GPIO.setup(7, GPIO.IN)      #red    GPIO 7
GPIO.setup(11, GPIO.IN)     #green  GPIO 0
GPIO.setup(13, GPIO.IN)     #yellow GPIO 2
GPIO.setup(15, GPIO.IN)     #blue   GPIO 3
 
#Setup GPIO LEDs
GPIOLED=[0,12,16,18,22]
GPIO.setup(12, GPIO.OUT)    #red    GPIO 1
GPIO.setup(16, GPIO.OUT)    #green  GPIO 4
GPIO.setup(18, GPIO.OUT)    #yellow GPIO 5
GPIO.setup(22, GPIO.OUT)    #blue   GPIO 6
 
#Connect the ground to pin 6 and the positive to pin 1 (3V3)
 
#Generate a random list of LED outputs
colour=[]
for n in range(1,max+2):
    colour.append(random.choice([RED,GREEN,YELLOW,BLUE]))
 
#Function to switch LEDs on then off
def LEDout(val):
    if (val==1):
        playWAVFile(redsound)
    elif (val==2):
        playWAVFile(greensound)
    elif (val==3):
        playWAVFile(yellowsound)
    elif (val==4):
        playWAVFile(bluesound)
    while pygame.mixer.get_busy():
        GPIO.output(GPIOLED[val], True)
    GPIO.output(GPIOLED[val],False)
    time.sleep(0.15)
    return[]
 
#Function to check when switch is pressed
def SwitchChosen():
    while True:
            if (GPIO.input(GPIOSwitch[RED])):
                return RED
            if (GPIO.input(GPIOSwitch[GREEN])):
                return GREEN
            if (GPIO.input(GPIOSwitch[YELLOW])):
                return YELLOW
            if (GPIO.input(GPIOSwitch[BLUE])):
                return BLUE
 
#Function to flash LEDs after mistake
def LoserLights():
    playWAVFile(losingtone)
    for cycle1 in range(0,6):
        for cycle2 in range(1,5):
            GPIO.output(GPIOLED[cycle2], True)
        time.sleep(0.5)
        for cycle2 in range(1,5):
            GPIO.output(GPIOLED[cycle2], False)
        time.sleep(0.2)
    return[]
 
#Main routine
while correct:
    print("Round %i" %RoundNo)
    #LED cycle
    for mout in range(1,RoundNo+1):
        LEDout(colour[mout])
    #Response
    for ans in range(1,RoundNo+1):
        push=SwitchChosen()
        LEDout(push)
        if(push!=colour[ans]):
            LoserLights()
            correct = False
            print("Unlucky!")
            print("You made it to round %i" %RoundNo)
            break
    RoundNo+=1
    if (RoundNo==max+1):
        print("WOW!! You Rock dude")
        break
    time.sleep(0.5)

Anem a executar aquest codi en una Raspberry Pi 3 on hem instal.lat 2017-02-16-raspbian-jessie-lite.img, acabada d'instal.lar.

Executem el codi i solucionem els errors que ens dóna, degut a què ens manquen certes llibreries.

$ python simon_original.py 
Traceback (most recent call last):
  File "simon_original.py", line 13, in <module>
    import numpy
ImportError: No module named numpy

NumPy is the fundamental package for scientific computing with Python

Hem d'instal.lar la llibreria numpy:

$ pip install numpy

per instal.lar pip,

$ sudo apt-get install python-pip

o millor:

$ sudo apt-get install python-numpy

Tornem a executar el nostre fitxer:

$ python simon_original.py 
Traceback (most recent call last):
  File "simon_original.py", line 15, in <module>
    import pygame
ImportError: No module named pygame
python-pygame - SDL bindings for games development in Python (Python 2)
$ sudo apt-get install python-pygame

i ara ja funciona:

$ python simon_original.py 
numid=3,iface=MIXER,name='PCM Playback Route'
  ; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0
  : values=1
Round 1

creat per Joan Quintana Compte, desembre 2016

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
Institut Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines