Autor Thema: Publishen von Fhem / Subscribieren bei einem eigenen Sketch funktioniert nicht  (Gelesen 497 mal)

Offline Gisbert

  • Full Member
  • ***
  • Beiträge: 364
  • Das Ziel ist das Ziel !
Liebe Forumsmitglieder,

ich habe einen Sketch auf einem Sonoff Dual, der einen Rollladen schaltet.

Der Sketch beinhaltet folgende Funktionen (bitte Nachsicht üben, falls die Begriffe nicht stimmen sollten:
a) Bei Eingabe der IP-Adresse des Sonoff Duals öffnet sich eine Webseite, mit der man den Rollladen steuern kann (Hoch, Runter, Luecke, Stop)
b) In Fhem gibt es einen Dummy, mit dem man den Rollladen schalten kann
c) Mit AMAD2 gibt es eine Sprachsteuerung, die ebenfalls den Rollladen schaltet

b) und c) haben mit dem Sketch eigentlich nichts zu tun, da Sie nur Befehle raussenden.

Mein Anliegen war es in Fhem eine Rückmeldung über den Schaltzustand des Rollladens zu bekommen.
Dazu habe ich MQTT in den Sketch implementiert.

Was bisher gut funktioniert, ist das Publishen vom Sonoff Dual / Subscribieren in Fhem.
Der umgekehrte Weg will einfach nicht funktionieren, d.h. Publishen in Fhem und Subscribieren auf dem Sonoff Dual.

Getest habe ich es mit MQTT.fx
- Subcribieren vom Sonoff Dual funktioniert (kein Wunder, das hat ja auch in Fhem funktioniert)
- Publishen von Fhem aus funktioniert, d.h. in MQTT.fx kommt es an, nicht aber auf dem Sonoff Dual

Das Forum hab ich schon erfolglos nach Hinweisen durchsucht.
Falls jemand eine Idee hat, warum das Empfangen von Nachrichten auf dem Sonoff Dual nicht funktioniert, würde ich mich sehr darüber freuen.

Meine Definition in Fhem:
defmod Rollladen MQTT_DEVICE
attr Rollladen IODev MyBroker
attr Rollladen icon fts_shutter_automatic
attr Rollladen publishSet_Terrasse_Luecke /Pub/Rollladen/Terrasse/Luecke
attr Rollladen publishSet_Terrasse_Set /Pub/Rollladen/Terrasse/Set
attr Rollladen room Mobile
attr Rollladen stateFormat Terrassentür: Terrasse
attr Rollladen subscribeReading_Terrasse /Rollladen/Terrasse

setstate Rollladen Terrassentür: Stop
setstate Rollladen 2017-07-17 15:15:58 Terrasse Stop
setstate Rollladen 2017-07-17 14:55:41 Terrasse_Luecke 5000
setstate Rollladen 2017-07-17 14:47:17 Terrasse_Set Hoch
setstate Rollladen 2017-07-17 15:15:58 transmission-state incoming publish received

Der Arduino-Sketch in den wesentliche Auszügen, die fürs Publishen notwendig sind (alles andere hab ich nicht hier reinkopiert, kann ich aber gerne zur Verfügung stellen):
// Update these with values suitable for your network.
byte mac[] = { 0x5C, 0xCF, 0x7F, 0xAA, 0xBB, 0xCC };
IPAddress ip(789, 123, 456, 78);
IPAddress MQTTserver(789, 123, 456, 55);

WiFiClient client;
PubSubClient MQTTclient(MQTTserver, 1883, callback, client);

