Créer des réseaux Arduino longue portée fiables avec le module HC-12
Dans ce tutoriel Arduino, nous allons apprendre à utiliser le module de communication série sans fil HC-12, capable d'établir une communication sans fil longue portée entre plusieurs cartes Arduino, avec des distances allant jusqu'à 1,8 km. Vous pouvez visionner la vidéo suivante ou lire le tutoriel écrit ci-dessous pour plus de détails.
Présentation
Pour ce tutoriel, j'ai réalisé deux exemples de base expliquant comment connecter le module HC-12 et établir une communication basique entre deux Arduino, ainsi qu'un exemple supplémentaire où, à l'aide d'un capteur accéléromètre sur le premier Arduino, je contrôle sans fil la position d'un moteur pas à pas sur le second Arduino.
Module de communication sans fil HC-12
Examinons de plus près le module de port série sans fil HC-12. Voici ses spécifications :
- Sa bande de fréquence de travail sans fil s'étend de 433,4 MHz à 473,0 MHz
- Il dispose de 100 canaux au total, avec un pas de 400 kHz entre chaque canal
- La puissance d'émission varie de -1 dBm (0,79 mW) à 20 dBm (100 mW)
- La sensibilité de réception va de -117 dBm (0,019 pW) à -100 dBm (10 pW)
Ces valeurs dépendent en réalité du débit en bauds série et over-the-air sélectionné, comme indiqué dans le tableau.
Le module HC-12 intègre un microcontrôleur qui n'a pas besoin d'être programmé par l'utilisateur. Pour configurer le module, on utilise simplement des commandes AT, qui peuvent être envoyées depuis un Arduino, un PC ou tout autre microcontrôleur via le port série. Pour entrer en mode commande AT, il suffit de mettre la broche « Set » du module à un niveau logique bas.
Arduino et HC-12
Connectons maintenant le module HC-12 à l'Arduino et réalisons le premier exemple. Voici le schéma de câblage. La tension de fonctionnement du module va de 3,2 V à 5,5 V et pour un fonctionnement plus stable, il est recommandé d'utiliser un condensateur de découplage et une alimentation externe. Toutefois, j'ai utilisé l'alimentation USB du PC pour les trois exemples de ce tutoriel sans rencontrer de problème.
J'ai connecté le premier module à un Arduino UNO et le second à un Arduino MEGA, mais vous pouvez bien sûr utiliser n'importe quelle carte.
Exemple 01 – Code Arduino
Voici le code Arduino pour le premier exemple, une communication basique entre les deux modules via le Moniteur Série.
/* Communication sans fil longue portée Arduino avec HC-12 Exemple 01 par Dejan Nedelkovski, www.HowToMechatronics.com */
#include <SoftwareSerial.h>
SoftwareSerial HC12(10, 11); // Broche TX HC-12, Broche RX HC-12
void setup() {
Serial.begin(9600); // Port série vers l'ordinateur
HC12.begin(9600); // Port série vers HC12
}
void loop() {
while (HC12.available()) { // Si HC-12 a des données
Serial.write(HC12.read()); // Envoyer les données au Moniteur Série
}
while (Serial.available()) { // Si le Moniteur Série a des données
HC12.write(Serial.read()); // Envoyer ces données au HC-12
}
}
Le même code est utilisé pour les deux Arduino. On peut connecter les deux Arduino sur deux ordinateurs distincts, mais aussi utiliser un seul ordinateur.
Dans ce cas, une fois le premier Arduino connecté à l'ordinateur, on sélectionne le modèle et le port COM, puis on téléverse le code. Ensuite, on connecte le second Arduino et il faut relancer l'IDE Arduino pour pouvoir sélectionner l'autre port COM auquel le second Arduino est connecté, et téléverser le même code.
Une fois les deux IDE Arduino en marche, on peut ouvrir les moniteurs séries et tester si la communication fonctionne correctement. Tout ce qu'on tape dans le moniteur série sera envoyé de l'un à l'autre Arduino.
Fonctionnement du code : Quand on tape quelque chose dans le moniteur série et qu'on clique sur Envoyer, au niveau du premier Arduino, la boucle while avec la fonction Serial.available() devient vraie et, grâce à la fonction HC12.write(), on envoie les données du moniteur série au module HC-12. Ce module transmet les données sans fil au second module HC-12 ; ainsi, au second Arduino, la boucle while avec HC12.available() devient vraie et, via Serial.write(), les données sont envoyées au moniteur série.
On peut utiliser le même code pour envoyer des commandes AT et configurer les paramètres du module. Il suffit de connecter la broche « Set » du module à la masse (GND) ou à une broche numérique de l'Arduino et de la mettre à l'état bas.
Pour tester si on a bien entré en mode commande, dans le moniteur série on tape « AT » et on doit recevoir une réponse « OK ». Il existe au total 12 commandes AT, utilisées pour modifier divers paramètres comme le débit en bauds, le canal, la puissance d'émission, etc. Par exemple, si on tape « AT+B38400 », le débit en bauds du module sera réglé à 38400.
Commandes AT :
1. AT – Commande de test.
Exemple : Envoyer « AT » au module, qui répond « OK ».
2. AT+Bxxxx – Modifier le débit en bauds du port série.
Débits disponibles : 1200, 2400, 4800, 9600, 19200, 38400, 57600 et 115200 bauds. Défaut : 9600 bauds.
Exemple : Envoyer « AT+B38400 » au module, qui répond « OK+B19200 ».
3. AT+Cxxxx – Changer le canal de communication sans fil, de 001 à 100.
Défaut : Canal 001, fréquence de travail 433,4 MHz. Chaque canal suivant est supérieur de 400 kHz.
Exemple : Pour régler le module sur le canal 006, envoyer « AT+C006 » ; le module répond « OK+C006 ». La nouvelle fréquence de travail sera 435,4 MHz.
Exemple 02
Passons maintenant au second exemple. Nous allons utiliser deux boutons poussoirs pour sélectionner différents canaux de communication et découvrir une autre méthode de stockage des données entrantes.
Note : Les broches « Set » des deux modules HC-12 sont connectées aux broches n°6 des deux Arduino, et les deux boutons, sur le premier Arduino, aux broches 4 et 3.
Code du premier Arduino (émetteur avec boutons) :
/* Arduino Communication sans fil longue portée HC-12 Exemple 02 - Changement de canaux par boutons - Côté boutons par Dejan Nedelkovski */
#include <SoftwareSerial.h>
#define setPin 6
#define button1 4
#define button2 3
SoftwareSerial HC12(10, 11);
byte incomingByte;
String readBuffer = "";
int button1State = 0, button1Pressed = 0;
int button2State = 0, button2Pressed = 0;
void setup() {
Serial.begin(9600);
HC12.begin(9600);
pinMode(setPin, OUTPUT);
pinMode(button1, INPUT);
pinMode(button2, INPUT);
digitalWrite(setPin, HIGH); // HC-12 mode normal, transparent
}
void loop() {
// Stockage des données entrantes dans une variable String
while (HC12.available()) {
incomingByte = HC12.read();
readBuffer += char(incomingByte);
}
delay(100);
// Envoi de données d'un HC-12 à l'autre via le Moniteur Série
while (Serial.available()) {
HC12.write(Serial.read());
}
// Si bouton 1 pressé, régler canal 01
button1State = digitalRead(button1);
if (button1State == HIGH && button1Pressed == LOW) {
button1Pressed = HIGH;
delay(20);
}
if (button1Pressed == HIGH) {
HC12.print("AT+C001");
delay(100);
digitalWrite(setPin, LOW); // Mode commande AT
delay(100);
HC12.print("AT+C001");
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
Serial.println("Canal changé avec succès");
digitalWrite(setPin, HIGH); // Quitter mode commande
button1Pressed = LOW;
}
// Si bouton 2 pressé, régler canal 02
button2State = digitalRead(button2);
if (button2State == HIGH && button2Pressed == LOW) {
button2Pressed = HIGH;
delay(100);
}
if (button2Pressed == HIGH) {
HC12.print("AT+C002");
delay(100);
digitalWrite(setPin, LOW);
delay(100);
HC12.print("AT+C002");
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
Serial.println("Canal changé avec succès");
digitalWrite(setPin, HIGH);
button2Pressed = LOW;
}
checkATCommand();
readBuffer = "";
}
void checkATCommand() {
if (readBuffer.startsWith("AT")) {
digitalWrite(setPin, LOW);
delay(200);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
digitalWrite(setPin, HIGH);
}
}
Code du second Arduino (récepteur) :
/* Arduino Communication sans fil longue portée HC-12 Exemple 02 - Changement de canaux par boutons par Dejan Nedelkovski */
#include <SoftwareSerial.h>
#define setPin 6
SoftwareSerial HC12(10, 11);
byte incomingByte;
String readBuffer = "";
void setup() {
Serial.begin(9600);
HC12.begin(9600);
pinMode(setPin, OUTPUT);
digitalWrite(setPin, HIGH); // Mode normal
}
void loop() {
while (HC12.available()) {
incomingByte = HC12.read();
readBuffer += char(incomingByte);
}
delay(100);
while (Serial.available()) {
HC12.write(Serial.read());
}
// Si bouton 1 pressé, régler canal 01
if (readBuffer == "AT+C001") {
digitalWrite(setPin, LOW);
delay(100);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
Serial.println("Canal changé avec succès");
digitalWrite(setPin, HIGH);
readBuffer = "";
}
// Si bouton 2 pressé, régler canal 02
if (readBuffer == "AT+C002") {
digitalWrite(setPin, LOW);
delay(100);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
Serial.println("Canal changé avec succès");
digitalWrite(setPin, HIGH);
readBuffer = "";
}
checkATCommand();
readBuffer = "";
}
void checkATCommand() {
if (readBuffer.startsWith("AT")) {
digitalWrite(setPin, LOW);
delay(100);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
digitalWrite(setPin, HIGH);
}
}
Description des codes :
On commence par définir les broches et mettre la broche « Set » à l'état haut pour que le module fonctionne en mode normal transparent. La première boucle while stocke les données entrantes dans une variable String pour mieux les gérer.
Les données arrivent toujours octet par octet. Par exemple, si on envoie la chaîne « Test123 » depuis le second Arduino, cette boucle fera 7 itérations. À chaque itération, avec HC12.read() on lit chaque octet entrant et on l'ajoute à la variable String « readBuffer ».
Voyons comment changer le canal de communication avec le premier bouton. Si on appuie sur le premier bouton, avec HC12.print() on envoie la chaîne « AT+C001 » au module HC-12 ou au second Arduino.
Quand cette chaîne est reçue au second Arduino, on met le module HC-12 en mode commande AT, puis on écrit la même chaîne « AT+C001 » dedans, ce qui règle le module sur le canal de communication numéro un.
On utilise la boucle while suivante pour afficher le message de réponse du module HC-12 confirmant que le canal a bien été changé.
De retour au premier Arduino, on fait la même procédure pour envoyer la commande AT au premier module HC-12. De la même façon, en appuyant sur le second bouton, on règle le canal de communication numéro deux. Ainsi, avec cette méthode, on peut sélectionner à tout moment avec quel module HC-12 on communiquera.
À la fin, la fonction personnalisée checkATCommand() vérifie si le message reçu est une commande AT, en testant si la chaîne commence par « AT ». Si c'est le cas, le module entre en mode commande AT et exécute la commande.
Communication sans fil HC-12 : Contrôle d'un moteur pas à pas avec un accéléromètre
Regardons maintenant le troisième exemple. Ici, on contrôle la position du moteur pas à pas sur le second Arduino, à l'aide du module accéléromètre sur le premier Arduino.
Le circuit contient aussi un micro-interrupteur pour trouver la position initiale du moteur pas à pas à 0 degrés.
Premier Arduino – Code émetteur :
/* Arduino Communication sans fil longue portée HC-12 Exemple 03 - Contrôle moteur pas à pas par accéléromètre - Émetteur, Accéléromètre par Dejan Nedelkovski */
#include <SoftwareSerial.h>
#include <Wire.h>
SoftwareSerial HC12(10, 11);
float angle;
int lastAngle = 0, count = 0, angleSum = 0;
// Registres accéléromètre ADXL345
#define Power_Register 0x2D
#define X_Axis_Register_DATAX0 0x32
#define X_Axis_Register_DATAX1 0x33
#define Y_Axis_Register_DATAY0 0x34
#define Y_Axis_Register_DATAY1 0x35
#define Z_Axis_Register_DATAZ0 0x36
#define Z_Axis_Register_DATAZ1 0x37
int ADXAddress = 0x53;
int X0, X1, X_out, Y0, Y1, Y_out, Z1, Z0, Z_out;
float Xa, Ya, Za;
void setup() {
HC12.begin(9600);
Wire.begin();
Serial.begin(9600);
delay(100);
Wire.beginTransmission(ADXAddress);
Wire.write(Power_Register);
Wire.write(8); // Bit D3 haut pour activer la mesure
Wire.endTransmission();
}
void loop() {
// Axe X
Wire.beginTransmission(ADXAddress);
Wire.write(X_Axis_Register_DATAX0);
Wire.write(X_Axis_Register_DATAX1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress, 2);
if (Wire.available() <= 2) {
X1 = Wire.read();
X1 = X1 << 8;
X_out = X0 + X1;
Xa = X_out / 256.0;
}
// Axe Y
Wire.beginTransmission(ADXAddress);
Wire.write(Y_Axis_Register_DATAY0);
Wire.write(Y_Axis_Register_DATAY1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress, 2);
if (Wire.available() <= 2) {
Y0 = Wire.read();
Y1 = Wire.read();
Y1 = Y1 << 8;
Y_out = Y0 + Y1;
Ya = Y_out / 256.0;
}
// Combiner X et Y pour obtenir l'angle de 0 à 180 degrés
if (Y_out > 0) {
angle = map(Y_out, 0, 256, 90, 0);
} else if (Y_out < 0) {
angle = map(Y_out, 256, 0, 90, 0);
angle = 90 - angle;
}
if (X_out < 0 && Y_out < 0) angle = 180;
if (X_out < 0 && Y_out > 0) angle = 0;
int angleInt = int(angle);
// Faire 100 lectures et envoyer la moyenne pour un résultat plus lisse
angleSum += angleInt;
count++;
if (count >= 100) {
angleInt = angleSum / 100;
angleSum = 0;
count = 0;
// Lissage supplémentaire : envoyer le nouvel angle seulement s'il diffère du précédent de ±2
if (angleInt > lastAngle + 2 || angleInt < lastAngle - 2) {
Serial.println(angleInt);
String angleString = String(angleInt);
// Envoi avec marqueur de début 's' et marqueur de fin 'e'
HC12.print("s" + angleString + "e");
delay(10);
lastAngle = angleInt;
}
}
}
Second Arduino – Code récepteur :
/* Arduino Communication sans fil longue portée HC-12 Exemple 03 - Contrôle moteur pas à pas par accéléromètre - Récepteur, Moteur pas à pas par Dejan Nedelkovski */
#include <SoftwareSerial.h>
SoftwareSerial HC12(10, 11);
char incomingByte;
String readBuffer = "";
const int dirPin = 4, stepPin = 3, button = 2;
int currentAngle = 0, lastAngle = 0, rotate = 0;
void setup() {
Serial.begin(9600);
HC12.begin(9600);
pinMode(dirPin, OUTPUT);
pinMode(stepPin, OUTPUT);
pinMode(button, INPUT_PULLUP);
delay(10);
digitalWrite(dirPin, HIGH);
boolean startingPosition = true;
while (startingPosition) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(200);
digitalWrite(stepPin, LOW);
delayMicroseconds(200);
if (digitalRead(button) == LOW) startingPosition = false;
}
delay(100);
}
void loop() {
readBuffer = "";
boolean start = false;
// Lecture de l'angle entrant
while (HC12.available()) {
incomingByte = HC12.read();
delay(5);
if (start) {
if (incomingByte != 'e') {
readBuffer += char(incomingByte);
} else {
start = false;
}
} else if (incomingByte == 's') {
start = true;
}
}
// Conversion en entier
currentAngle = readBuffer.toInt();
// Vérifier que l'angle est entre 0 et 180
if (currentAngle > 0 && currentAngle < 180) {
// Convertir l'angle en pas (selon la résolution de pas sélectionnée)
// Un cycle = 200 pas, 180° = 100 pas ; Résolution : Seizième de pas x16
currentAngle = map(currentAngle, 0, 180, 0, 1600);
digitalWrite(dirPin, LOW);
// Faire tourner le moteur du nombre de pas différent de la position précédente
if (currentAngle != lastAngle) {
if (currentAngle > lastAngle) {
rotate = currentAngle - lastAngle;
for (int x = 0; x < rotate; x++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(400);
digitalWrite(stepPin, LOW);
delayMicroseconds(400);
}
}
if (currentAngle < lastAngle) {
rotate = lastAngle - currentAngle;
digitalWrite(dirPin, HIGH); // Change la direction
for (int x = 0; x < rotate; x++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(400);
digitalWrite(stepPin, LOW);
delayMicroseconds(400);
}
}
}
lastAngle = currentAngle; // Mémoriser la position actuelle
}
}
Description des codes :
On définit d'abord les broches et on initialise les modules dans la section setup. Ensuite, on lit les valeurs des axes X et Y de l'accéléromètre et on les mappe sur des valeurs de 0 à 180 degrés. Les valeurs de l'accéléromètre peuvent être instables, donc pour lisser le résultat, j'ai utilisé la moyenne de cent lectures.
Pour un lissage encore plus poussé, j'envoie la nouvelle valeur de l'angle seulement si elle diffère de la précédente de 2.
Notez que lors de l'envoi de l'angle au module HC-12, j'envoie aussi le caractère « s » au début et « e » à la fin, ce qui m'aide à la réception sur le second Arduino.
Au second Arduino, on attend l'arrivée du marqueur de début « s », puis on lit la valeur de l'angle jusqu'à l'arrivée du marqueur de fin « e ». Ainsi, on est sûr de ne recevoir que la valeur de l'angle.
Ensuite, on convertit la valeur en entier, on mappe la valeur de 0 à 1600 pas (correspondant à la résolution seizième de pas sélectionnée sur le driver A4988), et on fait tourner le moteur pas à pas à l'angle courant.
C'est tout pour ce tutoriel Arduino. N'hésitez pas à poser vos questions dans la section commentaires ci-dessous.
Processus de fabrication
- Maîtriser la portée de communication sans fil : Comment puissance, débit et interférences façonnent la connectivité
- Comment le Sans Fil Longue Portée Alimenté par Batterie Transforme la Connectivité
- Créer des animations et jeux sur LCD avec Arduino UNO
- JX Wave Generator – Générateur DDS compatible Arduino avec affichage OLED
- Module GPS u-blox LEA‑6H 02 : guide d'intégration Arduino et Python
- Scanner IR de température sans fil – Arduino Nano, Bluetooth & MLX90614
- Contrôle Arduino UNO depuis Python 3 – Communication Série et Démo LED
- Construire une Radio FM Compacte avec Arduino Nano et RDA5807M
- Système avancé de détection de chutes avec Arduino, Windows IoT et Azure Cloud