[gelöst]isAck() und request-Message Probleme

Begonnen von frober, 01 März 2020, 19:42:41

Vorheriges Thema - Nächstes Thema

frober

Hallo zusammen,

ich habe eine Node mit zeitgesteuerten Relais, die Zeitsteuerung läuft autark und meldet danach den Status an Fhem zurück. Bei einem Reboot wird mit request der aktuelle Status von Fhem angefordert und das setzen der Zeit und einschalten der Relais erfolgt aus Fhem.

Nun möchte ich mittels Software-Ack sicherstellen, dass die Rückmeldung in Fhem ankommt.
Das Problem ist, das request's und Befehle aus Fhem kein Ack enthalten. Dadurch gibt es einen Endlosloop und der Serialmonitor läuft "über".

Wie kann ich die eingehenden Nachrichten unterscheiden?

Das einfachste wäre das command auszuwerten (req, set). "message.command" ist nicht verfügbar oder es lautet anders und ich habe es übersehen.

Hat jemand eine Idee...

Danke und Gruß
Bernd
Raspi 3b mit Raspbian Buster und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...

Beta-User

Moin,

das Problem in dieser Form hatte ich noch nicht, aber afaik kann das Framework zwischen "normalen" Anweisungen und ACK-Rückmeldungen unterscheiden. Im "void receive" müßte daher eine entsprechende Vorab-Prüfung laufen, etwa in diese Richtung:
if (message.isAck()) {
      Serial.println("Ack from gw rec.");
} else {
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

frober

Ich habe solche Abfragen, daher kommt auch das Problem.
Bei NACK wir ein Zähler gesetzt und die Anzahl der Versuche begrenzt.

Der Sketch startet normal, sobald ich die Zeit aus Fhem heraus setzte beginnt der Loop, da kein ACK/NACK erkannt wird.

Gesten habe ich eine Möglichkeit gefunden die ich noch testen muss.
Mit
message.getCommand()
kann ich hoffentlich die Art der Meldung (set/req) unterscheiden.

Falls ich heute Abend nicht weiter komme melde ich mich noch einmal mit meinen Sketch  oder bei Erfolg mit der Lösung.
Raspi 3b mit Raspbian Buster und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...

frober

Hier mal mein Codes, nur die relevanten Teile, noch ungetestet, lief jedoch in der IDE fehlerfrei durch:


//+++++++++ im Setup ++++++++++
for (int i = 0; i < noRelays; i++)
  {
    request(i, V_STATUS);                        // Set relay to last known state
    Relays[i].relayAckCountR = -1;               // for the control of the request
    wait(20);
    request(i, V_PERCENTAGE);                   // Set relay to last known state
    Relays[i].relayAckCountT = -1;              // for the control of the request
    wait(20);
  }
 

//++++++++++ loop +++++++++
void loop() {

for (int i = 0; i < noRelays; i++) {
    if (Relays[i].relayAckCountR < 0) {
      request(i, V_STATUS);                        // Set relay to last known state
      Relays[i].relayAckCountR = 0;
      wait(40);
      }
     if (Relays[i].relayAckCountT < 0) {
      request(i, V_PERCENTAGE);                    // Set relay to last known state
      Relays[i].relayAckCountT = 0;
      wait(40);
     
     }
     if (Relays[i].relayAckCountR > 0) {
      send(msgRelay[i].set(Relays[i].relayState ? true : false), true); // Send value to gw with ACK
      wait(40);
     
     }
     if (Relays[i].relayAckCountT > 0) {
      send(msgTime[i].set(Relays[i].relayTime), true); // Send value to gw with ACK
      wait(40);
     
     }
  }
}
 
//+++++++++++++++ recieve ++++++++++++ 
void receive(const MyMessage &message) {
 
  bool isAckMessage;
  switch (message.getCommand()) {                               // message.getCommand will give us the command type of the incomming message
      case C_SET:                                               //message is a set command  from controller to update relay state
        isAckMessage = true;                                    //it's a ACK-Message
        break;
      case C_REQ:                                               // message is a returning request from controller
        isAckMessage = false;                                   //it's not a ACK-Message
        break;
    }
   
//++++++++ Relays +++++++++
  if (message.isAck() && message.type == V_STATUS && isAckMessage == true) {
    Relays[message.sensor].relayAckCountR = 0;
   
    if (message.sensor < noRelays) {          // check if message is valid for relays..... previous line  [[[ if (message.sensor <=noRelays){ ]]]
      Relays[message.sensor].relayState = message.getBool();
      digitalWrite(Relays[message.sensor].relayPin, Relays[message.sensor].relayState ? RELAY_ON : RELAY_OFF); // and set relays accordingly
      //saveState( message.sensor, Relays[message.sensor].relayState ); // save sensor state in EEPROM (location == sensor number)
      }
    if (Relays[message.sensor].relayState == RELAY_ON) {
      Relays[message.sensor].startTime = millis();
    }
   
}
else if (!message.isAck() && message.type == V_STATUS && isAckMessage == true) {
      Relays[message.sensor].relayAckCountR--;
     
}
  else if (message.type == V_STATUS && isAckMessage == false) {
      Relays[message.sensor].relayAckCountR = 0;
     
}
else if (message.isAck() && message.type == V_PERCENTAGE && isAckMessage == true) {
   Relays[message.sensor].relayAckCountT = 0;
   
    if (message.sensor < noRelays) {          // check if message is valid for relays..... previous line  [[[ if (message.sensor <=noRelays){ ]]]
      Relays[message.sensor].relayTime = (message.getByte() * timeFactor);    // calculate to millis
      }
 
}
else if (!message.isAck() && message.type == V_PERCENTAGE && isAckMessage == true) {
      Relays[message.sensor].relayAckCountT--;
     
}
else if (message.type == V_PERCENTAGE && isAckMessage == false) {
      Relays[message.sensor].relayAckCountT = 0;
     
}
}


Sobald ich getestet habe gebe ich Rückmeldung.....
Raspi 3b mit Raspbian Buster und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...

frober

Erste Tests sind durch, die Überprüfung vom Command musste ich tauschen, da requests logischerweise als set zurück kommen.

Zitat
  bool isAckMessage;
  switch (message.getCommand()) {                               // message.getCommand will give us the command type of the incomming message
      case C_SET:                                               //message is a set command  from controller to update relay state
        isAckMessage = false;                                    //it's a ACK-Message
        break;
      case C_REQ:                                               // message is a returning request from controller
        isAckMessage = true;                                   //it's not a ACK-Message
        break;
    }

Leider habe nun ein Problem, dass ich mir nicht erklären kann. Zum Testen habe ich das GW und EINE Node. Bei der Synchronisation ist noch alles ok, dann wird plötzlich die Meldung an NodeID 1 gesendet.
Woher kann das kommen?
Zitat16112 TSM:READY:ID=2,PAR=0,DIS=1
16113 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=15,pt=6,l=2,sg=0,ft=0,st=OK:0100
16138 TSF:MSG:READ,0-0-2,s=255,c=3,t=15,pt=6,l=2,sg=0:0100
16139 TSF:MSG:SEND,2-2-0-0,s=255,c=0,t=17,pt=0,l=5,sg=0,ft=0,st=OK:2.3.1
16148 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=6,pt=1,l=1,sg=0,ft=0,st=OK:0
16211 TSF:MSG:READ,0-0-2,s=255,c=3,t=6,pt=0,l=1,sg=0:M
16212 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=11,pt=0,l=14,sg=0,ft=0,st=OK:Brunnenschacht
16223 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=12,pt=0,l=3,sg=0,ft=0,st=OK:1.1
16225 TSF:MSG:SEND,2-2-0-0,s=2,c=0,t=35,pt=0,l=0,sg=0,ft=0,st=OK:
16282 TSF:MSG:SEND,2-2-0-0,s=10,c=0,t=6,pt=0,l=16,sg=0,ft=0,st=OK:28DA2E2B0600007A
28DA2E2B0600007A
16323 TSF:MSG:SEND,2-2-0-0,s=1,c=0,t=21,pt=0,l=0,sg=0,ft=0,st=OK:
16338 TSF:MSG:SEND,2-2-0-0,s=0,c=0,t=4,pt=0,l=0,sg=0,ft=0,st=OK:
16359 TSF:MSG:SEND,2-2-0-0,s=1,c=0,t=4,pt=0,l=0,sg=0,ft=0,st=OK:
16380 TSF:MSG:SEND,2-2-0-0,s=2,c=0,t=4,pt=0,l=0,sg=0,ft=0,st=OK:
16401 MCO:REG:REQ
16401 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=26,pt=1,l=1,sg=0,ft=0,st=OK:2
18402 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=26,pt=1,l=1,sg=0,ft=0,st=OK:2
20403 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=26,pt=1,l=1,sg=0,ft=0,st=OK:2
22404 TSF:MSG:SEND,2-2-0-0,s=255,c=3,t=26,pt=1,l=1,sg=0,ft=0,st=OK:2
24406 TSF:MSG:SEND,2-2-1-1,s=1,c=2,t=24,pt=0,l=0,sg=0,ft=0,st=OK:
24407 TSF:RTE:N2N OK
24407 TSF:MSG:SEND,2-2-1-1,s=1,c=2,t=24,pt=0,l=0,sg=0,ft=0,st=OK:
24409 TSF:RTE:N2N OK
24412 TSF:MSG:SEND,2-2-1-1,s=1,c=2,t=24,pt=0,l=0,sg=0,ft=0,st=OK:
24413 TSF:RTE:N2N OK
24414 TSF:MSG:SEND,2-2-1-1,s=1,c=2,t=24,pt=0,l=0,sg=0,ft=0,st=OK:
24417 TSF:RTE:N2N OK
24429 TSF:MSG:SEND,2-2-1-1,s=1,c=2,t=24,pt=0,l=0,sg=0,ft=0,st=OK:
24429 TSF:RTE:N2N OK

Da kein Antwort kommt gibt es wieder einen Loop :(
Raspi 3b mit Raspbian Buster und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...

frober

Ich habe den Fehler, Überbleibsel von meinen vielen Ack-Versuchen.[emoji45]

Habe in der request-Anforderung noch ein true für ACK stehen. Für request gibt es kein ACK, sondern man kann die Empfänger-Node bestimmen.
D.h. true entspricht Node 1.[emoji849]

Raspi 3b mit Raspbian Buster und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...

frober

Es funktioniert :)

Der Ansatz die Message zu unterscheiden hat für meine Zweck nicht zum Ziel geführt:

- aus Fhem gesendete Befehle sind eine Set-Message
- requests kommen als Set-Message zurück
- Status-Meldung der Node (Set-Message) kommt als Set-Message mit Ack zurück

D.h alles sind Set-Message (keine Unterscheidung möglich)

Da ich einen Counter benutze um das erneute senden, der Status-Meldung, von der Node zu begrenzen habe ich diesen genutzt um an Ziel zu kommen. ;D

Kurz und knapp: Beim erfolgreichem Empfang geht der Counter auf 0, beim request auf <0 und beim send auf >0.

Hier mein Sketch, er ist zum schalten mehrere zeitgesteuerter Relais, zwecks Übersichtlichkeit habe ich die Debug-Meldungen entfernt:

/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2018 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*******************************
*
* DESCRIPTION
* The RS485 Gateway prints data received from sensors on the serial link.
* The gateway accepts input on seral which will be sent out on
* the RS485 link.
*
* Wire connections (OPTIONAL):
* - Inclusion button should be connected between digital pin 3 and GND
* - RX/TX/ERR leds need to be connected between +5V (anode) and digital pin 6/5/4 with resistor 270-330R in a series
*
* LEDs (OPTIONAL):
* - RX (green) - blink fast on radio message received. In inclusion mode will blink fast only on presentation received
* - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly
* - ERR (red) - fast blink on error during transmission error or receive crc error
*
* If your Arduino board has additional serial ports
* you can use to connect the RS485 module.
* Otherwise, the gateway uses AltSoftSerial to handle two serial
* links on one Arduino. Use the following pins for RS485 link
*
*  Board          Transmit  Receive   PWM Unusable
* -----          --------  -------   ------------
* Teensy 3.0 & 3.1  21        20         22
* Teensy 2.0         9        10       (none)
* Teensy++ 2.0      25         4       26, 27
* Arduino Uno        9         8         10
* Arduino Leonardo   5        13       (none)
* Arduino Mega      46        48       44, 45
* Wiring-S           5         6          4
* Sanguino          13        14         12
*
*/

#define SN "MultiTimerRelay"
#define SV "1.0"

// Enable debug prints to serial monitor
#define MY_DEBUG

// Enable RS485 transport layer
#define MY_RS485

// Define this to enables DE-pin management on defined pin
//#define MY_RS485_DE_PIN 2

// Set RS485 baud rate to use
#define MY_RS485_BAUD_RATE 19200

// Enable this if RS485 is connected to a hardware serial port
#define MY_RS485_HWSERIAL Serial

#define MY_NODE_ID 2   // Id of the Node
#define MY_TRANSPORT_WAIT_READY_MS 30000  //loop is beginning at 3000ms whitout connection


#include <MySensors.h>

//++++++++ Relays +++++++++
#define RELAY_ON 1                         // switch around for ACTIVE LOW / ACTIVE HIGH relay
#define RELAY_OFF 0
#define noRelays 3                         // min 2
const int relayPin[] = {7, 8, 9};          // switch around pins to your desire
uint32_t relayTime[] = {0, 0, 0};          // on time for Relay to set from Fhem
volatile uint32_t startTime[] = {0, 0, 0}; // time to switch Relay on
int relayAckCountR[noRelays];              // ACK retry counter relay
int relayAckCountT[noRelays];              // ACK retry counter time
const uint32_t timeFactor = 1000UL;        // to send minutes = 60000UL or seconds = 1000UL; UL says compiler is unsigned long!!!

class Relay                                // relay class, store all relevant data (equivalent to struct)
{
  public:
    int relayPin;                          // physical pin number of relay
    boolean relayState;                    // relay status (also stored in EEPROM)
    uint32_t relayTime;                    // on time for Relay
    uint32_t startTime;                    // time to switch Relay on
    int relayAckCountR;                     // ACK retry counter relay
    int relayAckCountT;                     // ACK retry counter time
};

Relay Relays[noRelays];
MyMessage msgRelay[noRelays];
MyMessage msgTime[noRelays];


void before()
{
 
}

void presentation()
{
  // Send the sketch version information to the gateway and Controller
  sendSketchInfo(SN, SV);

   //++++++++ Relays +++++++++
   for (int i = 0; i < noRelays; i++)
  {
    present(i, S_DIMMER);                                  // present sensor to gateway
    wait(20);
  }
}

void setup()
{
  //++++++++ Relays +++++++++
  for (int i = 0; i < noRelays; i++) {   
    Relays[i].relayPin = relayPin[i];           // assign physical pins
    Relays[i].startTime = startTime[i];         // assign time to switch relay on
    Relays[i].relayTime = relayTime[i];         // assign time for relay on
    msgRelay[i].sensor = i;                     // initialize messages
    msgRelay[i].type = V_STATUS;
    msgTime[i].sensor = i;                       
    msgTime[i].type = V_PERCENTAGE;
    pinMode(Relays[i].relayPin, OUTPUT);
    Relays[i].relayState = RELAY_OFF;           // set all relaysState off
    digitalWrite(Relays[i].relayPin, Relays[i].relayState ? RELAY_ON : RELAY_OFF); // write all relays off
    //send(msgRelay[i].set(Relays[i].relayState ? true : false));
    wait(20);
    }
  for (int i = 0; i < noRelays; i++)
  {
    request(i, V_STATUS);                        // Set relay to last known state
    Relays[i].relayAckCountR = -1;               // for the control of the request
    wait(100);
    request(i, V_PERCENTAGE);                   // Set relay to last known state
    Relays[i].relayAckCountT = -1;              // for the control of the request
    wait(100);
  }
}

void loop()
{
  //++++++++ Relays +++++++++
  uint32_t currentTime = millis();
 
  //Zeitsteuerung der Relays
  for (byte i = 0; i < noRelays; i++) {
    if ( Relays[i].relayState == RELAY_ON && (currentTime - Relays[i].startTime > Relays[i].relayTime)) {
      digitalWrite(Relays[i].relayPin, !Relays[i].relayState ? RELAY_ON : RELAY_OFF);
      Relays[i].relayState = !Relays[i].relayState;
      send(msgRelay[i].set(Relays[i].relayState ? true : false), true); // Send value to gw with ACK
      Relays[i].relayTime = 0;
      Relays[i].relayAckCountR = 3;
      wait(100);
      send(msgTime[i].set(Relays[i].relayTime), true); // Send value to gw with ACK
      Relays[i].relayAckCountT = 3;
      wait(100);

     }
  }
  for (int i = 0; i < noRelays; i++) {
    if (Relays[i].relayAckCountR < 0) {
      wait(200);
      request(i, V_STATUS);                        // Set relay to last known state
      Relays[i].relayAckCountR = 0;
      wait(100);
   
     }
     if (Relays[i].relayAckCountT < 0) {
      wait(200);
      request(i, V_PERCENTAGE);                    // Set relay to last known state
      Relays[i].relayAckCountT = 0;
      wait(100);
   
     }
  if (Relays[i].relayAckCountR > 0) {
      wait(200);
      send(msgRelay[i].set(Relays[i].relayState ? true : false), true); // Send value to gw with ACK
      wait(100);
     
     }
     if (Relays[i].relayAckCountT > 0) {
      wait(200);
      send(msgTime[i].set(Relays[i].relayTime), true); // Send value to gw with ACK
      wait(100);
     
     }
  }
}


void receive(const MyMessage &message) {

//++++++++ Relays +++++++++
  if (message.type == V_STATUS && Relays[message.sensor].relayAckCountR <= 0) {
    Relays[message.sensor].relayAckCountR = 0;
    if (message.sensor < noRelays) {          // check if message is valid for relays..... previous line  [[[ if (message.sensor <=noRelays){ ]]]
      Relays[message.sensor].relayState = message.getBool();
      digitalWrite(Relays[message.sensor].relayPin, Relays[message.sensor].relayState ? RELAY_ON : RELAY_OFF); // and set relays accordingly
      //saveState( message.sensor, Relays[message.sensor].relayState ); // save sensor state in EEPROM (location == sensor number)
      }
    if (Relays[message.sensor].relayState == RELAY_ON) {
      Relays[message.sensor].startTime = millis();
    }
}
else if (message.isAck() && message.type == V_STATUS && Relays[message.sensor].relayAckCountR > 0) {
      Relays[message.sensor].relayAckCountR = 0;
}
else if (!message.isAck() && message.type == V_STATUS && Relays[message.sensor].relayAckCountR > 0) {
      Relays[message.sensor].relayAckCountR--;
}
else if (message.type == V_PERCENTAGE && Relays[message.sensor].relayAckCountT <= 0) {
    if (message.sensor < noRelays) {          // check if message is valid for relays..... previous line  [[[ if (message.sensor <=noRelays){ ]]]
      Relays[message.sensor].relayTime = (message.getByte() * timeFactor);    // calculate to millis
      }
}
else if (message.isAck() && message.type == V_PERCENTAGE && Relays[message.sensor].relayAckCountT > 0) {
      Relays[message.sensor].relayAckCountT = 0;
}
else if (!message.isAck() && message.type == V_PERCENTAGE && Relays[message.sensor].relayAckCountT > 0) {
      Relays[message.sensor].relayAckCountT --;
}
}


Grüße
Bernd
Raspi 3b mit Raspbian Buster und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...