SoftwareSerial et deuxième UART pour Arduino nano

Bonjour,

Je remets ce sujet car je me heurte (après avoir utilisé une carte mega + stripled + module HC-06 bluetooth) a un problème de liaison série associé à la carte Nano.
Je n'arrive pas en effet à faire fonctionner SoftwareSerial et ma carte Nano dans le but de stocker les données de mon module bluetooth HC-06 et les transmettre ensuite en I2C vers ma carte Mega.
Mon soft fonctionne bien avec une carte mega mais impossible ( en ayant essayé d'autres I/O ) avec la carte Nano ?
Je souhaite utiliser une deuxième liaison série pour pouvoir débugger un minimum avec moniteur série et l'UART physique présente sur la Nano.
Si quelqu'un a une idée ou déjà utilisé une deuxième UART qui fonctionne sur une Nano , je suis preneur.
Merci

         /*Carte arduino nano de lecture données série via module HC-06 bluetooth
et retransmission sur demande maitre ( carte arduino mega) via I2C */

#include <Wire.h>
#include <SoftwareSerial.h>
//
//
// Variables liaison série
char serial_char;// stockage des chaines de caractères avant envoi I2C
byte indice = 0;
//
SoftwareSerial mySerial(10,11); // pin10 =  D6 = RX, pin11 =  D7 = TX
//
void setup()
{
  //Serial.begin(38400);     // opens serial port, sets data rate to 9600 bps
  //while (!Serial);
  //Serial.print("Hello ");
  mySerial.begin(9600);
  delay(500);
  /*mySerial.print("AT+BAUD3");
  /delay(2000);
  while (mySerial.available())
  {
  char str = mySerial.read();
  Serial.print(str);   
  }*/
  Wire.begin(2);                // Joindre le bus i2c avec l'adresse #2
  Wire.onRequest(requestEvent); // enregistrer l'événement (lorsqu'une demande arrive)*/
}
void loop()
{
 while (mySerial.available())
    {
    //read the incoming byte:
    serial_char = mySerial.read();
     //delay(500);
    //Serial.print("Char ");
    //Serial.println(serial_char);
     //delay(500);
    indice = indice+1;
    }

bonjour fm, moi j'utilise actuellement les librairies Wire et SoftwareSerial sur un nano "esclave" sans aucun soucis.

je récupéré la teleinfo de mon compteur EDF via SoftwareSerial , et la renvoie "décodé" via le bus I2C vers un arduino maitre.
J'ai gardé la liaison série classique pour le debug.

#include <SoftwareSerial.h>
#include <Wire.h>

SoftwareSerial Serial_Teleinfo(3, 4); 
// Créé une liason série sur les pins 3 et 4
// Seulement Pin 3 sur l'optocoupleur


long top1 = 0;
long top2 = 0;
long obtention = 0;
long Index_HC = 0;
long Index_HP = 0;
byte I_A = 0;
int P_W = 0;
char PERIODE = ' ';
byte compteurOK = 0;
unsigned long temps_d_acquisition = 0;

int go = 0;

volatile byte* Long1ArrayPtr;
volatile byte* Long2ArrayPtr;
volatile byte* Long3ArrayPtr;
volatile byte* Int1ArrayPtr;




void setup() {
  pinMode(13, OUTPUT);
  Wire.begin(8);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);     // Port série pour liasion Arduino <-> PC
  Serial_Teleinfo.begin(1200); // Port SoftwareSerial pour liasion Arduino <-> Teleinfo
}


void loop() {
  if (go == 1) {
    recup_teleinfo();
  }
}



void requestEvent()
{

  byte Data[16];
  if  (compteurOK == 1) {
    digitalWrite(13, HIGH);
    Long1ArrayPtr = (byte*) &obtention;
    Data[0] = Long1ArrayPtr[0];
    Data[1] = Long1ArrayPtr[1];
    Data[2] = Long1ArrayPtr[2];
    Data[3] = Long1ArrayPtr[3];
    Long2ArrayPtr = (byte*) &Index_HC;
    Data[4] = Long2ArrayPtr[0];
    Data[5] = Long2ArrayPtr[1];
    Data[6] = Long2ArrayPtr[2];
    Data[7] = Long2ArrayPtr[3];
    Long3ArrayPtr = (byte*) &Index_HP;
    Data[8] = Long3ArrayPtr[0];
    Data[9] = Long3ArrayPtr[1];
    Data[10] = Long3ArrayPtr[2];
    Data[11] = Long3ArrayPtr[3];
    Int1ArrayPtr = (byte*) &P_W;
    Data[12] = Int1ArrayPtr[0];
    Data[13] = Int1ArrayPtr[1];
    Data[14] = I_A;
    Data[15] = byte(PERIODE);
    Data[16] = compteurOK;
  }
  else {
    Data[0] = 0;
    Data[1] = 0;
    Data[2] = 0;
    Data[3] = 0;
    Data[4] = 0;
    Data[5] = 0;
    Data[6] = 0;
    Data[7] = 0;
    Data[8] = 0;
    Data[9] = 0;
    Data[10] = 0;
    Data[11] = 0;
    Data[12] = 0;
    Data[13] = 0;
    Data[14] = 0;
    Data[15] = 0;
    Data[16] = 0;
  }
  Wire.write(Data, 17);



}

void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character
     Serial.print(c);         // print the character
  }
  go = Wire.read();    // receive byte as an integer
  Serial.println(go);  // print the integer
  top1 = millis();
  temps_d_acquisition = millis();
  compteurOK = 0;
}

