Concours Z-UNO – Projet de Gérard C.

Concours ZUNO : les réalisations

Planète Domotique a lancé en septembre dernier un concours autour de la carte de développement Z-UNO. Suite à un appel de candidatures, trois projets ont été retenus et les lauréats ont gagné leur propre carte de développement. Il est désormais temps de découvrir chaque jour sur ce blog un des projets concrétisés avec le Z-UNO. À la fin de la semaine, vous pourrez voter pour le projet qui vous aura le plus plu, et permettre ainsi au gagnant de remporter un bon d’achat de 100 € !

Aujourd’hui nous vous proposons de découvrir le projet de Gérard C.

Dans le cadre du concours « Concours carte Z-UNO : développez votre propre périphérique Z-Wave ! » organisé par Planète Domotique, j’ai réalisé le projet décrit dans cet article qui m’a permis de me familiariser avec ce module.

Le projet : un projecteur PIR Z-Wave avec le Z-Unologoprj

L’objet du projet est un projecteur extérieur halogène Z-Wave avec détection de mouvement et seuil crépusculaire.
En reprenant l’idée de Planète Domotique utilisée dans l’annonce des lauréats du concourt, cela donne le pictogramme à droite.

 

L’idée de ce projet vient du fait que je n’ai pas trouvé de solution basée sur Z-Wave pour disposer d’un projecteur extérieur avec détection de mouvement et crépusculaire.

Je m’explique :

  • Extérieur : très peu d’appareils Z-Wave sont homologués pour l’extérieur, et quand ils le sont, c’est parfois en IP20 seulement au lieu d’un IP 44 (cas du projecteur utilisé ici).
  • Détection crépusculaire : parmi les solutions que j’ai explorées, soit celles-ci ne disposent pas de logique permettant de prendre en compte le paramètre jour / nuit, soit l’implémentent mal (par exemple le Fibaro FGMS-001 mesure l’éclairage en permanence, donc y compris celui généré par une lampe dont il aurait commandé lui même l’allumage suite à une détection de mouvement, ce qui fait qu’il se croit le jour lorsqu’un mouvement a été détecté la nuit).
  • Détection de mouvement « brute » : je souhaitais avoir un indicateur de mouvement disponible en permanence (y compris de jour) afin d’activer par exemple un carillon ou encore une caméra (j’ai commencé un projet avec un Raspberry et une WebCam + Motion).

Après quelques explorations, mon choix s’était tout d’abord orienté vers le module Fibaro Universal Binary Sensor FGBS-001. Mais au cours d’autres recherches, j’ai découvert le Z-Uno, et j’ai tout de suite pensé que ce module était bien mieux adapté à mes besoins.

Par chance, je suis également tombé sur le concours organisé par Planète Domotique, et j’ai trouvé que ce serait intéressant de développer ce projet dans ce cadre là.

Le cahier des charges

  • J’ai voulu favoriser le plus possible l’utilisation d’un projecteur PIR existant pour les raisons suivantes :
    • Autonomie de fonctionnement en cas de dysfonctionnement Z-Wave.
    • Tirer partie de la robustesse aux intempéries : le projecteur est prévu pour fonctionner en extérieur. Si je commence à ajouter des détecteurs PIR ou de lumière ou autres, je vais devoir gérer le problème de l’étanchéité à l’eau, mais aussi à la poussière, aux insectes, la tenue aux UVs, etc.
  • Disposer d’indicateurs de type « Sensors » dans le contrôleur domotique pour:
    • La détection de mouvement la nuit permettant d’asservir des éclairages.
    • La détection de mouvement « brute », sans condition jour / nuit permettant d’asservir des accessoires divers.
    • La détection de condition jour / nuit pour utilisations diverses.
  • Disposer d’une commande de type « Switch » permettant la commande de la lampe du projecteur depuis le contrôleur domotique.
  • Support des associations Z-Wave :
    • Le projecteur doit être capable de piloter via des associations d’autres dispositifs Z-Wave via deux groupes, un pour la détection de mouvement de nuit et un pour la détection de mouvement inconditionnelle.
    • Il doit être possible de contrôler la lampe du projecteur depuis un autre dispositif Z-Wave via une association.

Réalisation, le premier prototype

