Go Down

Topic: Eclairage commandé par télécommande de portail (Read 1 time) previous topic - next topic

t3c16

POST EN TRAVAUX @Modérateurs : possible que je me sois trompé de section, désolé...

Etat courant du projet au 2-2-18:
Lecture et enregistrement du SN de la télécommande + bouton. OK.
Ecriture des fonctions pour le programme fonctionnel. En cours.
Elimination de l'Arduino pour utiliser un ATMega autonome. 0%
Fabrication du circuit imprimé et intégration. 0%


Intro :
Le but du montage est de déclencher l'allumage temporisé d'un projecteur de jardin à l'ouverture du portail.
Le projecteur est trop loin de la carte de commande pour utiliser la sortie éclairage.

Les fonctions suivantes justifient de ne pas utiliser une liaison radio simple entre la carte et le projecteur :
- Il devra pouvoir être allumé par une télécommande indépendante.
- Il devra également pouvoir être allumé de façon permanente.
- Une télécommande inconnue ne peut pas commander le projecteur.
- L'allumage sera conditionné par l'éclairement ambiant.

L'apprentissage et l'effacement des télécommandes, l'allumage permanent et l'arret du projecteur, et pourquoi pas l'apprentissage du seuil de luminosité devront être réalisables par des appuis prolongés mesurés (et pourquoi pas des combinaisons de touches).


Le portail est de marque BEN....A et la télécommande de série ToGo utilise une puce HCS301 pour générer un code tournant. Je me réfère au datasheet de la puce dans ce post.

Pour ce projet, compte tenu du niveau de sécurité nécessaire (=0), seule la partie du code "Serial Number" et "Button Status" seront utiles.

Matériel de développement :
- Arduino Uno + kit de développement
- Emetteur ToGo2 (HCS301)
- Récepteur 433MHz acheté sur internet 10€ les 5 RX/TX
- Une photorésistance + résistance.


1_ Extraction du code

a. La librairie RCSwitch :
Incompatible avec HCS301.

b. Débrouille-toi :
Après des heures d'arrachage de cheveux, les conclusions sont les suivantes :
- La fonction AnalogRead est très lente, il faut utiliser cette astuce pour augmenter la fréquence d'échantillonnage de façon à descendre en dessous de la centaine de microsecondes. Cela tombe bien car nous n'avons pas (plus) besoin d'analog read. Cela m'a été très utile en développement.
- La sortie série est également une calamité lorsque l'on tente d'analyser un signal, il faut donc absolument se débrouiller pour stocker les évènements interessants dans un array (bool toto[n]) et d'organiser l'envoi de façon périodique. J'ai cru comprendre qu'il y a des façons de paralléliser tout cela, je ne m'y suis pas aventuré. Eviter les array de int et long qui sont affreusement chers en mémoire.

La télécommande utilise le protocole suivant lors de l'émission d'une trame :
(Fig 4-1 du datasheet)


Le "preamble" correspond à un dutycycle à 50%, c'est ce que détecte la première partie du programme. La fréquence est d'à peu près 1850Hz (soit 800 microsecondes par cycle, soit 400 entre chaque front).

Le "header" correspond à un moment (mesuré à environ 3900 microsecondes) ou le signal est un 0.

Puis viennent les premiers 32 bits qui correspondent à la partie cryptée du code, ça ne sert à rien dans ce projet, mais cela sera utile au lecteur qui souhaite approfondir le sujet du hopping code.

Ensuite viennent les 28 bits du SN. Puis 4 pour les boutons, puis 2 pour des états divers (le repeat est toujours à 1 avec ma télécommande, je ne l'utiliserai donc pas pour mesurer le temps d'appui sur le bouton).

Les bits sont transmis de la façon suivante :
0 logique : 110
1 logique : 100

J'ai décidé d'opérer de la façon suivante pour les déterminer :

Attendre le front montant.
Mesurer combien de temps le signal reste à 1.
Si c'est inférieur à environ 500 microsecondes, alors j'ai un 1 logique.
Si c'est supérieur à la même valeur, alors j'ai un 0 logique.

Pour le moment je trouve cela très fiable et convenable.