void recup_teleinfo() {
  digitalWrite(13, LOW);
  go = 0;
  char charIn_Trame_Teleinfo = 0; // stock chaque charactère recu de la trame teleinfo

  String Ligne;      // stock la ligne complette (entre LF(0x0A) et CR(0x0D))
  String Etiquette;  // stock l'intitulé
  String Valeur;     // stock la valeur apres l'intitulé
  char Checksum;
  /*
    Trame recu par la teleinfo      (Expliquations ! non recu par la teleinfo)

    ADCO 040422040644 5          (N° d'identification du compteur : ADCO (12 caractères))
    OPTARIF HC.. <                  (Option tarifaire (type d'abonnement) : OPTARIF (4 car.))
    ISOUSC 45 ?                 (Intensité souscrite : ISOUSC ( 2 car. unité = ampères))
    HCHC 077089461 0          (Index heures creuses si option = heures creuses : HCHC ( 9 car. unité = Wh))
    HCHP 096066754 >          (Index heures pleines si option = heures creuses : HCHP ( 9 car. unité = Wh))
    PTEC HP..                   (Période tarifaire en cours : PTEC ( 4 car.))
    IINST 002 Y                 (Intensité instantanée : IINST ( 3 car. unité = ampères))
    IMAX 044 G                  (Intensité maximale : IMAX ( 3 car. unité = ampères))
    PAPP 00460 +                  (Puissance apparente : PAPP ( 5 car. unité = Volt.ampères))
    HHPHC E 0                 (Groupe horaire si option = heures creuses ou tempo : HHPHC (1 car.))
    MOTDETAT 000000 B         (Mot d'état (autocontrôle) : MOTDETAT (6 car.))
  */


  //RAZ des valeurs
  Index_HC = Index_HP = 0;
  I_A = 250;
  P_W = 32000;
  PERIODE = 'A';
  obtention = 0;
  while ( Index_HC == 0 || Index_HP == 0 || I_A == 250 || P_W == 32000 || PERIODE == 'A' ) { // recommence tant que l'on a pas recu tous les élément voulus


    //Attend un début de ligne (0x0A)
    charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ;
    while (charIn_Trame_Teleinfo != 0x0A) { // reste dans cette boucle tant qu'on ne recoit pas le Charactere de début de ligne 0x0A
      if ((millis() - temps_d_acquisition) > 1000 ) {
        Serial.println("Teleinfo Inaccesible");  // Affiche un erreur si la teleinfo est inaccesible et retourne à Loop
        loop();
      }
      if (Serial_Teleinfo.available()) { // Tant qu'il y a des caractères disponibles
        charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
      }
    }
    //Vide Ligne
    Ligne = "";
    //Vide Etiquette
    Etiquette = "";
    //Concatene les carateres recus jusqu'a l'espace suivant (0x20)
    charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ;
    while (charIn_Trame_Teleinfo != 0x20) { // reste dans cette boucle tant qu'on ne recoit pas le Charactere Espace
      if (Serial_Teleinfo.available()) { // Tant qu'il y a des caractères disponibles
        charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
        if (charIn_Trame_Teleinfo != 0x20) {
          Etiquette += charIn_Trame_Teleinfo; // concatene les caractères reçus sauf les espaces
        }
        Ligne += charIn_Trame_Teleinfo;
      }
    }

    //Vide Valeur
    Valeur = "";
    //Concatene les carateres recus jusqu'a l'espace suivant (0x20)
    charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ;
    while (charIn_Trame_Teleinfo != 0x20) { // reste dans cette boucle tant qu'on ne recoit pas le Charactere Espace
      if (Serial_Teleinfo.available()) { // Tant qu'il y a des caractères disponibles
        charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
        if (charIn_Trame_Teleinfo != 0x20) {
          Valeur += charIn_Trame_Teleinfo; // concatene les caractères reçus sauf les espaces
        }
        Ligne += charIn_Trame_Teleinfo;
      }
    }

    //Concatene les carateres recus jusqu'a la fin de ligne (0x0D)
    charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ;
    while (charIn_Trame_Teleinfo != 0x0D) { // reste dans cette boucle tant qu'on ne recoit pas le Charactere de fin de ligne
      if (Serial_Teleinfo.available()) { // Tant qu'il y a des caractères disponibles
        charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
        if (charIn_Trame_Teleinfo != 0x0D) {
          Ligne += charIn_Trame_Teleinfo; // concatene les caractères reçus sauf le Charactere de fin de ligne (0x0D)
        }
        if (charIn_Trame_Teleinfo != 0x0D) {
          Checksum = charIn_Trame_Teleinfo;
        }
      }
    }
    //Controle du Checksum (Le dernier caractere de la ligne et un caractere de controle)

    char Controle = 0;
    String trame = Etiquette + " " + Valeur;
    for (byte i = 0; i < (trame.length()); i++) {
      Controle += trame[i];
    }
    Controle = (Controle & 0x3F) + 0x20;


    if (Controle == Checksum) { // Si le checksum correspond bien au code controlé

      //Associe la valeur lue à son etiquette
      if (Etiquette.substring (0, 4) == "HCHC") {
        Index_HC = Valeur.toInt();
      }
      if (Etiquette.substring (0, 4) == "HCHP") {
        Index_HP = Valeur.toInt();
      }
      if (Etiquette.substring (0, 4) == "PTEC") {
        PERIODE  = Valeur[1];
      }
      if (Etiquette.substring (0, 4) == "IINS") {
        I_A      = Valeur.toInt();
      }
      if (Etiquette.substring (0, 4) == "PAPP") {
        P_W      = Valeur.toInt();
      }
    }
  }
  if (( Index_HC != 0) && (Index_HP != 0) && (I_A != 250) && (P_W != 32000) && (PERIODE != 'A' )) {
    compteurOK = 1;
    Serial.print("Temps d'aquisition : "); Serial.print(millis() - temps_d_acquisition); Serial.println(" Millisecondes");

    Serial.print(" => Index HC : "); Serial.print(Index_HC); Serial.println(" Wh");
    Serial.print(" => Index HP : "); Serial.print(Index_HP); Serial.println(" Wh");
    Serial.print(" => PERIODE : H") ; Serial.println(PERIODE);
    Serial.print(" => Intensite instantanee : "); Serial.print(I_A); Serial.println(" A");
    Serial.print(" => Puissance apparente : "); Serial.print(P_W); Serial.println(" W");
    top2 = millis();
    obtention = top2 - top1;
    Serial.print("Temps obtention : "); Serial.print(obtention); Serial.println(" Millisecondes");
  }

}