// handle message arrived
void callback(char* topic, byte* payload, unsigned int length)
{
  payload[length] = '\0';
  String sPayload = String((char *)payload);
  String sTopic = String(topic);
    if (sTopic == "/Pub/Rollladen/Terrasse/Set")
      {
        if (sPayload == "Stop")
        {
          switch_relay(0);
          ulMotorEventEnd=millis();
          if(MQTTclient.connect("arduinoClient", "user", "passwd"))
          {
            MQTTclient.publish("/Rollladen/Terrasse","Stop");
          }
        }
        else if (sPayload == "Runter")
        {       
          delay(RELAISWAIT);       
          switch_relay(0);
          delay(MOTORWAIT);
          switch_relay(1);
          ulMotorEventEnd=millis()+EVENT;
          if(MQTTclient.connect("arduinoClient", "user", "passwd"))
          {
            MQTTclient.publish("/Rollladen/Terrasse","Runter");
          }       
        }
        else if (sPayload == "Luecke")
        {       
          delay(RELAISWAIT);       
          switch_relay(0);
          delay(MOTORWAIT);
          switch_relay(1);
          ulMotorEventEnd=millis()+zeit_luecke;
          if(MQTTclient.connect("arduinoClient", "user", "passwd"))
          {
            MQTTclient.publish("/Rollladen/Terrasse","Lücke");
          }       
        }
        else if (sPayload == "Hoch")
        {       
          delay(RELAISWAIT);       
          switch_relay(0);
          delay(MOTORWAIT);
          switch_relay(2);
          ulMotorEventEnd=millis()+EVENT;
          if(MQTTclient.connect("arduinoClient", "user", "passwd"))
          {
            MQTTclient.publish("/Rollladen/Terrasse","Hoch");
          }       
        }
      }
    if (sTopic == "/Pub/Rollladen/Terrasse/Luecke")
      zeit_luecke = sPayload.toInt();
}


long lastReconnectAttempt = 0;

