Go Down

Topic: Protocole Somfy (Reverse engi RTS) (Read 78204 times) previous topic - next topic

Black_Ware

Bonjour yoyolb ;)

Après de multiples essais, je n'arrive toujours pas à comprendre comment fonctionne le code decode_somfy.c
Pourrais-tu nous expliquer ta démarche ?

Merci

Black

yoyolb

Bonjour,

Je ne suis pas reparti du code "decode_somfy.c", ce n'est en effet pas du code arduino.
J'ai écrit un mini algo en me basant sur les infos du site "pushstack" (bravo à l'auteur au passage).

Testé en réception et en émission.

Je n'ai pas le code sous la main, j'essaye de vous le mettre à disposition ce soir.

NB: ce n'est qu'un prototype fonctionnel, loin d'être aussi élaboré que "decode_somfy.c".

Lionel.


Black_Ware

Bonjour et merci de ta réponse :3

Oui en effet somfy_decoder.c est un programme que j'ai réussi à compiler sous linux mais après impossible d'en tirer quoi que ce soit, il me semble qu'il faut indiquer la trame en bit stream quelque part mais je ne sais pas si c'est dans le fichier remotes.txt ou en argument.

J'ai repris du coup la page du site à zéro mais cela reste encore un peu brouillon dans ma tête par exemple pour le rolling-code.
Et oui l'auteur de ce site a fait un boulot propre et complet. Chapeau à lui !

Et merci Lionel de nous aider   :D

Jo  8)

yoyolb

Bonsoir,

Comme promis ci-dessous mon code.
Quelques commentaires, à moitié en français... et à moitié en anglais...
La classe CCodecSomfyRTS se charge du décodage et de l'encodage.

Décodage:
=> appeler la méthode "pulse()" à chaque changement d'état (front montant / descendant) avec durée du pulse. La méthode se charge du décodage Manchester et stocke les données au fil de l'eau. Quand toute la trame a été reçue, appel à la méthode interne "decode()". Cette méthode décode la trame reçue, la vérifie (checksum) et affiche sur la console le message décodé (adresse, rolling code, touche pressée).

Emission:
=> appeler la méthode "transmit()" avec en paramètre le code de la touche à simuler ainsi que le rolling code courant.
NB 1: adresse actuellement en dur dans le code, à positionner manuellement (data[4] à data [6]).
NB 2: on se substitue à une vraie télécommande et il faut donc incrémenter le rolling code à chaque émission, ce qui contribue à désynchroniser la télécommande... donc prudence.

TODO: implémenter l'enregistrement en tant que nouvelle télécommande !!!

//
// (C)opyright yoyolb - 2014/08/05
// Version 0.1
//
// Tested with Arduino UNO and:
//   - RX module RR3-433 (433.92 MHz) => port A1
//   - TX module RT4-433 (433.92 MHz) => port A0
//
//
#define PORT_TX A0
#define PORT 2   // Arduino UNO = A1

// To store pulse length, in microseconds
volatile word pulse;

