Somfy / RTS Funkprotokoll und Geräte

Danke für deine 5 Minuten googlen, @pmayer. Das mit dem USB habe ich natürlich auch gesehn, darum mein Poste, da ich am Handy war - dumme Angewohnheit bei Responsce-Pages :wink: - habe ich mich hier wohl etwas zu kurz gefasst.
Eigentlich wolte ich fragen, ob es möglich ist, also dir bekannt, ob und wie ich der Node vorgaukeln kann, mit SPI anstelle USB zu kommunizieren.

Hast natürlich recht, dazu kommt das Problem mit 868 vs 433.42 MHz.

Hab bei mir in der Bastelkiste noch JeeNodes USB V3 von jeelabs.org (www.jeelabs.org/ju3) gefunden, und probiere jetzt mal damit, ob ich was lauschen/senden kann, bevor ich mich dann an Node-red mache.

1 Like

Du müsstest eben alles was in der culFW für Somfy implementiert ist auf dem Pi per SPI implementieren, damit das funktioniert. Ich glaube nicht, dass das auf die Schnelle geht. Sathya hat dazu ja schon was gesagt: geplant, aber leider keine Prio…

Danke @pmayer ja, ich glaube ich muss hier aufgeben und warten, bis es @sathya implementiert hat. Ich versteh leider zu wenig von der Materie. Ich biete mich dafür gerne als Betatester an.

1 Like

So, heute endlich ein Schrittchen weiter:

Ich habe danke folgenden Blogs einen Wemos D1 zusammen mit einem 433.92 Mhz Transmitter als Fernbedienung nutzen können. Die Reichweite trotz nicht optimaler Frequenz (RTS hätte 433.42 Mhz) langt mit 5 Metern gutzum Testen.

Arduino: https://github.com/Nickduino
ESP8266: https://4x5mg.inphoto.co.il/2018/06/10/controlling-somfy-blinds-with-esp8266/
Somfy RTS: https://pushstack.wordpress.com/somfy-rts-protocol/

Der Code für den ESP8266 ist leider nicht veröffentlicht, ich konnte ihn jedoch einfach nachbauen. Nickduino verwendet direktes Porthandling für AVR, das muss für den ESP angepasst werden. Ebenso der Nutzen des EEPROMS für den Rollingcode, hier hatte ich jedoch noch keinen Erfolg.

Benötigt werden eine ID für die Fernbedienung, ein fortlaufender Rollingcode und dann gibt es die folgenden Befehle:

#define HAUT 0x2   <---- Motor auf
#define STOP 0x1   <---- Motor Stop/My Position
#define BAS 0x4      <---- Motor ab
#define PROG 0x8   <---- Fernbedienung anlernen

Angelernt wird die Fernbedienung, in dem auf einer bestehenden Fernbedienung die Programmiertaste gedrückt wird, die Jalousien fahren kurz hoch runter, dann wird der Befehl 0x8 gesendet. Wenn die Jalousien nun ebenfalls mit hoch runter reagieren, ist gut.

Ich könnte mir das auch für mqtt vorstellen: die Fernbedienungs ID als Topic und die Befehle als Payload. Der Rollingcode bleibt in einer Datenbank oder file und wird automatisch generiert.

Sorry, für die unprofesionelle Ausdrucksweise, bei Interesse oder Unklarheit einfach nachfragen.

2 Likes

So, es ist vollbracht! Dank oben erwähnter Quellen, dem komplett Neuaufsetzen meines Homegear Raspberry PI und der Geduld und Hilfe von @pmayer kann ich meine Somfy RTS Storen (Jalousien) via Node-red steuern💪

Ich verwende MQTT, wwlches auf einem ESP-01 als Client verarbeitet wird und dann via 433 MHz an die Somfy Storen gesendet wird.

Dabwi wird auch das EEPROM des ESP-01 miteinbezogen.

2 Likes

Super!

Hast du den Code und die Beschaltung irgendwo auf Github? Könnte anbieten dafür eine Platine inkl. ESP12-S zu bauen.
Oder gibt es schon Hardware die das kann?

Ich bin mehr so der Spaghetricodeprogrammierer, da schäme ich mich, das irgendwo zu veröffentlichen. Hab ddn Code grösstenteils ja auch zusammenkopiert. Kenn mich da mit dem Urheberrecht zuwenig aus. Ich maile es dir gerne mal und dann können wir ja schauen.

2 Likes

Wenn du es zusammenkopiert hast, war es zu 90% von Github und stand unter GPL/LGPL oder MIT - ist also kein Problem, so lange du im Idealfall den Urheber nennst.
Einen guten Überblick darüber erhältst du hier: https://choosealicense.com/licenses/

