Arduino Maitre
Arduino Maitre

Si ce n’est pas encore fait, je vous propose de commencer par lire la présentation générale de ce projet ici

Il est temps de rentrer dans le vif du sujet! Réalisons la première brique de notre système domotique connecté, l’arduino maître!

Pour commencer, je vous conseille de réaliser cette partie sur une platine d’essai, avec un arduino Uno. Lorsque vous serez en mesure de valider le bon fonctionnement du système avec votre première station météo, vous pourrez alors figer le tout sur un PCB.

Matériel nécessaire

Le matériel décrit ci dessous correspond à ce que j’ai utilisé. Il est bien sur possible d’adapter. Notamment pour le module HC12 qui gère les communications radio: Si vous n’avez pas peur de modifier le code des programmes arduino, des couples émetteur/récepteur chinois plus bas de gamme peuvent faire l’affaire

Pour les tests:

En utilisation nominale :

Présentation générale

L’Arduino maître sera connecté à un PC dont il recevra des instructions. Par exemple:

  • « Demande les informations de la station météo 1 »
  • « Demande à l’arduino chauffage de s’arrêter »

Il reçoit également les réponses des autres arduinos afin de les transmettre au PC.

Par ailleurs, il a également un rôle de chef d’orchestre. Il s’assure qu’une seule transmission soit effectuée à la fois afin de ne pas créer d’interférence entre les différents arduinos. Dans le cas ou il est en attente d’une réponse et que le PC lui demande une deuxième information par exemple.

Le montage

Ci dessous un schéma du montage que nous allons réaliser:

J’ai décidé de connecter le module HC12 aux pins 8, 9 et 10 de l’arduino. C’est arbitraire, mais si vous souhaitez changer, il faudra modifier le code en conséquence (rien de bien compliqué!)

  • Les pins GND et VCC permettent d’alimenter le module HC12. Ils sont reliés au 5V et au GND de l’Arduino
  • Le pin SET est connecté au pin 10 de l’arduino. Il permet de passer le module HC12 en mode « configuration », pour préciser la puissance, le canal et le débit utilisés.
  • Le pin TXD est connecté au pin 9 de l’arduino et permet de transmettre des messages
  • Le pin RXD est connecté au pin 8 de l’arduino et permet de recevoir des messages

Le code

Notre montage réalisé, il est temps de programmer le tout!

Le code proposé ci dessous pourrait largement être amélioré. Il est le fruit de nombreux aller-retours et autres modifications. Un bon refactoring est nécessaire. Si l’un d’entre vous souhaite l’améliorer, je suis preneur!

maitre.ino

#include <SoftwareSerial.h>

#define INPUT_SIZE 30
#define PARAM_SIZE 5
#define SET_PIN 10
int firstLoop = 0;
boolean doCallMeteo = false;
char meteoKey[PARAM_SIZE+1];
boolean doCallMeteo2 = false;
boolean doCallMeteo3 = false;
boolean doCallChauffage = false;
char chauffageOrder[PARAM_SIZE+1];
boolean doCallChauffageInfo = false;
int VW_MAX_MESSAGE_LEN = 200;

SoftwareSerial hc12(9,8);
char EOT = 4;

int vitesse = 2400;

void setup() {
  Serial.begin(2400);
  
  pinMode(SET_PIN,OUTPUT);
  digitalWrite(SET_PIN,LOW);
  delay(300);
  hc12.begin(2400);
  hc12.print("AT+B2400");
  delay(200);
  hc12.print("AT+C093");
  delay(200);
  hc12.print("AT+P8");
  delay(200);
  
  digitalWrite(SET_PIN,HIGH);// enter transparent mode

}