// Interrupt handler
#if defined(__AVR_ATmega1280__)
void ext_int_1(void) {
#else
ISR(ANALOG_COMP_vect) {
#endif
    static word last;
   
    // determine the pulse length in microseconds, for either polarity
    pulse = micros() - last;
    last += pulse;
}


void setup () {
    Serial.begin(115200);
    Serial.println("\n[SomfyDecoder]");
   
    pinMode(PORT_TX, OUTPUT);
    digitalWrite(PORT_TX, 0);
   
#if !defined(__AVR_ATmega1280__)
    pinMode(PORT, INPUT);  // use the AIO pin
    digitalWrite(PORT, 1); // enable pull-up

    // use analog comparator to switch at 1.1V bandgap transition
    ACSR = _BV(ACBG) | _BV(ACI) | _BV(ACIE);

    // set ADC mux to the proper port
    ADCSRA &= ~ bit(ADEN);
    ADCSRB |= bit(ACME);
    ADMUX = PORT - 1;
#else
   attachInterrupt(1, ext_int_1, CHANGE);

   DDRE  &= ~_BV(PE5);
   PORTE &= ~_BV(PE5);
#endif
}

// Microseconds
const word k_tempo_wakeup_pulse = 9415;
const word k_tempo_wakeup_silence = 89565;
const word k_tempo_synchro_hw = 2416;
const word k_tempo_synchro_hw_min = 2416*0.7;
const word k_tempo_synchro_hw_max = 2416*1.3;
const word k_tempo_synchro_sw = 4550;
const word k_tempo_synchro_sw_min = 4550*0.7;
const word k_tempo_synchro_sw_max = 4550*1.3;
const word k_tempo_half_symbol = 604;
const word k_tempo_half_symbol_min = 604*0.7;
const word k_tempo_half_symbol_max = 604*1.3;
const word k_tempo_symbol = 1208;
const word k_tempo_symbol_min = 1208*0.7;
const word k_tempo_symbol_max = 1208*1.3;
const word k_tempo_inter_frame_gap = 30415;

class CCodecSomfyRTS {
  public:
    enum t_status {
      k_waiting_synchro,
      k_receiving_data,
      k_complete
    };
   
  public:
    CCodecSomfyRTS();
    t_status pulse(word p);
    bool decode();
    bool transmit(byte cmd, unsigned long rolling_code);
   
  protected:
    t_status _status;
    byte _cpt_synchro_hw;
    byte _cpt_bits;
    byte _previous_bit;
    bool _waiting_half_symbol;
    byte _payload[7];
};

CCodecSomfyRTS::CCodecSomfyRTS()
: _status(k_waiting_synchro)
, _cpt_synchro_hw(0)
, _cpt_bits(0)
, _previous_bit(0)
, _waiting_half_symbol(false) {
  for(int i=0; i<7; ++i) _payload = 0;
}

CCodecSomfyRTS::t_status CCodecSomfyRTS::pulse(word p) {
  switch(_status) {
    case k_waiting_synchro:
      if (p > k_tempo_synchro_hw_min && p < k_tempo_synchro_hw_max) {
        ++_cpt_synchro_hw;
      }
      else if (p > k_tempo_synchro_sw_min && p < k_tempo_synchro_sw_max && _cpt_synchro_hw >= 4) {
        _cpt_bits = 0;
        _previous_bit = 0;
        _waiting_half_symbol = false;
        for(int i=0; i<7; ++i) _payload = 0;
        _status = k_receiving_data;
      } else {
        _cpt_synchro_hw = 0;
      }
      break;
     
    case k_receiving_data:
      if (p > k_tempo_symbol_min && p < k_tempo_symbol_max && !_waiting_half_symbol) {
        _previous_bit = 1 - _previous_bit;
        _payload[_cpt_bits/8] += _previous_bit << (7 - _cpt_bits%8);
        ++_cpt_bits;
      } else if (p > k_tempo_half_symbol_min && p < k_tempo_half_symbol_max) {
        if (_waiting_half_symbol) {
          _waiting_half_symbol = false;
          _payload[_cpt_bits/8] += _previous_bit << (7 - _cpt_bits%8);
          ++_cpt_bits;
        } else {
          _waiting_half_symbol = true;
        }
      } else {
        _cpt_synchro_hw = 0;
        _status = k_waiting_synchro;
      }
      break;
     
    default:
      Serial.println("Internal error !");
      break;
  }
 
  t_status retval = _status;
 
  if (_status == k_receiving_data && _cpt_bits == 56) {
    retval = k_complete;
    decode();
    _status = k_waiting_synchro;
  }
 
  return retval;
}

bool CCodecSomfyRTS::decode() {
  // Dé-obfuscation
  byte frame[7];
  frame[0] = _payload[0];
  for(int i = 1; i < 7; ++i) frame = _payload ^ _payload[i-1];
   
  //for(int i = 0; i < 7; ++i) Serial.print(frame, HEX); Serial.print(" ");
  //Serial.println("");
 
  // Verification du checksum
  byte cksum = 0;
  for(int i = 0; i < 7; ++i) cksum = cksum ^ frame ^ (frame >> 4);
  cksum = cksum & 0x0F;
  if (cksum != 0) Serial.println("Checksum incorrect !");
 
  // Touche de controle
  switch(frame[1] & 0xF0) {
    case 0x10: Serial.println("My"); break;
    case 0x20: Serial.println("Up"); break;
    case 0x40: Serial.println("Down"); break;
    case 0x80: Serial.println("Prog"); break;
    default: Serial.println("???"); break;
  }
 
  // Rolling code
  unsigned long rolling_code = (frame[2] << 8) + frame[3];
  Serial.print("Rolling code: "); Serial.println(rolling_code);
 
  // Adresse
  unsigned long address = ((unsigned long)frame[4] << 16) + (frame[5] << 8) + frame[6];
  Serial.print("Adresse: "); Serial.println(address, HEX);
}

bool CCodecSomfyRTS::transmit(byte cmd, unsigned long rolling_code) {
  // Construction de la trame claire
  byte data[7];
  data[0] = 0xA0;
  data[1] = cmd << 4;
  data[2] = (rolling_code & 0xFF00) >> 8;
  data[3] = rolling_code & 0x00FF;
  data[4] = 0x   ; // Address
  data[5] = 0x   ; // Address
  data[6] = 0x   ; // Address
 
  // Calcul du checksum
  byte cksum = 0;
  for(int i = 0; i < 7; ++i) cksum = cksum ^ data ^ (data >> 4);
  data[1] = data[1] + (cksum & 0x0F);
 
  // Obsufscation
  for(int i = 1; i < 7; ++i) data = data ^ data[i-1];

  // Emission wakeup, synchro hardware et software
  digitalWrite(PORT_TX, 1);
  delayMicroseconds(k_tempo_wakeup_pulse);
  digitalWrite(PORT_TX, 0);
  delayMicroseconds(k_tempo_wakeup_silence);
 
  for(int i=0; i<7; ++i) {
    digitalWrite(PORT_TX, 1);
    delayMicroseconds(k_tempo_synchro_hw);
    digitalWrite(PORT_TX, 0);
    delayMicroseconds(k_tempo_synchro_hw);
  }
 
  digitalWrite(PORT_TX, 1);
  delayMicroseconds(k_tempo_synchro_sw);
  digitalWrite(PORT_TX, 0);
  delayMicroseconds(k_tempo_half_symbol);
 
  // Emission des donnees
  for(int i=0; i<56;++i) {
    byte bit_to_transmit = (data[i/8] >> (7 - i%8)) & 0x01;
    if (bit_to_transmit == 0) {
      digitalWrite(PORT_TX, 1);
      delayMicroseconds(k_tempo_half_symbol);
      digitalWrite(PORT_TX, 0);
      delayMicroseconds(k_tempo_half_symbol);
    }
    else
    {
      digitalWrite(PORT_TX, 0);
      delayMicroseconds(k_tempo_half_symbol);
      digitalWrite(PORT_TX, 1);
      delayMicroseconds(k_tempo_half_symbol);
    }
  }
 
  digitalWrite(PORT_TX, 0);
  delayMicroseconds(k_tempo_inter_frame_gap);
}


void loop() {
  static CCodecSomfyRTS codecSomfyRTS;
  static word cpt = 0;
 
  cli();
  word p = pulse;
  pulse = 0;
  sei();

//  codecSomfyRTS.transmit(0x4, XXXX);

  if (p != 0) {
    codecSomfyRTS.pulse(p);
  }
}

Black_Ware

Cool ;) un grand merci à toi ^^ Je test ça demain

