01. Du mouvement à la donnée
Une pompe transmet des vibrations à son support. Le MPU6050 mesure l’accélération suivant trois axes. L’ESP32 acquiert une fenêtre, calcule ses indicateurs et les rend disponibles au téléphone en Bluetooth et à InfluxDB par réseau cellulaire.
↳ ESP32 → Bluetooth BLE → ILM-Pompes sur Android : réglages, spectre et référence de pompe saine.
L’OLED SH1106 de 128 × 64 pixels garde un retour local : intervalle d’envoi, gamme, cadence, RMS et fréquence dominante. Le téléphone configure le système ; l’ESP32 calcule et transmet les mesures au serveur.
02. Préparer et câbler le montage
Le montage réunit un FireBeetle ESP32 compatible BLE, un module Joy-IT SEN-MPU6050, un OLED SH1106, une carte support SIM7080G, une antenne LTE et une SIM avec service LTE-M. Le circuit violet photographié est une carte spécifique : sa reproduction demande son schéma, sa nomenclature et la vérification de ses alimentations.


| Signal | Côté ESP32 | Contrôle |
|---|---|---|
| MPU6050 SDA / SCL | GPIO21 / GPIO22 | Bus partagé avec l’OLED ; niveaux à 3,3 V. |
| MPU6050 AD0 | GND : 0x68 ; 3,3 V : 0x69 | Le code teste les deux adresses et WHO_AM_I = 0x68. |
| OLED SDA / SCL | GPIO21 / GPIO22 | Adresse 0x3C dans ce code. |
| Modem TX → ESP RX | GPIO16 | Adapter 1,8 V ↔ 3,3 V sur la carte support. |
| ESP TX → modem RX | GPIO17 | UART croisé et masse commune. |
| XDA / XCL / INT | Non utilisés | Ce firmware lit le FIFO par I²C. |
Vérifier la tension VCC du module exact avant branchement. Les résistances de rappel SDA/SCL doivent tirer les lignes vers 3,3 V. Plusieurs modules sur le même bus mettent leurs résistances en parallèle : contrôler la résistance équivalente et garder les fils courts, surtout à 400 kHz.
La fixation fait partie de la mesure. Fixer rigidement le capteur sur le point choisi de la pompe, repérer ses axes et immobiliser les câbles. Un capteur tenu à la main mesure aussi son propre support. Conserver emplacement, orientation et serrage entre essais. Effectuer la fixation sur une machine arrêtée et sécurisée.
03. Choisir la bonne acquisition
La gamme fixe l’amplitude maximale par axe. La cadence fixe le nombre d’échantillons par seconde et le filtre associé. L’intervalle d’envoi fixe seulement la périodicité des publications vers InfluxDB.
| Cadence | Filtre matériel | Recherche du pic | Points / axe |
|---|---|---|---|
| 250 Hz | 44 Hz | 2–40 Hz | 512 |
| 500 Hz | 94 Hz | 2–80 Hz | 1 024 |
| 1 000 Hz, défaut | 184 Hz | 2–160 Hz | 2 048 |
Les gammes ±2, ±4, ±8 et ±16 g correspondent à 16 384, 8 192, 4 096 et 2 048 LSB/g. Une petite gamme donne un pas numérique plus fin, mais moins de marge avant saturation. La gravité occupe déjà environ 1 g sur un axe vertical avant retrait de la moyenne : elle peut contribuer à saturer le capteur.
Le filtre atténue progressivement les fréquences hautes ; il ne produit pas une coupure idéale. La bande utile est volontairement inférieure à la limite de Nyquist. Le pas FFT vient de Δf = cadence / N ; il ne garantit pas une exactitude absolue de 0,488 Hz.
04. Comprendre le code ESP32
Le sketch Code_MPU6050_salle_des_pompes_1h_LTE_retry.ino orchestre le matériel. Les en-têtes séparent les responsabilités :
AnalyseVibrations.h : conversion, RMS, pic et FFT.
ConfigurationEnvoiBLE.h : commandes, service BLE et mémoire NVS.
TramesVibration.h et SpectreBLE.h : données destinées à Android.
ReferenceInflux.h, EnvoiInflux.h, IntervalleEnvoi.h : blanc, décision d’envoi et temporisation.
AttenteModem.h : interprétation des réponses du modem.
setup() : préparer la mesure locale
Le démarrage initialise le port série à 115 200 bauds, le bus I²C sur 21/22 à 400 kHz, l’écran et le BLE. Il recharge les réglages persistants, identifie le MPU6050 et acquiert une fenêtre. L’UART du SIM7080 reçoit un tampon RX de 4 096 octets. Aucun scan ni POST n’est lancé au démarrage : le LTE est vérifié lorsqu’un envoi est nécessaire. AUTO est désactivé initialement et son état enregistré survit au redémarrage.
initialiserMPU() : identifier avant de configurer
for(uint8_t a=0x68;a<=0x69;++a) {
adresseMPU=a; uint8_t id;
if(!lireReg(0x75,&id,1)||id!=0x68) continue;
// Le code complet poursuit : reset, réveil, filtre et gamme.
}Cet extrait du firmware lit WHO_AM_I au registre 0x75. Le composant s’identifie par 0x68 même lorsque son adresse de bus est 0x69. Le code configure ensuite le filtre (0x1A), le diviseur de cadence (0x19) et la gamme (0x1C).
mesurerVibrations() : conserver une cadence matérielle
Le programme réinitialise et active le FIFO accéléromètre, puis lit les triplets X/Y/Z par lots de dix au maximum. Chaque point occupe six octets. Le FIFO de 1 024 octets est vidé régulièrement : la fenêtre complète de 2 048 points est constituée dans la RAM de l’ESP32. Les lectures arrivent par lots, mais l’espacement des échantillons vient du capteur.
Un débordement FIFO, une erreur I²C, une durée d’acquisition supérieure à trois secondes ou une valeur proche de la saturation invalide la fenêtre. Un échec n’est jamais transformé en vibration nulle.
analyserVibrations() : RMS, pic et fréquence
// Principe pédagogique condensé du calcul :
a = (brut - moyenneAxe) * 9.80665 / sensibiliteLSB(gamme);
RMS_X = sqrt(sommeCarresX / N);
RMS_totale = sqrt(RMS_X*RMS_X + RMS_Y*RMS_Y + RMS_Z*RMS_Z);
// FFT par axe après fenêtre de Hann, puis somme des puissances.La moyenne de chaque axe est retirée avant conversion en m/s² : cela retire sa composante continue, dont la gravité statique, sans corriger toutes les variations d’orientation. Le pic correspond au maximum de la norme de l’accélération recentrée.
La FFT radix-2 est calculée séparément sur les trois axes, après fenêtrage de Hann. Leurs puissances sont ensuite additionnées. Appliquer d’abord la norme pourrait doubler certaines fréquences. Un pic est interpolé entre raies, puis accepté si le signal est suffisant et concentré. Les critères initiaux de RMS totale 0,05 m/s² et concentration 35 % sont des seuils de détection à valider, pas des seuils de santé.
loop() : coordonner sans confondre les rôles
La boucle traite les réglages BLE, renouvelle les statistiques et décide des envois manuels ou AUTO. Les callbacks BLE utilisent des instantanés protégés ; ils ne réalisent ni acquisition I²C ni POST. Les attentes modem rendent la main et peuvent servir affichage, réglages et acquisition si le budget restant le permet. Des pauses restent possibles : l’âge de la dernière fenêtre est donc affiché.
05. Le SIM7080G : transmettre hors Wi-Fi
Le SIM7080G est un modem LPWA compatible LTE Cat-M et NB-IoT. Le projet utilise LTE-M pour joindre l’API d’écriture InfluxDB. Le téléphone n’a pas à fournir sa connexion Internet : il échange localement avec l’ESP32 en BLE.
Le composant nu possède une plage VBAT de 2,7 à 4,8 V selon SIMCom, et un UART logique 1,8 V. Ces caractéristiques ne définissent pas automatiquement l’entrée de la carte support. Contrôler régulation, découplage et adaptation de niveaux sur le schéma. L’alimentation doit tenir les appels de courant radio : ne pas déduire un raccordement direct au 3,3 V de l’ESP32 de la seule photo.
Le firmware cherche à réutiliser un réseau déjà opérationnel avant reconfiguration. Le scan complet d’opérateurs est désactivé par défaut. La sélection automatique et un secours Orange sont présents, à adapter à la SIM et au pays. Vérifier l’APN de votre contrat et la couverture LTE-M au point d’installation.
| Étape | Contrôle | Interprétation |
|---|---|---|
| UART | AT | OK prouve le dialogue, pas Internet. |
| SIM et réseau | CPIN? / CEREG? / CGATT? | SIM utilisable, enregistrement et attachement paquet. |
| Session | Contexte PDP | Préparer une session de données utilisable. |
| HTTP | SHCONF / SHBOD / SHREQ | Configurer et exécuter l’écriture. |
| Réception | HTTP 204 reconnu | L’API InfluxDB accepte la requête d’écriture. |
La séquence du firmware utilise Content-Type: data-binary, une longueur de corps excluant le Ctrl-Z final, puis la requête. Un premier OK ne termine pas une opération HTTP asynchrone. Le corps est limité à 1 024 octets : un dépassement est refusé avant transmission.
06. Configurer avec l’APK Android
ILM-POMPES 1.3.0 · ANDROID 8+
Le réglage au pied de la machine.
L’application retrouve l’ESP32, affiche ses statistiques et configure l’acquisition. La version 1.3.0 montre le spectre FFT de la dernière fenêtre ; elle nécessite le firmware 1.5 pour cette fonction.
- Installer l’APK ILM-Pompes 1.3.0 ; autoriser cette source d’installation si Android le demande.
- Activer Bluetooth et autoriser les appareils à proximité. Sous Android 8–11, autoriser et activer aussi la localisation pour la recherche BLE.
- Rechercher ILM-Pompes, sélectionner le bon ESP32, accepter l’appairage et saisir
000000(six zéros). - Choisir la gamme ±2/4/8/16 g et la cadence 250/500/1 000 Hz. Enregistrer et attendre confirmation et relecture.
- Lire RMS, axes, pic, fréquence et âge. Au-delà de dix secondes, la mesure est ancienne et la comparaison au blanc est suspendue.
Les réglages et la référence sont conservés dans la NVS de l’ESP32. Les deux trames de statistiques possèdent un numéro de séquence commun ; Android vérifie leur cohérence avant affichage.