Pourquoi?

SoftwareSerial mySerial(10,11); // pin10 =  D6 = RX, pin11 =  D7 = TX

Quand tu écris "SoftwareSerial mySerial(10,11)" tu dois utiliser les pins Digital 10 et Digital 11 !

Bonjour Ptitnitro !

Merci pour la réponse. Je vais essayer de voir la différence entre ton code et le mien.
J'ai essayé plusieurs pin pour Softwareserial avec mon arduino nano pour finir avec la 10 et 11 ( en fait celles que j'ai testées avec ma carte mega et ça fonctionne bien avec la mega ?)
Actuellement je continue avec le serial Hardware de la nano mais sans debug du coup ( plutôt pénible en aveugle )

Récupérer les infos de télétransmission au compteur, intéressant mais pour quel projet ? Est ce que la vitesse de ton Softwareserial fonctionne aussi à 9600 bauds ( vitesse idem moniteur ) ?

Je viens de percuter sur l'info des pins de Software: D10 et D11 ! Venant du hardware j'étais persuadé que le paramètre passé était le numéro de pin ! Erreur bête, merci pour la réponse !

Bonjour,

pour ce projet là:

Suivi consommation eau et EDF:

https://forum.arduino.cc/index.php?topic=373614.msg2576722#new

Non le Serial_Teleinfo pour la liaison compteur est à 1200 bauds , et pour mon moniteur 9600 que j'utilise uniquement en debug.

