RS485 Gatway with relay and DS18B20 sensors



  • Hello.
    I built a MySensor. It has 4 relays and 4 temperature sensors.

    /**
       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.
     *******************************
       DESCRIPTION
       Example sketch showing how to send in DS1820B OneWire temperature readings back to the controller
       http://www.mysensors.org/build/temp
       Enhanced Version to keep ChildIDs fix based on petewill's http://forum.mysensors.org/topic/2607/video-how-to-monitor-your-refrigerator.
      - Present Dallas-Hardware-ID as comment
    */
    
    // Enable debug prints to serial monitor
    //#define MY_DEBUG
    
    // Enable to print out an array of the attached DallasSensors to Serial
    // comment this after initial setup and adopt MAX_ATTACHED_DS18B20 accordingly
    //#define PRINT_ARRAY
    
    // Enable to print out an array of the attached DallasSensors to Serial
    // comment this after initial setup and adopt MAX_ATTACHED_DS18B20 accordingly
    #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 9600 //19200
    
    // Enable this if RS485 is connected to a hardware serial port
    //#define MY_RS485_HWSERIAL Serial1
    
    // Enable serial gateway
    //#define MY_GATEWAY_SERIAL
    
    #define MY_NODE_ID 10
    #define SN "Heizung_OG"
    #define SV "1.0"
    
    //#include <SPI.h>
    #include <MySensors.h>
    #include <DallasTemperature.h>
    #include <OneWire.h>
    
    //Parameter für 1-wire DS18B20
    //#define COMPARE_TEMP 1 // Send temperature only if changed?
    #define ERASE_HASH // Clear EEPROM, if no 1w-device is present?
    #define ONE_WIRE_BUS 3 // Pin where dallase sensor is connected
    #define MAX_ATTACHED_DS18B20 4
    uint8_t DS_First_Child_ID = 1; //First Child-ID to be used by Dallas Bus; set this to be higher than other Child-ID's who need EEPROM storage to avoid conflicts
    OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
    DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature.
    float lastTemperature[MAX_ATTACHED_DS18B20];
    //unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
    unsigned long TempSendFreqency = 60000; // Sleep time between reads (in milliseconds)
    unsigned long lastTemp = 0;
    boolean metric = true;
    
    // Initialize temperature message
    MyMessage DallasMsg(0, V_TEMP);
    MyMessage msgId(0, V_ID);
    
    #ifdef PRINT_ARRAY
    DeviceAddress dallasAddresses[8];
    #else
    /* Tabelle von Testsensoren
    DeviceAddress dallasAddresses[] = {
      {0x28, 0x8D, 0x4F, 0xC0, 0x06, 0x00, 0x00, 0xD0}, // Position Oben
      {0x28, 0xDD, 0x0D, 0xC0, 0x06, 0x00, 0x00, 0x94}  // Position
    };
    */
    DeviceAddress dallasAddresses[] = {
      {0x28, 0xFF, 0x04, 0x22, 0xA1, 0x16, 0x03, 0xD9},  // Position Oben
      {0x28, 0xFF, 0xE2, 0xA4, 0xA1, 0x16, 0x03, 0x13},  // Position 3/4
      {0x28, 0xFF, 0x16, 0x20, 0xA1, 0x16, 0x03, 0x21},  // Position 2/4
      {0x28, 0xFF, 0xA2, 0xE3, 0xA0, 0x16, 0x04, 0xE8}   // Position Unten
    };
    
    #endif
    
    int  resolution = 10;
    int  conversionTime = 750;
    
    //Parameter fuer Schaltkreise
    #define noRelays 4                     // Anzahl der Schaltkreise
    const int relayPin[] = {A0, A1, A2, A3};       //  switch around pins to your desire
    //#define RELAY_PIN 4  // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
    //#define NUMBER_OF_RELAYS 1 // Total number of attached relays
    #define RELAY_ON 1  // GPIO value to write to turn on attached relay
    #define RELAY_OFF 0 // GPIO value to write to turn off attached relay
    
    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)
    };
    
    Relay Relays[noRelays];
    MyMessage msg[noRelays];
    
    void before()
    {
      conversionTime = 750 / (1 << (12 - resolution));
      // Startup up the OneWire library
      sensors.begin();
      // requestTemperatures() will not block current thread
      sensors.setWaitForConversion(false);
      // Fetch the number of attached temperature sensors
      //numSensors = sensors.getDeviceCount();
      #ifdef PRINT_ARRAY
      printAddressArray();
      #endif
    }
    
    void setup()
    {
      wait(100);
      // Initialize Relays with corresponding buttons
      for (int i = 0; i < noRelays; i++) {
        Relays[i].relayPin = relayPin[i];
        msg[i].sensor = i;                                   // initialize messages
        msg[i].type = V_LIGHT;
        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);
      }
      // Senden der MAC Adressen der gefundenen Sensoren
      for (int i = 0; i < MAX_ATTACHED_DS18B20; i++) {
          send(msgId.setSensor(DS_First_Child_ID + i).set(dallasAddresses[i], 8));
      }
      metric = getControllerConfig().isMetric;
    }
    
    void presentation()
    {
        // Send the sketch version information to the gateway and Controller
        sendSketchInfo(SN, SV);
        wait(100);
        // present sensor to gateway
        for (int i = 0; i < noRelays; i++)
          present(i, S_LIGHT);
        wait(100);
        // Register all sensors to gw (they will be created as child devices)
        // Fetch the number of attached temperature sensors
        // Present all sensors to controller
        for (int i = 0; i < MAX_ATTACHED_DS18B20; i++) {
          present(DS_First_Child_ID + i, S_TEMP);
        }
    }
    
    
    void loop()
    {
        unsigned long vergangeneZeit = millis();
        if (vergangeneZeit - lastTemp > TempSendFreqency) {
        // Fetch temperatures from Dallas sensors
        sensors.requestTemperatures();
        // sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)
        sleep(conversionTime);
        // Read temperatures and send them to controller
        for (int i = 0; i < MAX_ATTACHED_DS18B20; i++) {
          // Fetch and round temperature to one decimal; original method uses "sensors.getTempCByIndex(i)"
          float temperature = static_cast<float>(static_cast<int>((metric ? sensors.getTempC(dallasAddresses[i]) : sensors.getTempF(dallasAddresses[i])) * 10.)) / 10.;
    
          // Only send data if temperature has changed and no error
          //#if COMPARE_TEMP == 1
            if (lastTemperature[i] != temperature && temperature != -127.00 && temperature != 85.00) {
              //#else
              //if (temperature != -127.00 && temperature != 85.00) {
          //#endif
                // Send in the new temperature
                send(DallasMsg.setSensor(i + DS_First_Child_ID).set(temperature, 1));
                // Save new temperatures for next compare
                lastTemperature[i] = temperature;
              }
          }
          lastTemp = millis();
        }
      }
    
    
    
    
    
    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
            send(msg[message.sensor].set(Relays[message.sensor].relayState ? true : false));
            saveState( message.sensor, Relays[message.sensor].relayState ); // save sensor state in EEPROM (location == sensor number)
          }
        }
        wait(20);
    }
    
    

    The Gatway is controlled by the FHEM.

    My problem is, if the temperature is transmitted by the Gatway and at the same time one of the relays is switched, it says in the fhem that the relay is ON, but the relay on the Getway is OFF.

    How can I get the relay safely on or off?
    Thank you!



  • Three remarks on that:

    • Avoid doubleing ChildID's for temperature and relays (1-3 are "dual-functional", DS_First_Child_ID = 4 would help)
    • Use wait(conversionTime) instead of sleep(conversionTime); millis will otherwise be reset.
    • Better use FHEM's options to request an ACK from the Node; so state will not be update on controller side until ACK is sent from Node. (send(msg[message.sensor].set(Relays[message.sensor].relayState ? true : false)); is not needed; in FHEM: attr <MYSENSOR_0?> requestAck)


  • Thank you for your support!
    It now works without losing some switching commands.

    I have adapted the Gateway software according to your suggestions.
    And in the fhem, I defined the devices as follows:

    define MYSENSOR_10 MYSENSORS_DEVICE 10
    setuuid MYSENSOR_10 5c61b043-f33f-3ee4-4a0b-da4876cebf4faa94
    attr MYSENSOR_10 IODev meinMSG
    attr MYSENSOR_10 mapReading_id4 4 id
    attr MYSENSOR_10 mapReading_id5 5 id
    attr MYSENSOR_10 mapReading_id6 6 id
    attr MYSENSOR_10 mapReading_id7 7 id
    attr MYSENSOR_10 mapReading_power 0 power
    attr MYSENSOR_10 mapReading_power1 1 power
    attr MYSENSOR_10 mapReading_power2 2 power
    attr MYSENSOR_10 mapReading_power3 3 power
    attr MYSENSOR_10 mapReading_status 0 status
    attr MYSENSOR_10 mapReading_status1 1 status
    attr MYSENSOR_10 mapReading_status2 2 status
    attr MYSENSOR_10 mapReading_status3 3 status
    attr MYSENSOR_10 mapReading_temperature4 4 temperature
    attr MYSENSOR_10 mapReading_temperature5 5 temperature
    attr MYSENSOR_10 mapReading_temperature6 6 temperature
    attr MYSENSOR_10 mapReading_temperature7 7 temperature
    attr MYSENSOR_10 mode node
    attr MYSENSOR_10 requestAck 1
    attr MYSENSOR_10 room Heizung_OG
    attr MYSENSOR_10 setReading_power 1
    attr MYSENSOR_10 setReading_power1 1
    attr MYSENSOR_10 setReading_power2 1
    attr MYSENSOR_10 setReading_power3 1
    attr MYSENSOR_10 setReading_status off,on
    attr MYSENSOR_10 setReading_status1 off,on
    attr MYSENSOR_10 setReading_status2 off,on
    attr MYSENSOR_10 setReading_status3 off,on
    
    

    To control the relays individually, I defined a "readingsProxy" for each relay:

    define Relais1 readingsProxy MYSENSOR_10:status
    attr Relais1 devStateIcon on:sani_heating_level_100@red:off off:sani_heating_level_0@#18B8ED:on
    attr Relais1 group Heizungskreise,
    attr Relais1 room Heizung_OG
    attr Relais1 setFn {($CMD eq "on")?"status on":"status off"}
    attr Relais1 setList on off
    
    

    Thank you!


Log in to reply
 

Suggested Topics

  • 8
  • 90
  • 2
  • 7
  • 1
  • 1

19
Online

11.4k
Users

11.1k
Topics

112.7k
Posts