yoyolb

Bonjour,

Je reposte mon code en utilisant une pièce jointe car ma précédente tentative n'a pas été une réussite. En effet, le copier-coller vers le forum a altéré le code (suppression de , insertion de smileys parasites...).

Le code en PJ devrait être correct cette fois !

Lionel.


jojolapin

D'abord, Merci pour ce travail! je pense qu'il va servir à pas mal de monde qui attendait une solution low-cost pour piloter du SOMFY!

j'ai sauté sur l'occaz et acheté un RR3-433 et un  RT40-433 et testé ca sur un Arduino uno, malheureusement... impossible de lire les trames émises par mes télécommandes...

==> il y a une chose que je ne comprend pas dans le setup :
"ADMUX = PORT - 1;", soit la valeur décimale 14 (port étant initialisé à 15 (A1))

d'après les références que j'ai trouvé sur le contenu de ADMUX
bit 7 = REFS1
bit 6 = REFS0 
bit 5 = ADLAR
bit 4 = -
bit 3 = MUX3
bit 2 = MUX2
bit 1 = MUX1
bit 0 = MUX0
==> avec REFS1 = 1 et REFS0 = 1 pour une référence à 1.1V

yoyolb

Bonsoir,

Hmmm... dans le code que j'ai fourni PORT est positionné à 2 (#define PORT 2)  en début de fichier.

