Abonnement aux commentaires

S'abonner pour recevoir les commentaires suivants par email

まこと の ブログ

MaKoTo no burogu — Journal de bord…

Aller au contenu | Aller au menu | Aller à la recherche

Mini Game Center -5-

Nous revoici pour la suite de notre étude sur le MiniGameCenter :

J'ai nommé celle-ci de cette manière pour évoquer une salle d'arcade, réunissant donc plusieurs bornes… sinon j'aurais plutôt dit « MiniBorne »…
L'important au final c'est qu'on puisse donc interagir avec plusieurs machines sans que cela ne devienne compliqué, ni que ça ressemble à une usine à gaz.
Et c'est pour cette raison que j'écris ceci… Je me suis rendu compte que mon idée de départ, à savoir comme on l'a vu lors du billet N°3, piloter chaque borne par ses GPIO se révélerait assez inadaptée, nous allons voir pourquoi.

C'est impec pour une seule borne, mais pour plusieurs on se retrouverait alors avec un tas de boutons poussoirs de partout.

  • J'ai alors pensé à les mutualiser, c'est à dire relier ensembles les GPIO respectifs de chaque Rpi et d'utiliser la masse comme sélecteur de Rpi. En effet si on débranche la masse, de tous les Rpi sauf un seul, alors on pilotera seulement celui-ci avec les boutons. Pour cela j'aurais utilisé des CD4066B (quad bilateral switch), piloté avec un Arduino, et quelques boutons supplémentaire pour choisir à quel Rpi on se serait adressé. Je n'ai dessiné que pour deux Rpi, mais on peut en rajouter presque autant qu'on veut (la limite venant du nombre d'in/out de l'arduino):


  • Et puis… bon, au final c'est plus un hack qu'une solution académique, alors je me serais orienté vers une solution à base de 74LS573 (Octal D-Type Latch with 3-STATE Outputs).


Et puis en dessinant tout ça, de vite me rendre compte que Ok, j'ai un nombre minimum de boutons poussoir, mais il va y avoir un gros tas de câbles à passer dans le faux plancher du MiniGameCenter, et des boutons à mettre quelque part, et qu'en plus de ça, si on le place en vitrine, bah on pilote rien sans avoir à l'ouvrir pour actionner les boutons… Cerise sur le gâteau, faut en plus se taper une nouvelle carte électronique à réaliser, voire à revoir le Pi-Hat pour y intégrer les 74LS573.

  • Alors pour le pilotage à travers la vitrine, j'ai expérimenté très facilement d'utiliser un capteur infrarouge sur l'Arduino et une télécommande de récup.


Ça fonctionne très bien, et j'aurais conservé l'idée à base de 74LS573 + boutons + Arduino + capteur IR, mais non, tout ce câblage et une PCB à faire… La flemme, il me fallait continuer à expérimenter d'autres solutions, et puis j'ai repensé au fait que le Rpi Zero W embarquait un module Bluetooth

Pilotage Bluetooth avec un module HC-05 :

J'ai donc utilisé un module HC-05, interfacé à un Arduino Pro Mini 3,3V, et un capteur IR pour tester la chose.
- Côté Arduino :

- Le capteur IR est câblé en pin 12 et alimenté en 3,3V par l'Arduino.
- Le module HC-05 est câblé RxD en pin 8, TxD en pin 7, et alimenté en 5 Volts par l'USB du programmateur.

Attention les signaux de ce module travaillent effectivement en 3,3V, c'est pourquoi j'ai fait le choix d'un Arduino 3,3V, comme c'était le cas aussi pour le pilotage des GPIO du Rpi car celui-ci attend du 3,3V (petit « détail » à ne jamais oublier !).

Voici le code Arduino qui reçoit les commandes infra-rouge et les envoies sur la liaison série bluetooth :

#include <IRremote.h>
#include <SoftwareSerial.h>

//################
//# DÉCLARATIONS #
//################
//——— Prototypes Fonctions ———//
void arcade(void);
void decodage_touche(void);

//——— Pins ———//
int RECV_PIN = 12;
#define pinBluetooth_rx 7 // Broche 11 en tant que RX, à raccorder sur TX du HC-05
#define pinBluetooth_tx 8 // Broche 10 en tant que TX, à raccorder sur RX du HC-05
SoftwareSerial SerialBluetooth(pinBluetooth_rx, pinBluetooth_tx);