Le code : (Ce code ainsi que ceux présentés plus bas, bien que très basiques, sont libres de droits, mais leur utilisation commerciale est soumise à l'autorisation de l'auteur, qui s'est creusé la tête pour les mettre au point)

Code: [Select]

// J COSSE - le 02/02/2018

// Réception du code transmis par une puce HCS301
// Rx of a HCS301 transmitted code

//This very basic code is free of rights,
//but any commercial use is submitted to the authorization of
//its autor.

// VARIABLES GLOBALES //
int d = 0; //compteur dutycyle - max dans setup
long temps1;
long temps0;
long dur = 0; // durée
int b = 0; // compteur lectures
unsigned long crypt;
undigned long SN;
int button;
int repeat;



int rx = 0; //bit reçu
int prx = 0; //bit reçu précédemment


bool HeaderOK = false; //bool à TRUE à la détection du header


// SETUP //
int pinrx = 2; //PIN data récepteur
int tpsignal[2] = {400,100}; // 0 : frequence/2 (= temps d'une impulsion). 1 : tolérance (+/-). En us.
int dmax = 20; // nombre de fronts pour la reconnaissance du duty cycle
int Th = 1000; // Temps header (header mesuré à 3900us). Le header est un 0 logique prolongé.

void setup() {
Serial.begin(2000000);
pinMode(pinrx, INPUT);
}

void loop() {
  
// Détection du duty cycle
d=0;

  while (d<=dmax) {
    rx = digitalRead(pinrx);
    // Si la nouvelle lecture est différente de l'ancienne, donne le temps de persistence de l'ancienne
    // Si compris dans la valeur tpsignal +/- tolérance alors incrémente d (le compteur duty cycle).
    if (rx != prx) {
      temps1 = micros();
      dur = temps1 - temps0;
      temps0 = temps1;
      prx = rx;
      if (dur > tpsignal[0]-tpsignal[1] && dur < tpsignal[0]+tpsignal[1]) {
        d++;
      }
         else {d=0;}
    }
  }


// HEADER
// Attente d'un 0 logique d'une durée supérieure à Th (temps header).
// Dimensionner Th de façon à laisser le temps au programme d'aller à la section suivante et pour détecter le premier front.

  while (HeaderOK==false) {
    rx = digitalRead(pinrx);
    if (rx == 0) {
      temps0 = micros();
      while (rx == 0 && HeaderOK==false) {
        rx = digitalRead(pinrx);
        temps1 = micros();
        dur = temps1-temps0;
        if (dur >= Th) {HeaderOK = true;}
      }
    }
  }
//Serial.println (" Header reçu");
HeaderOK = false;
crypt = 0;
SN = 0;
button = 0;
repeat = 0;


//Ecriture du code dans les variables

for (int i=0; i<=65; i++) {
  while (rx==0) {rx=digitalRead(pinrx);}
  temps0 = micros();
  while (rx==1) {
    temps1 = micros();
    dur = temps1 - temps0;
    rx=digitalRead(pinrx);
  }

// Il y a une erreur dans ce qui suit, les bitwise mangent le premier bit de crypt, parce que c'est
// le seul à faire 32 bits. Le "code" n'est pas exploitable en l'état...
    
  if (dur < tpsignal[0]+tpsignal[1]) { // bit reçu = 1
    if (i<=31) {crypt=crypt+1; crypt = crypt << 1;}
    if (i>31 && i<=59) {SN=SN+1; SN = SN << 1;}
    if (i>59 && i<=63) {button=button+1; button = button << 1;}
    if (i==65) repeat=1;
    }
  if (dur > tpsignal[0]+tpsignal[1]) {// bit reçu = 0
    if (i<=31) crypt = crypt << 1;
    if (i>31 && i<=59) SN = SN << 1;
    if (i>59 && i<=63) button = button << 1;
    if (i==65) repeat=0;
  }
}

Serial.println("Numéro de série de la télécommande :");
Serial.print(SN,BIN); Serial.print("  ");Serial.println(SN);

Serial.println("Bouton :");
Serial.println(button,BIN);
}


 


t3c16

2_ Programme :

Main :
Code: [Select]
//J COSSE le 4-4-18
//Ce programme permet la commande d'un projecteur avec une télécommande HCS301.
//- Enregistrement et effacement de télécommandes
//- Allumage temporisé conditionné par la luminosité. Seuil réglable.
//- Allumage permanent.


// SETUP //
#define pinrx 2 //PIN data récepteur
#define projo 4 //PIN d'allulage du projecteur
#define led 4 //PIN LED de status
#define photoresistance A0 //PIN d'entrée signal photorésistance
#define tpsignal 400 //frequence/2 (= temps d'une impulsion).
#define tolerance 200 // tolérance pour la détection des états
#define dmax 20 //nombre de fronts pour la reconnaissance du duty cycle
#define Th 1000  // Temps header (header mesuré à 3900us). Le header est un 0 logique prolongé.
#define endtransmit 100  // temps sans signal pour définir la fin de transmission en ms.

#define temporis 5000  // < ou = temporis ms pour allumage temporisé
#define permanent 10000 // eclairage permanent
#define record 15000 // enregistrement et effacement de tx
#define lightadjust 20000 // enregistrement de l'intensité lumineuse pour temporis

#define temporisation 30 // temporisation pour l'utilisation dans ce mode


unsigned long rxcode; //code reçu
unsigned long repeat;
unsigned long tempsa;
unsigned long tempsb;
unsigned long durab = 0;

unsigned long temps;
unsigned long arret; //pour l'heure d'arrêt en temporisé

int preset = 0; //valeur de luminosité ambiante pour autoriser le mode tempo

unsigned long txlist[30]; // Pour stocker les tx appris. Attention, si le nombre évolue, le faire évoluer dans les boucles de memorize, verify et erase. On n'utilisera jamais le dernier membre, il provoque des erreurs (enregistrements aléatoires)
bool index[30]; // Pour savoir si un emplacement est disponible

bool knownTX = 0;
bool projstatus = 0;
bool tempoON = 0;
bool luxOK = 0;

#include <EEPROM.h>



void setup() {

  pinMode(pinrx, INPUT);
  pinMode(projo, OUTPUT);
  pinMode(led, OUTPUT);
  
  downloadTX();
  
  
  //Serial.begin(4800); //Attention, une vitesse trop importante cause beaucoup d'erreurs au debuggage. Notamment si lecture dans EEPROM.
  //for (int i=0; i<1024; i++) {EEPROM.update(i,0);}  //Pour purger la EEPROM si besoin.
  //for (int i=0; i<=29; i++){Serial.print(index[i]); Serial.print("  "); Serial.println(txlist[i],BIN);}
  //for (int i=0; i<1024; i++) {Serial.println(EEPROM.read(i));}
  
  LED(2);
  }


void loop() {
  rxcode = code();
  repeat = rxcode;

  //code() renvoie 0 si aucun bouton n'est activé.
  //si code() renvoie autre chose qu'un 0, alors tant que code() renvoie le même signal on chronomètre. On a donc le code et le temps d'appui.
  
  if (rxcode != 0) {
    
    tempsa = millis();
    
    while (repeat == rxcode) {
      tempsb = millis();
      repeat = code();}

    durab = tempsb - tempsa;
    knownTX = verify(rxcode);
    
    // ALLUMAGE TEMPORISE
    luxOK = lux();
    if(durab<temporis && knownTX==1 && luxOK==1) {
      arret = millis() + temporisation * 1000;
      digitalWrite(projo,HIGH);
      projstatus = 1;
      tempoON = 1;
      }


    // ALLUMAGE PERMANENT  
    if(durab>=temporis && durab<permanent && knownTX==1) {
      tempoON = 0;
      
      if (projstatus == 0) {
        digitalWrite(projo,HIGH);
        projstatus = 1;
      }
      else if (projstatus == 1) {
        digitalWrite(projo,LOW);
        projstatus = 0;
      }
    }


    // ENREGISTREMENT OU SUPPRESSION
    if (durab>=permanent && durab<record) {
        if (knownTX==0) memorize(rxcode);
        if (knownTX==1) erase(rxcode);
    }


    // AJUSTEMENT DE LUMINOSITE POUR MODE TEMP
    if (durab>=record && durab<lightadjust && knownTX==1) {
      preset_lux();
    }
  
  }


//Arrêt du projecteur en temporisation
  else {
    temps = millis();
    if(temps > arret && tempoON==1) {
       digitalWrite(projo,LOW);
       projstatus=0;
       tempoON==0;
    }
  }

}




Le code et les fonctions sont en PJ de ce post.

Go Up