Sur des petits courants, de 0.3A AC, avec un capteur de 1V pour 50A SCT-013-050 , la mesure du courant est assez bruitée comme on peut l’observer sur la figure suivante.
https://www.alldatasheet.com/html-pdf/1160244/YHDC/SCT013-050/114/1/SCT013-050.html
D’ailleurs, ce bruit est mesurable car lorsqu’il n’y a pas de courant qui est de 0.034A.
Ce courant n’est pas significatif par rapport à un système qui peut mesurer 50A.
Il faudrait un amplificateur avec différents calibres pour améliorer cette précision pour les petits courants
Par contre, la précision de la mesure de la puissance active et apparente est correcte même pour les petits courants en utilisant la puissance instante divisée par 2500 échantillons toutes les 2 secondes
La puissance est bien nulle lorsqu’il n’y en a pas de courant
Voici le programme de base utilisant la somme de puissance instantanée divisée par 2500 échantillons pour avoir la puissance moyenne donc le calcul de l’énergie se fait sur l’intégration de la puissance tous les 2s.
/TTGO dispaly ZMPT101B sensor voltage
// Mesure de tension et intensity AC RMS power energy
#include <TFT_eSPI.h>
#include <SPI.h>
hw_timer_t * timer = NULL;
TFT_eSPI tft = TFT_eSPI(); //library 1,4" color 135, 240
const int nbrEchantillons = 2500; // periodes
int sampler=0;
bool flag;
bool flagenvoie;
float sommeV = 0;
float sommeI=0;
float sommeCarres = 0;
float sommeCarresI = 0;
float sommePower;
int valeurADC; // tension,
int valeurI; //current
const int nbrsampler=50;
const float calibreI=0.019;
const float calibreV=0.5;
int16_t tableV[nbrsampler];
int16_t tableI[nbrsampler];
byte indexTable=0;
int offsetV=3103;
int offsetI=2048;
int signalV;
int signalI;
float rmsV;
float tensionRMS;
float rmsI;
float currentRMS;
float MaxV; //
float minV;
float amplitudeV;
float MaxI; //
float amplitudeI;
float Power;
float S; //power appararente
float FP; //factor power
float joule2;
float Energy;
void IRAM_ATTR timer_isr() { //routine interruption 800u seconde
if (flagenvoie==1) {flag=0;} else {flag=1;} //bloque l'acquisition lors d'envoie des data
sampler++; //100 periode de 20ms
}//fin isr
void setup() {
Serial.begin(115200);
pinMode(22, OUTPUT);
tft.init(); //135 × 240.
tft.setRotation(0); //0 format portrait a l'endroit 2 180°
tft.fillScreen(TFT_BLACK);
tft.setCursor(0, 0, 2); //// (x,y,taille)
tft.setTextColor(TFT_GREEN,TFT_BLACK); //(couleur police, background police)
tft.println("IUT GEII SOISSONS");
analogReadResolution(12); // ADC 12 bits : 0 à 4095
analogSetAttenuation(ADC_11db); // Plage ADC environ 0 à 3,3 V
uint8_t timer_id = 1;
uint16_t prescaler = 80; // Between 0 and 65 535
int threshold = 800; // 800us
timer = timerBegin(timer_id, prescaler, true);
timerAttachInterrupt(timer, &timer_isr, true);
timerAlarmWrite(timer, threshold, true);
timerAlarmEnable(timer);
}
void loop() {
if (sampler>nbrEchantillons) { //400us*10000=4s 800us*2500=2
flagenvoie=1;
offsetV = sommeV / nbrEchantillons;
offsetI = sommeI / nbrEchantillons;
for (uint16_t i = 0; i < nbrsampler; i++)
{
Serial.print(tableV[i]);
Serial.print(",");
Serial.println(tableI[i]*4);
}
// determination amplitude
amplitudeV=((MaxV-offsetV)*calibreV);
amplitudeI=((MaxI-offsetI)*calibreI);
// Calcul RMS tension alternatif
rmsV = sqrt(sommeCarres / nbrEchantillons);
tensionRMS = (rmsV *calibreV); //3.3*582/4095=0.47
rmsI = sqrt(sommeCarresI / nbrEchantillons);
currentRMS= (rmsI*calibreI); //3.3* /4095=0.004
tft.setCursor(0, 20, 4);// (x,y,taille)
tft.setTextColor(TFT_GREEN,TFT_BLACK);
tft.print("V: ");
tft.print(tensionRMS,0);
tft.print(" ");
tft.setCursor(0, 50, 4);// (x,y,taille)
tft.setTextColor(TFT_GREEN,TFT_BLACK);
tft.print("aV: ");
tft.print(amplitudeV,0);
tft.print(" ");
tft.setCursor(0, 80, 4);// (x,y,taille)
tft.setTextColor(TFT_RED,TFT_BLACK);
tft.print("I: ");
tft.print(currentRMS,3);
tft.print(" ");
/*tft.setCursor(0, 110, 4);// (x,y,taille)
tft.setTextColor(TFT_RED,TFT_BLACK);
tft.print("aI: ");
tft.print(amplitudeI,3);
tft.print(" ");
*/
Power=(sommePower*calibreV*calibreI)/ nbrEchantillons; //power real
S=currentRMS*tensionRMS ; //power apparent
if (currentRMS>0.05) {FP=Power/S; } else {FP=1; } //factor power
joule2=joule2+Power; //W*2s
Energy=(joule2*2)/3600; //wh
tft.setCursor(0, 110, 4);// (x,y,taille)
tft.setTextColor(TFT_RED,TFT_BLACK);
tft.print("E: ");
tft.print(Energy,0);
tft.print(" ");
tft.setCursor(0, 140, 4);// (x,y,taille)
tft.setTextColor(TFT_BLUE,TFT_BLACK);
tft.print("P: ");
tft.print(Power,0);
tft.print(" ");
tft.setCursor(0, 170, 4);// (x,y,taille)
tft.setTextColor(TFT_BLUE,TFT_BLACK);
tft.print("S: ");
tft.print(S,0);
tft.print(" ");
tft.setCursor(0, 200, 4);// (x,y,taille)
tft.setTextColor(TFT_BLUE,TFT_BLACK);
tft.print("FP: ");
tft.print(FP,2);
tft.print(" ");
sommePower=0;
sommeCarres=0;
sommeCarresI=0;
sommeV=0;
sommeI=0;
MaxV = 0;
minV = 4095;
MaxI = 0;
sampler=0;
flagenvoie=0;
} //end sampler
if (flag==1) {
flag=0;
if (digitalRead(22)==0) {digitalWrite(22,1); } else {digitalWrite(22,0); }
valeurADC = analogRead(27); //pinZMPT sensor voltage
sommeV += valeurADC; //
if (valeurADC > MaxV) {MaxV = valeurADC;}
signalV = valeurADC - offsetV; // Suppression de l'offset pour RMS
valeurI = analogRead(26); //pin intensity
sommeI += valeurI;
signalI = valeurI - offsetI;
if (valeurI > MaxI) {MaxI = valeurI;}
sommeCarres += signalV * signalV; // Carré du signal
sommeCarresI += signalI * signalI; // Carré du signal
sommePower += signalI * signalV;
tableV[indexTable] = signalV;
tableI[indexTable] = signalI;
indexTable++;
if (indexTable>=nbrsampler) {indexTable=0; }
} //flag==1
} //end loop
Sachant que la puissance instantanée contient une ondulation du double de la puissance secteur donc à 100 Hz. Donc pour avoir la puissance moyenne, il est possible d’utiliser une filtre IIR passe pas de fréquence de coupure de 1s qui donnera une puissance plus lissée.
Dans un premier temps, un filtre du premier ordre sera utilisé qui donne une atténuation de 0.016 donc 1.6% pour une fréquence de coupure de 0.16Hz et une constante de temps de 1s.
Donc l’énergie peut être déterminée toutes les 800us.
Evidemment, un filtre du second ordre aurait pu être utilisée. Mais avec une fréquence de coupure de 1Hz avec 800us d’échantillonnage, l’atténuation sera de 0.04 mais la précision des coefficients du filtre demandé ne pourra être réaliser avec 7 chiffres significatifs
a1 = -1.99289;
a2 = 0.99291;
Par contre, il serait possible de mettre 2 filtres du premier ordre en cascade si l’on veut améliorer la précision.
Voici le programme avec la puissance moyenne par le filtre
/TTGO dispaly ZMPT101B sensor voltage
// Mesure de tension et intensity AC RMS filtre IIR power energy
#include <TFT_eSPI.h>
#include <SPI.h>
hw_timer_t * timer = NULL;
TFT_eSPI tft = TFT_eSPI(); //library 1,4" color 135, 240
const int nbrEchantillons = 2500; // periodes
int sampler=0;
bool flag;
bool flagenvoie;
float sommeV = 0;
float sommeI=0;
float sommeCarres = 0;
float sommeCarresI = 0;
int valeurADC; // tension,
int valeurI; //current
const int nbrsampler=50;
const float calibreI=0.019;
const float calibreV=0.5;
int16_t tableV[nbrsampler];
int16_t tableI[nbrsampler];
byte indexTable=0;
int offsetV=3103;
int offsetI=2048;
int signalV;
int signalI;
float rmsV;
float tensionRMS;
float rmsI;
float currentRMS;
float MaxV; //
float minV;
float amplitudeV;
float MaxI; //
float amplitudeI;
float Powerinstant;
float Power;
float Powern;
float Powerf;
float S; //power appararente
float FP; //factor power
float joule2;
float Energy;
// const float alpha = 1.0f - expf(-0.0008 / cstemps); //0.0016 cstemps=1
const float alpha=0.0016;
void IRAM_ATTR timer_isr() { //routine interruption 800u seconde
if (flagenvoie==1) {flag=0;} else {flag=1;} //bloque l'acquisition lors d'envoie des data
sampler++; //100 periode de 20ms
}//fin isr
void setup() {
Serial.begin(115200);
pinMode(22, OUTPUT);
tft.init(); //135 × 240.
tft.setRotation(0); //0 format portrait a l'endroit 2 180°
tft.fillScreen(TFT_BLACK);
tft.setCursor(0, 0, 2); //// (x,y,taille)
tft.setTextColor(TFT_GREEN,TFT_BLACK); //(couleur police, background police)
tft.println("IUT GEII SOISSONS");
analogReadResolution(12); // ADC 12 bits : 0 à 4095
analogSetAttenuation(ADC_11db); // Plage ADC environ 0 à 3,3 V
uint8_t timer_id = 1;
uint16_t prescaler = 80; // Between 0 and 65 535
int threshold = 800; // 800us
timer = timerBegin(timer_id, prescaler, true);
timerAttachInterrupt(timer, &timer_isr, true);
timerAlarmWrite(timer, threshold, true);
timerAlarmEnable(timer);
}
void loop() {
if (sampler>nbrEchantillons) { //400us*10000=4s 800us*2500=2secondes affichage et bilan
flagenvoie=1;
offsetV = sommeV / nbrEchantillons;
offsetI = sommeI / nbrEchantillons;
for (uint16_t i = 0; i < nbrsampler; i++)
{
Serial.print(tableV[i]);
Serial.print(",");
Serial.println(tableI[i]*4);
}
// determination amplitude
amplitudeV=((MaxV-offsetV)*calibreV);
amplitudeI=((MaxI-offsetI)*calibreI);
// Calcul RMS tension alternatif
rmsV = sqrt(sommeCarres / nbrEchantillons);
tensionRMS = (rmsV *calibreV); //3.3*582/4095=0.47
rmsI = sqrt(sommeCarresI / nbrEchantillons);
currentRMS= (rmsI*calibreI); //3.3* /4095=0.004
tft.setCursor(0, 20, 4);// (x,y,taille)
tft.setTextColor(TFT_GREEN,TFT_BLACK);
tft.print("V: ");
tft.print(tensionRMS,0);
tft.print(" ");
tft.setCursor(0, 50, 4);// (x,y,taille)
tft.setTextColor(TFT_GREEN,TFT_BLACK);
tft.print("aV: ");
tft.print(amplitudeV,0);
tft.print(" ");
tft.setCursor(0, 80, 4);// (x,y,taille)
tft.setTextColor(TFT_RED,TFT_BLACK);
tft.print("I: ");
tft.print(currentRMS,3);
tft.print(" ");
/*tft.setCursor(0, 110, 4);// (x,y,taille)
tft.setTextColor(TFT_RED,TFT_BLACK);
tft.print("aI: ");
tft.print(amplitudeI,3);
tft.print(" ");
*/
Power=Powerf*calibreV*calibreI;
S=currentRMS*tensionRMS ; //power apparent
if (currentRMS>0.05) {FP=Power/S; } else {FP=1; } //factor power
tft.setCursor(0, 110, 4);// (x,y,taille)
tft.setTextColor(TFT_RED,TFT_BLACK);
tft.print("E: ");
tft.print(Energy,0);
tft.print(" ");
tft.setCursor(0, 140, 4);// (x,y,taille)
tft.setTextColor(TFT_BLUE,TFT_BLACK);
tft.print("P: ");
tft.print(Power,0);
tft.print(" ");
tft.setCursor(0, 170, 4);// (x,y,taille)
tft.setTextColor(TFT_BLUE,TFT_BLACK);
tft.print("S: ");
tft.print(S,0);
tft.print(" ");
tft.setCursor(0, 200, 4);// (x,y,taille)
tft.setTextColor(TFT_BLUE,TFT_BLACK);
tft.print("FP: ");
tft.print(FP,2);
tft.print(" ");
sommeCarres=0;
sommeCarresI=0;
sommeV=0;
sommeI=0;
MaxV = 0;
minV = 4095;
MaxI = 0;
sampler=0;
flagenvoie=0;
} //end sampler
if (flag==1) {
flag=0;
if (digitalRead(22)==0) {digitalWrite(22,1); } else {digitalWrite(22,0); }
valeurADC = analogRead(27); //pinZMPT sensor voltage
sommeV += valeurADC; //
if (valeurADC > MaxV) {MaxV = valeurADC;}
signalV = valeurADC - offsetV; // Suppression de l'offset pour RMS
valeurI = analogRead(26); //pin intensity
sommeI += valeurI;
signalI = valeurI - offsetI;
if (valeurI > MaxI) {MaxI = valeurI;}
sommeCarres += signalV * signalV; // Carré du signal
sommeCarresI += signalI * signalI; // Carré du signal
Powerinstant = signalI * signalV;
Powerf = Powern + alpha * (Powerinstant - Powern); //puissance flitrée
Powern = Powerf; //n-1
Power=Powerf*calibreV*calibreI;
joule2=joule2+Power; //
Energy=(joule2*0.0008)/3600; //wh
tableV[indexTable] = signalV; //enregiste les 50 derniers mesures
tableI[indexTable] = signalI;
indexTable++;
if (indexTable>=nbrsampler) {indexTable=0; }
} //flag==1
} //end loop
La méthode du filtre IIR passe bas peut être utilisée aussi sur la mesure du courant RMS et pour enlever la composante continue un filtre passe haut donc plus besoin de déterminer l’offset
ibrut[n]→filtre passe-haut →i[n]→i^2[n]→IIR RMS
mais ce sera pour plus tard