Report Relay state to GW



  • Good morning everybody I have following Problem:

    My controller (pimatic) opens via Gateway my Relay which activates a magnetic valve to fill my pond. depending on the water level, measured by my distance sensor.
    By rule pimatic closes the relay if the distance is less or equal to 14cm. NOW: one out of ten my communication fails and pimatic changes the state of the switch in the frontend, therefor the controller thinks the relay is off (valve closed) But in the GW debug I see the the message to turn of was NOACK. Meaning the water keeps running and my pond overflows.

    After reading everything I found Pimatic does not resend when NOACK or refuses Statechange when noack. IS IT POSSIBLE to report the state of the relay to the controller periodically like every time a distance Message gets send? Like correcting the "Wrong" state in the controler?

    Here is my node Sketch:

    
    /**
     * 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-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/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.
     *
     *******************************
     *
     * REVISION HISTORY
     * Version 1.0 - Henrik Ekblad
     * 
     * DESCRIPTION
     * Example sketch for a "light switch" where you can control light or something 
     * else from both HA controller and a local physical button 
     * (connected between digital pin 3 and GND).
     * This node also works as a repeader for other nodes
     * http://www.mysensors.org/build/relay
     */ 
    
    // Enable debug prints to serial monitor
    #define MY_DEBUG 
    
    #define MY_RF24_PA_LEVEL RF24_PA_MAX
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    // Enabled repeater feature for this node
    #define MY_REPEATER_FEATURE
    
    #include <SPI.h>
    #include <MySensors.h>
    #include <Bounce2.h>
    #include <NewPing.h>
    
    #define RELAY_PIN  4  // Arduino Digital I/O pin number for relay 
    #define BUTTON_PIN  3  // Arduino Digital I/O pin number for button 
    #define CHILD_ID_RELAY 1   // Id of the sensor child
    #define RELAY_ON 1
    #define RELAY_OFF 0
    
    #define CHILD_ID_DIST 10
    #define TRIGGER_PIN  6  // Arduino pin tied to trigger pin on the ultrasonic sensor.
    #define ECHO_PIN     5  // Arduino pin tied to echo pin on the ultrasonic sensor.
    #define MAX_DISTANCE 300 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
    
    NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.
    MyMessage msgDist(CHILD_ID_DIST, V_DISTANCE);
    int lastDist;
    bool metric = true;
    
    Bounce debouncer = Bounce(); 
    int oldValue=0;
    bool state;
    
    MyMessage msgRelay(CHILD_ID_RELAY,V_LIGHT);
    
    long previousMillis = 0;        // will store last time Distance was measured
    long interval = 10000;           // interval at which to measure (milliseconds) 
    
    void setup()  
    {  
      // Setup the button
      pinMode(BUTTON_PIN,INPUT);
      // Activate internal pull-up
      digitalWrite(BUTTON_PIN,HIGH);
      
      // After setting up the button, setup debouncer
      debouncer.attach(BUTTON_PIN);
      debouncer.interval(1);
    
      // Make sure relays are off when starting up
      digitalWrite(RELAY_PIN, RELAY_OFF);
      // Then set relay pins in output mode
      pinMode(RELAY_PIN, OUTPUT);   
          
      // Set relay to last known state (using eeprom storage) 
      state = loadState(CHILD_ID_RELAY);
      digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
      metric = getControllerConfig().isMetric;
    }
    
    void presentation()  {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("Relay & Button Distance", "1.0");
    
      // Register all sensors to gw (they will be created as child devices)
      present(CHILD_ID_RELAY, S_LIGHT);
      present(CHILD_ID_DIST, S_DISTANCE);  
    }
    
    /*
    *  Example on how to asynchronously check for new messages from gw
    */
    void loop() 
    {
      debouncer.update();
      // Get the update value
      int value = debouncer.read();
      if (value != oldValue && value==0) {
          send(msgRelay.set(state?false:true), true); // Send new state and request ack back
      }
      oldValue = value;
    
    
      // here is where you'd put code that needs to be running all the time.
     
      // check to see if it's time to blink the LED; that is, if the 
      // difference between the current time and last time you blinked 
      // the LED is bigger than the interval at which you want to 
      // blink the LED.
      
      unsigned long currentMillis = millis();
     
      if(currentMillis - previousMillis > interval) {
        // save the last time you blinked the LED 
        previousMillis = currentMillis;   
    
    int dist = metric?sonar.ping_cm():sonar.ping_in();
      Serial.print("Ping: ");
      Serial.print(dist); // Convert ping time to distance in cm and print result (0 = outside set distance range)
      Serial.println(metric?" cm":" in");
    
    
      if (dist != lastDist) {
          send(msgDist.set(dist));
          lastDist = dist;
      }
      }
    
     // sleep(SLEEP_TIME);
    } 
    
     
    void receive(const MyMessage &message) {
      // We only expect one type of message from controller. But we better check anyway.
      if (message.isAck()) {
         Serial.println("This is an ack from gateway");
      }
    
      if (message.type == V_LIGHT) {
         // Change relay state
         state = message.getBool();
         digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
         // Store state in eeprom
         saveState(CHILD_ID_RELAY, state);
        
         // Write some debug info
         Serial.print("Incoming change for sensor:");
         Serial.print(message.sensor);
         Serial.print(", New status: ");
         Serial.println(message.getBool());
       } 
    }
    


  • It's pretty easy to send back the actual state of the relay.

    But imho there are several options to improve the behaviour of the whole thing:

    • if it's more secure to start the whole thing with the relay switched off instead of reading value from EEPROM. Then ask the controller about the desired state and keep it off until message arrives or button is pressed?
    • keep track of the actual state of your relay within the loop(). By now, the switching logic of the button/switch seems to be weired. Does this work as expected?
    • if it's a security function: let the node decide whether the relay should be switched off, dependend on the meassured value and the state of the relay. Just inform the controller it had been switched off.

    It might be necessary to add some overriding functionality (eg. keep relay on for x minutes if it has been switched on using the button and so on).



  • @rejoe2 Hi sorry but I never really solved this problem but u are totally right!

    since I am not really firm with writing sketch this was my first attempt. The sketch works as long as i have communication. to the controller as soon as I Dont have communication nothing works as it should.

    How can I ask the controller about the desired state?

    How can i make the button work on the node but report the state change to the controller?

    can i just kill the code where i update von eeprom?

    I would be greatfull for any indication!

    I will post my own attemps as soon as I figured out what i am doing ☺ 😂

    Thanks Philipp



  • Based on the great sketch korttoma posted here, the following could work:

    // Enable debug prints to serial monitor
    #define MY_DEBUG
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    
    #define SN "RelayButtonArray"
    #define SV "1.0"
    
    #include <MySensors.h>
    #include <SPI.h>
    #include <Bounce2.h>
    #define RELAY_ON 0                      // switch around for ACTIVE LOW / ACTIVE HIGH relay
    #define RELAY_OFF 1
    //
    
    #define noRelays 4                     //2-4
    const int relayPin[] = {14, 15, 16, 17};       //  switch around pins to your desire
    const int buttonPin[] = {6, 7, 4, 5};   //  switch around pins to your desire
    
    class Relay             // relay class, store all relevant data (equivalent to struct)
    {
      public:
        int buttonPin;                   // physical pin number of button
        int relayPin;             // physical pin number of relay
        boolean relayState;               // relay status (also stored in EEPROM)
    };
    
    Relay Relays[noRelays];
    Bounce debouncer[noRelays];
    MyMessage msg[noRelays];
    
    /*
      void before() {
      for (int sensor=1, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) {
        // Then set relay pins in output mode
        pinMode(pin, OUTPUT);
        // Set relay to last known state (using eeprom storage)
            digitalWrite(pin, RELAY_OFF); //digitalWrite(pin, loadState(sensor)?RELAY_ON:RELAY_OFF);
      }
      }*/
    
    void setup() {
      wait(100);
      // Initialize Relays with corresponding buttons
      for (int i = 0; i < noRelays; i++) {
        Relays[i].buttonPin = buttonPin[i];              // assign physical pins
        Relays[i].relayPin = relayPin[i];
        msg[i].sensor = i;                                   // initialize messages
        msg[i].type = V_LIGHT;
        pinMode(Relays[i].buttonPin, INPUT_PULLUP);
        wait(100);
        pinMode(Relays[i].relayPin, OUTPUT);
        //Relays[i].relayState = loadState(i);                               // retrieve last values from EEPROM
    	// digitalWrite(Relays[i].relayPin, Relays[i].relayState ? RELAY_ON : RELAY_OFF); // and set relays accordingly
        //send(msg[i].set(Relays[i].relayState ? true : false));                 // make controller aware of last status
        wait(50);
        debouncer[i] = Bounce();                        // initialize debouncer
        debouncer[i].attach(buttonPin[i]);
        debouncer[i].interval(30);
        wait(50);
      }
    }
    
    void presentation()
    {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo(SN, SV);
    
      wait(100);
    
      for (int i = 0; i < noRelays; i++)
        present(i, S_LIGHT);                               // present sensor to gateway
        request(i, V_LIGHT); //...not sure if this is the right syntax
      wait(100);
    }
    
    
    void loop()
    {
      for (byte i = 0; i < noRelays; i++) {
        if (debouncer[i].update()) {
          
          int value = debouncer[i].read();
          
          if ( value == LOW) {
            Relays[i].relayState = !Relays[i].relayState;
            digitalWrite(Relays[i].relayPin, Relays[i].relayState ? RELAY_ON : RELAY_OFF);
            send(msg[i].set(Relays[i].relayState ? true : false));
            // save sensor state in EEPROM (location == sensor number)
            //saveState( i, Relays[i].relayState );
    
          }
    
        }
      }
      //wait(20);
    }
    
    void receive(const MyMessage &message) {
      if (message.type == V_LIGHT) {
        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)
        }
      }
      wait(20);
    }
    

    I tried to just comment out things not needed, so you might find it easier to understand the differences between loading from EEPROM and requesting the state from controller side.



  • @rejoe2 THANKS !!!! I Will post again with edited sketch... but will take some time! Thanks alot!



  • Think I got the button now working on the node and just informing the controler, cant figure out to ask the controller for the state on startup and how to send periodicly the relay state

    void presentation()  {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("Relay & Button Distance", "1.0");
    
      // Register all sensors to gw (they will be created as child devices)
      present(CHILD_ID_RELAY, S_LIGHT);
      present(CHILD_ID_DIST, S_DISTANCE);  
    
      // Set relay to last known state (using eeprom storage) 
      state = loadState(CHILD_ID_RELAY);
      digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
      
      metric = getControllerConfig().isMetric;
    }
    
    /*
    *  Example on how to asynchronously check for new messages from gw
    */
    void loop() 
    {
      debouncer.update();
      // Get the update value
      int value = debouncer.read();
      if (value != oldValue && value==0) {
          digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF); //realy state change on the node?
          send(msgRelay.set(state?false:true)); // Send new state 
      }
      oldValue = value;
    
    int dist = metric?sonar.ping_cm():sonar.ping_in();
      Serial.print("Ping: ");
      Serial.print(dist); // Convert ping time to distance in cm and print result (0 = outside set distance range)
      Serial.println(metric?" cm":" in");
    
      if (dist != lastDist) {
          send(msgRelay.set(state?false:true), true); //Do I send relay status here?
          send(msgDist.set(dist));
          lastDist = dist;
      }
    
      sleep(SLEEP_TIME);
    } 
    
     
    void receive(const MyMessage &message) {
      // We only expect one type of message from controller. But we better check anyway.
      if (message.isAck()) {
         Serial.println("This is an ack from gateway");
      }
    
      if (message.type == V_LIGHT) {
         // Change relay state
         state = message.getBool();
         digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
         // Store state in eeprom
         saveState(CHILD_ID_RELAY, state);
    


  • Asking the controller at startup: issue a request, see https://www.mysensors.org/download/sensor_api_20#requesting-data, imo you should as for V_STATUS.

    Then your controller should respond, the response is handled by the receive()-function (=no action needed), but to get this to work, your node should not sleep (you sketch seems to be incomplete wrt. that).
    But you should first switch off everyhing to have it in a defined state and avoid loading values from the EEPROM.

    Wrt. to regular status info: If that's necessary, you should try to use a so called "non blocking loop", use g**gle or similar for that. Imo, it's not necessary to update the value as it doesn't change. Better use the heartbeat funcionality instead (dependend on your controller).


 

283
Online

8.8k
Users

9.5k
Topics

100.2k
Posts