Tout d’abord, le projecteur en question (c’est un modèle assez courant dans les grandes surfaces de bricolage) :

img_0592

Maintenant, rentrons dans le vif du sujet, à savoir l’intégration de la partie électronique. Il a fallu ouvrir le projecteur et analyser l’électronique pour en déduire où connecter les I/O du Z-Uno.

Le bloc électronique est composé de deux circuits imprimés, l’un accueillant l’alimentation fournissant du 24V ainsi que le relais, l’autre accueillant la partie détection de mouvement et de lumière ainsi que la temporisation (à droite sur photos suivantes).

Voici le résultat en photos de ce démontage :

img_0594     img_0599

Le résultat de l’analyse du fonctionnement est le schéma suivant qui détaille la partie en aval de la détection de mouvement :

 pir-diagram

L’analyse des connexions à effectuer donne :

  • 2 fils d’alimentation 24 V, car l’alimentation de base sans transformateur avec condensateur série n’assurant pas d’isolation galvanique avec le secteur a été retirée.
  • 3 fils pour la logique: détection PIR, détection luminosité et commande du relais.

Ci-dessous les connexions effectuées et le tout remonté dans le boitier d’origine :

img_0603    img_0607

L’étape suivante a consisté en la réalisation d’un premier prototype sur plaquette d’expérimentation sans soudure. Les quelques composants mis en œuvre on été fixés sur une planchette afin d’assurer une meilleure stabilité mécanique de l’ensemble lors des tests.

img_0611

 

On distingue :

  • Le bloc électronique du projecteur.
  • L’alimentation 24 V provisoire.
  • Sur la plaquette d’expérimentation, le Z-Uno avec juste à côté un convertisseur DC/DC 24 / 3.3 V (il n’y a pas encore le LM324 au moment de cette photo).

Le schéma final de la partie électronique associée au Z-Uno est donné ci-dessous :

zuno-diagram

Les premières expérimentations

J’ai commencé par rencontrer mes premiers déboires au niveau électronique. Il y a un bug dans le firmware actuel du Z-Uno, et les I/O ne sont pas en haute impédance en mode « INPUT ». Donc les « digitalRead » ne retournaient pas les bonnes valeurs avec les résistances que j’utilisais pour me connecter au projecteur (de valeur la plus grande possible afin d’être au minimum intrusif).

Après un post sur le forum z-wave.me, il faut se rendre à l’évidence, il n’y aura pas de correction disponible rapidement.

J’ai donc ajouté un amplificateur opérationnel (LM 324) réalisant une adaptation d’impédance, et au passage j’amplifie un peu le signal de luminosité de la photo diode (voir schéma).

Ensuite, au niveau programmation, il n’a pas été facile de comprendre tout de suite la logique des APIs et des macros disponibles pour gérer les canaux (Channels) Z-Wave. Après de très nombreux essais et tâtonnements dans le programme plus une multitude d’inclusions / exclusions Z-Wave, et un autre post dans le forum, le premier proto est « tombé en marche ». Ca a été un grand soulagement ! Et un encouragement à continuer…

Ci-dessous, on peut voir un exemple où l’on voit une détection PIR de jour : Z05 est la condition jour / nuit (ici il fait jour, donc activée), Z03 est la détection PIR inconditionnelle (donc activée), Z04 est détection PIR nocturne (donc non activée).

Le Switch Z02 permet de commander l’allumage / extinction de la lampe.

domoticz-1

Maintenant, voyons l’écran de gestion des associations dans Domoticz :

domoticz-2
On aperçoit les associations sortantes du Z-Uno avec les groupes 2 (PIR inconditionnel) et 3 (PIR nocturne), ainsi que le switch TZ66 qui commande le Z-Uno via son groupe 2. Et ça fonctionne impeccablement !

Petit zoom sur les difficultés softwares non liées au Z-Uno

Bien que l’ensemble ne soit pas d’une grande complexité, il y a eu quelques points un peu plus pointus.

La détection de lumière

La détection de lumière réutilise la photodiode interne du projecteur. Je mesure une tension avec le convertisseur ADC du Z-Uno, or cette diode est plutôt montée en générateur de courant (injecté dans la base d’un transistor) dans le projecteur, donc les variations mesurées sont faibles et j’ai aussi constaté assez fluctuantes.

