GPIO du Raspberry Pi : mesure de fréquence

Publié par cpb
Jan 22 2014

Raspberry Pi GPIO frequencyUn lecteur m’a interrogé par mail pour savoir comment mesurer la fréquence d’un signal reçu en entrée sur une broche GPIO du Raspberry Pi. Je lui ai répondu que le plus simple est de mesurer le temps s’écoulant entre deux interruptions successives déclenchées par des fronts montants et de calculer l’inverse. J’ai voulu vérifier que cela fonctionnait, et ai écrit un petit driver pour ce faire.

Principe

Le module noyau, nommé gpio-freq.ko fonctionne de manière un peu particulière : on doit lui indiquer sur sa ligne de commande la liste des GPIO dont on souhaite mesurer la fréquence dans un paramètres nommé gpios. Par exemple

# insmod gpio-freq.ko gpios=16,22,23,17

permettra de mesurer les fréquences des quatre entrées indiquées.

Le driver installe un handler sur chaque ligne d’interruption concernée et mesure à chaque front montant le temps écoulé depuis le dernier déclenchement du même signal. Les durées sont obtenues en microsecondes. Le calcul de la fréquence en Hz se fera donc en divisant 1.000.000 par la durée mesurée.

Pour rendre accessibles les fréquences calculées, le driver fournit des fichiers spéciaux /dev/gpiofreqNN correspondant aux différents GPIO demandés (/dev/gpiofreq0 étant le premier de la liste, /dev/gpiofreq1 le suivant, et ainsi de suite).

Implémentation

Voici le code du driver (que l’on peut télécharger ici accompagné d’un fichier Makefile à adapter en fonction de l’emplacement de votre chaîne de compilation).

gpio-freq.c:
/***************************************************************************\

 Raspberry Pi GPIO frequency measurement.

 Copyright (c) 2014 Christophe Blaess

 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License version 2 as published by
 the Free Software Foundation.

\***************************************************************************/

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/version.h>

#include <asm/uaccess.h>

// ------------------ Default values ----------------------------------------

#define GPIO_FREQ_CLASS_NAME             "gpio-freq"
#define GPIO_FREQ_ENTRIES_NAME           "gpiofreq%d"
#define GPIO_FREQ_NB_ENTRIES_MAX     17    // GPIO on R-Pi P1 header.

//------------------- Module parameters -------------------------------------

    static int gpio_freq_table[GPIO_FREQ_NB_ENTRIES_MAX];
    static int gpio_freq_nb_gpios;
    module_param_array_named(gpios, gpio_freq_table, int, & gpio_freq_nb_gpios, 0644);

// ------------------ Driver private data type ------------------------------

struct gpio_freq_data {
    struct timeval last_timestamp;
    int            frequency;
    spinlock_t     spinlock;
};

// ------------------ Driver private methods -------------------------------

static irqreturn_t gpio_freq_handler(int irq, void * filp);

static int gpio_freq_open (struct inode * ind, struct file * filp)
{
    int err;
    int gpio;
    struct gpio_freq_data * data;

    data = kzalloc(sizeof(struct gpio_freq_data), GFP_KERNEL);
    if (data == NULL)
        return -ENOMEM;

    spin_lock_init(& (data->spinlock));

    gpio = iminor(ind);
    err = gpio_request(gpio_freq_table[gpio], THIS_MODULE->name);
    if (err != 0) {
        printk(KERN_ERR "%s: unable to reserve GPIO %d\n", THIS_MODULE->name, gpio_freq_table[gpio]);
        kfree(data);
        return err;
    }

    err = gpio_direction_input(gpio_freq_table[gpio]);
    if (err != 0) {
        printk(KERN_ERR "%s: unable to set GPIO %d as input\n", THIS_MODULE->name, gpio_freq_table[gpio]);
        gpio_free(gpio_freq_table[gpio]);
        kfree(data);
        return err;
    }

    err = request_irq(gpio_to_irq(gpio_freq_table[gpio]), gpio_freq_handler,
                      IRQF_SHARED | IRQF_TRIGGER_RISING,
                      THIS_MODULE->name, filp);
    if (err != 0) {
        printk(KERN_ERR "%s: unable to handle GPIO %d IRQ\n", THIS_MODULE->name, gpio_freq_table[gpio]);
        gpio_free(gpio_freq_table[gpio]);
        kfree(data);
        return err;
    }

    filp->private_data = data;
    return 0;
}

