Arduino mega avec Ethernet plantage aléatoire du port série et mqtt

Bonjour,

au début, cette arduino est relié à des relais qui me permettent de faire fonctionner les télécommandes de mes volets.
J'utilise plusieurs styles de communication, le Bluetooth et des requêtes http. J'aime avoir plusieurs possibilités au cas où l'un des 2 tombes en panne.

Et comme j'ai beaucoup de temps à passer, pourquoi ne pas utiliser le MQTT.
J'essaye d'abord sur un ESP, Il se connecte en Wi-Fi et sur mon broker tranquillement, il fait ce que je lui demande.

Ensuite, je le modifie pour le faire passer en Ethernet pour l'arduino. Ça marche pas trop mal…
mais au bout de quelques minutes, Tout est allumé mais ni le port série ni les messages MQTT sont reçus.
Pourtant tout est connecté.
Il suffit simplement de déconnecter et rebrancher le câble USB ou l'alimentation électrique pour qu'il repart. Pas très pratique à la longue.

J'ai essayé :

  • Déconnecter le port série
  • Modifier la vitesse du port série
  • Mettre différent délai

J'ai pensé que l'arduino manquait de puissance ou d'autre chose mais avant il fonctionne avec un serveur Web. Ensuite, Est-ce qu'il y a un problème avec le broker qui l'éjecte mais le port série aussi bloc.
Ou ça vient tout simplement d'un problème de mon code

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>