void loop() {

  
  //while(1){
    //hc12.print(1);
    //if (hc12.available())
    //{
      //Serial.write(hc12.read());
    //}
  //}
  firstLoop = 1;
  if(doCallMeteo){
    callMeteo();
  }
  if(doCallMeteo2){
    callMeteo2();
  }
  if(doCallMeteo3){
    callMeteo3();
  }
  if(doCallChauffage){
    callChauffage();
  }
  if(doCallChauffageInfo){
    callChauffageInfo();
  }
  if(Serial.available()){
    char input[INPUT_SIZE + 1];
    byte size = Serial.readBytes(input, INPUT_SIZE);
    Serial.flush();
    char* command = NULL;
    char* param = NULL;
    input[size] = 0;
    Serial.println(input);
    char* separator = strchr(input, ' ');
    *separator=0;
    command=input;
    param=separator + 1;
    if(strncmp(command, "METEO", 5) == 0 && strncmp(command, "METEO2", 6) != 0){
      doCallMeteo = true;
      strncpy(meteoKey, param, strlen(param));
      meteoKey[strlen(param)]=0;
    }
    if(strncmp(command, "CHAUFF", 6) == 0){
      doCallChauffage = true;
      strncpy(chauffageOrder, param, strlen(param));
      chauffageOrder[strlen(param)]=0;
    }
    if(strncmp(command, "METEO2", 6) == 0){
      doCallMeteo2 = true;
    }
    /*if(strcmp(command, "METEO3", 6) == 0){
      doCallMeteo3 = true;
    }*/
    if(strncmp(command, "CHINFO", 6) == 0){
      doCallChauffageInfo = true;
    }
      
  }
}

void readTimeout(unsigned long eotTimeoutMs, unsigned int MAXBUF, char* buffer){
  
  hc12.flush();
  for(int j = 0; j < MAXBUF; j++){
    buffer[j] = 0;
  }
  unsigned long initTime = millis();
  
  int dataAvailable = 0;
  while(!hc12.available() && millis() - initTime < eotTimeoutMs);
  if(hc12.available()){
    dataAvailable = 1;
  }
  
  int i = 0;
  char curCar = 0;
  while(dataAvailable && (byte)curCar != EOT && i < MAXBUF - 1 && millis() - initTime < eotTimeoutMs){
    if(hc12.available()){
      curCar = hc12.read();
      if(curCar != 1){   
        buffer[i++] = (char)curCar;
      }
      delay(10);  
    }
  }
  if(i >=1){
    buffer[--i] = '\0';
  }
  hc12.flush();
}

void printError(char* message){
  Serial.print("RECE ");
  Serial.print((char*) message);
  Serial.println(" NO_ANSWER"); 
}

void receptionMeteo() {
  char message[300] = "";

  // On attend de recevoir un message
  Serial.println("LOGA - Reception Meteo 1");
  readTimeout(9000, 300, message);
  if (message[0] != 0) {
    if(strncmp((char*)message, "RECE QT", 7) == 0){
      doCallMeteo = false;
      Serial.println("LOGA - Meteo recue");
      Serial.println((char*) message); // Affiche le message
      delay(2000);
    }
  }else{
    printError("QT");
  }
}

void receptionMeteo2() {
  char message[VW_MAX_MESSAGE_LEN];
  // On attend de recevoir un message
  Serial.println("LOGA - Reception Meteo 2");
  readTimeout(9000, 50, message);
  if (message[0] != 0) {
    if(strncmp((char*)message, "RECE N", 6) == 0){
      doCallMeteo2 = false;
      Serial.println("LOGA - Meteo 2 recue");
      // On copie le message, qu'il soit corrompu ou non
      Serial.println((char*) message); // Affiche le message
      delay(2000);
    }
  }else{
    printError("N");
  }
}

void receptionMeteo3() {
  char message[VW_MAX_MESSAGE_LEN];
  // On attend de recevoir un message
  Serial.println("LOGA - Reception Meteo 3");
  readTimeout(9000, VW_MAX_MESSAGE_LEN, message);
  if (message[0] != 0) {
    if(strncmp((char*)message, "RECE O", 6) == 0){
      doCallMeteo3 = false;
      Serial.println("Meteo 3 recue");
      // On copie le message, qu'il soit corrompu ou non
      Serial.println((char*) message); // Affiche le message
    }
  }else{
    printError("O");
  }
}

void receptionChauffage() {
  char message[VW_MAX_MESSAGE_LEN];
  // On attend de recevoir un message
  Serial.println("LOGA - Reception Chauffage");
  readTimeout(9000, VW_MAX_MESSAGE_LEN, message);
  if (message[0] != 0) {
    if(strncmp((char*)message, "RECE C", 6) == 0){
      doCallChauffage = false;
      Serial.println("LogA - Chauffage recu");
      Serial.println((char*) message); // Affiche le message
      delay(2000);
    }
  }else{
    printError("C");
  }
}

void receptionChauffageInfo() {
  char message[VW_MAX_MESSAGE_LEN];
  // On attend de recevoir un message
  Serial.println("LOGA - Reception Chauffage info");
  readTimeout(9000, VW_MAX_MESSAGE_LEN, message);
  if (message[0] != 0) {
    if(strncmp((char*)message, "RECE D", 6) == 0){
      doCallChauffageInfo = false;
      Serial.println("LogA - Info chauffage recue");
      Serial.println((char*) message); // Affiche le message
      delay(2000);
    }
  }else{
    printError("D");
  }
}

