Maintient d'un relais en temps

Bonsoir, je me suis fait un détecteur de mouvement + lumière.

J'ai une condition qui vérifie si il y a une détection et que la lumière est inférieur à un nombre fixé par un potentiomètre. Je voudrais aussi que si la détection ou la lumière change que le relais reste actif un temps fixé par un autre potentiomètre.

Mon problème actuelle est que si le mouvement n'est plus détecté ou que la lumière augmente, le relais ce coupe alors que je voudrait qu'il reste actif un temps.

#include <Wire.h>
#include <BH1750.h>

BH1750 lightMeter;

int mouvement = 4;
int relais = 2;
int com_nanuel = 3;
int com_auto = 7;
int seuil_luminosite = A2;
int seuil_temps = A3;
boolean etat_BP;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  lightMeter.begin(BH1750::CONTINUOUS_LOW_RES_MODE);
  pinMode(mouvement, INPUT_PULLUP);
  pinMode(com_nanuel, INPUT_PULLUP);
  pinMode(com_auto, INPUT);
  pinMode(relais, OUTPUT);
  digitalWrite(relais, HIGH);
  etat_BP = false;
}

void loop()
{
  int etat_mouvement = digitalRead(mouvement);
  Serial.print("Mouvement détecter: ");
  Serial.print(etat_mouvement);
  Serial.print("\t");

  float lux = lightMeter.readLightLevel();
  Serial.print("Light: ");
  Serial.print(lux);
  Serial.print(" lx");
  Serial.print("\t");

  int lumiere = analogRead(seuil_luminosite);
  float lumiere_choisi = map(lumiere, 0, 1023, 0, 200);
  Serial.print("Seuil de luminosité: ");
  Serial.print(lumiere_choisi);
  Serial.print(" lx");
  Serial.print("\t");

  int temps = analogRead(seuil_temps);
  float temps_choisi = map(temps, 0, 1023, 0, 300);
  Serial.print("Temps d'alumage: ");
  Serial.print(temps_choisi);
  Serial.print(" s");
  Serial.print("\t");
 
  int etat_com_manuel = digitalRead(com_nanuel);
  Serial.print(etat_com_manuel);
  Serial.print("\t");

  if(etat_com_manuel == 0) {etat_BP = !etat_BP;}
  Serial.print(etat_BP);
  Serial.print("\t");

  int etat_com_auto = digitalRead(com_auto);
  Serial.println(etat_com_auto);

  if(etat_mouvement == 1 && lux < lumiere_choisi)
  {
    digitalWrite(relais, LOW);
  }
  else if(etat_BP == 1)
  {
    digitalWrite(relais, LOW);
  }
  else if(etat_com_auto == 1)
  {
    digitalWrite(relais, LOW);
  }
  else
  {
    digitalWrite(relais, HIGH);
  }
}

C’est typiquement une définition de programme qui se prête bien à la programmation par machine à états (cf mon tuto éventuellement)

Il faut que du coup le passage à l'état bas de ton relais soit dépendant d'un minuteur.

avec quelque chose de la sorte, le wokwi pour le voir en action:

#define led 12
unsigned int detection=3;
unsigned int distance;
unsigned int timer = 0; 
void setup() {
  Serial.begin(9600);
  pinMode(led,OUTPUT);
}

void loop() {
  distance=random(10);
  Serial.print("cm=");
  Serial.print(distance);
  Serial.print("\ttimer=");
  Serial.println(timer);
  if (distance<detection && timer == 0) {
    timer = millis();
    digitalWrite(led,HIGH);
  } 
  
  if (timer > 0 && millis() - timer > 2000 )  {
    digitalWrite(led,LOW);
    timer = 0;
  } 
  delay(500);
}

J'ai lu le tuto et le code de terwal. Et donc soit je comprend rien soit sa marche pas.

Le problème selon moi est que si "etat_mouvement == 1 && lux < lumiere_choisi" l'une des deux condition change pendant le décompte de temps le relais ce coupe trop top

Il suffit de ne pas tester les conditions des présence ou de luminosité quand vous avez activé le relais

Votre système c'est le relai et son état (actif ou inactif).
les conditions de passage d'un état à un autre sont simples

  • quand le relai est actif, il faut attendre la fin d'un délai
  • quand le relai est inactif, on teste la présence et la luminosité

en pseudo code

si relais activé
  // le relais est actif on teste le temps passé
  si fin du délai 
     désactiver le relai
sinon 
  // le relais n'est pas actif, on teste les conditions d'activation
  si détection ET lumière < seuil
    activer le relais
    noter le moment d'activation

je veux bien mais je n'es pas d'info sur l'état du relai et pose un autre problème. j'ai deux condition de forçage qui ne sont pas soumis temps de désactivation

c'est vous qui le choisissez non ? donc vous pouvez le mémoriser

il me semble avoir compris qu'une fois activé la désactivation ne se fait qu'au bout d'un certain temps et rien d'autre ne peut interrompre le relais

Non justement, sur détection de mouvement et lumière active le relais ce désactive au bout d'un temps.

Mais quand je fait un appuis sur le bouton manuel sa reste allumé (pas de désactivation auto).
si activation ce fait par ESP32, le relais reste allumé jusqu'à ce que ESP32 le coupe (je les mal codé en plus)

ah il y a un bouton en plus. il est connecté à l'Arduino ?

J'ai pas mit le schémas complet

ni le correcteur orthographique :slight_smile:

ce genre de schéma est assez illisible mais est-ce que je vois deux Arduino ? à quoi sert l'ATTiny ou le MCP ?

L'ESP32 et MPC est la juste pour montré le forçage via un autre projet.

je fais les teste avec un arduino mais au final sa sera un attiny44

