Salut Bricofoy,
Ca donne quoi ta régulation via Arduino?
J'ai exactement le même projet et la même configuration de vanne (4 voies dans mon cas) pilotable avec 2 relais. Pour l'instant j'ai mis des sondes partout (E/S chaudière, E/S circuit radiateurs, Ambiance et extérieure) que je récupère via l'arduino et j'en suis au PID justement.
Dans mon cas le PID n'est pas forcément nécessaire car je veux l'utiliser simplement pour réguler la température en entrée du circuit de radiateurs. La régulation dans la maison étant effectuée via une courbe de chauffe "classique" mais avec prise en compte d'une sonde d'ambiance.
Mais bon j'aime l'idée d'utiliser la librairie PID pour ça 
Ma vieille régulation analogique (une CentraTherm Z55) fonctionne encore et donc pour l'instant je log ses données de fonctionnement (ON/OFF du circulateur, ON/OFF bruleur, températures variées) via mon logiciel de domotique JEEDOM. Screenshots ci-dessous
D'ailleurs j'ai une question sur ton code, je vois que la fonction heat_run() dans ton post ci-dessus ne correspond pas à celle sur Gitthub, en particulier le code après:
if( WaterPID.Compute() )
qui évite des mouvements trops fréquents du moteur de la vanne mais pas que: La valeur d'output du PID est assez différente (un temps de mouvement de vanne dans le post précédent vs un pourcentage d'ouverture de la vanne sur Github.
Quel est le code le plus récent? Je suis intérressé par ton feedback sur ce point
Merci
Bon ok, sorry, j'ai trouvé la réponse comme un grand dans l'historique du projet sur Github.
En téléchargeant la branche Master j'avais déjà la dernière version...
Du coup tu as abandonné l'idée de mettre un seuil sur les faibles variations de position du moteur ou bien c'est justement la finesse de 1% qui est ton seuil?
salut
la magie de git 
oui alors en effet, le seuil maintenant, c'est 1%. Le principe c'est que la fonction qui calcule la position de la vanne et ce qui la met en pratique sont dissociés, c'est plus simple et plus logique. Le pid donne une valeur entre 0 et 100% d'ouverture, et ensuite une machine à états met en place la valeur en bougeant la vanne.
Sauf que en pratique, ça ne fonctionne pas, mon arduino fait trop de trucs, et la machine à états qui fait bouger la vanne foire. Le proto est chez le client, du coup je n'ai pas pu investiguer plus, mais c'est assez étrange, mon pas de temps mini est sensé être 1% soit 1,5 s (avec un temps de mouvement total de 145s de butée à butée), et concrètement il se produit des micro-mouvement on entends juste le relais claquer et se relacher immédiatement ou presque.
Du coup pour le moment, la vanne est désactivée et je l'ai mise à a main à 30%.
En fait je me demande si je vais pas benner tout mon code pour cette partie et m'inspirer de ce qui est fait dans ce projet :
http://forum.arduino.cc/index.php?topic=400621.0
mais j'arrive pas à comprendre exactement comment ça fonctionne...
bon, je viens de relire mon code, et... je ne comprends pas pourquoi ça ne fonctionne pas
Vu le déroulement que j'ai dans ma loop, c'est à dire :
-mise à zéro du registre des sorties (sans appliquer le zéro sur les pins physiques)
-passage dans les MAE, donc positionnement des bits dans le registre des sorties
-application de l'état du registre sur les pins de sortie
normalement même si pour une raison X ou Y l'arduino rame parceque j'ai un truc qui bloque, à aucun moment l'arduino ne devrait couper l'activation de la sortie de commande de la vanne si le mouvement n'est pas fini (ou dépassé, si ça a ramé). Et pourtant c'est ce qui se passe.
////////////////////////mix state machine///////////////////////////////////////
void mix_close()
{
if(mix.isFirstRun())
Serial<<"mix_close"<<MVActualPos*1<<" "<<MVWantedPos<<_endl;
//we fully close the valve to make sure we know the initial position
moveMixValve(MVCLOSE);
if(mix.elapsed(C[1][0]*1E3)) //C1.0 is in s so we need to *1000 to get value in ms.
{
MVActualPos=0;
mix.next(mix_wait);
}
}
void mix_wait()
{
if(mix.isFirstRun())
Serial<<"mix_wait"<<MVActualPos*1<<" "<<MVWantedPos<<_endl;
MVWantedPos=round(MVWantedPos);
if(MVWantedPos<0) MVWantedPos=0;
if(MVWantedPos>100) MVWantedPos=100;
if(MVActualPos<MVWantedPos) mix.next(mix_moveOpen);
if(MVActualPos>MVWantedPos) mix.next(mix_moveClose);
}
void mix_moveOpen()
{
if(mix.isFirstRun())
Serial<<"mix_moveOpen"<<MVActualPos*1<<" "<<MVWantedPos<<_endl;
moveMixValve(MVOPEN);
if(mix.elapsed(C[1][0]*10)) //C1.0 is in s so we need to *1000 to get value in ms.
{ //then we /100 to get ms time needed to move 1% : so we *10
MVActualPos++;
mix.next(mix_wait);
}
}
void mix_moveClose()
{
if(mix.isFirstRun())
Serial<<"mix_moveClose"<<MVActualPos*1<<" "<<MVWantedPos<<_endl;
moveMixValve(MVCLOSE);
if(mix.elapsed(C[1][0]*10)) //C1.0 is in s so we need to *1000 to get value in ms.
{ //then we /100 to get ms time needed to move 1% : so we *10
MVActualPos--;
mix.next(mix_wait);
}
}
Merci pour le retour.
J'ai passé plus de temps sur ton code, j'ai fait quelques modifs et ça a l'air de marcher chez moi à quelques détails près.
Je ne comprends pas trop ton système de registres de sortie, R et RF (pas assez fort en prog!) donc je l'ai viré et j'ai rajouté un état dans la machine à états de la vanne afin de pouvoir l'arrêter quand la condition suivante est vraie:
MVActualPos==MVWantedPos
J'ai également ajouté du code pour arrêter le circulateur (heating pump) quand la vanne est à 0%
Avec ces deux changements tout à l'air de fonctionner normalement et la vanne s'ouvre/ferme en continu.
Pour moi le seul truc qui reste à améliorer dans mon code est la mesure du temps d'ouverture de la vanne et l'incrémentation de sa position. Dans mon cas j'ai un gros delay() de 1000ms lors de la lecture des sondes de température qui bloque la machine à états. Du coup quand on arrrive dans la focntion:
if(mix.elapsed(VanneTimeConstant/100))
il s'est écoulé un temps bien supérieur à VanneTimeConstant/100
void mix_halt()
{
if(mix.isFirstRun())
Serial<<"mix_halt "<<MVActualPos*1<<" "<<MVWantedPos<<_endl;
stopMixValve();
mix.next(mix_wait);
}
void mix_wait()
{
if(mix.isFirstRun())
Serial<<"mix_wait "<<MVActualPos*1<<" "<<MVWantedPos<<_endl;
MVWantedPos=round(MVWantedPos);
if(MVWantedPos<0) MVWantedPos=0;
if(MVWantedPos>100) MVWantedPos=100;
if(round(MVActualPos)==0)
{
digitalWrite(PIN_R8, LOW); //Heating Pump OFF
Serial<<"round(MVActualPos): "<<round(MVActualPos)<<" "<<MVWantedPos<<_endl;
}
else
{
digitalWrite(PIN_R8, HIGH); //Heating Pump ON
Serial<<"Heating pump ON. round(MVActualPos): "<<round(MVActualPos)<<" "<<MVWantedPos<<_endl;
}
if(MVActualPos==MVWantedPos) mix.next(mix_halt);
if(MVActualPos<MVWantedPos) mix.next(mix_moveOpen);
if(MVActualPos>MVWantedPos) mix.next(mix_moveClose);
}
Erratum,
je n'ai pas de delay() dans ma lecture de températures par contre j'ai 8 sondes DS18B20 avec des calculs de filtrage et moyennage sur les valeurs. De plus la fonction requestTemperatures() de la librairie DallasTemperature incorpore un delay de 750ms je crois, et je l'appelle deux fois (2 bus de sondes distincts) donc cela prend un peu de temps à calculer. J'imagine que c'est cela qui créé des soucis sur la mesure de temps du mouvement de la vanne.
Petite différence dans mon code avec le tiens, je n'ai pas de boucle loop(), j'utilise la librairie <SoftTimer.h> qui lance des tâches (tasks) indépendamment. Je trouve ça plus clair pour le code.
J'ai donc trois tasks:
1/ la lecture des sondes de température toutes les 2 secondes
2/ la boucle PID toutes les 5 secondes
3/ la boucle qui check si client ethernet toutes les 200 millisecondes
Bon je viens de comprendre qu'il y avait un mode asynchrone pour la lecture des sondes DS18B20 du coup j'ai passé mes lectures de sondes via ta machine à états comme dans ton code.
Ça améliore mon problème de timing sur le mouvement de la vanne 4 voies. Avant c'était faux de 100%, c'est passé à 30%...
En tous cas le pilotage de la vanne fonctionne bien dans mon cas.
Au démarrage de l'Arduino, la vanne se ferme bien à 100% pendant les 120 secondes requises par ma vanne sans erreur de calcul du temps requis (normal, l'arduino ne fait encore rien d'autre en même temps) puis la régulation démarre et la vanne suit la consigne
salut
cool que ça fonctionne pour toi
par contre je crois que tu te compliques la vie pour rien... à quoi sert ton état mix_halt ?? puisque si la condition MVActualPos==MVWantedPos est vraie, tu reste dans l'état mix_wait, ça n'appelle pas ni mix_open ni mix_close, donc ton état mix_halt ne sert à rien...
même si tu as besoin de la fonction stopMixValve que tu as rajoutée (pourquoi ??) tu peux l'appeler dans mix_wait sans rajouter d'état.
l'emploi de <SoftTimer.h> en plus de ma librairie de machines à état, ça fait un peu double-emploi je pense, vu que finalement elle font sensiblement la même chose, du moins de la manière dont je l'emploie dans ce projet : c'est dans la loop que tu lances les "taches" (chaque machine à états). si tu dois stopper une tache il suffit de ne pas lancer tache.run()
pour ce qui est des registres R et RF, en faait la logique est la suivante : chaque bit de la variable R ou RF correspond à une sortie de l'automate (une pin de commande des relais). Quand j'ai besoin d'activer une sortie dans le déroulement du code, je place juste à 1 le bit correspondant dans la variable R. Ensuite à la fin de la loop() donc après l'execution de tous les états des différentes machines à états du système, j'appelle outputWrite() qui va traduire l'état des bits de R en état réel des sorties concernées (avec digitalWrite() ). J'aurais aussi pu utiliser une variable de type bool pour chaque sortie, mais ça aurait utilisé bien plus de ram pour rien0, même si le code aurait sans doute été plus clair.
Ensuite au tout début de la loop, la variable R est mise à zéro, et le cycle recommence. (Mais l'état des pins de sortie n'est PAS modifié à cette étape)
L'intéret de faire comme ça, c'est que si une action doit se continuer d'un passage à l'autre dans un état de la machine, il n'y a pas de coupure de la sortie le temps du calcul qui se fait dans les machines à états, puisque le changement sur les sorties ne se fait qu'une fois que les nouveaux calculs sont finis
la variable RF quant a elle est là pour les sortie forcée dans le menu, afin qu'elles ne soit pas changées par les autres machines à états. Avant chaque changement demandé de bit de R, la fonction setOutput() vérifie si le bit correspondant de RF est actif ou non, avant d'effectuer la modif.
C'est pour ça que aec mon système, la fonction stopMixValve est inutile, car partant du moment ou moveMixValve n'est plus appelée, les bits de sorties ne sont plus activés et la vanne ne bouge plus
Salut et merci d'avoir pris le temps d'expliquer.
-
Tu as raison, mon état mix_halt ne sert à rien (d'ailleurs c'est une fonction, pas un état...) donc je vais simplifier comme tu le proposes (je n'avais jamais entendu parler d'une machine à états il y a quelques semaines de cela donc j'apprends...
)
-
tu as également raison sur l'utilisation de <SoftTimer.h> mais comme je l'utilisais avant de commencer à utiliser ta librairie Yasm, je l'ai laissé du coup...
-
pour ce qui est des registres R et RF, j'avais bien compris qu'il s'agit d'opérations au niveau des bits mais je n'avais pas fait l'effort de trop creuser, sachant que ni R ni RF n'étaient expliqués en commentaire.
Les deux questions/commentaires que j'ai maintenant sont:
1/ je ne vois pas dans ton code où/quand tu arrêtes la vanne, c'est pour cela que j'ai ajouté ma fonction "stopMixValve". J'imagine que c'est au niveau de R justement, mais je ne vois pas...
2/ N'il y a t-il pas une erreur dans ton code au niveau des bits de sortie qui actionnent la vanne justement, on dirait que R2 et R3 sont inversés:
#define PIN_R2 32 //heating water temp servo +/-
#define PIN_R3 30 //heating water temp servo on
void moveMixValve(bool _direction)
{
setOutput(BIT_R3, !_direction);
setOutput(BIT_R2, 1);
}
Ce sont les commentaires qui ne sont pas bons, sur la commande de la vanne
En fait j''ai modifié le branchement sur place car ça tombait mieux pour avoir le fil en face du bon relais... et j'ai pas changé les commentaires 
si si, mix_halt est un état dans lequel tu ne reste qu'un cycle avant de retourner sur mix_wait. Chaque état d'une machine YASM est une fonction, en effet, mais utilisée ainsi c'est bien un état de plus.
pour ce qui est de la commande de la vanne, en fait je ne l'arête pas à proprement parler, juste je ne la demande pas quand je suis dans l'état mix_wait, puisque moveMixValve n'est pas appelée dans cet état. C'est ce que j'expliquais sur le fonctionnement de R et son application sur les pins physiques uniquement à la fin de la loop : moveMixValve ne fait que positionner les bits correspondants de R à 1. Comme les bits sont remis à 0 à chaque début de loop, si elle n'est pas appelée pour les remettre à 1 avant le fin de loop et donc l'appplication de l'état sur les pins, les pins repassent à 0 et la vanne ne bouge plus. (Enfin à 1 plus exactement, car les relais sont à commande inversée...)
A moins bien sur que dans le menu forçage les états de R2 et R3 aient étés forcés à 1 (donc mis à 1, et mis à 1 aussi dans RF pour qu'ils ne soient pas remis à zéro en début de loop)
En fait je me rends compte que je n'ai peut-être pas assez détaillé le fonctionnement de RF : chaque bit de RF correspond au même relais que les bits de R, mais ce que fait le bit de RF si il est à 1, c'est de dire aux fonctions setOutput() et à la ligne R &= RF; //erase all output bits except forced ones de loop de ne pas toucher aux bits de R correspondants.
pour ce qui est de ce dernier truc en début de loop : c'est un "et" logique bit à bit entre R et RF. Ca fonctionne tout simplement ainsi :
R 01110101
& RF 00000110
= R 00000100
car 0&1 ou 0&0=0 et 1&1=1
ce qui permet donc de ne remettre à 0 que les bits de R dont le bit correspondant de RF est à 0
OK tout compris, la clé qui me manquait et que je n'avais pas vu était que R est remis à 0 en début de chaque cycle. Cela explique tout, merci et bon courage pour trouver ton bug alors.