void callMeteo() {
  
  //digitalWrite(SET_PIN,LOW);
  //delay(500);
  //hc12.print("AT+C047");
  //delay(3000);
  //digitalWrite(SET_PIN,HIGH);// enter transparent mode  
  //hc12.flush();
  
  char message[8] = "CALL QT";
  strcat(message, "\0");
  Serial.println((char*) message);
  hc12.write(message);
  receptionMeteo();
}

void callMeteo2() {
  
  //pinMode(SET_PIN,OUTPUT);
  //digitalWrite(SET_PIN,LOW);
  //delay(100);
  //hc12.print("AT+C037");
  //delay(100);
  //digitalWrite(SET_PIN,HIGH);// enter transparent mode
  
  char message[8] = "CALL N";
  strcat(message, "\0");
  hc12.write(message);
  receptionMeteo2();
}

void callMeteo3() {
  char message[8] = "CALL O";
  strcat(message, "\0");
  Serial.println((char*) message);
  hc12.write(message);
  receptionMeteo3();
}

void callChauffage() {
  char message[9] = "CALL C ";
  strcat(message, chauffageOrder);
  strcat(message, "\0");
  Serial.println((char*) message);
  hc12.write(message);
  receptionChauffage();
}

void callChauffageInfo() {
  char message[8] = "CALL D";
  strcat(message, "\0");
  Serial.println((char*) message);
  hc12.write(message);
  receptionChauffageInfo();
}

Quelles sont les choses à retenir sur ce code?

  • La bibilothèque SoftwareSerial est utilisée. Elle est normalement fournie avec l’éditeur arduino.
  • La configuration des différents pins:
    • Ligne 5
    • Ligne 16
  • La configuration du module HC12 se fait dans la fonction setup. J’ai personnellement eu des problèmes de transmission aléatoires qui m’ont fait modifier ces paramètres. Les valeurs doivent du coup pouvoir être optimisées. Attention, cela aura également un impact sur la configuration des autres arduinos.
    • AT+B2400 correspond à un débit de 2400 bauds. C’est peu, mais des débits bien supérieurs sont disponible. Dans notre cas, c’est suffisant.
    • AC+C093 correspond au choix du channel 93. Il y en a 100 disponibles, de 001 à 100. Ils sont espacés de 400kHz, le 001 correspondant à 433.4MHz. La fréquence 433.4MHz étant particulièrement chargée, il peut être utile de s’en écarter. Cependant, les antennes classiques 433.4MHz seront de moins en moins bien adaptées.
    • AT+P8 correspond au choix de la puissance, pouvant varier de 1 à 8. J’ai pu lire qu’une puissance élevée était parfois contre productive dans le cas ou émetteur et récepteur étaient proches. Dans notre cas, on mets le maximum.

Vous trouverez plus de détails sur le HC12 sur cette page

Le programme va surveiller l’entrée série pour différentes chaînes de caractères, correspondant aux ordres que le PC va lui envoyer. Lors de vos tests, ce sont ces chaînes de caractères que vous pourrez envoyer via l’éditeur Arduino.

  • METEO: Pour déclencher l’appel de la station météo 1
  • METEO2: Idem pour la station météo 2
  • CHAUFF 0 ou CHAUFF 1 : Pour commande l’allumage ou l’extinction du chauffage
  • CHINFO: Pour demander l’état du chauffage (allumé ou éteint)

Lorsque l’une de ces commande est détectée, un signal radio va être envoyé. Si tout se passe bien, une réponse sera alors reçue d’un des autres arduinos, et sera transmise au PC.

Dans le cas ou aucune réponse n’est détectée au bout d’un temps donné, le programme retente jusqu’à ce qu’une réponse correcte soit obtenue.

L’envoi et la réception des signaux se fait de manière séquentielle afin d’éviter les interférences.

Conclusion

Voilà! Vous avez normalement un Arduino prêt à commander les différents modules. Certes, ça ne sert pas encore à grand chose, mais ça ne saurait tarder!

Si vous voulez maintenant vraiment savoir quelle température il fait chez vous, rendez vous à la partie suivante!

Dernière modification: 14 octobre 2019

Auteur

Commentaires

Écrire une réponse ou un commentaire

Votre adresse email ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.