boolean reconnect()
{
  if (MQTTclient.connect("arduinoClient", "user", "passwd"))
  {
    MQTTclient.publish("/Rollladen/Terrasse","online");
    MQTTclient.subscribe("/Pub/Rollladen/Terrasse/Set");
    MQTTclient.subscribe("/Pub/Rollladen/Terrasse/Luecke");   
  }
  return MQTTclient.connected();

void setup()
{
  Ethernet.begin(mac, ip);
  // Note - the default maximum packet size is 128 bytes.
  // If the combined length of clientId, username and password exceed this,
  // you will need to increase the value of MQTT_MAX_PACKET_SIZE in PubSubClient.h

  MQTTclient.loop();
}

void loop()
{
  if (!MQTTclient.connected())
  {
    long now = millis();
    if (now - lastReconnectAttempt > 5000)
    {
      lastReconnectAttempt = now;
      if (reconnect())
      {
        lastReconnectAttempt = 0;
      }
    }
  }
  else
  {
    MQTTclient.loop();
  }
}
« Letzte Änderung: 17 Juli 2017, 15:47:55 von Gisbert »
Fhem 5.8 auf RPi3 B, Homematic, ESP8266, Sonoff Dual, 1-Wire-Temperatursensoren, Wlan-Kamera, WH3080-Wettereinheit

Offline Gisbert

  • Full Member
  • ***
  • Beiträge: 364
  • Das Ziel ist das Ziel !
Liebe Forumsmitglieder,

hat keiner eine Idee oder was Vergleichbares bei sich realisiert?
Das Problem scheint mir in den folgenden Zeilen zu liegen:

// handle message arrived
void callback(char* topic, byte* payload, unsigned int length)
{
  payload[length] = '\0';
  String sPayload = String((char *)payload);
  String sTopic = String(topic);
    if (sTopic == "/Pub/Rollladen/Terrasse/Set")
      {
        if (sPayload == "Stop")

Ich bin über jeden Vorschlag oder Hinweis dankbar.
Viele Grüße Gisbert
Fhem 5.8 auf RPi3 B, Homematic, ESP8266, Sonoff Dual, 1-Wire-Temperatursensoren, Wlan-Kamera, WH3080-Wettereinheit

Offline Gisbert

  • Full Member
  • ***
  • Beiträge: 364
  • Das Ziel ist das Ziel !
Hallo,

ich wollte mich nochmals in Erinnerung rufen, denn ich bin noch nicht weitergekommen mit meinem Problem.
Falls jemand eine Idee hat, oder was ähnliches implementiert hatte, dann wäre es super, wenn ich hier Hilfe finden könnte.

Viele Grüße Gisbert
Fhem 5.8 auf RPi3 B, Homematic, ESP8266, Sonoff Dual, 1-Wire-Temperatursensoren, Wlan-Kamera, WH3080-Wettereinheit

Offline dev0

  • Developer
  • Hero Member
  • ****
  • Beiträge: 2967
    • _.:|:._
Wenn niemand helfen kann/will dann hilft nur debuggen...

Offline Gisbert

  • Full Member
  • ***
  • Beiträge: 364
  • Das Ziel ist das Ziel !
Hallo dev0,

debuggen ist schwierig bis unmöglich (?), zumindest für mich, da die beiden Relais über I2C am ESP8266 des Sonoff Duals hängen.
Eine sonst übliche serielle Ausgabe ist damit anscheinend nicht möglich.
Eine Idee wäre es, den Sketch auf einen "normalen" ESP8266 anstatt auf den Sonoff Dual zu flashen, dann hätte man den seriellen Monitor als Ausgabemöglichkeit.
Da ich weder über ausreichende Programmierkenntnisse verfüge, noch weiß, wie man beim debuggen vorgehen sollte, bleibt es schwierig.

Ich gebe nicht auf, es dauert dann nur länger, bis ich am Ziel bin.

Viele Grüße Gisbert
Fhem 5.8 auf RPi3 B, Homematic, ESP8266, Sonoff Dual, 1-Wire-Temperatursensoren, Wlan-Kamera, WH3080-Wettereinheit

Offline PatrickR

  • Developer
  • Full Member
  • ****
  • Beiträge: 469
Hallo Gisbert!

debuggen ist schwierig bis unmöglich (?), zumindest für mich, da die beiden Relais über I2C am ESP8266 des Sonoff Duals hängen.
Eine sonst übliche serielle Ausgabe ist damit anscheinend nicht möglich.
Eine Idee wäre es, den Sketch auf einen "normalen" ESP8266 anstatt auf den Sonoff Dual zu flashen, dann hätte man den seriellen Monitor als Ausgabemöglichkeit.
Da ich weder über ausreichende Programmierkenntnisse verfüge, noch weiß, wie man beim debuggen vorgehen sollte, bleibt es schwierig.

Ich habe das in meinen Projekten quick'n'dirty implementiert und zwar über UDP:
// Debugging
#define DEBUG
#define DEBUG_HOSTNAME                    "epsilon"
#define DEBUG_PORT                        1234
#define DEBUGSTR_BUF_SIZE                 1024

#ifdef DEBUG
  #define DPRINT(...) \
    Udp.beginPacket(DEBUG_HOSTNAME, DEBUG_PORT); \
    Udp.write(__VA_ARGS__); \
    Udp.endPacket();
  #define DPRINTLN(...) \
    Udp.beginPacket(DEBUG_HOSTNAME, DEBUG_PORT); \
    Udp.write(__VA_ARGS__); \
    Udp.write("\n"); \
    Udp.endPacket();
  #define DPRINTF(...) \
    Udp.beginPacket(DEBUG_HOSTNAME, DEBUG_PORT); \
    snprintf(debugstr, sizeof(debugstr), __VA_ARGS__); \
    Udp.write(debugstr); \
    Udp.endPacket();
#else
  #define DPRINT(...)
  #define DPRINTLN(...)
  #define DPRINTF(...)
#endif

#ifdef DEBUG
  WiFiUDP Udp;
  char debugstr[DEBUGSTR_BUF_SIZE];
#endif

DPRINTF("updatevr100state(): %u -> %u\n", vr100state, newstate);

Dann kannst Du auf Deinem Clientrechner mit netcat lauschen:
nc -ul 1234

Patrick
« Letzte Änderung: 05 August 2017, 15:21:15 von PatrickR »
lepresenced - Tracking von Bluetooth-LE-Tags (Gigaset G-Tag) mittels PRESENCE

Offline Gisbert

  • Full Member
  • ***
  • Beiträge: 364
  • Das Ziel ist das Ziel !
Hallo Patrick, dev0 sowie jeder andere, der sich meinem Problem annimmt,

vielen Dank an Patrick für die Anleitung zum Debuggen, das überfordert mich erst recht.

Nochmals zu meinem Problem:
Ich möchte in einem Sketch auf einem ESP8266 einen Wert einlesen, und diesen Wert einer Variablen zuordnen.

Auszug aus meinem Sketch, der allerdings nicht funktioniert:
// Rollladen auf Lücke, von oben
unsigned int zeit_luecke = 26800;

// Topic für die Subskription der Fahrzeit des Rollladens auf Lücke
char* RollladenLuecke = "/Pub/Rollladen/Terrasse/Luecke";

// handle message arrived
void callback(char* topic, byte* payload, unsigned int length)
{
  payload[length] = '\0';
  String sPayload = String((char *)payload);
  String sTopic = String(topic);
    if (sTopic.equals(RollladenLuecke))
    {
      zeit_luecke = sPayload.toInt();
    }
}

Ich bin für jeden Hinweis dankbar.

Viele Grüße Gisbert
Fhem 5.8 auf RPi3 B, Homematic, ESP8266, Sonoff Dual, 1-Wire-Temperatursensoren, Wlan-Kamera, WH3080-Wettereinheit

Offline Gisbert

  • Full Member
  • ***
  • Beiträge: 364
  • Das Ziel ist das Ziel !
Hallo zusammen,

mit dem angehängten Sketch funktioniert jetzt die Übertragung einer Variablen (zur Zeitsteuerung der Fahrzeit des Rollladens auf Lücke), sowie natürlich die Steuerung eines Rollladens.
Der Befehl wird aus Fhem abgesetzt und wird auf dem Sonoff Dual subskribiert.
Es geschieht, aber nicht zuverlässing.
D.h. man muss den Befehl absetzen, den Rollladen hoch- und runterfahren lassen, den Befehl zur Übetragung der Varibalen erneut übetragen, dann funktioniert es.
Sehr umständlich, da ich aber diese Zeit nur einmal nach dem Einbau und ggf. nach einem Stromausfall setzen muss, komme ich damit erst mal zurecht.

Viele Grüße Gisbert

/*
 2-Channel-Relay with ESP8266 on Sonoff Dual

 Basic MQTT example with Authentication
 - connects to an MQTT server, providing username and password
 - publishes to a topic
 - subscribes to a topic
*/

#include <SPI.h>
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>


// Counters
unsigned long ulReqcount;
unsigned long ulReconncount;

// Create an instance of the server
// Specify the port to listen on as an argument
WiFiServer server(80);

// Time to wait when turning motor off or changing direction
// Recommended to allow motor capacitor to unload over motor coil
#define MOTORWAIT 300

// Time for direction relay to swtich before power is applied
// Recommended to avoid implicit switch of direction with power on
#define RELAISWAIT 200

// Duration of a motor event
#define EVENT 40000
unsigned long ulMotorEventEnd=0;  // end of current motor event

// Rollladen auf Lücke, von oben
unsigned int ZeitLuecke = 26800;

// Relaisschaltung
void switch_relay(byte b)
{
  if (0<=b && b<=2)
  {
    Serial.write(0xA0);
    Serial.write(0x04);
    Serial.write(b);
    Serial.write(0xA1);
    Serial.flush();
  }
}


const char* mqttServer = "192.168.XX.YY";
const int mqttPort = 1883;
const char* mqttUser = "USER";
const char* mqttPassword = "PASSWORD";

WiFiClient client;
PubSubClient MQTTclient(client);

// Topic für die Subskription der Fahrzeit des Rollladens auf Lücke
char* RollladenLuecke = "/Pub/Rollladen/Terrasse/Luecke";

// Topic für das Publishen des durchgeführten Fahrbefehls
char* RollladenPub = "/Sub/Rollladen/Terrasse";


void reconnect()
{
  while (!MQTTclient.connected())
  {
    Serial.println("Connecting to MQTT...");
    if (MQTTclient.connect("RollladenWohnzimmerTerrasse", mqttUser, mqttPassword))
      {
        Serial.println("connected");
        MQTTclient.publish(RollladenPub,"online");
        MQTTclient.subscribe(RollladenLuecke);
       
      } else
      {
        Serial.print("failed with state ");
        Serial.print(MQTTclient.state());
        delay(2000);
      }
  }
}


// Setup routine

void setup()
{
  ulReqcount=0;
  ulReconncount=0;

  Serial.begin(19200);
 
  // Switch off both relays
  switch_relay(0);
  delay(MOTORWAIT);

  WiFiManager wifiManager;
  wifiManager.autoConnect("Access Point: Wohnzimmer Terrassentür");
  Serial.println("");
  Serial.println("WiFi connected");
 
  // Start the server
  server.begin();
  Serial.println("Server started");

  // Print the IP address
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  MQTTclient.setServer(mqttServer, mqttPort);
  MQTTclient.setCallback(callback);

  while (!MQTTclient.connected())
  {
    Serial.println("Connecting to MQTT...");
    if (MQTTclient.connect("RollladenWohnzimmerTerrasse", mqttUser, mqttPassword))
      {
        Serial.println("connected"); 
      } else
      {
        Serial.print("failed with state ");
        Serial.print(MQTTclient.state());
        delay(2000);
      }
  }
}


// handle message arrived
void callback(char* topic, byte* payload, unsigned int length)
{
  payload[length] = '\0';
  String sPayload = String((char *)payload);
  String sTopic = String(topic);
    if (sTopic == "/Pub/Rollladen/Terrasse/Luecke")
    {
      ZeitLuecke = sPayload.toInt();
    }
}


void loop()
{
  // Stop motor if required
  if (ulMotorEventEnd<millis())
  {   
    switch_relay(0);
    delay(MOTORWAIT);
  }
   
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client)
  {
    return;
  }
 
  // Wait until the client sends some data
  Serial.println("new client");
  unsigned long ULTIMEOUT = millis()+250;
  while(!client.available() && (millis()<ULTIMEOUT) )
  {
    delay(1);
  }
  if (millis()>ULTIMEOUT)
  {
    Serial.println("client connection time-out!");
    return;
  }
 
  // Read the first line of the request
  String sRequest = client.readStringUntil('\r');
  client.flush();
 
  if (sRequest=="")
  {
    Serial.println("empty request! - stopping client");
    client.stop();
    return;
  }

  if (!MQTTclient.connected())
  {
    reconnect();
  } 
  MQTTclient.loop();
  delay(10);

 
  // Get path; end of path is either space or ?
  // Syntax is e.g. GET /?pin=MOTOR1STOP HTTP/1.1
  String sPath="",sParam="", sCmd="";
  String sGetstart="GET ";
  int iStart,iEndSpace,iEndQuest;
  iStart = sRequest.indexOf(sGetstart);
  if (iStart>=0)
  {
    iStart+=+sGetstart.length();
    iEndSpace = sRequest.indexOf(" ",iStart);
    iEndQuest = sRequest.indexOf("?",iStart);
   
    // Are there parameters?
    if(iEndSpace>0)
    {
      if(iEndQuest>0)
      {
        // There are parameters
        sPath  = sRequest.substring(iStart,iEndQuest);
        sParam = sRequest.substring(iEndQuest,iEndSpace);
      }
      else
      {
        // NO parameters
        sPath  = sRequest.substring(iStart,iEndSpace);
      }
    }
  }
 
// Output command to serial so that we can read it on the Arduino

  if(sParam.length()>0)
  {
    int iEqu=sParam.indexOf("=");
    if(iEqu>=0)
    {
      sCmd = sParam.substring(iEqu+1,sParam.length());
      // Output to serial, which is connceted to the Arduino
      Serial.println(sCmd);
    }
  }
 
// Format the html response

  String sResponse,sHeader;
 
// 404 for non-matching path

  if(sPath!="/")
  {
    sResponse="<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL was not found on this server.</p></body></html>";

    sHeader  = "HTTP/1.1 404 Not found\r\n";
    sHeader += "Content-Length: ";
    sHeader += sResponse.length();
    sHeader += "\r\n";
    sHeader += "Content-Type: text/html\r\n";
    sHeader += "Connection: close\r\n";
    sHeader += "\r\n";
  }
 
  // Format the html page
 
  else
  {
    ulReqcount++;
    sResponse  = "<html><head><title>WZ.Terrassentuer</title></head><body>";
    sResponse += "<font color=\"#000000\"><body bgcolor=\"#d0d0f0\">";
    sResponse += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\">";
    sResponse += "<h1>Rollladen Wohnzimmer Terrassent&uuml;r</h1>";
    sResponse += "<FONT SIZE=+2>";
    sResponse += "<p><a href=\"?pin=FunctionHoch\"><button>Hoch</button></a>&nbsp;<a href=\"?pin=FunctionStop\"><button>Stop</button></a>&nbsp;<a href=\"?pin=FunctionRunter\"><button>Runter</button></a>&nbsp;<a href=\"?pin=FunctionLuecke\"><button>L&uuml;cke</button></a></p>";

    // React on parameters

    if (sCmd.length()>0)
    {
      // Write received command to html page
      sResponse += "Kommando: " + sCmd + "<BR>";
     
        // Prepare relays - 230 Volt Schaltung Ein / Aus
        // Both relays off
        // switch_relay(0);
        // Relay 1 on
        // switch_relay(1);
        // Relay 2 on
        // switch_relay(2);
        // Both relays on - Das muss bei einer Rolladenschaltung unbedingt vermieden werden, da ansosnsten der Motor zerstört wird !!!
        // switch_relay(3);
        // Relais 1: Runterfahren
        // Relais 2: Hochfahren
             
      if(sCmd.indexOf("FunctionStop")>=0)
      {
        switch_relay(0);
        ulMotorEventEnd=millis();
        MQTTclient.publish(RollladenPub, "Stop");
      }

      else if(sCmd.indexOf("FunctionRunter")>=0)
      {       
        delay(RELAISWAIT);       
        switch_relay(0);
        delay(MOTORWAIT);
        switch_relay(1);
        ulMotorEventEnd=millis()+EVENT;
        MQTTclient.publish(RollladenPub, "Runter");
      }

      else if(sCmd.indexOf("FunctionLuecke")>=0)
      {       
        delay(RELAISWAIT);       
        switch_relay(0);
        delay(MOTORWAIT);
        switch_relay(1);
        ulMotorEventEnd=millis()+ZeitLuecke;
        MQTTclient.publish(RollladenPub, "L&uuml;cke");
      }

      else if(sCmd.indexOf("FunctionHoch")>=0)
      {
        delay(RELAISWAIT);       
        switch_relay(0);       
        delay(MOTORWAIT);
        switch_relay(2);
        ulMotorEventEnd=millis()+EVENT;
        MQTTclient.publish(RollladenPub, "Hoch");
      }
    }
 
    sResponse += "<FONT SIZE=-2>";
    sResponse += "<BR>Aufrufz&auml;hler=";
    sResponse += ulReqcount;
    sResponse += " - Verbindungsz&auml;hler=";
    sResponse += ulReconncount;
    sResponse += "<BR>";
    sResponse += "Aktueller Stand<BR>";
    sResponse += "</body></html>";
   
    sHeader  = "HTTP/1.1 200 OK\r\n";
    sHeader += "Content-Length: ";
    sHeader += sResponse.length();
    sHeader += "\r\n";
    sHeader += "Content-Type: text/html\r\n";
    sHeader += "Connection: close\r\n";
    sHeader += "\r\n";
  }


  // Send the response to the client
  client.print(sHeader);
  client.print(sResponse);
  client.stop();

  Serial.println("Client disonnected");
}
Fhem 5.8 auf RPi3 B, Homematic, ESP8266, Sonoff Dual, 1-Wire-Temperatursensoren, Wlan-Kamera, WH3080-Wettereinheit