Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workshop-Day: Play from MicroProcesseur with Bluetooth #292

Open
EloiStree opened this issue Nov 21, 2024 · 7 comments
Open

Workshop-Day: Play from MicroProcesseur with Bluetooth #292

EloiStree opened this issue Nov 21, 2024 · 7 comments

Comments

@EloiStree
Copy link
Owner

EloiStree commented Nov 21, 2024

Dans l'atelier précédent, nous avons vu comment :

  • Lire des joysticks dans Unity3D.
  • Créer nos propres joysticks avec un Raspberry Pi Pico W.
  • Concevoir des boutons avec des données numériques et analogiques.

Dans cet atelier, nous allons apprendre à utiliser le Bluetooth et les modules HC06/HC05 pour injecter des commandes sur un PC, et donc dans un jeu.


Code

Via Leonardo pour le XInput

Arduino dispose d’une bibliothèque permettant de transformer un Arduino Micro ou un Arduino Leonardo en manette Xbox 360.

  • En ajoutant un module HC06, vous obtenez une manette utilisable pour remapper ou tester vos jeux en QA.
  • En ajoutant un module Brook, vous obtenez une manette compatible avec les Xbox officielles.

Vous pouvez trouver le code ici :

Pour cet atelier, j’ai acheté 4 Leonardo. Je fournirai une adresse MAC HC06 à chaque équipe.
À vous de décider : coopérez ou affrontez-vous dans des jeux à 4 joueurs !

Voir comment configurer un Arduino Leonardo en manette Xbox XInput :


Via ESP32 pour le BLE

J’ai remarqué que beaucoup d’entre vous jouent à des jeux répétitifs en classe 😋.
Aucune critique ici ! Explorons comment utiliser un ESP32 pour automatiser certaines tâches ennuyeuses pour vous.

Le code est disponible ici :


Via Raspberry Pi Pico W

Si vous voulez jouer à Warcraft, vous pourriez risquer d’installer des logiciels sur votre PC.
Mais une alternative consiste à utiliser un Raspberry Pi Pico W pour simuler un clavier et une souris.

Au lieu d'utiliser Scratch To Warcraft Python code comme dans l’atelier précédent, essayons cette fois-ci de simuler directement un clavier et une souris.

Le code se trouve ici :


Wi-Fi et Python

Notez que, si le matériel permet une injection sans installation, vous pouvez également simuler tous les inputs d’un PC en utilisant Python ou C# dans Unity, si vous avez la main sur le PC.

Voici quelques exemples :


Dans cet atelier, nous allons jouer à TowerFall via un ESP32 dans le but de pratiquer l'utilisation du HC05.
image
https://store.steampowered.com/app/251470/TowerFall_Ascension/

image
https://youtu.be/iotmElvdsKU?t=1

Les HC05 et HC06 sont des petits composants de communication.
En résumé, ils permettent d’envoyer des caractères UTF8 entre deux appareils.
Si vous vous débrouillez bien avec ça, vous pouvez donc interagir entre des logiciels et de l’électronique via le Bluetooth.

Il y a aussi les TTL, qui permettent cela via USB, mais nous n’en utiliserons pas ici car c’est spécifique à des cas plus limités que le HC05.
image

Dans notre cas, nous allons pratiquer sur un ESP32 :

Pour communiquer entre le HC05 et l'ESP, nous allons utiliser 4 câbles.
image

  • Clair : pour fournir 3 volts au HC05
  • Sombre : pour fournir une sortie électrique
  • Deux de couleur :
    • Le Tx sur le Rx (ESP32 Tx est le 17)
    • Le Rx sur le Tx (ESP32 Rx est le 16)

Les deux autres câbles du HC05 servent à changer le nom, le mot de passe et le baudrate.

Dans un code Arduino, nous pouvons lire et envoyé des messages sur le Serial Port ainsi:

#define RXD2 16   // GPIO16 (U2RXD) Not Setable on the ESP32
#define TXD2 17   // GPIO17 (U2TXD) Not Setable on the ESP32

// Lancé une fois au démarrage
void setup(){
//...
     // Se Port permet de communiquer depuis le PC sur lequel l'ESP est branché
      Serial.begin(115200);
      // Comme on fait de l'Android et du Quest 3 l'on créé un port personnalisé pour le HC05
      Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);  
//...
}
//...
//Un While True : 
void loop()
{

  // Y a t il un message sur le port de l'ordinateur
  if (Serial.available() > 0) {
    // Une manière de lire avec des retours de ligne
    if(m_readingMode== TEXT_INTEGER_MODE){
        // Lit les messages qui arrivent et temporise jusqu'à une reception d'un retour de ligne.
        String input = Serial.readStringUntil('\n'); // Read the input until a newline character
        // Une fois réceptionné, envoyer au développeur le text.
        serialReceivedString(input);
    }
    // Une manière de lire "brute" byte-by-byte
    else{
        // Lit les bytes un par un
        byte incomingByte = Serial.read();
        // Envoi le byte au développeur.
        serialReceivedByte(incomingByte);
    }
  }
  // On recommence pour le Bluetooth avec le HC05. 
  if (Serial2.available() > 0) {
    if(m_readingMode== TEXT_INTEGER_MODE){
        String input = Serial2.readStringUntil('\n'); // Read the input until a newline character
        serialReceivedString(input);
        if(m_return_byte_received){
            Serial2.write('\n');
        }
    }
    else{
        byte incomingByte = Serial2.read();
        serialReceivedByte(incomingByte);
        if(m_return_byte_received){
            Serial2.write(incomingByte);
        }
    }
   // Permet de ne pas surcharger l'appareil.
   // Attend une milliseconde.
   delay(1);
  }
//...

En Circuit Python, cela ressebmle au code suivant:

import board
import busio

""" Allow to listen from on UART and write to another UART """    
class HCTTL:
    def __init__(self):
        ################ UART DEFINITION ################
        # UART0 is used for TTL communication with your computer as Serial Port
        self.uart0_ttl = busio.UART(board.GP0, board.GP1, baudrate=9600, bits=8, parity=None, stop=1)
        # UART1 is used for communication with the HC-05 Bluetooth with Android and Quest3
        self.uart1_hc05 = busio.UART(board.GP4, board.GP5, baudrate=9600, bits=8, parity=None, stop=1)
        self.char_left = ' '
        self.char_right = ' '
        
    def push_text_to_hc05(self, text:str, end_line=True):
        if end_line:
            self.uart1_hc05.write(bytes(text + "\n", "utf-8"))
        else:
            self.uart1_hc05.write(bytes(text, "utf-8"))

    def push_text_to_ttl(self, text:str, end_line=True):
        if end_line:
            self.uart0_ttl.write(bytes(text + "\n", "utf-8"))
        else:
            self.uart0_ttl.write(bytes(text, "utf-8"))

    def read(self):
        return self.uart1_hc05.read()

    def write(self, data):
        self.uart0_ttl.write(data)
        
    def get_double_char(self):
        return self.char_left, self.char_right
    
    def is_char_right_digit(self):
        return self.char_right.isdigit()
    def get_char_left(self):
        return self.char_left
    def get_char_right(self):
        return self.char_right
    
    def read_and_return_if_right_is_digit(self):      
        
        ## I consider that you use only once UART at the same time"""                  
        b= None
        if self.uart0_ttl.in_waiting > 0:
            b = self.uart0_ttl.read(1)
            if b:
                self.uart0_ttl.write(b)
                
        if self.uart1_hc05.in_waiting > 0:
            b = self.uart1_hc05.read(1)
            if b:
                self.uart1_hc05.write(b)
        
        if b:
            print(b)
            try:
                c = b.decode('utf-8')
                self.char_left=self.char_right
                self.char_right=c
                return c.isdigit()
            except Exception as e:
                print("An error occurred:", e)
        
        return False

Nous n'allons pas coder ces parties-là. Notre but est de pratiquer l'utilisation du HC05.

Si vous souhaitez voir à quoi peut ressembler le code sur Arduino :

Si vous désirez faire de même en Circuit Python :

L'important est de trouver un moyen de communication qui soit le plus court possible, car la transmission d'informations via Bluetooth est très lente (comparée à la vitesse électrique dans un circuit électrique).

Nous allons donc utiliser des drapeaux ^^
Image
👉 [Vidéo YouTube : Stratégies Bluetooth avec des drapeaux](https://www.youtube.com/watch?v=XKWrbB5th60)

Les drapeaux 🚩

  • 🚩🚩🏴 : Lancer l'attaque
  • 🚩🚩🏳️ : Retirer les troupes
  • 🏁🚩🏴 : Avancer les archers
  • 🏁🚩🏳️ : Protéger les archers
  • 🚩🏁🏴 : Envoyer la cavalerie
  • 🚩🏁🏳️ : Retirer la cavalerie de la zone de combat

Ce que nous venons de créer, c'est un index. Une table d'index.

Vous en connaissez sûrement une que vous utilisez souvent sur votre téléphone :
👉 [Table d'index des emojis 🍃🍂🍁](https://emojipedia.org/four-leaf-clover)

En informatique, les tables d'index sont partout, y compris dans ce texte que vous lisez actuellement :
👉 [Unicode et UTF-8](https://devblogs.microsoft.com/commandline/windows-command-line-unicode-and-utf-8-output-text-buffer)


Les bases : comment ça fonctionne ?

  • 0000 0001 : un octet (byte) qui vaut 1
  • 1111 1111 : un octet qui vaut 255

Face à la diversité des alphabets sur Terre, les Américains ont rapidement compris qu'ils n'étaient pas seuls. Ainsi, nous avons créé des tables pour les Européens avec 2 octets.

Mais avec 20+ langues européennes (et des langues mortes), il a fallu passer à 3 octets. Puis, en intégrant les langues asiatiques, nous sommes allés jusqu'à 4 octets, offrant ainsi 4 milliards de symboles.

👉 C'est ce qu'on appelle l'Unicode 🧙‍♂️.


Optimisation : exemples d'utilisation

Si votre projet est petit et ne nécessite que 50 actions, vous pouvez utiliser des lettres et des chiffres :

  • A : Allumer la pin 1
  • a : Désactiver la pin 1
  • B : Presser la touche "A" du clavier
  • b : Relâcher la touche "A" du clavier
  • C : Augmenter le volume
  • c : Diminuer le volume

Cela offre 50 à 80 possibilités.

En ajoutant une deuxième lettre, vous obtenez un système un peu plus complexe, mais avec 2500 à 6400 possibilités, suffisant pour de nombreux projets.

Exemple : Piano

Pour les touches, cela suffit, mais pas pour les vélocités. Les développeurs compressent alors l'information sur 3 octets :

  • 1 octet de type
  • 2 octets de contenu (note et vélocité)

👉 [Exemple compressé de données MIDI](#7)

Pour les manettes, un format similaire est utilisé. Par exemple :

  • 1899887766 : Code de 4 octets décrivant précisément l'état d'une manette.
    • 18 : Type (manette)
    • 99 : Axe horizontal gauche
    • 88 : Axe vertical gauche
    • 77 : Axe horizontal droit
    • 66 : Axe vertical droit

Cela peut aussi fonctionner pour les boutons :

  • 1711111111 : Boutons enfoncés (A, B, X, Y, etc.).

👉 [Format compressé pour manettes](#298)


Bluetooth Electronics

Matériel requis :

Applications utiles :

  1. Android : [Téléchargez Bluetooth Electronics](https://play.google.com/store/apps/details?id=com.keuwl.arduinobluetooth&hl=en)
    Application Android

  2. Windows : Utilisez un logiciel de port série.
    👉 [Exemple : Serial Port](Software: Serial Debug Assitant #299)


Étapes de configuration

  1. Branchez le HC05 aux pins de votre microcontrôleur (Arduino, Pico W, ESP32, etc.).
    Schéma de branchement

  2. Alimentation : Lorsque le HC05 est alimenté, il clignote rouge (mode "découvrable").

  3. Pairage : Depuis Android ou Windows, trouvez l'appareil (ID commençant par 98:D3...) et entrez le code 1234.
    Pairage

  4. Connexion : Sélectionnez le HC05 dans votre application et connectez-vous.
    Connexion Bluetooth


Félicitations ! 🧙‍♂️

Vous avez établi votre première connexion entre un téléphone et un composant électronique ⚡🔌 !
N'est-ce pas magnifique ? 😋
Félicitations

Maintenant que nous l'avons connecté, essayons de lui faire effectuer une action. Voici deux exemples de tableaux.

Pour des gamepads génériques :

image

Pour la Xbox :

image


Comme nous voulons simuler des touches de clavier, des manettes et des activations de pins,
je vous propose d'utiliser 4 caractères avec un retour à la ligne \n.

Vous retrouverez la convention sur :
[github.com/EloiStree/2024_08_29_ScratchToWarcraft](https://github.com/EloiStree/2024_08_29_ScratchToWarcraft?tab=readme-ov-file#touches-disponibles)
image


Version pour Xbox (XInput) :

image
[Voir sur GitHub](https://github.com/EloiStree/2024_08_29_ScratchToWarcraft/blob/main/README.md#xbox-xinput-version)

Malheureusement, vous ne pouvez pas simuler une manette XInput et un clavier en même temps.
Vous pouvez toutefois simuler une manette classique en même temps qu'un clavier, ainsi que d'autres périphériques.

Pour cet atelier, je propose de nous concentrer sur XInput.


Ajout d'un bouton :

image

Lorsque j'appuie sur le bouton jaune, il envoie Y pour appuyer et y pour relâcher.
(Y pour Yellow, G pour Green, B pour Blue, R pour Red.)

Si on laisse ainsi, cela fonctionnera mais avec un délai de 200 à 500 millisecondes,
le temps que l'Arduino attende une fin de ligne \n.

Ajoutons une fin de ligne explicite :
image

On peut soit utiliser la convention donnée :
Exemple pour A : 1300 pour appuyer, 2300 pour relâcher.
Soit utiliser la convention propre à l'application, pour expérimenter.


Exemple de code pour les boutons :

void traditionalTextCommand(String text) {
    text.trim();
    Serial.print("CMD:");
    Serial.println(text);

    // Commandes Bluetooth Electronics
    if (text == "G") pressA(true);
    if (text == "g") pressA(false);
    if (text == "Y") pressY(true);
    if (text == "y") pressY(false);
    if (text == "B") pressX(true);
    if (text == "b") pressX(false);
    if (text == "R") pressB(true);
    if (text == "r") pressB(false);
    if (text == "RECORD") recordStart();
    if (text == "record") recordStop();
}

Ajout des flèches :

N'oubliez pas d'ajouter les retours à la ligne.

image
image

Exemple de code pour un D-Pad à 4 ou 8 directions :

// Pour un D-Pad à 4 flèches
if (text == "1") pressArrowN();
else if (text == "2") pressArrowE();
else if (text == "3") pressArrowS();
else if (text == "4") pressArrowW();
else if (text == "0") releaseDPad();

// Pour un D-Pad à 8 flèches
else if (text == "5") pressArrowNE();
else if (text == "6") pressArrowSE();
else if (text == "7") pressArrowSW();
else if (text == "8") pressArrowNW();

Note : Ce code ne fonctionnera pas tel quel si le texte est convertible en entier.
En effet, il sera interprété comme une valeur numérique et traité différemment.
Je vous invite donc à utiliser des chiffres comme suit :

Label Press Release
Release D-pad 1310 2310
Press arrow north 1311 2311
Press arrow northeast 1312 2312
Press arrow east 1313 2313
Press arrow southeast 1314 2314
Press arrow south 1315 2315
Press arrow southwest 1316 2316
Press arrow west 1317 2317
Press arrow northwest 1318 2318

Ajout d'un joystick :

Nous rencontrons ici une difficulté : deux joysticks nécessitent une gestion différente.

Pour le second joystick, nous allons préfixer les données avec R
et ajouter une fin de ligne \n pour chaque commande.
image


Test du terminal :

Ajoutons un terminal pour visualiser les commandes.
image

Connectez-vous à votre HC05, accédez au menu principal et cliquez sur "Run" pour lancer les commandes.

image
image


Avec cela, vous pouvez tester vos commandes sur un terminal Bluetooth et observer leur effet en temps réel. 🎉

Notre ami GPT 🤖 propose ceci :

void ParseIfContainsJoystickFromBlueElec(String text){
    // 🤖 Généré par GPT, non testé encore.
    // Joystick gauche  X50Y50 de 0 à 100
    // Joystick gauche  LX50Y50 de 0 à 100
    // Joystick droit RX50Y50 de 0 à 100
    bool isFound = false;
    float x = 0;
    float y = 0;
    // Vérifier si la chaîne commence par LX, RX ou simplement X
    if (text.startsWith("LX")) {
        isFound = true;
        int xIndex = text.indexOf('X') + 1; // Après LX
        int yIndex = text.indexOf('Y', xIndex) + 1;
        if (xIndex > 0 && yIndex > 0) {
            x = text.substring(xIndex, text.indexOf('Y', xIndex)).toFloat();
            y = text.substring(yIndex).toFloat();
            setLeftHorizontal(x);
            setLeftVertical(y);
        }
    } 
    else if (text.startsWith("RX")) {
        isFound = true;
        int xIndex = text.indexOf('X') + 1; // Après RX
        int yIndex = text.indexOf('Y', xIndex) + 1;

        if (xIndex > 0 && yIndex > 0) {
            x = text.substring(xIndex, text.indexOf('Y', xIndex)).toFloat();
            y = text.substring(yIndex).toFloat();
            setRightHorizontal(x);
            setRightVertical(y);
        }
    } 
    else if (text.startsWith("X")) {
        isFound = true;
        int xIndex = text.indexOf('X') + 1;
        int yIndex = text.indexOf('Y', xIndex) + 1;

        if (xIndex > 0 && yIndex > 0) {
            x = text.substring(xIndex, text.indexOf('Y', xIndex)).toFloat();
            y = text.substring(yIndex).toFloat();
            setLeftHorizontal(x);
            setLeftVertical(y);
        }
    }
}

Il nous reste à ajouter deux triggers (curseurs) :

image

La convention est A100A, mais nous allons changer pour TL100\n TR100\n

// 🤖 GPT
else if (text.startsWith("TL")) {
        isFound = true;
        int tlIndex = text.indexOf('L') + 1; // Après TL

        if (tlIndex > 0) {
            triggerValue = text.substring(tlIndex).toInt();
            setLeftTrigger(triggerValue);
        }
    } 
    else if (text.startsWith("TR")) {
        isFound = true;
        int trIndex = text.indexOf('R') + 1; // Après TR

        if (trIndex > 0) {
            triggerValue = text.substring(trIndex).toInt();
            setRightTrigger(triggerValue);
        }
    }

Nous pourrions contrôler le joueur avec une bulle d'air, mais vérifions avant cela que le reste fonctionne 😋👏.

image
image

A-20.01,50.45*

void ParseIfContainsJoystickFromBlueElec(String text) {
    // Format : A-20.01,50.45*
    bool isFound = false;
    float x = 0;
    float y = 0;

    // Vérifier si la chaîne commence par 'A'
    if (text.startsWith("A")) {
        isFound = true;

        // Trouver les positions des caractères clés
        int startIndex = text.indexOf('A') + 1; // Commencer après 'A'
        int commaIndex = text.indexOf(',');
        int starIndex = text.indexOf('*');

        // Vérifier que tous les caractères nécessaires sont présents
        if (startIndex > 0 && commaIndex > startIndex && starIndex > commaIndex) {
            // Extraire et convertir les valeurs X et Y
            x = text.substring(startIndex, commaIndex).toFloat(); // De 'A' à ','
            y = text.substring(commaIndex + 1, starIndex).toFloat(); // De ',' à '*'
        }

        // Afficher les valeurs extraites
        Serial.println("Données Joystick détectées");
        Serial.print("X : ");
        Serial.println(x);
        Serial.print("Y : ");
        Serial.println(y);
    }

    if (!isFound) {
        Serial.println("Aucune donnée de Joystick trouvée dans la chaîne");
    }
}

Avant de continuer vers les tests pour vérifier que tout cela fonctionne bien, ajoutons un Switch.
Il permettra d'activer ou non les pins pour lire les joysticks dans la vraie vie.

image
image

    else if(text=="C") m_useHardwareJoystick=true;
    else if(text=="c") m_useHardwareJoystick=false;
    else if(text=="D") m_useHardwareLed=true;
    else if(text=="d") m_useHardwareLed=false;

Vous avez l'idée :)

Je vais ajouter au code :

    else if(text=="SBL") pressLeftSideButton(true);
    else if(text=="sbl") pressLeftSideButton(false);
    else if(text=="SBR") pressRightSideButton(true);
    else if(text=="sbr") pressRightSideButton(false);
    else if(text=="JL") pressLeftStick(true);
    else if(text=="jl") pressLeftStick(false);
    else if(text=="TL") setTriggerLeftPercent(1);
    else if(text=="tl") setTriggerLeftPercent(0);
    else if(text=="TR") setTriggerRightPercent(1);
    else if(text=="tr") setTriggerRightPercent(0);
    else if(text=="JR") pressRightStick(true);
    else if(text=="jr") pressRightStick(false);
    else if(text=="MR") pressMenuRight(true);
    else if(text=="mr") pressMenuRight(false);
    else if(text=="ML") pressMenuLeft(true);
    else if(text=="ml") pressMenuLeft(false);
    else if(text=="MC") pressHomeXboxButton(true);
    else if(text=="mc") pressHomeXboxButton(false);

Mais n'hésitez pas à explorer les possibilités :

image

Vérifions que le code fonctionne ?

Trouver un PC ou un téléphone Android à contrôler.

Si votre ESP32 est connecté à un ordinateur, vous pouvez aller sur le site :
https://hardwaretester.com/gamepad

S'il est connecté à Android, vous pouvez installer :

image
https://play.google.com/store/apps/details?id=uk.co.powgames.gamecondiag&hl=en

Une fois lancée, cette application permet de voir les événements des manettes classiques, claviers et manettes Xbox.

Essayez de faire fonctionner le code d'Electronic Bluetooth avec tous les boutons de la manette sur cette application.
Si cela fonctionne, vous êtes prêt à tester cela sur vos jeux préférés et voir si les développeurs l'ont rendu compatible avec les manettes.

Si c'est le cas, "Enjoy" sinon trouvez une autre zone de test.
Car je n'ai pas implémenté le clavier Bluetooth pour le moment dans mes bibliothèques.

@EloiStree
Copy link
Owner Author

@EloiStree
Copy link
Owner Author

EloiStree commented Nov 24, 2024

@EloiStree
Copy link
Owner Author

@EloiStree
Copy link
Owner Author

EloiStree commented Nov 25, 2024

Suite à cet exercice, pour plus de facilité qu'avec des nombres, je vous ai ajouté des lettres.
Voici les possibilités.

Il y a aussi, basé sur la grammaire de Bluetooth Electronics :

  • LX100Y100, RX100Y100 pour définir les joysticks
  • TL100 et TR100 pour définir les triggers
  • P1, p1 pour activer une pin de 0 à 9
  • A180,-180* pour un pitch roll

N'oubliez pas le retour de ligne \n.

    if(text=="G") pressA(true);
    else if(text=="BEDEBUG") m_useBluetoothElectronicFeedBack=true;
    else if(text=="bedebug") m_useBluetoothElectronicFeedBack=false;
    else if(text=="PINSWITCH") SwitchAllPins();
    else if(text=="g") pressA(false);
    else if(text=="Y") pressY(true);
    else if(text=="y") pressY(false);
    else if(text=="B") pressX(true);
    else if(text=="b") pressX(false);
    else if(text=="R") pressB(true);
    else if(text=="r") pressB(false);
    else if(text=="S" || text=="START") pressMenuRight(true);
    else if(text=="s" || text=="start") pressMenuRight(false);
    else if(text=="BACK") pressMenuLeft(true);
    else if(text=="back") pressMenuLeft(false);
    else if(text=="H") pressHomeXboxButton(true);
    else if(text=="h") pressHomeXboxButton(false);
    else if(text=="M") pressMenuLeft(true);
    else if(text=="m") pressMenuLeft(false);
    else if(text=="N") pressMenuRight(true);
    else if(text=="n") pressMenuRight(false);
    else if(text=="BA" || text=="BD") pressA(true);
    else if(text=="ba"|| text=="bd") pressA(false);
    else if(text=="BY"|| text=="BU") pressY(true);
    else if(text=="by"|| text=="bu") pressY(false);
    else if(text=="BX"|| text=="BL") pressX(true);
    else if(text=="bx"|| text=="bl") pressX(false);
    else if(text=="BB"|| text=="BR") pressB(true);
    else if(text=="bb"|| text=="br") pressB(false);
    else if(text=="AU") pressArrowN();
    else if(text=="AC") releaseDPad();
    else if(text=="AR") pressArrowE();
    else if(text=="AD") pressArrowS();
    else if(text=="AL") pressArrowW();
    else if(text=="AN") pressArrowN();
    else if(text=="AE") pressArrowE();
    else if(text=="AS") pressArrowS();
    else if(text=="AW") pressArrowW();
    else if(text=="ANW") pressArrowNW();
    else if(text=="ANE") pressArrowNE();
    else if(text=="ASE") pressArrowSE();
    else if(text=="ASW") pressArrowSW();
    else if(text=="ANW") pressArrowNW();
    else if(text=="ANE") pressArrowNE();
    else if(text=="ASE") pressArrowSE();
    else if(text=="ASW") pressArrowSW();
    else if(text=="RECORD") recordStart();
    else if(text=="record") recordStop();
    else if(text=="SBL") pressLeftSideButton(true);
    else if(text=="sbl") pressLeftSideButton(false);
    else if(text=="SBR") pressRightSideButton(true);
    else if(text=="sbr") pressRightSideButton(false);
    else if(text=="JL") pressLeftStick(true);
    else if(text=="jl") pressLeftStick(false);
    else if(text=="TL") setTriggerLeftPercent(1);
    else if(text=="tl") setTriggerLeftPercent(0);
    else if(text=="TR") setTriggerRightPercent(1);
    else if(text=="tr") setTriggerRightPercent(0);
    else if(text=="JR") pressRightStick(true);
    else if(text=="jr") pressRightStick(false);
    else if(text=="MR") pressMenuRight(true);
    else if(text=="mr") pressMenuRight(false);
    else if(text=="ML") pressMenuLeft(true);
    else if(text=="ml") pressMenuLeft(false);
    else if(text=="MC") pressHomeXboxButton(true);
    else if(text=="mc") pressHomeXboxButton(false);
    // Big Grey Circle
    else if(text=="M"){}
    // Big Rectancle 
    else if(text=="W"){}
    // Switch vertical on off
    else if(text=="C") m_useHardwareJoystick=true;
    else if(text=="c") m_useHardwareJoystick=false;
    // Power Button Switch on off
    else if(text=="D") digitalWrite(m_lepPin, HIGH);
    else if(text=="d") digitalWrite(m_lepPin, LOW);
    else if(text=="V") setVibrationOn(true);
    else if(text=="v") setVibrationOn(false);

@EloiStree
Copy link
Owner Author

Ce qu'il faut savoir sur la communication entre un module HC-05 et Unity3D, c'est que cela peut être complexe.

Les codes varient d'une plateforme à l'autre.

Si vous devez réaliser cette intégration, il est recommandé d'acheter un ou deux plug-ins directement depuis l'Asset Store, puis d'apprendre à les utiliser :
image
[Arduino Bluetooth Plugin – Asset Store](https://assetstore.unity.com/packages/tools/input-management/arduino-bluetooth-plugin-98960)

Si vous ne souhaitez pas acheter de plug-in, vous pouvez tenter de coder votre propre solution. Cependant, il est important de noter que maintenir un tel code compatible au fil des années peut être particulièrement difficile.

Une alternative qui peut fonctionner dans des conditions spécifiques consiste à exécuter un serveur sur le PC connecté à l'appareil Bluetooth, puis à établir une communication via ce serveur.

Pourquoi ? Parce que Python est puissant, flexible et bénéficie d'un excellent support communautaire. Vous pouvez trouver ici quelques scripts Python pour configurer une redirection Port Série ↔️ Bluetooth.


N'hésitez pas si vous avez besoin d'autres ajustements ou d'informations complémentaires !

import socket
import serial
import serial.tools.list_ports
import struct
import threading
import time

# Configuration
UDP_TO_SERIAL_PORT = 7046  # UDP port for receiving data to send to the serial port
SERIAL_TO_UDP_PORT = 7045  # UDP port for sending data received from the serial port
UDP_TARGET_IP = "127.0.0.1"  # Target IP for UDP communication
SERIAL_BAUDRATE = 9600
DEVICE_ID = "98D331F71DA0"

# Functions for serial communication
def list_serial_ports():
    ports = serial.tools.list_ports.comports()
    for port in ports:
        print(f"Device: {port.device}")
        print(f"Name: {port.name}")
        print(f"Description: {port.description}")
        print(f"HWID: {port.hwid}")
        print(f"VID: {port.vid}")
        print(f"PID: {port.pid}")
        print(f"Serial Number: {port.serial_number}")
        print(f"Location: {port.location}")
        print(f"Manufacturer: {port.manufacturer}")
        print(f"Product: {port.product}")
        print(f"Interface: {port.interface}")
        print("-" * 40)

def find_device_com(device_id):
    ports = serial.tools.list_ports.comports()
    for port in ports:
        if port.serial_number == device_id:
            return port.device
        if device_id in port.hwid:
            return port.device
    return None

# Thread for UDP -> Serial
def udp_to_serial(udp_port, serial_port):
    udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_sock.bind(("0.0.0.0", udp_port))
    print(f"Listening for UDP data on port {udp_port}...")
    
    while True:
        data, addr = udp_sock.recvfrom(1024)
        print(f"Received from UDP {addr}: {data}")
        serial_port.write(data)

# Thread for Serial -> UDP
def serial_to_udp(serial_port, udp_ip, udp_port):
    udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    print(f"Sending serial data to {udp_ip}:{udp_port}...")
    
    while True:
        if serial_port.in_waiting > 0:
            data = serial_port.read(serial_port.in_waiting)
            print(f"Received from Serial: {data}")
            udp_sock.sendto(data, (udp_ip, udp_port))
        time.sleep(0.01)

# Main
if __name__ == "__main__":
    # List serial ports
    list_serial_ports()
    
    # Find serial device
    com_port = find_device_com(DEVICE_ID)
    if not com_port:
        print(f"Device with ID {DEVICE_ID} not found!")
        exit(1)
    
    print(f"Connecting to {com_port}...")
    serial_port = serial.Serial(com_port, SERIAL_BAUDRATE, timeout=1)
    # Start threads
    udp_thread = threading.Thread(target=udp_to_serial, args=(UDP_TO_SERIAL_PORT, serial_port), daemon=True)
    serial_thread = threading.Thread(target=serial_to_udp, args=(serial_port, UDP_TARGET_IP, SERIAL_TO_UDP_PORT), daemon=True)
    
    udp_thread.start()
    serial_thread.start()    
    # Keep the main thread alive
    try:
        
        print("Press 'E' to exit...")
        while True:
            time.sleep(1)
            if input().strip().upper() == 'E':
                print("Exiting...")
                break
    except KeyboardInterrupt:
        print("Shutting down...")
        serial_port.close()

@EloiStree
Copy link
Owner Author

pip install pyserial

@EloiStree
Copy link
Owner Author

import socket
import threading

# Define the UDP IP address and ports
UDP_IP = "127.0.0.1"
SEND_PORT = 7046
RECEIVE_PORT = 7045

# Create a UDP socket for sending
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Create a UDP socket for receiving
receive_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
receive_socket.bind((UDP_IP, RECEIVE_PORT))

# Function to send a message
def send_message(message):
    message+="\n"
    send_socket.sendto(message.encode(), (UDP_IP, SEND_PORT))
    print(f"Sent message: {message}")

# Function to receive a message
def receive_message():
    while True:
        data, addr = receive_socket.recvfrom(1024)  # buffer size is 1024 bytes
        print(f"Received message: {data.decode()} from {addr}")

# Example usage
if __name__ == "__main__":
    # Start the receiver in a new thread
    receiver_thread = threading.Thread(target=receive_message)
    receiver_thread.daemon = True
    receiver_thread.start()

    # Send a message
    send_message("Hello, UDP!")

    # Keep the main thread alive to allow the receiver thread to run
    while True:
        user_input = input("Enter message to send: ")
        send_message(user_input)
        pass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant