ESP32 bluetoothserial.h récupérer une variable

Bonjour,
Ce petit code est censé permettre d'entrer le code wifi dans un terminal bluetooth.
Curieusement, je n'ai pas le temps d'entrer quoique ce soit.
La question passe comme si j'avais entré quelque chose puis il affiche "^M" lorsqu'il doit imprimer cette variable dans ce terminal...
Qu'est ce que j'ai mal fait?

#include "BluetoothSerial.h"
char BT_data;
 
BluetoothSerial SerialBT;
 
void setup() {
Serial.begin(115200);//démarrage du moniteur série

BT_start();
BT_get_wifi();
}

void BT_get_wifi() {
  while (!SerialBT.available()) {
    SerialBT.println("Appuyer sur ENVOI");
    delay(2000);
    }

  
  SerialBT.println("Veuillez indiquer le code de votre connexion WiFi:");
  BT_get_data();
  SerialBT.print(BT_data);
  SerialBT.println(" ok");
}

void BT_get_data() {
  delay(250);
  if (SerialBT.available()) {
  BT_data = SerialBT.read();}
  }
  

Il faut Nous donner un peu plus d’informations sur le montage
Le code n’est pas complet

#include "BluetoothSerial.h"
char BT_data;
 
BluetoothSerial SerialBT;
 
void setup() {
Serial.begin(115200);//démarrage du moniteur série

BT_start();
BT_get_wifi();
}

void BT_get_wifi() {
  while (!SerialBT.available()) {
    SerialBT.println("Appuyer sur ENVOI");
    delay(2000);
    }

  
  SerialBT.println("Veuillez indiquer le code de votre connexion WiFi:");
  BT_get_data();
  SerialBT.print(BT_data);
  SerialBT.println(" ok");
}

void BT_get_data() {
  delay(250);
  if (SerialBT.available()) {
  BT_data = SerialBT.read();}
  }
  

void BT_start() {
  WiFi.disconnect();
  delay(100);
  btStart();//démarre bt
  delay(100);
  SerialBT.begin("Accentra");//nomme le BT
  delay(100);

}


void loop() {
 
}

En effet j'ai perdu un petit bout en route.
j'utilise l'application serial bluetooth terminal sur android

Il y a des délais qui ne servent à rien. Écoutez le port série et recevez plus d’un caractère… si vous avez tapé juste envoi, c’est ce qui sera reçu si tout est bien configuré

Si vous voulez comprendre comment bien écouter le port série (ou gérer un flux asynchrone) vous pouvez jeter un oeil à mon petit tuto sur le sujet

Dans un premier temps le code écrit 'appuyer sur envoi' en boucle jusqu'à ce que j'appuie sur la flèche en bas à droite. Ca me laisse le temps de me connecter etc et de ne pas rater la suite...
Je vais voir votre lien

^M est le code ASCII pour "carriage return", aussi appelé \r chez Mr Arduino.

Bonsoir kammo

Essaies ce code:

void BT_get_wifi() {
  SerialBT.println("Appuyer sur ENVOI");
  while (!SerialBT.available()) {}
  BT_data = SerialBT.readStringUntil('\n');
  BT_data.trim();

  SerialBT.print(BT_data);
  SerialBT.println(" ok");
}