Ton pseudo code m'a l'air bien, tu essayer de coder ça.
Dans mon exemple, j'utilise la valeur de timer qui est différent de 0, quand le relais est actif.
Une fois le temps dépasser, le relais est désactivé et la variable timer remis à zéro :grin:

J'ai fait sa et sa marche visiblement mais il faut attendre le changement d'état du mouvement ou lumière pour passé à la condition suivant si je rajouté les deux forçage sait plus le cas

int etat_relais = 0;

if(etat_mouvement == 1 && lux < lumiere_choisi) {etat_relais = 1; digitalWrite(relais, LOW);}
else if(etat_relais == 1) {digitalWrite(relais, LOW); delay(temps_choisi * 1000); digitalWrite(relais, HIGH); etat_relais = 0;}

Postez un diagramme d’états correspondant à votre idée du besoin.

quel est le système ? (le relai sans doute)
quels sont les éléments pouvant déclencher une transition ? (le bouton, le temps, un système externe, ...)

remet ton code en entier, c'est plus simple pour discuter que devoir faire des copier/coller de différents messages.
globalement, c'est mieux si tu n'utilise pas de delay qui stoppe ton programme pendant le temps demander.

De même un code avec une indentation standard, permet au lecteur de te lire facilement.
Alors que là, c'est un peu pénible et n'incite pas à prendre du temps pour t'aider.

sinon si dans ton "else if" ta condition prenais aussi comme je l'ai fait en compte si le delais était dépasser, cela devrait marcher, je pense.

Le code complet:

#include <Wire.h>
#include <BH1750.h>

BH1750 lightMeter;

int mouvement = 4;
int relais = 2;
int com_nanuel = 3;
int com_auto = 7;
int seuil_luminosite = A2;
int seuil_temps = A3;
boolean etat_BP;
int etat_relais = 0;
int etat_forcage = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  lightMeter.begin(BH1750::CONTINUOUS_LOW_RES_MODE);
  pinMode(mouvement, INPUT_PULLUP);
  pinMode(com_nanuel, INPUT_PULLUP);
  pinMode(com_auto, INPUT);
  pinMode(relais, OUTPUT);
  digitalWrite(relais, HIGH);
  etat_BP = false;
}

void loop()
{
  int etat_mouvement = digitalRead(mouvement);
  Serial.print("Mouvement détecter: ");
  Serial.print(etat_mouvement);
  Serial.print("\t");

  float lux = lightMeter.readLightLevel();
  Serial.print("Light: ");
  Serial.print(lux);
  Serial.print(" lx");
  Serial.print("\t");

  int lumiere = analogRead(seuil_luminosite);
  float lumiere_choisi = map(lumiere, 0, 1023, 0, 200);
  Serial.print("Seuil de luminosité: ");
  Serial.print(lumiere_choisi);
  Serial.print(" lx");
  Serial.print("\t");

  int temps = analogRead(seuil_temps);
  float temps_choisi = map(temps, 0, 1023, 0, 300);
  Serial.print("Temps d'alumage: ");
  Serial.print(temps_choisi);
  Serial.print(" s");
  Serial.print("\t");
 
  int etat_com_manuel = digitalRead(com_nanuel);
  Serial.print("Com manuel: ");
  Serial.print(etat_com_manuel);
  Serial.print("\t");

  if(etat_com_manuel == 0) {etat_BP = !etat_BP;}
  Serial.print("Etat com manuel: ");
  Serial.print(etat_BP);
  Serial.print("\t");

  int etat_com_auto = digitalRead(com_auto);
  Serial.print("Etat com auto: ");
  Serial.print(etat_com_auto);
  Serial.print("\t");
  
  Serial.print("Etat relis: ");
  Serial.print(etat_relais);
  Serial.print("\t");
  Serial.print("Etat relis: ");
  Serial.println(etat_forcage);
  
  if(etat_mouvement == 1 && lux < lumiere_choisi && etat_relais == 0) {etat_relais = 1; digitalWrite(relais, LOW);}
  else if(etat_relais == 1) {digitalWrite(relais, LOW); delay(temps_choisi * 1000); digitalWrite(relais, HIGH); etat_relais = 0;}
  
  /*else if(etat_BP == 0) {digitalWrite(relais, LOW);}
  else if(etat_BP == 1) {digitalWrite(relais, HIGH);}
  else if(etat_com_auto == 1) {digitalWrite(relais, LOW);}
  else if(etat_com_auto == 0) {digitalWrite(relais, HIGH);}*/
}

Je pence avoir réussi à faire auto-maintient pour la détection. Maintenant je dois regardé pour les forcage

il faudrait remplacer le 1 par une variable de type "boolean" qui serait l'état voulu du relais

si vous tenez la broche com_nanuel à LOW (0) pendant plusieurs boucles (tour de loop()) vous allez inverser constamment etat_BP. Vous devriez faire une détection de changement d'état sans doute non ?

Et pour simplifier votre gestion des boutons, éventuellement utilisez la bibliothèque Button dans easyRun de @bricoleau ou OneButton de Matthias Hertel ou encore Toggle de @dlloyd.

Pourquoi écrire ça en condenser et perdre en lisibilité?

  if(etat_mouvement == 1 && lux < lumiere_choisi && etat_relais == 0) {
    etat_relais = 1;
    digitalWrite(relais, LOW);
  }
  else if(etat_relais == 1) {
    digitalWrite(relais, LOW);
     delay(temps_choisi * 1000);
     digitalWrite(relais, HIGH);
     etat_relais = 0;
  }

C'est quand même plus lisible?
du coup à quoi sert ton else, c'est vraiment ce que tu veux faire?
Si tu prend le temps(millis) dans le if et que tu compare le temps passer à ton seuil dans le else if, cela fait exactement ce que tu veux :slight_smile: