Contrôle Double Moteur avec Tachymètre et Affichage sur Écran Couleur
Description :
Ce projet consiste en une plateforme de pilotage et de monitoring de précision pour deux moteurs à courant continu (CC). Piloté par un ESP32, le système permet de commander la puissance des moteurs en temps réel via des potentiomètres, tout en offrant un retour visuel dynamique sur un écran couleur .
Fonctionnalités Principales
- Pilotage Manuel : Commande de la vitesse par Modulation de Largeur d’Impulsion (PWM 10-bit) via deux potentiomètres.
- Tachymétrie Précise : Calcul de la vitesse de rotation en RPM (Tours par minute) grâce à l’utilisation des décodeurs matériels (PCNT) de l’ESP32.
- Odométrie : Suivi de la position cumulée (nombre de tours total) pour chaque moteur.
Prérequis :
- 1 x Carte ESP32
- 2 x Potentiomètre 10KΩ
- 2 x JGB37-520 (12V 530 tr/min)
- 2 x DRV8871
- 1 x 2.0 inch TFT Display OLED LCD GMT020-02 240×320
- 1 x Breadboard
Version IDE :
- Arduino IDE 2.3.5
Bibliothèque :
- Adafruit_GFX.h (version: 1.17.4 par Adafruit)
- Adafruit_ST7789.h (version: 1.11.0 par Adafruit)
- Adafruit BusIO (version: 1.7.4 par Adafruit)
Vidéo de démonstration :
NA
Schéma de câblage :