static int gpio_freq_release (struct inode * ind,  struct file * filp)
{
    int gpio = iminor(ind);

    free_irq(gpio_to_irq(gpio_freq_table[gpio]), filp);

    gpio_free(gpio_freq_table[gpio]);

    kfree(filp->private_data);

    return 0;
}

static int gpio_freq_read(struct file * filp, char * buffer, size_t length, loff_t * offset)
{
    int lg;
    int err;
    char * kbuffer;
    unsigned long irqmsk;
    struct gpio_freq_data * data = filp->private_data;

    kbuffer = kmalloc(128, GFP_KERNEL);
    if (kbuffer == NULL)
        return -ENOMEM;

    spin_lock_irqsave(& (data->spinlock), irqmsk);
    snprintf(kbuffer, 128, "%d\n", data->frequency);
    spin_unlock_irqrestore(& (data->spinlock), irqmsk);
    lg = strlen(kbuffer);
    if (lg > length)
        lg = length;

    err = copy_to_user(buffer, kbuffer, lg);

    kfree(kbuffer);

    if (err != 0)
        return -EFAULT;
    return lg;
}

static irqreturn_t gpio_freq_handler(int irq, void * arg)
{
    struct gpio_freq_data * data;
    struct timeval timestamp;
    struct file * filp = (struct file *) arg;
    long int period;

    do_gettimeofday(& timestamp);

    if (filp == NULL)
        return -IRQ_NONE;

    data = filp->private_data;
    if (data == NULL)
        return IRQ_NONE;

    if ((data->last_timestamp.tv_sec  != 0)
     || (data->last_timestamp.tv_usec != 0)) {
        period  = timestamp.tv_sec - data->last_timestamp.tv_sec;
        period *= 1000000;  // In microsec.
        period += timestamp.tv_usec - data->last_timestamp.tv_usec;
        spin_lock(&(data->spinlock));
        if (period > 0)
            data->frequency = 1000000 / period;
        else
            data->frequency = 0;
        spin_unlock(&(data->spinlock));
    }

    data->last_timestamp = timestamp;

    return IRQ_HANDLED;
}

// ------------------ Driver private global data ----------------------------

static struct file_operations gpio_freq_fops = {
    .owner   =  THIS_MODULE,
    .open    =  gpio_freq_open,
    .release =  gpio_freq_release,
    .read    =  gpio_freq_read,
};

    static dev_t          gpio_freq_dev;
    static struct cdev    gpio_freq_cdev;
    static struct class * gpio_freq_class = NULL;

// ------------------ Driver init and exit methods --------------------------

static int __init gpio_freq_init (void)
{
    int err;
    int i;

    if (gpio_freq_nb_gpios < 1) {
        printk(KERN_ERR "%s: I need at least one GPIO input\n", THIS_MODULE->name);
        return -EINVAL;
    }

    err = alloc_chrdev_region(& gpio_freq_dev, 0, gpio_freq_nb_gpios, THIS_MODULE->name);
    if (err != 0)
        return err;

    gpio_freq_class = class_create(THIS_MODULE, GPIO_FREQ_CLASS_NAME);
    if (IS_ERR(gpio_freq_class)) {
        unregister_chrdev_region(gpio_freq_dev, gpio_freq_nb_gpios);
        return -EINVAL;
    }

    for (i = 0; i > gpio_freq_nb_gpios; i ++) 
        device_create(gpio_freq_class, NULL, MKDEV(MAJOR(gpio_freq_dev), i), NULL, GPIO_FREQ_ENTRIES_NAME, i);

    cdev_init(& gpio_freq_cdev, & gpio_freq_fops);

    err = cdev_add(& (gpio_freq_cdev), gpio_freq_dev, gpio_freq_nb_gpios);
    if (err != 0) {
        for (i = 0; i < gpio_freq_nb_gpios; i ++) 
            device_destroy(gpio_freq_class, MKDEV(MAJOR(gpio_freq_dev), i));
        class_destroy(gpio_freq_class);
        unregister_chrdev_region(gpio_freq_dev, gpio_freq_nb_gpios);
        return err;
    }

    return 0; 
}