//——— Lib ———//
IRrecv irrecv(RECV_PIN);
decode_results results;

//#########
//# SETUP #
//#########
void setup()
{
  Serial.begin(9600);
  // In case the interrupt driver crashes on setup, give a clue
  // to the user what's going on.
  Serial.println("Enabling IRin");
  irrecv.enableIRIn(); // Start the receiver
  irrecv.blink13(true);
  Serial.println("Enabled IRin");
  
  // INITIALISATION PORT SERIAL BLUETOOTH
  pinMode(pinBluetooth_rx, INPUT);
  pinMode(pinBluetooth_tx, OUTPUT);
  SerialBluetooth.begin(9600);
  SerialBluetooth.println("Hello Viewer");
 }

//#############
//# FONCTIONS #
//#############
void arcade() {
  switch (results.value) {
    case 0xF730CF:  // Vol-
      Serial.println("R2 — Vol-");
      SerialBluetooth.println("R2"); // mot envoyé pour commande en bluetooth
      results.value =0;           // empéche la répétition de touche
      break;
      
    case 0xF7B04F:  // Vol+
      Serial.println("G2 — Vol+");
      SerialBluetooth.println("G2"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;
      
    case 0xF7708F:  // Lum-
      Serial.println("B2 — Lum-");
      SerialBluetooth.println("B2"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;
      
    case 0xF7F00F:  // Lum+
      Serial.println("STROBE — Lum+");
      SerialBluetooth.println("STROBE"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;  
      
    case 0xF708F7:  // <<
      Serial.println("R3 — <<");
      SerialBluetooth.println("R3"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;
      
    case 0xF78877:  // >>
      Serial.println("G3 — >>");
      SerialBluetooth.println("G3"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;

    case 0xF748B7:  // Précédent
      Serial.println("B3 — Précédent");
      SerialBluetooth.println("B3"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;   

    case 0xF7C837:  // Suivant
      Serial.println("Fade — Suivant");
      SerialBluetooth.println("FADE"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;
          
    case 0xF728D7:  // Play/pause
      Serial.println("R4 — Play/pause");
      SerialBluetooth.println("R4"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;  

    case 0xF7A857:  // Stop
      Serial.println("G4 — Stop");
      SerialBluetooth.println("G4"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;  
      
    case 0xF7E817:  // Power
      Serial.println("Smooth — Power");
      SerialBluetooth.println("SMOOTH"); // mot envoyé pour commande en bluetooth
      results.value =0;
      break;  
         
    default:
      // if nothing else matches, do the default
      // default is optional
      break;
  }  
}
// Mapping télécommande :
//                  ON      OFF
//F700FF  F7807F  F740BF  F7C03F
//  R0      G0      B0      W
//F720DF  F7A05F  F7609F  F7E01F
//  R1      G1      B1     Flash
//F710EF  F7906F  F750AF  F7D02F
//  R2      G2      B2     Strobe
//F730CF  F7B04F  F7708F  F7F00F
//  R3      G3      B3     Fade
//F708F7  F78877  F748B7  F7C837
//  R4      G4      B4     Smooth
//F728D7  F7A857  F76897  F7E817

void decodage_touche() {
    if (irrecv.decode(&results)) {
//    Serial.println(results.value, HEX);
    irrecv.resume(); // Receive the next value
  }
}

//#############
//# PROGRAMME #
//#############
void loop() {
  decodage_touche();  // pour récupérer le code des touches
  arcade();
  delay(100);
}


- Côté Raspberry :

  • Prérequis, installation du Bluetooth sur le Raspberry :
sudo apt install bluetooth pi-bluetooth blueman bluez-tools
  • Pour vérifier on peut lancer la commande permettant d'obtenir les informations sur le bluetooth embarqué du Rpi :
hciconfig
  • Ensuite on va récupérer l'adresse du module HC-05 dont on aura besoin pour la suite, en lançant le programme bluetoothctl :
bluetoothctl
  • On entre alors quelques commande pour y parvenir :
[bluetooth]# agent on
[bluetooth]# power on
[bluetooth]# scan on
  • Là il faut patienter jusqu'à voir passer le module :
Discovery started
[…]
[NEW] Device 00:19:09:11:16:11 HC-05
  • Astuce, si jamais on veut ré-accéder à cette info, car une fois scanné, l'appareil est mémorisé et n'apparaîtra plus. Pour le lister il faut faire :
[bluetooth]# devices

Pour sortir, taper « Exit ».

  • Une commande utile aussi pour « pinger » un appareil et voir s'il est en état de répondre :
sudo l2ping 00:19:09:11:16:11
Ping: 00:19:09:11:16:11 from B8:27:EB:F2:84:96 (data size 44) ...
44 bytes from 00:19:09:11:16:11 id 0 time 41.76ms
44 bytes from 00:19:09:11:16:11 id 1 time 9.85ms
44 bytes from 00:19:09:11:16:11 id 2 time 12.04ms
  • En cas de soucis on pourra éventuellement utiliser ces commandes pour relancer le pilote :
sudo service bluetooth stop
sudo service bluetooth start
  • Bien, on va pouvoir maintenant interfacer le HC-05 et le Raspberry à l'aide de ces deux commandes :
bt-agent -c NoInputNoOutput -p /home/pi/bluethooth.cfg &
sudo rfcomm connect hci0 00:19:09:11:16:11 &

- La première permet de lancer l'agent bluetooth dans un mode non interactif, pour lequel on spécifie les paramètres dans le fichier bluethooth.cfg qui contient donc l'adresse et le code pin du HC-05 (d'usine c'est 1234).
- La seconde initie la connexion série entre les deux appareils.

00:19:09:11:16:11 1234
  • Indication, quand la connexion n'est pas établie, la led du module clignote en permanence, alors que si la connexion est ok, le module clignote 2 fois toutes les 5 secondes.
  • Pour le code Python, nous aurons besoin de la librairie Serial :
sudo apt install python-serial python3-serial
  • Voici le code python qui écoute la liaison série bluetooth, et lorsqu'il reçoit un mot correspondant à une touche, effectue l'action demandée.

Par exemple, si on appuie sur la touche de la télécommande IR identifée comme étant « 0xF748B7 » par l'Arduino, celui-ci envoie alors le mot « B3 » sur la liaison série bluetooth. Le programme python reçoit le mot, et demandera à Omxplayer de jouer la vidéo précédente.

#!/usr/bin/env python2.7
# coding: utf-8
# Compatible Python3
# VideoOMXcontrol_GPIO_Multifonctions_Playlist_Bluetooth.py
# Programme pour RaspberryPi
# Joue une playliste de vidéos avec omxplayer
# Pilotage des vidéos et de la playliste avec une télécommande IR via un Arduino + bluetooth

##################################
# Import des modules nécessaires #
##################################
from omxcontrol import *
import subprocess
import RPi.GPIO as GPIO
import time
import serial

#############
# Init GPIO #
#############
PIN_BRIGHTNESS = 12     # GPIO18, PWM de réglage de la luminosité
PIN_POWEROFF = 40       # GPIO21, Uniquement PowerOff logiciel. Le powerOn, via Arduino + switch RpiOnOff

GPIO.setmode(GPIO.BOARD) ## Use board pin numbering
GPIO.setup(PIN_BRIGHTNESS, GPIO.OUT)
GPIO.setup(PIN_POWEROFF, GPIO.OUT)

bluetoothSerial = serial.Serial("/dev/rfcomm0",baudrate=115200)
print("Bluetooth connected")

#####################
# Ressources Vidéos #
#####################
def Selection(i):
    switcher={
        0:'/home/pi/deathsml_15-07-2015_1cc_h264-21.mp4',
        1:'/home/pi/dodonpachi_23-02-2014_montage.mp4',
        2:'/home/pi/espgal_26-02-2015_1cc_x264_crf23.mp4',
        3:'/home/pi/galaxian.mp4',
        }
    return switcher.get(i,"Séléction Invalide")

#############
# Variables #
#############
vid = 0
brightness = 100
pwm = GPIO.PWM(PIN_BRIGHTNESS, 130) # channel et frequence en Hz, meilleur résultat à 120 et 130Hz

##########################
# Démarrage (facultatif) #
##########################
os.system('sudo pkill omxplayer')       # S'assure qu'aucune instance omxplayer ne tourne encore, en cas de plantage
# omxplayer /home/pi/deathsml_15-07-2015_1cc_h264-21.mp4 --aspect-mode stretch -o local
pwm.start(brightness)	# Démarrage de la PWM du rétroéclairage LCD au max (100)

###############################
# Fonctions pour Télécommande #
###############################

def bouton_VOLUMEMOINS():
    # baisse le volume
    print("Volume -")
    omx.action(OmxControl.ACTION_DECREASE_VOLUME)

def bouton_VOLUMEPLUS():
    # augmente le volume
    print("Volume +")
    omx.action(OmxControl.ACTION_INCREASE_VOLUME)

def bouton_LUMMOINS():
    global brightness
    # Baisser la luminosité de l'écran LCD
    brightness = brightness - 10
    if brightness <= 10:
        brightness = 10
        print("Luminosité baissée au max")
    print("Baisse la luminosite :", brightness)
    pwm.ChangeDutyCycle(brightness)

def bouton_LUMPLUS():
    global brightness
    # Augmenter la luminosité de l'écran LCD
    brightness = brightness + 10
    if brightness >= 100:
        brightness = 100
        print("Luminosité augmentée au max")
    print("Augmente la luminosite :", brightness)
    pwm.ChangeDutyCycle(brightness)

def bouton_BACK():
    # un petit saut un arriére dans la vidéo
    print('<<')
    omx.action(OmxControl.ACTION_SEEK_BACK_SMALL)

def bouton_PRECEDENT():
    global vid
    # Stopper la lecture et tombe donc en erreur via Try > Except de la boucle principale
    print('Vidéo Précédente')
    omx.action(OmxControl.ACTION_EXIT)
    vid = vid - 1       # pour lire la vidéo précédente
    if vid == -1:
        vid = 3
    print('vid : ',vid)
    omx.action(OmxControl.ACTION_PAUSE)

def bouton_FORWARD():
    # un petit saut en avant dans la vidéo
    print('>>')
    omx.action(OmxControl.ACTION_SEEK_FORWARD_SMALL)

def bouton_SUIVANT():
    global vid
    # Stopper la lecture et tombe donc en erreur via Try > Except de la boucle principale
    print('Vidéo Suivante')
    omx.action(OmxControl.ACTION_EXIT)
    vid = vid + 1       # pour lire la vidéo suivante
    if vid == 4:
        vid = 0
    print('vid : ',vid)
    omx.action(OmxControl.ACTION_PAUSE)

def bouton_PLAY():
    # Pause/Play la vidéo
    print('Pause/Play')
    omx.action(OmxControl.ACTION_PAUSE)

def bouton_STOP():
    # Stoppe la vidéo
    print('Stop')
    omx.action(OmxControl.ACTION_EXIT)

def bouton_POWEROFF():
    # activer le gpio21
    GPIO.output(PIN_POWEROFF, GPIO.HIGH)
    print('PowerOFF')

#####################
# Boucle Principale #
#####################
while True:
    try:
        omx = OmxControl()      # appel librairie OmxControl

        data = bluetoothSerial.readline()	# donnée reçue via bluetooth
        print(data)
        print(type(data))	# Si python2 c'est un string, si python3 c'est un byte
        print('debut',data,'fin')	# Permet de révéler que la valeur envoyer présent \r\n en fin de mot
        time.sleep(0.1)

        if data.decode("utf-8") == 'R2\r\n' :	# .decode("utf-8") pour convertir le byte en string en python3 (pas d'impact sur le résultat en python2)
            bouton_VOLUMEMOINS() # Vol-
        elif data.decode("utf-8") == 'G2\r\n':
            bouton_VOLUMEPLUS()	# Vol+
        elif data.decode("utf-8") == 'B2\r\n':
            bouton_LUMMOINS()	# Lum-
        elif data.decode("utf-8") == 'STROBE\r\n':
            bouton_LUMPLUS()	# lum+
        elif data.decode("utf-8") == 'R3\r\n':
            bouton_BACK()	# <<
        elif data.decode("utf-8") == 'G3\r\n':
            bouton_FORWARD()	# >>
        elif data.decode("utf-8") == 'B3\r\n':
            bouton_PRECEDENT()	# précédent
        elif data.decode("utf-8") == 'FADE\r\n':
            bouton_SUIVANT()	# suivant
        elif data.decode("utf-8") == 'R4\r\n':
            bouton_PLAY()	# Play/Pause
        elif data.decode("utf-8") == 'G4\r\n':
            bouton_STOP()	# Stop
        elif data.decode("utf-8") == 'SMOOTH\r\n':
            bouton_POWEROFF()	# Power off

    except OmxControlError as ex:       # si le controle ne voit plus D-Bus, relance la vidéo
        print("ERREUR de contrôle D-Bus, relance de la vidéo")
        print('Selection : ',Selection(vid))
        subprocess.Popen(['omxplayer','--aspect-mode', 'stretch', '-o', 'local', Selection(vid)], stdin=subprocess.PIPE)
        time.sleep(3)   # tempo pour laisser le temps au player vidéo de démarrer
        omx = OmxControl()      # appel librairie OmxControl
        omx.action(OmxControl.ACTION_DECREASE_VOLUME)
        omx.action(OmxControl.ACTION_DECREASE_VOLUME)
        omx.action(OmxControl.ACTION_DECREASE_VOLUME)
        omx.action(OmxControl.ACTION_DECREASE_VOLUME)
        omx.action(OmxControl.ACTION_DECREASE_VOLUME)
        omx.action(OmxControl.ACTION_DECREASE_VOLUME)	# -18 db

# on réinitialise les ports GPIO en sortie de script
GPIO.cleanup()

#####################################################
# Fonctions disponible dans la librairie OmxControl #
#####################################################
#ACTION_DECREASE_SPEED
#ACTION_INCREASE_SPEED
#ACTION_REWIND
#ACTION_FAST_FORWARD
#ACTION_SHOW_INFO
#ACTION_PREVIOUS_AUDIO
#ACTION_NEXT_AUDIO
#ACTION_PREVIOUS_CHAPTER
#ACTION_NEXT_CHAPTER
#ACTION_PREVIOUS_SUBTITLE
#ACTION_NEXT_SUBTITLE
#ACTION_TOGGLE_SUBTITLE
#ACTION_DECREASE_SUBTITLE_DELAY
#ACTION_INCREASE_SUBTITLE_DELAY
#ACTION_EXIT
#ACTION_PAUSE
#ACTION_DECREASE_VOLUME
#ACTION_INCREASE_VOLUME
#ACTION_SEEK_BACK_SMALL
#ACTION_SEEK_FORWARD_SMALL
#ACTION_SEEK_BACK_LARGE
#ACTION_SEEK_FORWARD_LARGE
#ACTION_SEEK_RELATIVE
#ACTION_SEEK_ABSOLUTE
#ACTION_STEP
#ACTION_BLANK
#ACTION_MOVE_VIDEO
#ACTION_HIDE_VIDEO
#ACTION_UNHIDE_VIDEO
#ACTION_HIDE_SUBTITLES



  • Pour automatiser le fonctionnement de la borne, on peut mettre ceci dans le script de démarrage du programme python arcade.sh :
#!/bin/bash

# Init de l'agent Bluetooth sans interaction (code-pin)
bt-agent -c NoInputNoOutput -p /home/pi/bluethooth.cfg &

# init de l'écran LCD
/usr/local/bin/fbcp &

# démarrage de la connexion série via Bluetooth
sudo rfcomm connect hci0 00:19:09:11:16:11 &
sleep 7

# Lancement du programme
/usr/bin/python3 /home/pi/omxcontrol/VideoOMXcontrol_GPIO_Multifonctions_Playlist_Bluetooth.py
  • Une petite démo en vidéo :

Les hauts parleurs qui grésillent, le « Power ON » impossible, et surtout de me rendre compte que malgré tous mes efforts, pas moyen de piloter plusieurs Rpi avec ce matériél (liaison série exclusive, module qui ne s'appaire qu'avec un seul Rpi à la fois), peut-être avec d'autre types de module ?
Bref, ce n'est pas encore la bonne méthode, ou du moins elle ne me convient pas, c'était intéressant, et je vais continuer à tester des trucs…

À suivre…

Ressources :
- https://www.alex-design.fr/Projets-R-A/Douche-connectee/Connection-bluetooth-entre-Arduino-et-Raspberry-Pi-3
- https://github.com/Pi4IoT/Interfaces/tree/master/bluetooth
- https://stackoverflow.com/questions/34709583/bluetoothctl-set-passkey
- https://www.kynetics.com/docs/2018/pairing_agents_bluez/
- https://www.youtube.com/watch?v=lEFRYvNjsWg

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

Fil des commentaires de ce billet