Cela correspond à la PIN A1 de ma carte Arduino UNO (mais PORT doit bien être positionné à 2 pour cela).

Lionel.


winnaing

Hi
I am following up this topic and try to test the sketch given by yoyolb and not successful. Maybe I don't know how to test it.
What I did was that I conncet RX pin to Uno A1 (which is digital 15), monitor from serial monitor. Nothing happy.

Appreciate it if someone who tested successfully share the procedure and show how to use it.

Regards
WiN

mck21

Bonsoir à tous,

Un grand bravo et merci à yoyolb de partager son code.

J'ai réussi à écouter mes télécommande telis (un peu galère les branchement du RR3... 3 masses, 4 VCC, 1 fil data (pin 14) sur A0). J'ai extrait ce schema de branchement des datasheets présente sur le site du fabricant : http://www.telecontrolli.com/risorse/download1/

Par contre, pour l'envoi de trame, je n'y suis pas arrivé (bien que les branchement pour le RT4 / RT40 soit plus simples). Peux-tu me dire si tu as commenté certaines lignes lors de tests qui avait l'air d'usurper le rolling d'une télécommande ?

Encore merci à tous.

Supermat

Bonsoir,

Merci pour ce super boulot, j'arrive également à décoder mes télécommande, mais la  commande transmit ne semble pas fonctionner.

Pour être sûr :

Si je recois (Ce ne sont pas les miennes, j'ai modifié les valeur):
My
Rolling code: 1000
Adresse: 4A5B5C

Je dois mettre dans le programme ?
data[4] = 0x4A; // Address: USE YOUR OWN ADDRESS !!!
data[5] = 0x5B; // Address: USE YOUR OWN ADDRESS !!!
data[6] = 0x5C; // Address: USE YOUR OWN ADDRESS !!!


Et lancer la commande (une seule fois, sur le click d'un poussoir) :
int g_rolling = 1001;
codecSomfyRTS.transmit(0x4, g_rolling); //0x4 pour descendre

Sommes nous d'accord? Chez moi cela ne fonctionne pas.

Et encore merci pour ce super boulot.

AingTii

Hello,

wow, I'm surprised about your work!

But its really hard to understand the "bing" translation from francais to german.

If I understood it right , you use the analog ports of arduino one an not the digital?

I have an arduino uno and 433Mhz Moduls to send and receive.

But I didn't understand where I shout connect it to the arduino which port?
And also I didn't understand realy what I have to do after starting arduino?

May I ask you to explain it in English for me please?

thank you very much.

Best regards
Jens

AingTii

I just tried! WOW!!!!

Is that only to read or can I send the signal also ?
If yes, how?

than you very much!

Jens

icare

Halo,
Hello,

wow, I'm surprised about your work!

But its really hard to understand the "bing" translation from francais to german.

If I understood it right , you use the analog ports of arduino one an not the digital?

I have an arduino uno and 433Mhz Moduls to send and receive.

But I didn't understand where I shout connect it to the arduino which port?
And also I didn't understand realy what I have to do after starting arduino?

May I ask you to explain it in English for me please?

thank you very much.

Best regards
Jens
Du kanst mir Fragen stellen in Deutscher Sprache über Private Mail
MfG
2B OR NOT(2B) = FF
Arduino 1.0.5 à 1.8.5 + gEdit + Ubuntu 18.04 LTS

icare

Bonsoir,
Comment faites vous pour transmettre un code ?
@
2B OR NOT(2B) = FF
Arduino 1.0.5 à 1.8.5 + gEdit + Ubuntu 18.04 LTS

Go Up