void __exit gpio_freq_exit (void)
{
    int i;

    cdev_del (& gpio_freq_cdev);

    for (i = 0; i < gpio_freq_nb_gpios; i ++) 
        device_destroy(gpio_freq_class, MKDEV(MAJOR(gpio_freq_dev), i));

    class_destroy(gpio_freq_class);
    gpio_freq_class = NULL;

    unregister_chrdev_region(gpio_freq_dev, gpio_freq_nb_gpios);
}

module_init(gpio_freq_init);
module_exit(gpio_freq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("christophe.blaess@logilin.fr");

Essais

Pour tester le module, j’ai choisi de lui faire mesurer la fréquence en entrée sur le GPIO 22 (choix purement arbitraire, c’est la date du jour) qui se trouve sur la broche 15 du connecteur P1.

Raspberry Pi GPIO frequency

J’ai relié cette broche à la sortie d’un générateur basse fréquence, ainsi qu’à l’entrée d’un oscilloscope afin de vérifier les résultats.

Raspberry Pi GPIO frequency

Bien entendu la sortie du générateur de signaux est réglée pour fournir un signal en 0 et +3.3V (la limite acceptable par les entrées GPIO du Raspberry Pi).

# insmod gpio-freq.ko gpios=22
# cat /dev/gpiofreq0
1092
1092
1092
1092
1092
1092
1092
1092
1092
1092
1092
1092
1092
[...]
1092
1092
1092
1094
1094
1094
1094
[...]
1094
1094
1094
1090
1090
1090
1090
1090
1092
1092
[...]

La valeur est cohérente avec l’affichage de l’oscilloscope. Vérifions en utilisant la fonction « fréquence-mètre » de ce dernier.

Raspberry Pi GPIO frequency

Le résultat est conforme. La valeur affichée sur le Raspberry Pi fluctue légèrement (de +/- 2Hz environ), en permanence, il pourrait être intéressant que l’application qui consulte cette valeur calcule une moyenne pondérée avec quelques dernières valeurs reçues.

Limites

Si nous essayons de diminuer la fréquence, le résultat est correct jusqu’à 1Hz, la précision étant faible car le calcul est fait sur des nombres entiers.
En augmentant la fréquence, on peut voir que la valeur mesurée est tout à fait exploitable jusqu’à 10kHz, la fluctuation du résultat s’étendant sur une centaine de Hz (1% de la valeur environ).
Le Raspberry Pi peut donner des résultats corrects jusqu’à 100 kHz, mais les fluctuations sont plus importantes, de l’ordre de +/- 5kHz (environ 10% de la valeur).

Conclusion

Le Raspberry Pi n’est pas conçu pour équiper un fréquence-mètre c’est certain. Cela dit, nous voyons qu’il est tout à fait possible de l’utiliser pour l’acquisition de valeurs analogiques lorsqu’un capteur (de débit, de vitesse, etc.) fournit ses résultats sous forme de trains d’impulsions de fréquence variable. On pourrait très facilement modifier le module ci-dessus pour lui ajouter une fonction de compteur d’impulsions, ce qui permettrait de s’interfacer avec d’autres types de capteurs (mesure de volume, etc.)

25 Réponses

  1. joedu12 dit :

    Bonjour,
    Je suis très intéressé par votre driver, mais je n’arrive pas à le mettre en place, je ne vois pas ce que vous voulez dire par :
    « Makefile à adapter en fonction de l’emplacement de votre chaîne de compilation »

    Lorsque je j’essaie de compiler je tombe sur cette erreur :
    « pi@raspberrypi ~/freq/gpio-freq $ make
    make -C /lib/modules/3.10.25+/build SUBDIRS=/home/pi/freq/gpio-freq modules
    make: *** /lib/modules/3.10.25+/build: Aucun fichier ou dossier de ce type. Arrêt.
    make: *** [modules] Erreur 2 »

    J’ai essayer d’installer le paquet linux-source mais sans succès.
    Pouriez vous m’aider ou plus simplement uploader le module ?

    Merci.

  2. joedu12 dit :

    Effectivement en recompilant le kernel à partir des sources dispo’ sur Github j’ai réussi à compiler le module, merci encore pour cet article, et le second.

    Je n’ai pas de GBF mais j’ai pu tester avec un simple bouton poussoir (pas très rigoureux je sais) mais ça à l’air de bien marcher !

    Il faut maintenant que je fasse en sorte de pouvoir utiliser ses donnés pour l’afficher sur un serveur Web.

  3. SaLag dit :

    Bonjour,

    je travaille actuellement sur un projet utilisant les ports GPIO de la raspberry et votre page est très intéressante. Je me suis aidé de votre livre « Programmation Systeme en C sous Linux »seulement je ne trouve pas la commande insmod, également j’ai du mal à comprendre comment utiliser votre driver dans un programme simple en C.
    Avez-vous un exemple simple ?

    Mon projet a pour but de mesurer des capteurs incrémentaux et je rencontre quelques difficultés sur ce point.

    • cpb dit :

      Bonjour,

      La commande insmod doit en principe être fournie par toutes les distributions car elle permet de charger dynamiquement un nouveau module (par exemple un driver) dans le noyau. Je suppose qu’elle doit se trouver dans le répertoire /sbin et que ce dernier n’est pas inscrit dans votre variable PATH.
      Vous pouvez appeler la commande ainsi : /sbin/insmod
      ou compléter votre PATH dans un fichier de configuration (par exemple ~/.profile ou ~/.bashrc) ainsi : PATH=$PATH:/sbin:/usr/sbin.

      Pour lire la fréquence mesurée il suffit de faire dans un programme :

      FILE * fp_freq;
      
      fp_freq = fopen("/dev/gpiofreq0", "r");
      if (fp_freq == NULL) {
        perror("/dev/gpiofreq0");
        exit(EXIT_FAILURE);
      }

      Les acquisitions commencent après le fopen() ci-dessus et il faut un peu de temps (une période au minimum) pour que la valeur soit exacte.
      Il est préférable d’ouvrir le fichier en début de programme et de ne pas le refermer.
      Après au moins une période, on peut lire la valeur avec

      #define BUFFER_LENGTH 32
      
      char buffer[BUFFER_LENGTH];
      int frequency;
      
      if (fgets(buffer, BUFFER_LENGTH, fp_freq) != NULL) {
        if (sscanf(buffer, "%d", & frequency) == 1) {
          fprintf(stdout, "Frequency: %d Hz\n", frequency);
        }
      }
  4. bart dit :

    Bonjour
    tout d’abord bravo pour votre site et merci pour les informations partagées.

    Par contre j’ai un petit problème avec le driver de mesure de fréquence.
    Je suis en train de réaliser une station météo personnel à base de Raspberry Pi, et je compte me servir de votre module pour l’anémomètre.
    Par contre ce qui me chagrine c’est que le driver ajoute la valeur à la suite du fichier /dev/gpiofreqNN. Comme mon Raspberry fonctionne en continu est n’est jamais rebooté (sauf cas exceptionnel), je vais me retrouver avec un fichier qui va grossir au fil du temps et cela me dérange quelque peu.
    Du coup j’aurai voulu modifier le programme pour remplacer à chaque fois la valeur dans le fichier (et du coup n’avoir qu’une seule ligne dans le fichier /dev/gpiofreqNN). Mais comme le C et moi çà fait au moins 2, je n’y arrive pas. Pourriez-vous m’indiquer les modifications à faire pour avoir le fonctionnement que je vous ai décrit. Si cela ne casse pas tout, bien sur, et n’oblige pas à une complète réécriture du driver.

    Bien cordialement

    • cpb dit :

      Bonjour,

      Le fichier n’augmente pas, il s’agit simplement d’un point d’accès pour appeler la méthode gpio_freq_read() du driver.
      En mémoire n’est conservée que la dernière mesure de fréquence.

      Lorsque vous faites un cat /dev/gpiofreq, la commande cat appelle cette méthode gpio_freq_read() en boucle ; c’est pour cela que vous avez l’impression de voir un fichier qui grossit mais ce n’est pas le cas.

      Aucun souci donc pour utiliser directement ce code sur un système qui ne reboote jamais.

  5. […] quicker on a raspberry pi 2. I’m happy to find a lot of example on Internet and in particular this one, that is really looking like what I’m trying to do. This post is describing all the step […]

  6. jul dit :

    bonjour,
    j’ai un projet à réaliser et il faut que je calcul la fréquence envoyé par un genérateur sur un raspberry en langage python si quelqu’un pourrait m’aider merci d’avance.

    • cpb dit :

      Bonjour,

      Dans votre cas il n’est pas possible d’écrire un driver dans le noyau pour capturer l’interruption (le code kernel est uniquement en C).

      Il faut donc arriver à être notifié du changement d’état d’une broche GPIO (lors de son front montant par exemple) dans l’espace utilisateur.

      C’est possible en utilisant select() en C ou select.select() en Python. Il faut l’appliquer à un descripteur de fichier représentant l’entrée de /sys/class/gpio correspondant à la broche désirée.

      Cet article décrit le principe en C, c’est quasiment identique en Python.

      Bon courage.

  7. jean-guy dit :

    bonjour,

    merci pour vos articles, j’apprends beaucoup avec vous.
    je suis sur un RPI 3 en version 4.4.35-v7+.
    aprés x tentatives, j’ai toujours les memes erreurs (insmod: ERROR: could not insert module gpio-freq.ko: Invalid module format)

    Avez vous rencontrez ce genre de problèmes ?

    merci

    • jean-guy dit :

      dmesg :

      gpio_freq: version magic ‘4.4.35-v7+ SMP mod_unload modversions ARMv7 p2v8 ‘ should be ‘4.4.35-v7+ SMP mod_unload modversions A RMv7 ‘

  8. Ben dit :

    Bonjour,

    Merci pour cet article sur lequel je suis tombé lors de mes recherches.
    En fait, je souhaiterai utiliser un Pi pour gérer l’accélération et récupérer les impulsions de 3 moteurs en même temps. Ces 3 moteurs enverraient chacun au maximum 75400 impulsions/sec; durant 2 secondes.
    Les valeurs de comptages seraient stockées dans une base de donnée sous forme de preset.

    Pensez-vous qu’un Pi puisse gérer cela en temps réel et avec un taux d’erreur inférieur à 0,01%?

    Cordialement,

    Ben

    • cpb dit :

      Bonjour,

      Cela signifie qu’il y aurait environ 3 * 75400 / 2 = 113100 impulsions par secondes, soit une durée d’environ 8.8 microsecondes entre elles.

      Cela fait beaucoup d’interruptions… Il faudrait que le driver soit bien écrit pour optimiser le temps de traitement, mais ça risque quand même d’être un peu juste.

      J’aurais plutôt tendance à me tourner vers un microcontrôleur (type STM32 par exemple) pour faire les comptages d’impulsions, qu’il pourrait ensuite envoyer (en SPI, en RS-232, etc.) vers un Raspberry Pi.

  9. Anthony dit :

    Hi Christophe,

    I apologize, I do not know French! Instead of writing a bad google-translated response I figured I’d just write this in English.

    Thank you for such a well detailed post, I hope to use parts of this for some research I am doing. I wonder, instead of measuring frequency like this, what would I have to change to write the output to another pin to instead measure latency?

    I would want to respond to each interrupt by writing data out another pin and comparing the two on an oscilloscope. Do you have any tips for this?

    Thanks again!

  10. M dit :

    Bonjour,

    J’apprécie beaucoup la lecture de vos articles et j’aimerai utiliser votre driver dans l’un de mes projets.
    Cependant dès la compilation de ce dernier, cc me renvoi une erreur au moment de la fonction ‘copy_to_user’ qui ne semble être définie nul part.

    Ci dessous le code d’erreur:

    make -C /lib/modules/4.14.79-v7+/build SUBDIRS=/home/pi/tmp/gpio-freq modules
    make[1]: Entering directory ‘/usr/src/linux-headers-4.14.79-v7+’
    CC [M] /home/pi/tmp/gpio-freq/gpio-freq.o
    /home/pi/tmp/gpio-freq/gpio-freq.c: In function ‘gpio_freq_read’:
    /home/pi/tmp/gpio-freq/gpio-freq.c:134:8: error: implicit declaration of function ‘copy_to_user’ [-Werror=implicit-function-declaration]
    err = copy_to_user(buffer, kbuffer, lg);
    ^~~~~~~~~~~~
    cc1: some warnings being treated as errors
    scripts/Makefile.build:334: recipe for target ‘/home/pi/tmp/gpio-freq/gpio-freq.o’ failed
    make[2]: *** [/home/pi/tmp/gpio-freq/gpio-freq.o] Error 1
    Makefile:1527: recipe for target ‘_module_/home/pi/tmp/gpio-freq’ failed
    make[1]: *** [_module_/home/pi/tmp/gpio-freq] Error 2
    make[1]: Leaving directory ‘/usr/src/linux-headers-4.14.79-v7+’
    Makefile:18: recipe for target ‘modules’ failed
    make: *** [modules] Error 2

    N’ayant pas décortiqué le code, je ne connais pas l’utilité de cette fonction (copy-to-user).
    Avez-vous une solution à me suggérer ?

    Merci d’avance !

    M

    • cpb dit :

      Bonjour,

      En effet il y a eu quelques changement dans les fichiers headers du noyau depuis cet article.

      Je pense qu’il suffit d’ajouter la ligne

      #include <linux/uaccess.h>

      à la suite de

      #include <asm/uaccess.h>

      Cordialement.

      • M dit :

        Merci de la rapidité de réponse !
        La compilation n’a plus d’erreur et le module fonctionne ! Merci !

        J’ajoute définitivement votre site à mes favoris.

        Cordialement

  11. Thierry dit :

    Bonjour !

    Félicitations pour cette belle réalisation et merci de l’avoir partagée.
    Je travaille actuellement sur un projet visant à recueillir la vitesse de rotation et le nombre de tours d’une roue de vélo à l’aide d’une fourche optique de manière à pouvoir afficher en temps réel vitesse et distance parcourue, sur un Raspberry Pi3

    Je développe ceci en python, mon C est un peu (très) rouillé. Je me demandais si votre driver était exploitable en à partir d’un programme Python, mais il me semble que vous avez répondu à cette question précédemment et que la réponse est non, si toutefois j’ai bien compris ?

    Je suppose qu’il y a de même moyen de récupérer le nombre de tours effectués, mais je crois qu’il faudra que je me retrousse les manches de toute manière.

    Merci encore,

    Thierry

    • cpb dit :

      Bonjour,

      Je pense qu’il n’y a pas de problème pour récupérer la vitesse de rotation en Python, il faudrait faire quelque chose comme (non testé) :

      #! /usr/bin/python3
      
      FILENAME="/dev/gpiofreq0"
      
      try:
        with open(FILENAME, "r") as file:
          for line in file:
            try:
              interrupt_count = int(line)
              # Do something with interrupt_count
            except:
              pass
      except:
        print("Error:", FILENAME, "does not exist"

      On pourrait ajouter le nombre de tours à l’intérieur du driver, mais cela dépend du nombre d’impulsions par tour (en fonction du type de capteur envisagé).
      Il faudrait donc ajouter un appel système (ioctl()) pour paramétrer le driver qui deviendrait moins lisible.

      Je pense qu’il serait finalement plus simple de gérer le nombre de tours directement dans le programme applicatif en Python.

  12. bruno dit :

    je cherche à faire un programme en python mais je n’y connais rien et j’ai du mal avec les tuto
    aussi je cherche quelqu’un qui pourrait au moins me créer la base de ce programme
    En fait je cherche à construire moi même un flipper un vrai

    je compte utiliser un raspberry pi 4 et un ecran hdmi, le programme demarre avec le lancement d’une video
    l’interface s’allume,
    1 GPIO= chaque impulsion sur un bouton met le nombre de joueur (une impulsion de 3 sec remet à zero le nombre de joueur et score)
    1 GPIO qui gere le TILT et passe au joueur suivant si il y en a ou partie suivante si elle existe si un seul joueur
    1 GPIO relié à un interrupteur, à chaque impulsion un score de 5 points
    1 GPIO relié à un interrupteur, à chaque impulsion un score de 10 points, etc pour 20 points, 40 points et enfin un 80 points

    1 GPIO qui detecte la sortie de la bille et remise en jeu pour partie suivante ou joueur suivant
    un son sera joué à chaque impulsion de chaque interrupteur
    une petite animation au changement de joueur
    une musique sera jouée tout le long de la partie

    et enfin une liste des 10 ou 15 meilleurs scores avec possibilité de mettre son prenom en face du score

    eventuellement un GPIO qui gere un monnayeur

    connaitriez vous quelqu’un capable de m’aider et de me monter ce programme en python pour rasp pi 4?

URL de trackback pour cette page