Comprendre le spectre
L’axe horizontal reste fixé à 0–40, 0–80 ou 0–160 Hz. L’ordonnée indique l’accélération RMS par raie, combinaison énergétique des axes. L’échelle verticale s’adapte au maximum : comparer deux hauteurs sans lire cette échelle peut tromper.
La courbe est remplacée à chaque fenêtre, sans historique défilant. Ses amplitudes sont quantifiées sur huit bits relativement au maximum pour le transport BLE ; les indicateurs numériques gardent leur précision habituelle. La lecture longue est figée et contrôlée par une empreinte d’intégrité.
Le pic à 33,42 Hz peut être une rotation ou une harmonique. S’il représente exactement la rotation (1×), cela correspond à environ 2 005 tr/min. Vérifier avec la vitesse réelle avant toute interprétation mécanique.
Choisir quand envoyer
L’intervalle va de 60 à 86 400 secondes (une minute à 24 heures ; une heure initialement). Envoyer maintenant vers InfluxDB sauvegarde et relit les réglages affichés puis demande une nouvelle fenêtre et son envoi par l’ESP32. Attendre QUEUED → RUNNING → HTTP204 : l’accusé BLE ne constitue pas une confirmation serveur.
AUTO est désactivé par défaut. Sans AUTO, un échec manuel ne déclenche aucun retry. Avec AUTO, les envois suivent l’intervalle enregistré et un échec permet un nouvel essai après cinq minutes. Activer AUTO n’envoie pas immédiatement ; le désactiver empêche les prochains départs sans annuler une requête déjà commencée.
En cas de problème, utiliser Copier le journal Bluetooth. Après une mise à jour, si les services sont incohérents, oublier l’ancienne association Android et refaire l’appairage. Le PIN fixe n’est pas un secret individuel : adapter l’accès BLE pour un déploiement exposé.
07. Faire le blanc de pompe saine
Sur une pompe considérée saine, stabilisée et bien instrumentée, enregistrer les réglages puis utiliser Faire le blanc — pompe saine. L’ESP32 attend une nouvelle fenêtre, l’enregistre en NVS et confirme. Android relit la référence et garde une copie distincte pour chaque ESP32.
Un blanc de 1 m/s² et une mesure de 1,2 m/s² donnent +20 %. La comparaison exige la même gamme et la même cadence ; une référence inférieure à 0,01 m/s² ne donne pas de ratio. Garder aussi vitesse, charge, température, emplacement et fixation comparables. La date de référence provient de l’horloge du téléphone.
Le blanc survit aux redémarrages. Une nouvelle capture ne remplace l’ancienne qu’après réussite. Sa capture seule ne déclenche pas de POST. Un écart décrit une évolution de l’indicateur, sans fournir un verdict automatique de panne.
08. Retrouver les mesures dans InfluxDB
Le firmware écrit dans IoT-ILMTECH avec les tags salle=salle_des_pompes et capteur=MPU6050. Il transmet notamment :
vibration_rms_x_ms2 · vibration_rms_y_ms2 · vibration_rms_z_ms2
vibration_rms_ms2 · vibration_pic_ms2 · echantillonnage_hz
gamme_g · filtre_hz · echantillons · fenetre_s
frequence_valide · frequence_dominante_hz (si exploitable)
blanc_disponible · comparaison_valide · ecart_rms_pct (si comparable)Le blanc associé est écrit dans IoT-ILMTECH_blanc avec son identifiant et sa date originale date_capture_unix_s. Le pourcentage envoyé est calculé dans l’ESP32. Une comparaison impossible ne produit pas un faux zéro ; une raison est fournie.
Après HTTP 204, relire le point dans le bucket attendu, vérifier ses tags, unités et réglages. Cette étape confirme la destination réelle avant de construire des courbes.
09. Mettre en service et vérifier
- Hors tension : vérifier polarités, tensions, niveaux UART, masse commune, SIM et antenne. Fixer le capteur et repérer ses axes.
- Compilation : ouvrir le .ino avec tous ses .h ; copier secrets.example.h vers secrets.h et renseigner le jeton localement. Adapter la destination serveur. Bibliothèques : Adafruit GFX, API historique Adafruit_SH1106 utilisée par le projet et M5_SIM7080G ; Wire, BLE et Preferences viennent du cœur Arduino-ESP32.
- Environnement : le projet rapporte des compilations avec Arduino-ESP32 2.0.14 et FireBeetle DFRobot 0.2.1. Sélectionner la carte exacte et vérifier les compatibilités avant téléversement.
- Contrôle local : moniteur série à 115 200 bauds, MPU identifié à 0x68 ou 0x69, écran à 0x3C. Vérifier une fenêtre valide sans saturation.
- Contrôle Android : connecter l’APK, modifier gamme et cadence, attendre leur relecture. Observer le spectre et son échelle ; une petite RMS au repos peut venir du bruit.
- Référence : capturer un blanc reproductible, redémarrer et vérifier sa relecture.
- Réseau : AUTO désactivé, demander un envoi, attendre HTTP204 et relire la base avant d’activer le périodique.
| Symptôme | Premier contrôle |
|---|---|
| MPU absent | SDA/SCL, alimentation, AD0, adresse, WHO_AM_I. |
| Acquisition invalide | Saturation, gamme, erreurs I²C, débordement FIFO. |
| Fréquence inexploitable | Signal faible ou sans pic net ; examiner RMS et spectre. |
| Mesure ancienne | Âge, activité modem, erreurs d’acquisition. |
| Envoi non confirmé | État de demande, SIM, réseau, PDP, réponse HTTP, destination. |
Les photos et la capture documentent le prototype et un affichage fonctionnel. Les compilations et contrôles logiciels du projet ne remplacent pas un étalonnage physique ou une campagne d’essais sur pompe. Cette bande de basse fréquence ne remplace pas une instrumentation de roulements à plusieurs kilohertz.
10. Versions et références
Versions du projet : application ILM-Pompes 1.3.0 et firmware ESP32 1.5. Leur fonctionnement et leur configuration sont décrits dans ce tutoriel.
Lire le module d’analyse complet
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <math.h>
// Accelerometre MPU6050 : cadence interne 1 kHz avec DLPF actif.
constexpr bool gammeValide(uint32_t g) { return g==2 || g==4 || g==8 || g==16; }
constexpr bool cadenceValide(uint32_t hz) { return hz==250 || hz==500 || hz==1000; }
constexpr uint8_t codeCadence(uint32_t hz) { return hz==250 ? 0 : hz==500 ? 1 : 2; }
constexpr uint16_t tailleFenetre(uint32_t hz) { return hz==250 ? 512 : hz==500 ? 1024 : 2048; }
constexpr uint8_t regFiltre(uint32_t hz) { return hz==250 ? 3 : hz==500 ? 2 : 1; }
constexpr uint16_t filtreHz(uint32_t hz) { return hz==250 ? 44 : hz==500 ? 94 : 184; }
constexpr float bandeMaxHz(uint32_t hz) { return hz==250 ? 40.0f : hz==500 ? 80.0f : 160.0f; }
constexpr uint8_t regGamme(uint32_t g) { return g==2 ? 0 : g==4 ? 8 : g==8 ? 16 : 24; }
constexpr float sensibiliteLSB(uint32_t g) { return 32768.0f / g; }
struct AnalyseVibrations {
float rms[3] = {0,0,0};
float rmsTotal = 0, pic = 0, frequenceHz = 0, concentration = 0;
bool frequenceValide = false;
};
struct SpectreVibrations {
uint16_t nombre=0;
float amplitudes[329]={};
};
// Appel uniquement dans la tache principale : espace de travail statique partage.
inline AnalyseVibrations analyserVibrations(const int16_t (*brut)[3], uint16_t n,
uint32_t cadence, uint32_t gamme, SpectreVibrations *spectre=nullptr) {
if(spectre) spectre->nombre=0;
AnalyseVibrations resultat;
if (!brut || !cadenceValide(cadence) || !gammeValide(gamme) || n!=tailleFenetre(cadence)) return resultat;
static float re[2048], im[2048], puissance[1025];
const double pi = 3.14159265358979323846;
const float facteur = 9.80665f / sensibiliteLSB(gamme);
double moyenne[3]={0,0,0}, carres[3]={0,0,0}, pic2=0;
for (uint16_t i=0;i<n;++i) for (uint8_t a=0;a<3;++a) moyenne[a]+=brut[i][a];
for (uint8_t a=0;a<3;++a) moyenne[a]/=n;
for (uint16_t i=0;i<n;++i) {
double norme2=0;
for (uint8_t a=0;a<3;++a) {
double ac=(brut[i][a]-moyenne[a])*facteur;
carres[a]+=ac*ac; norme2+=ac*ac;
}
if (norme2>pic2) pic2=norme2;
}
for (uint8_t a=0;a<3;++a) resultat.rms[a]=sqrt(carres[a]/n);
resultat.rmsTotal=sqrt((carres[0]+carres[1]+carres[2])/n);
resultat.pic=sqrt(pic2);
// Seuil de detection initial, a valider sur pompe ; pas un seuil de sante.
for (uint16_t k=0;k<=n/2;++k) puissance[k]=0;
for (uint8_t a=0;a<3;++a) {
for (uint16_t i=0;i<n;++i) {
const float hann=0.5f-0.5f*cos(2*pi*i/(n-1));
re[i]=(brut[i][a]-moyenne[a])*facteur*hann; im[i]=0;
}
// FFT radix-2, puis addition des puissances des trois axes.
// Ne pas prendre la norme du signal avant FFT : cela doublerait certaines frequences.
for (uint16_t i=1,j=0;i<n;++i) {
uint16_t bit=n>>1;
for (;j&bit;bit>>=1) j^=bit;
j^=bit;
if (i<j) { float t=re[i];re[i]=re[j];re[j]=t; t=im[i];im[i]=im[j];im[j]=t; }
}
for (uint16_t len=2;len<=n;len<<=1) {
const float wr0=cos(-2*pi/len), wi0=sin(-2*pi/len);
for (uint16_t i=0;i<n;i+=len) {
float wr=1,wi=0;
for (uint16_t j=0;j<len/2;++j) {
uint16_t u=i+j,v=u+len/2;
float vr=re[v]*wr-im[v]*wi,vi=re[v]*wi+im[v]*wr;
re[v]=re[u]-vr; im[v]=im[u]-vi; re[u]+=vr; im[u]+=vi;
float w=wr*wr0-wi*wi0; wi=wr*wi0+wi*wr0; wr=w;
}
}
}
for (uint16_t k=0;k<=n/2;++k) puissance[k]+=re[k]*re[k]+im[k]*im[k];
}
const float pas=(float)cadence/n;
if(spectre) {
spectre->nombre=(uint16_t)floor(bandeMaxHz(cadence)/pas)+1;
// Energie unilaterale, normalisee par l'energie de la fenetre Hann.
const float normalisation=2.0f/(n*(3.0f*(n-1)/8.0f));
spectre->amplitudes[0]=0; // composante continue retiree
for(uint16_t k=1;k<spectre->nombre;++k)
spectre->amplitudes[k]=sqrtf(puissance[k]*normalisation);
}
if(resultat.rmsTotal<0.05f) return resultat;
const uint16_t debut=(uint16_t)floor(2.0f/pas), fin=(uint16_t)ceil(bandeMaxHz(cadence)/pas);
uint16_t picBin=debut; double total=0;
for (uint16_t k=debut;k<=fin;++k) {
total+=puissance[k]; if (puissance[k]>puissance[picBin]) picBin=k;
}
if (total<=0 || puissance[picBin]<puissance[picBin-1] || puissance[picBin]<puissance[picBin+1]) return resultat;
resultat.concentration=(puissance[picBin-1]+puissance[picBin]+puissance[picBin+1])/total;
if (resultat.concentration>1) resultat.concentration=1;
if (resultat.concentration<0.35f) return resultat; // pas de pic dominant net
const float a=logf(puissance[picBin-1]+1e-20f), b=logf(puissance[picBin]+1e-20f), c=logf(puissance[picBin+1]+1e-20f);
const float denom=a-2*b+c;
float delta=fabsf(denom)>1e-12f ? 0.5f*(a-c)/denom : 0;
if (delta>0.5f) delta=0.5f; if (delta< -0.5f) delta=-0.5f;
resultat.frequenceHz=(picBin+delta)*pas;
resultat.frequenceValide=resultat.frequenceHz>=2 && resultat.frequenceHz<=bandeMaxHz(cadence);
return resultat;
}