Da brauchst du dir wirklich gar keine Sorgen zu machen. Hier hab ich beispielsweise ein work-in-progress auch auf Github: https://github.com/codmpm/esp-ccs811-bme280-mqtt
Für Github gibt es auch genug Boilerplates, wo man sich eine Grundstruktur für das Repo abgucken kann: https://github.com/afonsopacifer/open-source-boilerplate (z.B.)

The worst code is my own from 6 weeks ago.

Mir fällt gerade ein, dass ich hier auch was mit 433 gemacht hatte: https://github.com/codmpm/esp-mqtt-doorbell-receiver - und wenn ich mir den Code heute angucken, schüttele ich auch mit dem Kopf :wink:

1 Like

Weiß gerade nicht, ob es lohnt eigenen Hardware dafür zu bauen. So was ähnliches gibt es schon.

Schön find ichs nicht aber es ist im groben das drauf, was ich auch eingesetzt hätte…

Das müsste gehen und der Preis ist ja unschlagbar. Mein Wunsch/Ziel wäre es zwar die SOMFI Jalousien direkt aus Node-red via Homegear anzusteuern - ohne Umweg über WiFi und MQTT.

Nuja, du brauchst ein Funkmodul mit dem du auf irgend einem Weg reden must. Ich find den weg ansich ganz gut, weil mqtt genau dafür gemacht ist.
Außerdem hast du so die Möglichkeit das “Funkmodul” in die Nähe deiner Rollos zu packen, da die Frequenz ja nicht ganz stimmt. Und lieber eine dedizierte “Bridge” als alles zentral am Pi “angebastelt”.

Wie sieht es eigentlich damit aus? Hast du was gefunden, wie man genau auf diese Frequenz kommen kann?

Noch eine kleine Anmerkung: RTS wird meine ich von der CUL-Firmware unterstützt. Nur zum neueren Protokoll (hab gerade den Namen vergessen) fehlen so viele Informationen, dass es sich nicht implementieren lässt.

Ich werde mir so einen CUL bauen, sobald ich dazu kommen, sonst jann ich die CC1100 irgendwie nicht gebrauchen. Was mir aber noch nicht klar ist, wie könnte ich denn RTS via CUL von Homegear oder Node-red aus ansprechen?

Über Node-RED weiß ich es nicht genau, vermutlich gibt es dort aber auch einen serial-Node. In Node-BLUE würdest du den serial-Node verwenden. Das eingehende Paket würde ich anschließend über PHP in einem function-Node auswerten.

1 Like

Super Projekt…
Kannst Du mir den Code schicken - das ist genau das was ich suche

Danke

Wäre super, wenn ich den Code auch bekommen könnte: jens.wetzl@me.com
Habe auch versucht den Arduino-Code für ESP zu übersetzen aber es funktioniert bisher nicht :frowning:

1 Like

Ich hab auch schon etliches versucht - irgendwie kriege ich es nicht hin.
Wer kann mir helfen den Code zu bekommen ??
wendt-christian@t-online.de
Christian

1 Like

Hallo Jungs, seid ihr noch dran interessiert? Dann grabe ich das Projekt gerne wieder aus. Bekannte Probleme, die es noch zu lösen gilt:

  • Im Moment hadere ich noch an der Verbindung mit MQTT. Auf einem ESP-01 scheint das Ding nicht so stabil zu laufen. Steure ich es jedoch via UART an, reagiert es meist ziemlich stabil.
  • Da ich momentan drei Aktoren damit steuere, muss der jeweilige Rolling Code ins Eeprom geschrieben werden.
  • Mein TX Modul funkt auf 433.92 MHz anstatt der Somfy 433.42 MHz. Das heisst die Reichweite ist knapp 5 Meter.

Wäre toll, wenn wir die Kiste gemeinsam zum Fliegen brächten!

Zum Warmlaufen hier noch die Basisliteratur :wink:

1 Like

Sketch mit EEPROM und MQTT:

/* This sketch allows you to emulate a Somfy RTS or Simu HZ remote.
If you want to learn more about the Somfy RTS protocol, check out https://pushstack.wordpress.com/somfy-rts-protocol/

   The rolling code will be stored in EEPROM, so that you can power the Arduino off.

   Easiest way to make it work for you:
    - Choose a remote number
    - Choose a starting point for the rolling code. Any unsigned int works, 1 is a good start
    - Upload the sketch
    - Long-press the program button of YOUR ACTUAL REMOTE until your blind goes up and down slightly
    - send 'p' to the serial terminal
  To make a group command, just repeat the last two steps with another blind (one by one)

  Then:
    - m, u or h will make it to go up
    - s make it stop
    - b, or d will make it to go down
    - you can also send a HEX number directly for any weird command you (0x9 for the sun and wind detector for instance)
*/