Pour fiabiliser la mesure, j’ai implémenté un calcul de moyenne mobile sur 32 mesures avec des échantillons pris toutes les secondes. De même, un hystérésis d’environ 10 % est utilisé pour la détection des transitions jour / nuit pour avoir des transitions nettes et sans multiples « hésitations » lorsque l’on est proche du seuil.

La gestion des timings

Ayant affaire à un programme mono tâche devant s’exécuter en boucle, il faut prendre quelques précautions avec les timings, et notamment ne pas utiliser de « wait ».

Il faut donc mettre en œuvre l’équivalent de petits (voire microscopiques) automates d’états dont les états mémorisent les actions à effectuer (ici toutes minutes et toutes les secondes) et sont mis à jour par les différentes parties du programme. Mais rien de bien compliqué ! Voir le code en fin d’article.

Fonctions ajoutées après le premier prototype

Après ce premier essai concluant, j’ai souhaité ajouter un paramètre Z-Wave permettant de fixer un délai supplémentaire par rapport au signal fourni par le projecteur PIR pour la signalisation en Z-Wave.

Si ce paramètre (pirOverTime dans le code) est à zéro, alors les notifications de détection de mouvement (jour + nuit) suivent (temporellement) le timer interne du projecteur.

Si une valeur non nulle est donnée, alors les notifications de fin détection de mouvement sont retardées du nombre spécifié (en secondes). Il est ainsi possible d’avoir une temporisation de 5 mn en Z-Wave alors que le projecteur peut être réglé sur 2 mn par exemple.

Remarque: comme le signal extrait du projecteur est pris après la temporisation interne de ce dernier, il n’est pas possible de distinguer la détection de mouvement de la temporisation. C’est pourquoi il n’est possible que d’allonger la durée, et non de la raccourcir.

Pour l’instant, le firmware du Z-Uno ne permet pas de gérer le paramétrage à distance du Z-Uno depuis le contrôleur domotique (voir ce post). Donc le paramètre décrit ci-dessus est en dur dans le code, en attendant une future version.

La mise au propre finale

C’est toujours une partie difficile (en tous cas pour moi) que de finaliser proprement une réalisation en mode prototype avec mise en boitier pour utilisation in situ. D’autant plus qu’ici le paramètre étanchéité est important.
Pour commencer, j’ai eu de la chance de trouver un boîtier étanche (IP65) et avec pates de fixation qui c’est avéré parfaitement adapté :

boitierzuno

Il sera donc fixé juste à proximité du projecteur, dans la continuité de l’arrivée 220 V.

Ensuite, j’ai soudé tous les composants et un support pour le Z-Uno sur une plaquette de prototypage et utilisé une méthode de câblage volant avec petits fils (y compris du fil à wrapper).

Les photos suivantes montrent le résultat intermédiaire où l’on peut voir en plus du Z-Uno le bloc d’alimentation 220 / 24 V (orange) le petit convertisseur DC / DC pour le 3.3 V (parallélépipède noir), le LM324 et les quelques résistances, condensateurs et connecteurs.

Le papier cartonné et scotché est là pour isoler le 220 V en entrée du bloc AC / DC afin que je ne me prenne pas une secousse lors des tests…

img_0628    img_0629

Les photos suivantes montrent le tout intégré dans le boitier en position finale avant fermeture. On aperçoit sur le couvercle une rainure qui permet d’accueillir un joint caoutchouc fourni avec le boîtier.

Pour l’instant les câbles vers le projecteur sont provisoires, ils passeront par la suite au travers d’un presse étoupe pour l’étanchéité (aussi sur la photo, en gris) que je n’ai pas encore pu mettre faute d’avoir reçu un taraud (il me manquait le bon diamètre).

img_0631    img_0633

Par la suite, j’envisage d’ajouter un petit switch ILS permettant de commander le bouton service du Z-Uno à l’aide d’un aimant à travers le boiter plastique sans avoir à l’ouvrir (permettant de gérer l’étanchéité à bon compte). Je n’en ai pas à ma disposition pour l’instant, et laisse donc ce point pour plus tard.