(ce n'est pas essayé :wink:)

Cordialement
jpbbricole

Il faut dans ce cas aussi changer

En String

Sérieusement pour recevoir un seul caractère sortir l’artillerie lourde de la String qui peut faire un timeout suivant comment est configuré le terminal émetteur n’est pas utile.

Si on veut que la fonction soit bloquante tant qu’on n’a pas reçu le line feed on fait

void attenteUtlisateur() {
  SerialBT.println(F("Appuyer sur ENVOI"));
  while (SerialBT.read() != '\n') yield();
  SerialBT.println(F("OK"));
}

Je vais essayer tout ça, le temps de prendre un grand café. Pour l'envoi, ce n'est peut-être même pas nécessaire. Si je fais cela, c'est que l'ESP commence d'envoyer des infos sur le terminal BT alors que je n'y suis pas encore connecté. Il suffirait qu'il soit capable de voir quand je suis dessus pour commencer à communiquer.

Ensuite, une fois cela fait, le plu compliqué c'est qu'il lise le code du wifi. Il me semble avoir déjà fait ça il y a longtemps, il faut lire tous les caractères un à un et le mettre bout à bout, je ne sais plus trop comment...

L’ESP ne fait pas ça tout seul, c’est votre code qui le fait. Donc effectivement attendre un « Hello » de la part du terminal client est bonne. Par contre ça veut dire que sans doute personne ne sera là pour écouter le

SerialBT.println(F("Appuyer sur ENVOI"));

Il faudrait tester s’il y a un pairing - ça dépend du module BT

Bonjour J-M-L

Oui, sérieusement, il faut arrêter cette psychose autour de String qui fait partie intégrante du langage Arduino. Cette "artillerie lourde" n'est pas pour un seul caractère, mais pour tout le code wifi.

Ma proposition est aussi bloquante.

Cordialement
jpbbricole

void wait_user() {    
  while (!SerialBT.available()) {
    SerialBT.println("Appuyer sur ENVOI");
  }
  BT_data = SerialBT.readStringUntil('\n');
  BT_data.trim();

  SerialBT.println("Vous avez entré");
  SerialBT.println(BT_data);
  SerialBT.println(" ok");
}

avec ça, ça fonctionne.
J'essaie d'aller plus loin.

Merci pour ce dépannage, j'avance :)

Ce n’est pas une psychose, c’est un fait documenté que des problèmes peuvent surgir à cause de l’allocation dynamique dans cette classe et il faut en être conscient. La doc arduino dit qu’on a les deux options et que la classe prend plus de mémoire

Text strings can be represented in two ways. you can use the String data type, which is part of the core as of version 0019, or you can make a string out of an array of type char and null-terminate it. This page described the latter method. For more details on the String object, which gives you more functionality at the cost of more memory, …

Ce qu’apporte la classe String c’est vraiment la possibilité de ne pas se soucier de la longueur - jusqu’au moment bien sûr où ça devient un souci de mémoire. Les méthodes utilitaires proposées sont équivalentes aux fonctions traditionnelles des cStrings, voire moins puissantes (cf votre usage de sprintf() par exemple dans un autre thread - pourquoi avoir pris une cString dans ce cas si la String est la réponse générale…)

C’est pour cela qu’Intégrer des String quand on en n’a pas besoin n’est pas le meilleur conseil qu’on puisse donner dans le cas présent, un peu comme quand vous avez utilisé sprintf(), c’était plus adapté.


Votre proposition n’est pas bloquante à tous les coups, ça dépend du terminal qui est connecté en face et de l’utilisateur. Si vous avez un terminal configuré pour émettre les caractères quand ils sont tapés et pas uniquement quand on appuie sur return ou une ligne parasitée avec un ou deux caractères aléatoires qui semblent être émis alors si je tape une lettre puis j’attends une seconde ou si l’utilisateur ne tape pas return tout de suite après les parasites alors votre code aura atteint le timeout de la fonction readStringUntil() et va continuer.


Edit / PS: il y a ce vieux lien qui reference quelques soucis qu'on rencontré des personnes avec la classe String et un peu de contexte pour aller plus loin. Un des membres du forum en anglais a aussi une liste de plusieurs posts où le souci a été résolu en enlevant les Strings mais je ne le retrouve pas là.

Il faut relativiser.
Sur un AVR (y compris MEGA) String est à utiliser avec parcimonie, et de préférence avec String::reserve().
Sur ESP32, et même ESP8266, je ne me prive pas d'utiliser des objets String. J'ai un client WEB / JSON ESP8266 avec écran TFT qui tourne depuis des années sans problèmes, en utilisant des objets String.
Mais tout dépend de l'occupation en RAM du projet, et de la taille des Strings à allouer. Si mon projet était plus gourmand (54% actuellement) je m'inquiéterais probablement.

Deuxio, il y a une question de culture. La plupart des utilisateurs Arduino n'ont aucune idée de ce qu'implique l'utilisation de la classe String, l'allocation dynamique, la gestion du tas, de la pile, la fragmentation, etc. Donc ils utilisent Strings sans savoir, par facilité, et par manque de formation de base en C / C++.

oui il faut juste en être conscient et les utiliser à bon escient

je reste sur ma position que pour attendre un line feed de manière bloquante, un bon vieux while() qui lit ce qui arrive en attendant '\n' est ce qui est la plus simple (la fonction read() retournant -1 quand il n'y a rien à lire) et le yield() étant là pour éviter que le watchdog ne morde

d'où le

  while (SerialBT.read() != '\n') yield();

au lieu de

  String BT_data;
  while (!SerialBT.available()) {}
  BT_data = SerialBT.readStringUntil('\n');
  BT_data.trim();

Si j'en crois le code du demandeur, il attend une chaîne de caractères, donc l'attente d'un '\n' ne suffit pas. Le résultat de readStringUntil() est donc nécessaire.
Mais j'avoue que la demande est floue, comme souvent :wink:

En effet. Même si je me sers de ce code pour indiquer la présence de l'utilisateur dans l'application (Serial BT Terminal), je m'en sers plus loin (je suis dessus en ce moment) pour récupérer le mdp du Wifi.

Le programme cherche les wifi visibles à portée et tente, un à un, de s'y connecter en utilisant ce code.

WiFi.begin(WiFi.SSID,BT_data);

Sauf que le format de BT_data semble tout coincer. Il attend un char si je comprends bien. J'essaie encore et je reviens.

Résumons:
1-via le terminal BT, demander le code wifi, le stocker dans une variable BT_data
2-utiliser BT_data pour se logger au wifi

le code d'origine utilisait un seul char :slight_smile:

mais oui, s'il y a besoin de plus il faut un buffer

readStringUntil() va cependant faire un timeout si le terminal envoie caractère par caractère et que c'est ma maman qui tape le mot de passe du wifi avec un doigt et en prenant son temps entre deux touches :slight_smile:

Bonjour kammo

Où tu as besoin de char tu écris BT_data.c_str(), par exemple:

WiFi.begin(WiFi.SSID,BT_data.c_str());

Cordialement
jpbbricole