/* Motor 1   Rollingcode  >31  Remote 0x521370
   Motor 3   Rollingcode  >72  Remote 0x521373
   Motor 4   Rollingcode  >274  Remote 0x521374

*/
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "EEPROM.h"
extern "C" {
#include "user_interface.h"
}


/************************* SOMFY Settings *********************************/

#define TXP 0 //DAT PIN of RFChip
#define SYMBOL 640
#define HAUT 0x2        //UP
#define STOP 0x1        //MY
#define BAS 0x4         //DOWN
#define PROG 0x8        //PROG
#define REMOTE 0x521373    //<-- Change it!

/************************* WiFi Access Point *********************************/


#define WLAN_SSID       "SSID"
#define WLAN_PASS       "PASS"

/************************* MQTT Settings *********************************/

#define AIO_SERVER      "homegear.local"
#define AIO_SERVERPORT  1883                   // use 8883 for SSL
#define AIO_USERNAME    ""
#define AIO_KEY         ""

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;
// or... use WiFiClientSecure for SSL
//WiFiClientSecure client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY);

/****************************** Feeds ***************************************/

// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>

// Setup a feed for Subscription
Adafruit_MQTT_Subscribe storen = Adafruit_MQTT_Subscribe(&mqtt, "wohnzimmer/storen/mitte");

void MQTT_connect();


unsigned int newRollingCode = 1;       //<-- Change it!
unsigned int rollingCode = 72;
byte frame[7];
byte checksum;

void BuildFrame(byte *frame, byte button);
void SendCommand(byte *frame, byte sync);


void setup() {
  Serial.begin(115200);
  delay(500);
  EEPROM.begin(512);

  // EEPROM.write(0,0); // RollingCode manuell anpassen high byte
  // EEPROM.write(1,59);  // RollingCode manuell anpassen low byte
  // EEPROM.commit();


  Serial.print("EEPROM Wert = ");
  Serial.print(EEPROM.read(0));
  Serial.print(",");
  Serial.println(EEPROM.read(1));



  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);
  wifi_station_set_hostname("somfy");
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP address: "); Serial.println(WiFi.localIP());
  Serial.print("Hostname:   "); Serial.println(WiFi.hostname());

  // Setup MQTT subscription for storen.
  mqtt.subscribe(&storen);


  pinMode(TXP, OUTPUT);; // Pin DAT
  digitalWrite(TXP, LOW); // LOW

  if (rollingCode < newRollingCode) {
    rollingCode = newRollingCode;
  }

  byte high = EEPROM.read(0); //read the first half
  byte low = EEPROM.read(1);  //read the second half
  rollingCode = (high << 8) + low; //reconstruct the integer
  //rollingCode = EEPROM.read(0);  //EEPROM Auslesen


  Serial.print("Simulated remote number : "); Serial.println(REMOTE, HEX);
  Serial.print("Current rolling code    : "); Serial.println(rollingCode);
}

void loop() {
  if (Serial.available() > 0) {
    char serie = (char)Serial.read();
    Serial.println("");
    //    Serial.print("Remote : "); Serial.println(REMOTE, HEX);
    if (serie == 'm' || serie == 'u' || serie == 'h') {
      Serial.println("Monte"); // Somfy is a French company, after all.
      BuildFrame(frame, HAUT);
    }
    else if (serie == 's') {
      Serial.println("Stop");
      BuildFrame(frame, STOP);
    }
    else if (serie == 'b' || serie == 'd') {
      Serial.println("Descend");
      BuildFrame(frame, BAS);
    }
    else if (serie == 'p') {
      Serial.println("Prog");
      BuildFrame(frame, PROG);
    }
    else {
      Serial.println("Custom code");
      BuildFrame(frame, serie);
    }

    Serial.println("");
    SendCommand(frame, 2);
    for (int i = 0; i < 2; i++) {
      SendCommand(frame, 7);
    }
  }

  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  // this is our 'wait for incoming subscription packets' busy subloop
  // try to spend your time here

  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(5000))) {
    // Check if its the storen feed
    if (subscription == &storen) {
      Serial.print(F("MQTT Storen: "));
      Serial.println((char *)storen.lastread);

      if (strcmp((char *)storen.lastread, "up") == 0) {
        BuildFrame(frame, HAUT);
      }
      if (strcmp((char *)storen.lastread, "stop") == 0) {
        BuildFrame(frame, STOP);

      }
      if (strcmp((char *)storen.lastread, "down") == 0) {
        BuildFrame(frame, BAS);

      }
      Serial.println(" fire!");
      SendCommand(frame, 2);
      for (int i = 0; i < 2; i++) {
        SendCommand(frame, 7);
      }
    }
  }

  // ping the server to keep the mqtt connection alive
  // NOT required if you are publishing once every KEEPALIVE seconds
  if (! mqtt.ping()) {
    mqtt.disconnect();
  }
}


// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.


void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
    retries--;
    if (retries == 0) {
      // basically die and wait for WDT to reset me
      while (1);
    }
  }
  Serial.println("MQTT Connected!");
}


// Function to Build SOMFY RTS Frame

void BuildFrame(byte *frame, byte button) {
  unsigned int code = rollingCode;
  frame[0] = 0xA7; // Encryption key. Doesn't matter much
  frame[1] = button << 4;  // Which button did  you press? The 4 LSB will be the checksum
  frame[2] = code >> 8;    // Rolling code (big endian)
  frame[3] = code;         // Rolling code
  frame[4] = REMOTE >> 16; // Remote address
  frame[5] = REMOTE >>  8; // Remote address
  frame[6] = REMOTE;       // Remote address

  Serial.print("Frame         : ");
  for (byte i = 0; i < 7; i++) {
    if (frame[i] >> 4 == 0) { //  Displays leading zero in case the most significant
      Serial.print("0");     // nibble is a 0.
    }
    Serial.print(frame[i], HEX); Serial.print(" ");
  }

  // Checksum calculation: a XOR of all the nibbles
  checksum = 0;
  for (byte i = 0; i < 7; i++) {
    checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
  }
  checksum &= 0b1111; // We keep the last 4 bits only


  //Checksum integration
  frame[1] |= checksum; //  If a XOR of all the nibbles is equal to 0, the blinds will
  // consider the checksum ok.

  Serial.println(""); Serial.print("With checksum : ");
  for (byte i = 0; i < 7; i++) {
    if (frame[i] >> 4 == 0) {
      Serial.print("0");
    }
    Serial.print(frame[i], HEX); Serial.print(" ");
  }


  // Obfuscation: a XOR of all the bytes
  for (byte i = 1; i < 7; i++) {
    frame[i] ^= frame[i - 1];
  }

  Serial.println(""); Serial.print("Obfuscated    : ");
  for (byte i = 0; i < 7; i++) {
    if (frame[i] >> 4 == 0) {
      Serial.print("0");
    }
    Serial.print(frame[i], HEX); Serial.print(" ");
  }
  Serial.println("");
  Serial.print("Rolling Code  : "); Serial.println(rollingCode);
  rollingCode = rollingCode + 1;
  //  We store the value of the rolling code in the EEPROM. It should take up to 2 adresses but the
  EEPROM.write(0, highByte(rollingCode));   //write the first half
  EEPROM.write(1, lowByte(rollingCode));    //write the second half
  Serial.print("Write EEPROM Adr 0 : "); Serial.println(highByte(rollingCode));
  Serial.print("Write EEPROM Adr 1 : "); Serial.println(lowByte(rollingCode));

  EEPROM.commit();                          // Arduino function takes care of it.
}




// Function to Send SOMFY RTS Command

void SendCommand(byte *frame, byte sync) {
  if (sync == 2) { // Only with the first frame.
    //Wake-up pulse & Silence
    digitalWrite(TXP, HIGH);
    delayMicroseconds(9415);
    digitalWrite(TXP, LOW);
    delayMicroseconds(89565);
  }

  // Hardware sync: two sync for the first frame, seven for the following ones.
  for (int i = 0; i < sync; i++) {
    digitalWrite(TXP, HIGH);
    delayMicroseconds(4 * SYMBOL);
    digitalWrite(TXP, LOW);
    delayMicroseconds(4 * SYMBOL);
  }

  // Software sync
  digitalWrite(TXP, HIGH);
  delayMicroseconds(4550);
  digitalWrite(TXP, LOW);
  delayMicroseconds(SYMBOL);


  //Data: bits are sent one by one, starting with the MSB.
  for (byte i = 0; i < 56; i++) {
    if (((frame[i / 8] >> (7 - (i % 8))) & 1) == 1) {
      digitalWrite(TXP, LOW);
      delayMicroseconds(SYMBOL);
      digitalWrite(TXP, HIGH);
      delayMicroseconds(SYMBOL);
    }
    else {
      digitalWrite(TXP, HIGH);
      delayMicroseconds(SYMBOL);
      digitalWrite(TXP, LOW);
      delayMicroseconds(SYMBOL);
    }
  }

  digitalWrite(TXP, LOW);
  delayMicroseconds(30415); // Inter-frame silence
}
1 Like