Eventuellement, on pourrait aussi considérer l’implémentation d’un watch dog, mais je n’ai pas eu de problème de plantage durant mes essais. Au pire une coupure de tension au niveau du tableau électrique assurera un reset si besoin.

Remontage du projecteur

Après les derniers ajustements et la finalisation du câblage entre le projecteur et le boitier Z-Uno, voici l’allure générale du montage juste avant fermeture des boîtiers :

img_0637

Toute la connectique entre les deux boîtiers a été rassemblée dans une gaine souple raccordée via des presse étoupes pour une parfaite étanchéité.

Pour les derniers tests, et la vérification des aspects mécaniques avant de prendre la perceuse et de monter sur l’échelle, mais aussi pour les besoins de la photo, j’ai fixé temporairement le projecteur et le boitier Z-Uno sur une planche. Tout va bien, le tout est conforme à mes attentes initiales.

De plus, en positionnant le boitier Z-Uno au dessus du projecteur, il ne se remarquera finalement pas tant que ça (le tout sera fixé à approximativement 3 m du sol), et l’antenne radio est du coup situé sur le point haut, ce qui ne gâche rien au point de vue des problématiques RF.

Voici ce que cela donne en photo :

img_0648-bis

La suite

Je ne vais pas fixer le projecteur immédiatement sur le mur, mais attendre la prochaine mise à jour du firmware de manière à disposer de la possibilité de paramétrer le fonctionnement depuis Domoticz.

Bien sûr, il y aurait la possibilité de compter sur une mise à jour « in-situ » après montage définitif, puisque le Z-Uno supporte la programmation OTA (Over The Air). Mais je n’ai pas pu expérimenter cette possibilité, et de plus, je ne vois pas comment faire pour l’instant pour télécharger le code (depuis Domoticz ou autre) une fois celui-ci compilé. Je vais investiguer ce point qui est assez important sur ce type de réalisation assez peu accessible après mise en service.

Aussi, lorsque le projecteur sera installé, j’envisage – en plus des utilisations habituelles en éclairage avec couplage vers d’autres éclairages et capteurs – d’utiliser la fonction détection de mouvement inconditionnelle pour piloter un carillon discret. Un petit script Domoticz sera sans doute requis pour désactiver le tout lorsque tout le monde dort…

Par la suite, je reprendrais le projet WebCam mentionné au début. Maintenant que je vais disposer d’un détecteur de mouvements extérieur fonctionnant aussi de jour, j’aurais de quoi activer la WebCam.

Conclusion, crédits et remerciements

  • Tout d’abord, merci à Planète Domotique pour avoir offert un Z-Uno qui est après tout le point de départ de ce projet. Le fait d’avoir réalisé ce mini projet sous forme d’un concours a été motivant (je précise que je ne suis pas du tout un habitué des concours), c’est une très bonne idée.
  • Ensuite une mention très bien pour le Z-Uno. Ce module est de mon point de vue une réussite, malgré ses quelques défauts de jeunesse. On peut noter :
    • Une très bonne abstraction du stack Z-Wave. Sans quoi, la réalisation de ce type de projets tournerait au cauchemar.
    • La Réalisation du module est très aboutie (petit format, excellente qualité…). Pas de problème non plus du point de vue électronique et intégration, le tout reste très simple.
    • L’intégration dans l’environnent Arduino est bien réussie. Il ne faut pas oublier qu’à la base, ce module n’a pas grand chose à voir avec un Arduino. Il est bâti autour d’un chipset complexe avec comme processeur un 8051 qui n’est pas un AT Mega 328. Du côté logiciel, il y a un petit noyau multitâches, dont le programme Arduino (le fameux « loop() ») est une des tâches. Et le tout est transparent : on ne se rend pas compte de cette complexité lors de la programmation !
    • Le support sur le forum de la part des employés de z-wave.me est très réactif.
  • La majorité des composants utilisés ont été commandés sur Farnell dont je loue au passage la rapidité de traitement des commandes, le choix monstrueux et les prix très raisonnables.

Le code

Et pour terminer, voici le code du programme :

#define DEBUG
#define DEBUGVERB

// I/O definition
#define REL_PIN     10
#define LED_PIN     13
#define PIR_PIN     22

