Bonjour à Tous,
Je suis novice en Arduino mais par nécessité j'ai décidé de me lancer dans un projet de Routeur d'énergie pour excédent de production de panneaux solaires.
J'en suis à l'état de premier prototype de programme en cours d'implémentation. La carte support Arduino est une Nano (Atmega 328P).
Je lui ai adjoint une carte Display OLED 128x64 SH1106.
Les informations de Puissance électrique sont récupérées à partir d'un petit compteur d'énergie Eastron SDM230, installé au niveau de l'arrivée Enedis du tableau électrique général de la maison.
La transmission des données entre compteur d'énergie et Arduino se fait par une ligne RS485 bi-directionnelle en utilisant le protocole Modbus RTU (vitesse de transmission 9600 bauds).
Le but du routeur est de diriger l'excédent d'énergie produit par les panneaux solaires sur une résistance éléctrique (exemple : un ballon de production d'eau chaude sanitaire).
J'ai donc commencé à écrire un programme composé pour le moment des fonctionnalités suivantes :
- Récupération des données du compteur d'énergie, en l'occurrence pour l'application : la Puissance.
= > Testée, fonctionne parfaitement - Affichage de la Puissance (pour l'instant celle consommée)
=> Testée, fonctionne parfaitement - Commande du Relais Statique (SSR) par contrôle du déphasage par rapport au Zero Crossing
du Secteur 50Hz. Cette Fonctionnalité utilise l'interruption de l'entrée Arduino D2
(Rising edge ZeroCrossing) et le Timer 2 pour la commande de déphasage.
=> Testée, fonctionne parfaitement
Le contrôle du déphasage n'étant pas linéaire par rapport à la puissance, j'ai du utiliser un tableau de conversion de 256 valeurs correspondant au déphasage par pas de 64µS couvrant env. (x256) 9mS soit une demie période 50Hz.
Et c'est là que se situe mon problème. Le fait de transmettre la valeur d'une variable comme index du tableau bloque les fonctions RS485 Modbus et affichage OLED (l'affichage reste vierge).
La fonction commande SSR fonctionne mais avec une valeur inadaptée du déphasage.
Si je remplace, dans la fonction "retard", la variable d'index du tableau (valRetard) par une constante tout fonctionne et avec la bonne valeur de déphasage (celle correspondant à la constante).
Le compilateur ne détecte pas d'erreur (IDE Arduino V2.03), le téléversement se passe sans problème.
Je suis désolé mais entant que nouvel utilisateur je ne peux pas envoyer de fichier.
Copie de programme ci-après.
merci pour votre aide !
/*
ROUTAGE PUISSANCE AVEC COMPTEUR ENARGIE EAstron SDM230 (Library U8g2lib Module OLED 128x64) _Tst Pw 01_
-------------------------------------------------------------------------------------------------------
!!!! Programme en cours d'écriture et de mise au point !!!!
Ce programme est intégré dans Arduino Nano pour gérer la puissance excedentaire produite par des panneaux solaires. Cette puissance excedentaire
est routée sur une résistance électrique ex/ Ballon Sanitaire). L'étage de puissance est géré par Relais statique (SSR) commandé par une impulsion basse tension
déphasée en fonction de la valeur de la puissance à router.
La valeur de la puissance (Pw) est transmise via Modbus (RTU) à 9600 bauds, par un compteur d'énergie Eastron SDM230. La valeur de cette puissance est fournie
en flottant par le compteur.
*/
#include "U8g2lib.h" // Library Afficheur Oled
#define MAX485_RE_DE 4 // Output : Détermination Sens de transmission sur ligne RS485
#define Pulse_SSR 5 // Output : Impulsin de déclenchement SSR/Triac
#define ZeroCross 2 // Input : Détection du passage à Zéro
#define Trace 6 // Output : Trace pour calcul des temps d'exécution
byte Pw_TX[8] {0x01,0x04,0x00,0x0C,0x00,0x02,0xB1,0xC8}; // Tableau Requête SDM230
byte Pw_RX[11]; // Tableau de REception SDM230
int Pw; // Valeur de la puissance
int Pwbar; // Valeur de la puissance (affichage bargraph OLED 128x64)
byte Count; // Compteur d'affichage (Rafraichissement affichage OLed env 1fois/sec)
byte valRetard; // Variable index du tableau Delay_Puissance
/*
La puissance à routée se fait par découpage de la sinusoïde (déphasage de l'impulsion SSR par pas de 64µS de 0 à 255). Ce déphasage n'est pas linéaire.
Le pas 255 correspond à: 64µS x 8 = 512µS qui est la pleine puissance routée. Le pas 0 à: 64µs x 144 = 9216µS (0,9mS) qui est la puissance minimum routée.
La demie période à 50Hz étant de 20/2 = 10mS
*/
byte Delay_Puissance [256] {
144, 143, 141, 140, 139, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 128,
127, 127, 126, 125, 124, 123, 123, 122, 122, 121, 121, 120, 119, 119, 118, 118,
117, 117, 116, 116, 115, 115, 114, 114, 114, 113, 113, 112, 112, 112, 111, 111,
110, 110, 109, 109, 109, 108, 108, 108, 107, 107, 107, 106, 106, 106, 105, 105,
104, 104, 104, 103, 103, 103, 102, 102, 102, 101, 101, 101, 100, 100, 100, 99,
99, 99, 99, 98, 98, 98, 97, 97, 97, 96, 96, 95, 95, 95, 95, 94,
94, 94, 93, 93, 93, 93, 92, 92, 91, 91, 91, 91, 90, 90, 90, 89,
89, 89, 88, 88, 88, 87, 87, 87, 86, 86, 86, 85, 85, 84, 84, 84,
84, 83, 83, 83, 82, 82, 82, 82, 81, 81, 80, 80, 80, 80, 79, 79,
79, 78, 78, 78, 77, 77, 77, 76, 76, 76, 75, 75, 74, 74, 74, 73,
73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 69, 69, 69, 69, 68,
68, 67, 67, 67, 66, 66, 65, 65, 65, 64, 64, 63, 63, 63, 62, 62,
62, 61, 61, 61, 60, 60, 60, 59, 59, 58, 58, 58, 57, 57, 56, 56,
56, 55, 55, 54, 54, 53, 53, 52, 52, 52, 51, 51, 50, 50, 50, 49,
49, 48, 48, 47, 46, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 39,
39, 38, 37, 36, 35, 33, 32, 31, 27, 25, 20, 15, 12, 10, 9, 8
};
// Sélection Ecran OLED SH1106 128x64 dans la Library U8g2
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
/*
La gestion du déphasage de l'impulsion de commande du SSR se fait par le Timer 2 de l'arduino. La synchronisation avec le "Zéro" de la sinusoïde se fait sur interruption:
impulsion sur l'entrée 2 au moment du passage à Zéro du secteur 50Hz.
*/
void setup() {
TCCR2A = 0; // Registre Timer 2 TCCR2A (WGM20, = 0)
TCCR2B = 0b00000111; // Clock Timer 2 / 1024 soit 64 micro-s et WGM22 = 0
attachInterrupt(digitalPinToInterrupt(2), zeroCrossInt, RISING); // Interruption Detection Zero
u8g2.begin(); // Lancement Ecron Oled
delay(2000);
pinMode(MAX485_RE_DE, OUTPUT); // Sens de tansmission Buffer RS485
pinMode(Pulse_SSR, OUTPUT);
pinMode(ZeroCross, INPUT);
pinMode(Trace, OUTPUT);
digitalWrite(MAX485_RE_DE, 0); // Initialisation en Réception de la ligne RS485 Modbus
Serial.begin(9600); // Vitesse Transm 9600 bauds de la ligne serie RS485 Modbus
}
void loop() {
//digitalWrite(Trace, 1);
switch (Serial.available()) { // Vérification Etat du Buffer Read : Si 0 Transm OK, Si 11 Réception
case 0:
digitalWrite(MAX485_RE_DE, 1); // Mise en Transmission
++Count;
Serial.write(Pw_TX, 8); // Envoi Requête pour Lecture Tension
Serial.flush();
digitalWrite(MAX485_RE_DE, 0); // Mise en Reception
break;
case 11:
Serial.readBytes(Pw_RX, 11); // Lecture Buffer Reception et remplissage tableau
break;
}
delayMicroseconds(1200); // Délai de Sortie d"échange Master/Slave
// Calcul Valeur Pw (La Puissance envoyée par le Meter Eastron est en Float sur 4 octets)
union { // Concaténation de la valeur de la Tension en HEX
int32_t combined;
uint8_t bytes[4];
} u;
u.bytes[3] = Pw_RX[4]; //0 63
u.bytes[2] = Pw_RX[5]; //1 63
u.bytes[1] = Pw_RX[6]; //2 3
u.bytes[0] = Pw_RX[7]; //3
int32_t x= (u.combined); // Conversion en valeur
float y = *(float*)&x;
Pw = int(y); // Conversion Float en Integer
Pwbar = Pw/2.4;
switch (Count) { // Compteur d'Affichage OLED ttes les secondes
case 1:
Aff_Oled();
break;
case 30:
Count = 0;
Pw =0;
break;
}
Energy();
}
void Energy () {
valRetard = Pw*100/16; // Calcul de l'index (du tableau) de déphasage
// Cette valeur doit être comprise entre 0 et 255
if (valRetard <= 0) {
valRetard = 0;
}
if (valRetard > 255) {
valRetard = 255;
}
}
void Aff_Oled () { // Affichage OLED 128x64 Valeur de la puissance routé + bargrapf
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_7x13_tf);
u8g2.drawStr(38,20, "Routage");
u8g2.setCursor(43,36);
u8g2.print(Pw);
u8g2.drawStr(78,36, "w");
u8g2.drawFrame(14,45,104,10);
u8g2.drawBox(16,47,Pwbar,6);
u8g2.sendBuffer();
}
void zeroCrossInt() { // Routine interruption ZeroCrossing
retard();
}
void retard() { // Initialisation et lancement Timer 2 Calcul retard déclenchement SSR
TIMSK2 = 0; // Inhibition interruption TIMER 2
TCNT2 = 0; // Mise à 0 Timer 2
OCR2A = Delay_Puissance[valRetard]; // Déphasage en multiple de 64µS déclench SSR
valRetard = 0;
TCNT2 = 0; // Mise à 0 Timer 2
TIMSK2 = 0x02; // Autorisation interruption Timer 2
}
ISR(TIMER2_COMPA_vect){ // Routine interruption Comparaison Timer 2 au registre OCR
pulse();
}
void pulse() { // Génération Impulsion SSR
TIMSK2=0; // Inhibition interruption TIMER 2
digitalWrite(Pulse_SSR, 1); // Génération de l'impulsion de 100µS pour déclencher le SSR
delayMicroseconds(100);
digitalWrite(Pulse_SSR, 0);
}