Salut ptitnitro62000 !
Ton projet de lecture conso eau elec est très intéressant ( à étudier de plus près ) et tu le maitrise bien. De mon côté j'avance sur le mien mais mon expérience en soft est un peu limite ( De l'assembleur mais il y a plus de 20 ans maintenant ! :slight_smile: ).
Je me bats encore pas mal avec mon I2C pour récupérer les infos provenant de ma carte nano de manière synchrone !
La question est comment récupérer le bon nombre de caractères provenant de la nano qui elle, remplie un buffer de manière asynchrone ( provenance bluetooth ).
J'aavais essayé de récupérer et envoyer le nbre de caractères au début du transfert mais pas terrible encore ( pb requestform ? )

J'ai ensuite modifié et simplifié pour envoyer sans le nbre de caractères mais celà pose problème côté Réception carte Mega

Je mets mon code ( nano + mega ) pour aide éventuelle ?

CARTE NANO

         /*Carte arduino nano de lecture données série via module HC-06 bluetooth
et retransmission sur demande maitre ( carte arduino mega) via I2C */

#include <Wire.h>
#include <SoftwareSerial.h>
//
SoftwareSerial mySerial(10,11); // D10 = RX, D11 = TX
//
// Variables liaison série
char serial_char[255];// stockage des chaines de caractères avant envoi I2C
byte index = 0;//nbre de caractères à envoyer I2C
byte index_precedent = 0;
boolean initial = false;//init envoi nbre de caractères à récupérer via I2C
boolean myserie = false;// données série bluetooth acquises
boolean envoi = false;// envoi I2C terminé
byte ack;
//
void setup()
{
  Serial.begin(9600);     // opens serial port, sets data rate to 38400 bps
  while (!Serial);
  mySerial.begin(9600);
  /*mySerial.print("AT+BAUD3");
  /delay(2000);
  while (mySerial.available())
  {
  char str = mySerial.read();
  Serial.print(str);   
  }*/
  Wire.begin(2);                // Joindre le bus i2c avec l'adresse #2
  Wire.onRequest(requestEvent); // enregistrer l'événement (lorsqu'une demande arrive)
  //Wire.onReceive(receiveEvent);// ack du maitre
  //for (byte i=0; i<=255; i++){serial_char[i]= 'A';}
  index_precedent = 0;
}
void loop()
{
 while (mySerial.available())
    {
    index = index+1;
    serial_char[index] = mySerial.read();
    Serial.print("serie_index ");
    Serial.println(index);
    Serial.print("Char ");
    Serial.println(serial_char[index]);
    }
}
// Fonction qui est exécutée lorsque des données sont demandées par le Maître envoi données I2C
// Cette fonction est enregistrée comme un événement ("event" en anglais), voir la fonction setup()
void requestEvent()
{       
        if ( index > index_precedent )
        {
        for ( byte i = index_precedent+1; i <= index ; i++)
        {
        Serial.print("ok_nano ");
        Serial.println(serial_char[i]);
        Wire.write(serial_char[i]); // renvoi des caractères stockés
        } 
        index_precedent = index;  
        }   
}