// Luminosity is measured as an analog value (ADC0) and compared to DAY_THRESHOLD threshold.
//   Value above is considered day and bellow is night
//   Luminosity is reported every LUM_REPORT minutes (will be a Z-Wave param in the future).
//   LUM_MEASURE_WAIT defines delay in seconds (+/- 1) we should wait before measuring lum
//     (see lumReadState explanation).
//   LUM_HYSTERESIS holds the hysteresis to apply to luminosity when checking for transistions.
#define LUM_PIN     3
#define DAY_THRESHOLD 500
#define LUM_REPORT  5
#define LUM_HYSTERESIS 40
#define LUM_MEASURE_WAIT 3
#define LUM_DONT_MEASURE -1
#define LUM_CAN_MEASURE 0

// Globals var holding current states for Z-Wave devices
// -----------------------------------------------------
byte lampState;
byte lastPIRState;
byte pirState;
byte lastLumState;
byte lumState;

// Other vars
// ----------
// In the Projector, luminosity measure is deactivated when  Lamp Relay is on, so value read is irrelevant.
// lumReadState is used to control when it can be read:
//    0: OK can be read
//   -1: Cannot be read, PIR is currently in detected state
//   >0: used as a countdown when PIR detection ended to let relay switch off stabilization and photo
//       diode activate.
//       The countdown in lumReadState is decremented each second when positive.
int8_t lumReadState = 0;
int lumVal; // Raw luminosity read from ADC
boolean aNewMinute = false;
unsigned long previousMillis = 0;
byte seconds = 0; // For seconds, 1 byte also used as we just want to check within 1 minute
byte minutes = 0; // Time calculations done in mn with 8 bits, having so a roll-over of about 4 h
int8_t checkRelay = -1; // Used to check if the relay switched or not during motion detection

// Luminosity measurement is done by a photodiode in the projector. The setup is more a current generator,
// but we measure a voltage. So variations are smalls and fluctuant.
// Therefore, a Rolling Average has been implemented to smooth it.
// Measures are averaged over LUM_NUM_SAMPLES into lumRollingAverage. Samples are hold into lumSamples.
// Result is held as an X LUM_NUM_SAMPLES and not divided. This means that when using lumRollingAverage,
// threshold and hysteresis should be also multiplied by this factor in the code.
// LUM_NUM_SAMPLES defines how many samples are used. With current implementation, 64 is the maximum
// because the result is an uint16 and it is not divided by the samples number.
// So ADC 0..1024 X 64 = 65536. To go beyond 64, ulong should be used.
#define LUM_NUM_SAMPLES 32
unsigned int lumRollingAverage;
unsigned int lumSamples[LUM_NUM_SAMPLES];
byte lumBuffPtr;
// First lum report will be sent after LUM_REPORT mn to let things stabilize at startup
byte lumReportTimer = 0;

// Added capacity to set the Motion Detection signalization sent over Z-Wave longer than the one
// provided by the projector. It is not possible to implement a shorter period because the
// projector provides only signal after its internal timer (so cannot distinguish motion from delay).
// When setting from the domotic controller will be available, this will be a setting.
// Meanwhile, this is given statically by PIR_OVER_TIME:
//  0: means Z-Wave signalling will follow the projector
//  t:  defines how long in seconds will be added to the projector timer.
// pirOverTimeCD is used as a countdown when projector signal ended. 0 means do not process.
#define PIR_OVER_TIME 0
unsigned int pirOverTime;
unsigned int pirOverTimeCD;

// Channels declaration. This code reports the following to the Z-Wave controller:
//  - The raw Motion Detection from Projector PIR Sensor as a SENSOR_BINARY
//  - A Motion Detection that is sent only during the night as a SENSOR_BINARY
//  - The Day / Night condition as a SENSOR_BINARY
//  - A switch that drives the Lamp Relay in the Projector as SWITСH_BINARY

