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 Somfy Smoove Origin RTS Protocol | PushStack
   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
}
Obenstehendes Sketch hat mal funktioniert, dann habe ich mich an das Schrieben des aktuellen Rollingcodes ins Eeprom gewagt. Problematisch wurde dann, ich möchte mehrere Motoren ansteuern können, heisst, ich muss wissen welchen REMOTE ID ich verwende und wo gerade derren ROLLINGCODE steht.
Um dies zu vereinfachen, habe ich dann mal das ganze MQTT Gedöns rauschgelöscht und das nachfolgende Sketch geschrieben. Das ist gut ein halbes Jahr her, somit weiss ich gerade nicht mehr, was davon funktioniert hat.
Wichtig zu wissen: Das Ganze wollte ich auf einem ESP-01 zum Fliegen bringen, damit ich direkt aus Node-red via MQTT auf RTS kann. ESP-01 unterstützt jedoch nicht die einfachen EEPROM Operationen des Arduino. Somit würde ich wohl beim nächsten Versuch auf einen Arduino Nano wechseln und diesen via I2C, UART oder GPIO mit Node-red und Homegear ansteuern, doch man beachte die kurze Reichweite, solange man noch mit 433.92 MHz funkt!
Sketch mit EEPROM ohne 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  >185  Remote 0x521373
   Motor 4   Rollingcode  >274  Remote 0x521374
*/
#include "EEPROM.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!
unsigned int newRollingCode = 185;       //<-- Change it!
unsigned int rollingCode = 185;
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();
  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("EEPROM Wert = ");
  Serial.print(high);
  Serial.print(",");
  Serial.println(low);
  pinMode(TXP, OUTPUT);; // Pin DAT
  digitalWrite(TXP, LOW); // LOW
  if (rollingCode < newRollingCode) {
    rollingCode = newRollingCode;
  }
  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);
    }
  }
}
// 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
}
Die Diskussion ist eröffnet …