CARTE MEGA

#include <Wire.h>
byte ack = 0;
byte indice = 0;
//
void setup()
{
  Wire.begin(2);        // joindre le bus i2c (adresse est optionnelle pour un maître)
  Serial.begin(9600);  // démarré une communication série
  Serial.println("hello");
}

void loop()
{
  Wire.requestFrom(2,1);// récupérer le nbre de caractères à lire
  indice = Wire.read(); // réception des caractères stockés
  //Serial.print("indice ");
  //Serial.println(indice);
  //Wire.write(ack);
  if (indice >= 1)
    {
    Serial.print("indice ");
    Serial.println(indice);
    Wire.requestFrom(2,indice);    // lecture du nbre de caractères stockés
    for (int j = 1 ; j <= indice ; j++)
      {
      char c = Wire.read(); // réception des caractères stockés
      Serial.println(c);
      }
      indice =0;// raz indice après réception*/
    }
}

Tu as noté dans ton code de la mega:

  Wire.begin(2);        // joindre le bus i2c (adresse est optionnelle pour un maître)

NON, c 'est:   Wire.begin();  qu'il faut noté,

Ta nano est déjà sur l'adresse #2, ta mega est maitre t'es pas obligé de mettre d’adresse, où si tu veux mettre une adresse à ta mega met pas la même que la nano !!!!

regarde l'exemple en image: L'architecture Bus I2C:

Oui c'est vrai, pas d'adresse pour le maitre, merci.
En fait celà ne change rien. J'essai actuellement de renvoyer un accusé réception de ma carte maitre après avoir reçu le nbre de caractères à lire et de l'autre côté récupérer ce flag accusé réception côté esclave pour renvoyer les caractères correspondants.
Je ne sais pas si c'est la bonne solution mais je me heurte tjs au problème de lire le bon nbre de caractères via requestfrom( adresse, nbre de caractères ? ).

Représente sur un petit dessin les échanges I2C que tu souhaite avoir entre le maitre et l'esclave, ainsi que les échanges Bleutooth. Çà sera + clair pour tous.

J'ai réussi à synchroniser le nombre de lecture et écriture entre le maitre et l'esclave en dépilant mon buffer lu via serial.
ça fonctionne bien du côté de ma carte nano ( slave ) par contre je ne lis que le dernier carcatères du côté de ma carte mega ( master ) !?
Et là je ne comprends pas ? Surement encore une "coquille" qui traine !?

Le code à suivre

Carte Nano

{
 while (mySerial.available())
    {
    index = index+1;
    serial_char[index] = mySerial.read();
    Serial.print("serie_index ");
    Serial.println(index);
    Serial.print("Char ");
    Serial.println(serial_char[index]);
    }
}
// Fonction qui est exécutée lorsque des données sont demandées par le Maître envoi données I2C
// Cette fonction est enregistrée comme un événement ("event" en anglais), voir la fonction setup()
void requestEvent()
{      
        if (!ack && ((index-index_precedent)>=1))
        {
        Wire.write(index-index_precedent);
        Serial.print("index ");
        Serial.println(index-index_precedent);
        }
        else
        {
          if ( index > index_precedent )
          {
          for ( byte i = index_precedent+1; i <= index ; i++)
          {
          Serial.print("ok_nano ");
          Serial.println(serial_char[i]);
          Wire.write(serial_char[i]); // renvoi des caractères stockés
          } 
          index_precedent = index;
          ack = false;  
          }
        }   
}
void receiveEvent(int howMany) 
{
    ack = Wire.read(); // receive byte as a character
    Serial.print("ack ");         // print the character
    Serial.println(ack);         // print the character
}