// With those declarations, as an example, we have in Domoticz:
//   - PIR sensor will appear as a Sensor with ID 03
//   - PIR_Night sensor will appear as a Sensor with ID 04
//   - Day/Night detection will appear as a Sensor with ID 05
//   - Relay will appear with as a Switch with ID 02
//   - All other devices that appear in Domoticz should be ignored
ZUNO_SETUP_CHANNELS(
  ZUNO_SWITCH_BINARY(getterLamp, setterLamp),
  ZUNO_SENSOR_BINARY(ZUNO_SENSOR_BINARY_TYPE_GENERAL_PURPOSE, getterDummy),
  ZUNO_SENSOR_BINARY(ZUNO_SENSOR_BINARY_TYPE_GENERAL_PURPOSE, getterPir),
  ZUNO_SENSOR_BINARY(ZUNO_SENSOR_BINARY_TYPE_GENERAL_PURPOSE, getterPirNight),
  ZUNO_SENSOR_BINARY(ZUNO_SENSOR_BINARY_TYPE_GENERAL_PURPOSE, getterLum)
);

// Groups declaration
// !! Real association groups number are shifted by one. Means CTRL_GROUP_1 is in reality number 2.
// This is because number 1 is Life Line group.
// Group 2 is activated when motion is detected,
// Group 3 is activated when motion is detected AND it is the night.
ZUNO_SETUP_ASSOCIATIONS(ZUNO_ASSOCIATION_GROUP_SET_VALUE, ZUNO_ASSOCIATION_GROUP_SET_VALUE);