byte mac[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
byte ip[] = {192, 168, 1, 38};

//EthernetServer serveur(80);

// réception/émission Bluetooth
#define SEND_BT_TX 1
#define RECEIVE_BT_RX 0
//SoftwareSerial HC06(RECEIVE_BT_RX, SEND_BT_TX);

// mqtt
const char *mqttServer = "***";
const int mqttPort = 1883;
const char *mqttUser = "***";
const char *mqttPassword = "***";
const char *topicOut = "arduino/volets/retourRelais/state";
const char *topicIn = "arduino/volets/envoiRelais/state";

int pinVolets[30] = {
    /* M - S - D  */
    49, 20, 21, // g
    22, 23, 24, // a
    25, 26, 27, // b
    28, 29, 30, // c
    31, 32, 33, // d
    34, 35, 36, // e
    37, 45, 44, // f
    43, 42, 41, // 1
    40, 39, 38, // 2
    46, 47, 48  // 3
};

EthernetClient ethClient;
PubSubClient mqttClient(ethClient);

String relais, receptionBt, receptionMqtt;
boolean mqttEnvoi;

/********************************/
/* Reconnection au serveur MQTT */
/********************************/
void reconnection_mqtt() {
  // Loop until we're reconnected
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (mqttClient.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      mqttClient.publish("outTopic", "hello world");
      // ... and resubscribe
      mqttClient.subscribe("inTopic");
    }
    else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// Réception MQTT
void callback(char *topic, byte *payload, unsigned int length) {
  Serial.print("Message reçu : ");
  Serial.println(topic);
  for (int i = 0; i < length; i++) {
    // Serial.print((char) payload[i]);
    receptionMqtt += (char)payload[i];
  }
  Serial.println();
  mqttEnvoi = false;
  recuperation_relais(receptionMqtt);
  receptionMqtt = "";
  // Serial.write(payload, length);
}

void envoi_mqtt(const char *y, String x) {
  // Conversion String-> char
  char mqttEnvoi[x.length() + 1];
  x.toCharArray(mqttEnvoi, x.length() + 1);
  // Envoi MQTT
  mqttClient.publish(y, mqttEnvoi);
}

/********************************************/
/* Récupération des relais via le Bluetooth */
/********************************************/
void reception_msg_bt() {
  if (Serial.available() > 0) {
    mqttEnvoi = true;
    receptionBt = Serial.readString();
    recuperation_relais(receptionBt);
  }
}

void recuperation_relais(String x) {
  for (int i = 0; i < x.length(); i++) {
    int a = x.indexOf('-');
    if (a != -1) {
      relais = x.substring(0, a);
      x.remove(0, a + 1);
      if (mqttEnvoi) {
        envoi_mqtt(topicOut, relais);
      }
      Serial.println(relais);
      action_relais(relais);
    }
    else {
      relais = x;
      x.remove(0);
      if (mqttEnvoi) {
        envoi_mqtt(topicOut, relais);
      }
      Serial.println("relais: " + relais);
      action_relais(relais);
    }
  }
}

void action_relais(String w) {
  int relaisInt = w.toInt();
  for (int i = 0; i < 30; i++) {
    if (pinVolets[i] == relaisInt) {
      digitalWrite(relaisInt, LOW);
      delay(2000);
      digitalWrite(relaisInt, HIGH);
      delay(200);
      Serial.println(" Relais actif :");
      Serial.println(relaisInt);
    }
  }
}

void setup() {
  Serial.begin(9600);
  Ethernet.begin(mac, ip);
  delay(1000);
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.connect("mega", mqttUser, mqttPassword);
  mqttClient.subscribe(topicIn);
  mqttClient.setCallback(callback);
  for (int p = 0; p < 30; p++) {
    pinMode(pinVolets[p], OUTPUT);
    digitalWrite(pinVolets[p], HIGH);
  }
}

void loop() {
  if (!mqttClient.connected()) {
    reconnection_mqtt();
  }
  mqttClient.loop();
  reception_msg_bt();
  mqttClient.subscribe("inTopic");
}

Ce n'est pas parfait loin de la, ça doit peut-être même faire grincer des dents pour certains.
J'ai fait quelques essais avec certaines fonctions pas toujours utiles mais ça m'apprend.

J'espère que j'ai pu être assez clair dans mes explications.

Merci beaucoup d'avance pour vos réponses futures.

La Mega possède 4 port série.
Pourquoi partager Serial entre les messages d'état du système et la gestion du Bluetooth?
Même remarque concernant l'usage de SoftwareSerial, pourquoi SoftwareSerial alors que tu as plusieurs ports série matériel à disposition?
Il serait judicieux d'utiliser :

  • Serial pour le téléchargement du code et les messages de debug
  • Serial1, 2 ou 3 pour le reste

Quel est le compte-rendu de compilation concernant l'usage de la RAM, car il y a des fonctions gourmandes en mémoire comme la pile Ethernet, l'usage des String et aussi beaucoup de chaînes de caractères.

Merci pour la réponse,

Pourquoi je partage le même Serial, en toute franchise, quand j'ai fait mon tout premier code j'ai utilisé ce système-là et ça fonctionnait donc je n'ai pas réfléchi plus loin. Et je l'ai repris.
Maintenant je comprends subitement que la bibliothèque SoftwareSerial ne sert à rien ici. (Mieux vaut tard que jamais)

Pour le compte rendu compilation, les variables prennent 10 %.
L'usage des strings, j'ai vu qu'en fouillant un peu le forum que c'était mieux d'utiliser les "Char" mais je n'arrive pas à faire la même chose.
Ça ne doit pas être trop compliqué et sûrement bien plus efficace.

Je vais essayer de trifouiller 2-3 trucs avec tout ça.
Merci

Regardez s'il n'y a pas un timeout avec MQTT qui fait qu'au bout d'un moment vous vous déconnectez et la reconnection ne fonctionnerait pas ? (vérifiez les traces dans cette partie)

C'est la question que je me suis posé aussi.

Mais le port série se bloque en même temps, c'est pour ça que je ne comprends pas

ça ressemble alors plus à un plantage de l'arduino...

rajoutez un petit code asynchrone à la fin de la loop qui fait clignoter une LED toutes les secondes, ça vous permettra de voir si la loop() tourne toujours

Merci pour l'info.

Je vais le faire le plus rapidement possible et je reviens vers vous

J'ai mis en place une led qui clignote toutes les secondes comme vous m'avez demandé d'essayer de faire.

J'ai même supprimé la bibliothèque inutile.

La première mise en route a duré environ 17 heures. Et d'un seul coup, plantage sans aucune raison car je n'en vois pas des données tous les 5 minutes.

Après le redémarrage, ça va faire 5 heures (à la date de ce message) que l'arduino tourne.

quand vous dites plantage ça veut dire que la LED ne clignote plus ?

La led s'arrête (état allumé ou éteint) et la communication MQTT S'arrête

OK donc ça sent bien le plantage...

pas simple à débuguer. pouvez vous poster le code "à jour" ?

Il n'y a pas beaucoup de différence avec le premier

#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>

byte mac[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
byte ip[] = {192, 168, 1, 38};

// mqtt
const char *mqttServer = "";
const int mqttPort = 1883;
const char *mqttUser = "";
const char *mqttPassword = "";
const char *topicOut = "arduino/volets/retourRelais/state";
const char *topicIn = "arduino/volets/envoiRelais/state";

int pinVolets[30] = {
    /* M - S - D  */
    49, 20, 21, // g
    22, 23, 24, // a
    25, 26, 27, // b
    28, 29, 30, // c
    31, 32, 33, // d
    34, 35, 36, // e
    37, 45, 44, // f
    43, 42, 41, // 1
    40, 39, 38, // 2
    46, 47, 48  // 3
};

//Test s'y loop fonctionne bien
int maLed = 14;
bool ledState = 0;
long prevMillis = 0;
int ledDelay = 1000;

EthernetClient ethClient;
PubSubClient mqttClient(ethClient);

String relais, receptionBt, receptionMqtt;
bool mqttEnvoi;

/********************************/
/* Reconnection au serveur MQTT */
/********************************/
void reconnection_mqtt() {
  // Loop until we're reconnected
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (mqttClient.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      mqttClient.publish("outTopic", "hello world");
      // ... and resubscribe
      mqttClient.subscribe("inTopic");
    }
    else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// Réception MQTT
void callback(char *topic, byte *payload, unsigned int length) {
  Serial.print("Message reçu : ");
  Serial.println(topic);
  for (int i = 0; i < length; i++) {
    // Serial.print((char) payload[i]);
    receptionMqtt += (char)payload[i];
  }
  Serial.println();
  mqttEnvoi = false;
  recuperation_relais(receptionMqtt);
  receptionMqtt = "";
  // Serial.write(payload, length);
}

void envoi_mqtt(const char *y, String x) {
  // Conversion String-> char
  char mqttEnvoi[x.length() + 1];
  x.toCharArray(mqttEnvoi, x.length() + 1);
  // Envoi MQTT
  mqttClient.publish(y, mqttEnvoi);
}

/********************************************/
/* Récupération des relais via le Bluetooth */
/********************************************/
void reception_msg_bt() {
  if (Serial.available() > 0) {
    mqttEnvoi = true;
    receptionBt = Serial.readString();
    recuperation_relais(receptionBt);
  }
}

void recuperation_relais(String x) {
  for (int i = 0; i < x.length(); i++) {
    int a = x.indexOf('-');
    if (a != -1) {
      relais = x.substring(0, a);
      x.remove(0, a + 1);
      if (mqttEnvoi) {
        envoi_mqtt(topicOut, relais);
      }
      Serial.println(relais);
      action_relais(relais);
    }
    else {
      relais = x;
      x.remove(0);
      if (mqttEnvoi) {
        envoi_mqtt(topicOut, relais);
      }
      Serial.println("relais: " + relais);
      action_relais(relais);
    }
  }
}

void action_relais(String w) {
  int relaisInt = w.toInt();
  for (int i = 0; i < 30; i++) {
    if (pinVolets[i] == relaisInt) {
      digitalWrite(relaisInt, LOW);
      delay(2000);
      digitalWrite(relaisInt, HIGH);
      delay(200);
      Serial.println(" Relais actif :");
      Serial.println(relaisInt);
    }
  }
}

void setup() {
  Serial.begin(115200);
  //Serial1.begin(9600);
  Ethernet.begin(mac, ip);
  delay(1000);
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.connect("mega", mqttUser, mqttPassword);
  mqttClient.subscribe(topicIn);
  mqttClient.setCallback(callback);
  for (int p = 0; p < 30; p++) {
    pinMode(pinVolets[p], OUTPUT);
    digitalWrite(pinVolets[p], HIGH);
  }
  pinMode(maLed, OUTPUT);
}

void loop() {
  if (!mqttClient.connected()) {
    reconnection_mqtt();
  }
  mqttClient.loop();
  reception_msg_bt();
  mqttClient.subscribe("inTopic");
  if (millis() - prevMillis > ledDelay) {
    ledState =! ledState;
    digitalWrite(maLed, ledState);
    prevMillis = millis();
  }
}

la première chose que je ferais ce serait de virer toutes ces String... Vous n'êtes pas à l'abri d'un souci mémoire.

pourquoi faire ça dans la loop?

  mqttClient.subscribe("inTopic");

La fonction reconnection_mqtt() contient une boucle while (!mqttClient.connected()) , ce qui peut bloquer indéfiniment si le client MQTT ne parvient pas à se reconnecter.

Voyez vous des choses imprimées dans le terminal série avant le plantage ?

Comme plus haut, j'ai vu ça qu'il fallait éviter le plus possible les variables "String" par les "char".
Je vais Essayer de mettre en place ce changement.

Et la boucle loop, J'ai suivi bêtement 1-2 articles comme je ne connaissais vraiment pas le principe.
Je vais les effacer et tester. Mais maintenant ça paraît tellement logique.

Et avant le plantage, aucun message sur le terminal série

Re bonjour,

J'ai suivi les conseils de votre précédent message et ça fait 4 jours que ça tourne sans problème. En espérant que ça dure.
Je changerai les variables tranquillement sachant que ça fonctionne déjà comme ça.

C'est quand même fou d'avoir la solution sous les yeux et de ne pas le voir.

Merci beaucoup pour l'aide et la solution.

tant mieux !

bonne continuation