Code :
#include <Adafruit_GFX.h> // Bibliothèque de base pour les graphismes
#include <Adafruit_ST7789.h> // Bibliothèque spécifique pour l'écran ST7789
#include <SPI.h> // Communication série haute vitesse avec l'écran
#include "driver/pcnt.h" // Driver ESP32 pour le compteur d'impulsions matériel
// --- CONFIGURATION ÉCRAN GMT020 (ST7789) ---
#define TFT_CS 5 // Pin Chip Select
#define TFT_RST 4 // Pin Reset
#define TFT_DC 27 // Pin Data/Command
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// Définition des couleurs personnalisées
#define COULEUR_FOND 0x1082 // Bleu-gris foncé pour le fond des cadres
#define COULEUR_VIDE 0x0000 // Noir pour effacer les barregraphes
// --- CONFIGURATION MOTEURS ---
#define M1_PHA 34 // Encodeur Moteur 1 Phase A
#define M1_PHB 35 // Encodeur Moteur 1 Phase B
#define M2_PHA 32 // Encodeur Moteur 2 Phase A
#define M2_PHB 33 // Encodeur Moteur 2 Phase B
#define POT1_PIN 36 // Potentiomètre vitesse Moteur 1
#define POT2_PIN 39 // Potentiomètre vitesse Moteur 2
#define PWM_M1 12 // Sortie PWM vers driver Moteur 1
#define PWM_M2 13 // Sortie PWM vers driver Moteur 2
// Paramètres PWM
const int freq = 5000; // Fréquence de 5 kHz
const int res = 10; // Résolution de 10 bits (0 à 1023)
// Paramètres mécaniques pour le calcul des tours
const float GEAR_RATIO = 19.0; // Rapport de réduction du moteur
const int ENCODER_PPR = 11; // Impulsions par tour (Pulse Per Revolution)
const float TOTAL_PPR = GEAR_RATIO * ENCODER_PPR * 2.0; // Total d'impulsions pour 1 tour de roue
// Variables de temps et compteurs
unsigned long prevMillis = 0;
const long interval = 500; // Rafraîchissement de l'affichage toutes les 500ms
double totalPulsesM1 = 0; // Cumul total des impulsions Moteur 1
double totalPulsesM2 = 0; // Cumul total des impulsions Moteur 2
/**
* Configuration du module PCNT (Pulse Counter) de l'ESP32
* Permet de compter les impulsions des encodeurs en arrière-plan sans charger le CPU.
*/
void setup_pcnt(pcnt_unit_t unit, int gpioA, int gpioB) {
pcnt_config_t config = {
.pulse_gpio_num = gpioA,
.ctrl_gpio_num = gpioB,
.lctrl_mode = PCNT_MODE_KEEP, // Garde le mode actuel si bas
.hctrl_mode = PCNT_MODE_REVERSE, // Inverse le comptage si haut (sens rotation)
.pos_mode = PCNT_COUNT_INC, // Incrémente sur front montant
.neg_mode = PCNT_COUNT_DEC, // Décrémente sur front descendant
.counter_h_lim = 32767,
.counter_l_lim = -32768,
.unit = unit,
.channel = PCNT_CHANNEL_0,
};
pcnt_unit_config(&config);
pcnt_counter_pause(unit);
pcnt_counter_clear(unit);
pcnt_counter_resume(unit);
}
void setup() {
Serial.begin(115200);
// Initialisation des sorties PWM
ledcAttach(PWM_M1, freq, res);
ledcAttach(PWM_M2, freq, res);
// Initialisation de l'écran
tft.init(240, 320);
tft.setRotation(1); // Mode paysage
tft.fillScreen(ST77XX_BLACK);
// Dessin statique de l'interface (cadres des moteurs)
tft.fillRect(5, 5, 310, 108, COULEUR_FOND);
tft.drawRect(5, 5, 310, 108, ST77XX_GREEN);
tft.fillRect(5, 120, 310, 108, COULEUR_FOND);
tft.drawRect(5, 120, 310, 108, ST77XX_CYAN);
// Initialisation des compteurs matériels pour les deux encodeurs
setup_pcnt(PCNT_UNIT_0, M1_PHA, M1_PHB);
setup_pcnt(PCNT_UNIT_1, M2_PHA, M2_PHB);
}
void loop() {
// 1. Lecture des potentiomètres (0-4095) et conversion pour le PWM (0-1023)
int valPot1 = analogRead(POT1_PIN);
int valPot2 = analogRead(POT2_PIN);
int duty1 = map(valPot1, 0, 4095, 0, 1023);
int duty2 = map(valPot2, 0, 4095, 0, 1023);
// 2. Application de la puissance aux moteurs
ledcWrite(PWM_M1, duty1);
ledcWrite(PWM_M2, duty2);
// 3. Gestion de l'affichage à intervalle régulier (non-bloquant)
unsigned long currentMillis = millis();
if (currentMillis - prevMillis >= interval) {
// Récupération des valeurs des encodeurs
int16_t count1, count2;
pcnt_get_counter_value(PCNT_UNIT_0, &count1);
pcnt_get_counter_value(PCNT_UNIT_1, &count2);
pcnt_counter_clear(PCNT_UNIT_0); // Reset du compteur pour la prochaine mesure
pcnt_counter_clear(PCNT_UNIT_1);
// Mise à jour de la distance totale
totalPulsesM1 += count1;
totalPulsesM2 += count2;
// Calcul des RPM : (impulsions / temps) -> tours / minute
float rpm1 = (float(count1) * (60000.0 / interval)) / TOTAL_PPR;
float rpm2 = (float(count2) * (60000.0 / interval)) / TOTAL_PPR;
// --- AFFICHAGE MOTEUR 1 ---
tft.setTextColor(ST77XX_WHITE, COULEUR_FOND);
tft.setTextSize(2);
tft.setCursor(15, 12);
tft.print("MOTEUR 1");
tft.setTextSize(4);
tft.setCursor(15, 38);
tft.setTextColor(ST77XX_GREEN, COULEUR_FOND);
tft.printf("%4.0f RPM", abs(rpm1));
tft.setTextSize(2);
tft.setCursor(210, 75); // Positionnement des tours (T:)
tft.setTextColor(ST77XX_WHITE, COULEUR_FOND);
tft.printf("T:%5.0f", abs(totalPulsesM1/TOTAL_PPR));
// Mise à jour du barregraphe rouge Moteur 1
int bar1_w = map(duty1, 0, 1023, 0, 280);
tft.fillRect(15 + bar1_w, 95, 280 - bar1_w, 10, COULEUR_VIDE); // Efface l'ancien
tft.fillRect(15, 95, bar1_w, 10, ST77XX_RED); // Dessine le nouveau
// --- AFFICHAGE MOTEUR 2 ---
tft.setTextColor(ST77XX_WHITE, COULEUR_FOND);
tft.setTextSize(2);
tft.setCursor(15, 127);
tft.print("MOTEUR 2");
tft.setTextSize(4);
tft.setCursor(15, 153);
tft.setTextColor(ST77XX_CYAN, COULEUR_FOND);
tft.printf("%4.0f RPM", abs(rpm2));
tft.setTextSize(2);
tft.setCursor(210, 190);
tft.setTextColor(ST77XX_WHITE, COULEUR_FOND);
tft.printf("T:%5.0f", abs(totalPulsesM2/TOTAL_PPR));
// Mise à jour du barregraphe rouge Moteur 2
int bar2_w = map(duty2, 0, 1023, 0, 280);
tft.fillRect(15 + bar2_w, 210, 280 - bar2_w, 10, COULEUR_VIDE);
tft.fillRect(15, 210, bar2_w, 10, ST77XX_RED);
prevMillis = currentMillis; // Enregistre le temps pour le prochain cycle
}
}