void setup() {
  // Set I/O Directions
  pinMode(LED_PIN, OUTPUT);
  pinMode(REL_PIN, OUTPUT);
  pinMode(PIR_PIN, INPUT);
  pinMode(LUM_PIN, INPUT);
  
  // Global variables initialisation
  lampState = 0;
  pirState = lastPIRState = digitalRead(PIR_PIN);
  lumVal = analogRead(A0);  // Raw reading @boot, may be not acurate
  lumState = lastLumState = lumVal < DAY_THRESHOLD ? LOW : HIGH;
  pirOverTime = PIR_OVER_TIME;  // Will be a setting when available through Firmware
  pirOverTimeCD = 0;

  // Relay switched off on reset
  digitalWrite(REL_PIN, LOW);

  lumRollingAverage = 0;
  for (byte i; i < LUM_NUM_SAMPLES; i++) lumSamples[i] = 0; lumBuffPtr = 0; #ifdef DEBUG Serial.begin(); #endif } // Main loop // --------- void loop() { // Check if a new second elapsed if (millis() - previousMillis > 1000L) {
    previousMillis = millis();
    seconds++;

    // Update luminosity rolling average every second
    // ----------------------------------------------
    // Rolling average is : (sample_1 + sample_2 + ... + sample_n) / n
    // If we consider n times average, when a new sample arrives, we have
    //   sample_2 + ... + sample_n + sample_n+1,
    // so just having to subtract oldest sample and adding the new one. Not worth dividing by samples
    // number to not put too stress on the microcontroller with arithmetic. Even if we could consider
    // using a power of two. There is no need to do that, just have to do the math with a samples number
    // multiplier factor
    lumVal = analogRead(A0);  // Raw photodiode measurement
    if (lumReadState == LUM_CAN_MEASURE) {  // As explained, don't do it when motion detected!
      lumRollingAverage -= lumSamples[lumBuffPtr];  // Substract oldest
      lumRollingAverage += lumVal;  // Adding new one
      lumSamples[lumBuffPtr] = lumVal;  // Buffer & pointer management
      if (++lumBuffPtr == LUM_NUM_SAMPLES) lumBuffPtr = 0;
    }

    // Processing luminosity state
    // ---------------------------
    // In the code bellow, we are using the averaged luminosity computed every second.
    if (lumReadState == LUM_CAN_MEASURE) {  // As explained, don't do it when motion detected!
      // Luminisity is not directly compared to DAY_THRESHOLD, but an hysteresis is used.
      if (lastLumState == LOW && lumRollingAverage > ((DAY_THRESHOLD + LUM_HYSTERESIS) * LUM_NUM_SAMPLES))
        lumState = HIGH;
      else if (lastLumState == HIGH &&
                            lumRollingAverage < ((DAY_THRESHOLD - LUM_HYSTERESIS) * LUM_NUM_SAMPLES)) lumState = LOW; // else we don't change lumState if (lumState != lastLumState) { // Change detected on Lum lastLumState = lumState; // save new state if (lumState != LOW) { #ifdef DEBUG Serial.print("LUM is HIGH / "); Serial.println(lumVal, DEC); #endif } else { #ifdef DEBUG Serial.print("LUM is LOW / ");Serial.println(lumVal, DEC); #endif } zunoSendReport(5); // Report Lum over Z-Wave upon change } } // Checking relay state not yet used, so nothing valuable done here. #ifdef DEBUGVERB if (checkRelay > 0) {
        Serial.print("checkRelay ("); Serial.print(checkRelay, DEC); Serial.print(") Lum read ");
        Serial.println(lumVal, DEC);
      } else {
        Serial.print("Lum read "); Serial.print(lumVal, DEC); Serial.print("  AVG ");
        Serial.println(lumRollingAverage, DEC);
      }
    #endif
    
    // Check over time for Motion Detection
    if (pirOverTimeCD > 0) {  // Countdown in progress
      pirOverTimeCD--;
      if (pirOverTimeCD == 0) { // Countdown ended, signal motion off
        signalPIRZWave();
      }
    }

    // Manage time related stuff
    // -------------------------
    if (seconds == 60) {
      seconds = 0;
      minutes++;
      aNewMinute = true;
    }
    // Updating countdown counters when they are active.
    if (lumReadState > 0) lumReadState--;
    if (checkRelay > 0) checkRelay--;
  }

  // Minute interval processing
  // --------------------------
  if (aNewMinute) {
    aNewMinute = false;
    #ifdef DEBUGVERB
      Serial.println("New Minute");
    #endif
    // Shal we report lum?
    if (++lumReportTimer == LUM_REPORT) {
      lumReportTimer = 0;
      #ifdef DEBUG
        Serial.print("Sending Luminosity (");
        if (lumState != LOW)
          Serial.println("Day)");
        else
          Serial.println("Night)");
      #endif
      zunoSendReport(5); // Report luminosity over Z-Wave
    }
  }

  // Processing PIR sensor
  // ---------------------
  pirState = digitalRead(PIR_PIN);  // Get motion state from PIR
  if (pirState != lastPIRState) { // Transition detected on PIR
    lastPIRState = pirState;
    if (pirState != LOW) { // Motion detected
      digitalWrite(LED_PIN, HIGH);  // LED on
      lumReadState = LUM_DONT_MEASURE;  // Disable Luminosity reading while PIR is ON
      checkRelay = 10;  // Will check relay state for 10 seconds
      pirOverTimeCD = 0;  // Always cancel additional delay when motion detected

      // Z-Wave signalisation
      signalPIRZWave();
      #ifdef DEBUG
        Serial.println("Motion detected");
      #endif
    } else {  // End of motion from projector
      digitalWrite(LED_PIN, LOW);   // LED off
      // Enabling Luminosity reading after LUM_MEASURE_WAIT seconds after PIR went OFF
      //   (only if Relay is not activated)
      if (lampState == 0)
        lumReadState = LUM_MEASURE_WAIT;

      // If no additional delay setup on Z-Wave signalisation, reports motion off,
      // otherwise arm timer form later report.
      if (pirOverTime == 0) {
        signalPIRZWave();
      } else {
        pirOverTimeCD = pirOverTime;  // Arm countdown timer and don't signal motion off
      }
      #ifdef DEBUG
        Serial.println("Motion ended");
      #endif
    }
  }
}

// Function that reports Motion Detection over Z-Wave.
// No parameter, lastPIRState is used to know what to report
void signalPIRZWave() {
  // If state to be reported is ON, must always report
  zunoSendReport(3); // Report unconditional motion over Z-Wave
  // Trigger Group cmd in case of an association made
  zunoSendToGroupSetValueCommand(CTRL_GROUP_1, (lastPIRState == 0) ? 0 : 0xff);
  if (lumState == LOW) {  // Action limited to "when night only"
    zunoSendReport(4);  // Motion if night
    // Trigger Group cmd if night
    zunoSendToGroupSetValueCommand(CTRL_GROUP_2, (lastPIRState == 0) ? 0 : 0xff);
  }
}

// Call backs
// ----------