Carte mega

#include <Wire.h>
boolean ack = false;
byte index = 0;
//
void setup()
{
  Wire.begin();        // joindre le bus i2c (adresse est optionnelle pour un maître)
  Serial.begin(9600);  // démarré une communication série
  Serial.println("hello");
}

void loop()
{
  delay(2000);
  if (index < 1)
  {
  Wire.requestFrom(2,1);    // lecture du nbre de caractères stockés
  index = Wire.read();
  Serial.print("index ");
  Serial.println(index);
  }
  else
  {
  ack = true; 
  Serial.print("index_suite ");
  Serial.println(index);
  Wire.beginTransmission(2); 
  Wire.write(ack);            // sends instruction byte
  Wire.endTransmission();     // stop transmitting
  delay(200);
  Wire.requestFrom(2,index);// récupérer le nbre de caractères à lire
  for (int j = 1 ; j <= index ; j++)
 // while(Wire.available() > 1) 
      {
      char c = Wire.read(); // réception des caractères stockés
      Serial.print("char ");
      Serial.println(c);
      }
  ack = false;
  index = 0;
  }
}

J'ai essayé de faire un croquis ( sur papier ... peut être pas idéal ) résumé des infos et de la structure.
Si ça aide à la compréhension ?

I2C.pdf (183 KB)

Salut, oui j'avais finalement réussi par comprendre :wink: , Mais pas évident pour ceux qui n'ont pas suivi le sujet du début.
Ce soir vu que j'aurai un peu de temps et que j'ai tout le matos pour le faire, je vais mettre dans les mêmes conditions.

Le top du top, ça serais une fois le problème résolue, de basculer en bidirectionnel!

la mega qui envoie des données à la nano et la nano revoie en serial sur le bluetouch.

j'ai réalise le montage, et mis tes codes.... c'est une usine à gaz...

je vais tenter de réécrire un code ... et commencer par des échanges simple

Il faudra que tu renseigne sur les caractéristiques de la librairie wire (qui gère l'I2C)!!!
parce que vouloir faire passer d'un coup 255 char, alors que les échanges I2C sont limité à 30 octets !!!!

Concernant la librairie Softserial, elle intégré un buffer de 64 bytes (-2) soit 62 caractères.

code carte mega: (maitre)

char c;
#include <Wire.h>

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
    Serial.print("Hello demanrrage MEGA");
}

void loop() {
  Wire.requestFrom(8, 1);    

  while (Wire.available()) { // slave may send less than requested
    c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(50);
  Wire.beginTransmission(8); // transmit to device #8
  Wire.write("char is ");        // sends five bytes
  Wire.write(c);              // sends one byte
  Wire.endTransmission();    // stop transmitting
  delay(50);
}

code carte nano: (esclave)

#include <Wire.h>
#include <SoftwareSerial.h>

SoftwareSerial mySerial(3, 4);

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  Serial.print("Hello demanrrage du nano");
  mySerial.begin(9600);
}

void loop() {
  delay(100);
}

void requestEvent() {
  byte c;
  if (mySerial.available())
  {
    c = mySerial.read();
  }
  else {
    c = 0;  //(Null char.)
  }
  Serial.print("Char :");
  Serial.println((char)(c));
  Wire.write(c);
}

void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
    char d = Wire.read(); // receive byte as a character
    Serial.print(d);         // print the character
  }
  char e = Wire.read();    // receive byte as an integer
  Serial.println(e);         // print the integer
}