// Callback for Lamp Set Value. The Relay on REL pin drives the lamp
void setterLamp(byte value) {
  #ifdef DEBUG
    Serial.print("setterLamp called with "); Serial.println(value, DEC);
  #endif
  if (value > 0) {  // Relay ON
    lumReadState = LUM_DONT_MEASURE;  // Disable Luminosity reading while Relay is activated
    digitalWrite (REL_PIN, HIGH);
  } else {  // Relay OFF
    // Enabling Luminosity reading after LUM_MEASURE_WAIT seconds after Relay switched OFF
    //   (only if PIR is off)
    if (pirState == LOW)
      lumReadState = LUM_MEASURE_WAIT;
    digitalWrite(REL_PIN, LOW);
  }
  // Save the value thas was set to be able to send to controller when asked in getter()
  lampState = value;
}

// Callback for Lamp Get Value
byte getterLamp() {
  #ifdef DEBUG
    Serial.print("getterLamp called, returned "); Serial.println(lampState, DEC);
  #endif
  return lampState;
}

// Dummy Callback for solving channel duplication
byte getterDummy() {
  #ifdef DEBUG
    Serial.print("getterDummy called, returned 0");
  #endif
  return 0;
}

// Callback for PIR Get Value
byte getterPir() {
  #ifdef DEBUG
    Serial.print("getterPir called, returned ");Serial.println((lastPIRState == 0) ? 0 : 0xff, HEX);
  #endif
  return ((lastPIRState == 0) ? 0 : 0xff);
}

// Callback for PIR Get Value when night only
// Could have pointed to getterPir, there is no difference yet. But ready in case timer will be added later.
byte getterPirNight() {
  #ifdef DEBUG
    Serial.print("getterPirNight called, returned ");Serial.println((lastPIRState == 0) ? 0 : 0xff, HEX);
  #endif
  return ((lastPIRState == 0) ? 0 : 0xff);
}

// Callback for Lum Get Value
byte getterLum() {
  #ifdef DEBUG
    Serial.print("getterLum called, returned ");Serial.println((lumState == 0) ? 0 : 0xff, HEX);
  #endif
  return ((lumState == 0) ? 0 : 0xff);
}

A propos GerardC

Depuis toujours passionné de technologie en général, et en particulier par l'informatique (système, PC ou microcontrôleurs) et l'électronique. D'autres domaines comme le Home Cinema, l'impression 3D et les machines CNC font également parties de mes pistes d'exploration. Je suis très intéressé par la domotique, et j'ai choisi Domoticz et Z-Wave pour mon installation. Mais je n'hésite pas à ajouter des extensions "maison" à base de Raspberry ou Arduino. Professionnellement, je suis dans le secteur de l'IT système et web en tant qu'architecte, donc sans grand lien avec l'informatique embarquée.

A voir aussi

Z-UNO – Système pour chauffage au bois

Me chauffant au bois, j’ai décidé de me monter un système me permettant de gérer …

2 commentaires

  1. Oh super ton article .. très intéressant à lire.
    une petite question par curiosité concernant les deux transformateurs 220v vers 24v et 24v vers 3.3v … au niveau chauffe c’est pas trop vu que ton boîtier est étanche ? je suis intéressé par la référence du 3.3 stp. je ne le vois pas sur la photo.
    en tout cas bravo pour cette réalisation.

  2. Merci !

    J’ai aussi lu ton article, que j’ai trouvé très intéressant. Le Home Cinéma est aussi un sujet qui m’intéresse bien (j’ai par le passé interfacé en liaison série / USB une commande de bandeau LED asservie à MythTV).

    Pour ce qui est de tes questions :

    Les considérations thermiques : très bonne question. Mais vu les très faibles puissances mises en jeux, il n’y a pas de problème de chauffe, le Z-Uno consomme assez peu en réception et les émissions sont rares. Pareil pour le relais, il est de faible puissance et peu actif. Durant toute la phase d’essai, je n’ai pas constaté de chauffe. D’autre part, les deux alimentations sont à découpage, donc dissipent très peu – je me suis fait d’ailleurs un point d’honneur à remplacer les régulateurs linéaires internes du Z-Uno par le convertisseur 24 / 3.3.

    Référence du convertisseur 24 / 3.3 : XP Power IE2403S (réf Farnell : 2536053), et le choix est assez grand tant en tensions qu’en puissances.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *