MySensors + Mega 2560 + DS18B20 + Relay

Pracą kotłowni zarządza Arduino Mega 2560. Próbuję zintegrować to z Home Assistantem przy pomocy MySensors. Chciałbym mieć podgląd temperatur z czujników podłączonych do Arduino, oraz sterować przekaźnikami, które chciałbym dołożyć. Korzystając z instrukcji na stronie Ethernet Gateway połączyłem systemy przy pomocy karty sieciowej W5100. Zmodyfikowałem sketch Temperature Sensor i udało mi się odczytac parametry czujników w HA. Po zaadaptowaniu sketchu Example with button uzyskałem możliwość sterowania z poziomu HA przekaźnikiem podpiętym do Arduino. Oba sketche indywidualnie działają bez problemów. Gdy każdy z nich indywidualnie zintegruje ze sketchem sterującym kotłownią również wszystko działa. Nie mogę sobie jednak poradzić z połączeniem odczytu temperatur i sterowania przekaźnikiem w jednym programie. Próbowałem różnych modyfikacji, jednak ogarniam to na poziomie kopiuj-wklej i albo program się nie kompiluje, albo czujniki i przekaźniki nie są widoczne w HA.

Sketch wygląda tak:

#define MY_DEBUG // Enable debug prints to serial monitor
#define MY_GATEWAY_W5100 // Enable gateway ethernet module type
#define MY_IP_ADDRESS 192,168,68,130 // Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP)
#define MY_PORT 5003 // The port to keep open on node server mode / or port to contact in client mode
#define MY_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
#define MY_INCLUSION_MODE_FEATURE // Enable inclusion mode
#define MY_INCLUSION_MODE_DURATION 60 // Set inclusion mode duration (in seconds)
#define MY_DEFAULT_LED_BLINK_PERIOD 300 // Set blinking period
#define MY_REPEATER_FEATURE

#if defined(MY_USE_UDP)
#include <EthernetUdp.h>
#endif
#include <Ethernet.h>
#include <MySensors.h>
#include <Bounce2.h>
#include <DallasTemperature.h>
#include <OneWire.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 1   // Id of the sensor child
#define RELAY_ON 0
#define RELAY_OFF 1
#define COMPARE_TEMP 1 // Send temperature only if changed? 1 = Yes 0 = No
#define ONE_WIRE_BUS A5 // Pin where dallase sensor is connected 
#define MAX_ATTACHED_DS18B20 16
#define LED1pin 13 // LED1 do pinu 13

Bounce debouncer = Bounce(); 
int oldValue=0;
bool state;

MyMessage msg(CHILD_ID,V_LIGHT);
MyMessage msg(0,V_TEMP); // Initialize temperature message

unsigned long SLEEP_TIME = 3000; // Sleep time between reads (in milliseconds)

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. 

DeviceAddress Koc = { 0x28, 0x9E, 0xB1, 0xB5, 0xD, 0x0, 0x0, 0x3E };
DeviceAddress Kocp = { 0x28, 0xFF, 0x25, 0x96, 0x28, 0x20, 0x1B, 0x47 };
DeviceAddress Buf1 = { 0x28, 0xFF, 0xF7, 0x95, 0x28, 0x20, 0x1B, 0x12 };

float lastTemperature[MAX_ATTACHED_DS18B20];
int numSensors=0;
bool receivedConfig = false;
bool metric = true;


void before()

{
  sensors.begin(); // Startup up the OneWire library
}

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(5);

  // 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);
  digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
}

void presentation()  {
  // Send the sketch version information to the gateway and Controller
  sendSketchInfo("Relay & Button", "1.0");
  numSensors = sensors.getDeviceCount(); // Fetch the number of attached temperature sensors 
  for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) // Present all sensors to controller
  {   
     present(i, S_TEMP);
  }

  // Register all sensors to gw (they will be created as child devices)
  present(CHILD_ID, S_LIGHT);
}

/*
*  Example on how to asynchronously check for new messages from gw
*/
void loop() 
{
  sensors.requestTemperatures(); // Fetch temperatures from Dallas sensors
  debouncer.update();
  // Get the update value
  int value = debouncer.read();
  if (value != oldValue && value==0) {
      send(msg.set(state?false:true), true); // Send new state and request ack back
  }
  oldValue = value;
    int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());  // query conversion time and sleep until conversion completed
  sleep(conversionTime); // sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)
  for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) // Read temperatures and send them to controller 
  {
    float temperature = static_cast<float>(static_cast<int>((getControllerConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; // Fetch and round temperature to one decimal
    // 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(msg.setSensor(i).set(temperature,1)); // Send in the new temperature
      lastTemperature[i]=temperature;  // Save new temperatures for next compare
    }
  }
  
  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, state);
    
     // Write some debug info
     Serial.print("Incoming change for sensor:");
     Serial.print(message.sensor);
     Serial.print(", New status: ");
     Serial.println(message.getBool());
   } 
}

Przy próbie kompilacji pojawiają się takie błędy:



MySensors_przekaznik_czujnik:36:14: error: redefinition of 'MyMessage msg'

C:\Users\Paweł\Desktop\MySensors\MySensors_przekaznik_czujnik\MySensors_przekaznik_czujnik.ino:35:11: note: 'MyMessage msg' previously declared here

exit status 1

redefinition of 'MyMessage msg'


Rozumiem, ze parametr “MyMessage msg” może być użyty tylko raz, jednak sketch dla czujników temperatury ma inne wartości i dla przekaźników inne. Jak to poprawnie połączyć?

…gdyż ponieważ pierwsza linijka present tworzy czujniki i ID = 0,1,2
druga present próbuje stworzyć czujnik CHILD_ID =1 - czyli już zajęty.

Rozwiązaniem mogłoby być zmiana #define CHILD_ID 3 - nie eleganckie ale doraźnie skuteczne.
Podobnie tu jest

Trzeba użyć unikalnych nazw.
np. wszystko w kodzie co dotyczy temperatur przemianuj z msg na msgT
Reszty nie analizowałem, bo może już nie być takiej potrzeby :wink:

edit… mały przykład dla lepszego zrozumienia

#define TZew_CHILD  0
#define TCO_CHILD   1
#define TCUW1_CHILD 2
#define TCUW2_CHILD 3
#define TZECO_CHILD  4
#define TZNORM_CHILD 5
#define TZCUW_CHILD 6

MyMessage TZewMsg(TZew_CHILD, V_TEMP);
MyMessage TCOMsg(TCO_CHILD, V_TEMP);
MyMessage TCUW1Msg(TCUW1_CHILD, V_TEMP);
MyMessage TCUW2Msg(TCUW2_CHILD, V_TEMP);
MyMessage TZECOMsg(TZECO_CHILD, V_TEMP);
MyMessage TZNORMMsg(TZNORM_CHILD, V_TEMP);
MyMessage TZCUWMsg(TZCUW_CHILD, V_TEMP);


void presentation()
{


    present(TZew_CHILD, S_TEMP,"Temp_Zewn");
    present(TCO_CHILD, S_TEMP,"Temp_CO");
    present(TCUW1_CHILD, S_TEMP,"Temp_CWU1");
    present(TCUW2_CHILD, S_TEMP,"Temp_CWU2");
    present(TZECO_CHILD, S_TEMP,"Zad_ECO");
    present(TZNORM_CHILD, S_TEMP,"Zad_NORM");
    present(TZCUW_CHILD, S_TEMP,"Zad_CWU");

@pawelj5 raptem masz 4 czujniki (3 DS i 1 przekaźnik)… jak nie ogarniasz działania tych pętli to zrób to sekwencyjnie na “piechotę” - będzie prościej. Na początek utwórz jeden i kolejno (nawet poprzez kopiowanie) kolejne.

Dziękuję RobinI30 za poświecony czas i wytłumaczenie jak to funkcjonuje. Czujników i przekaźników jest więcej. W przykładzie podałem 3 czujniki i 1 przekaźnik, bo na takim zestawie testuję wszystko “na stole”. Wygląda na to, że teraz program się kompiluje, ale poprawność działania sprawdzę dopiero wieczorem po pracy.

Edit:

Spędziłem nad tym już kilkanaście godzin i przerosło mnie to. Nie potrafię utworzyć działającego programu według powyższych wskazówek. Mam 8 przekaźników i 8 czujników temperatury. Kompilacja dla 8 przekaźników która działa u mnie poprawnie tworzy w integracji MySensors przekaźniki od 1 do 8. Kompilacja dla czujników temperatury też działa bez zarzutów i tworzy czujniki o numeracji od 0 do 7. Jeżeli próbuję je połączyć otrzymuję w MySensors przekaźniki o numeracji od 1 do 8 i czujnik o numerze 0, pozostałe czujniki nie są widoczne. Jak przerobić poniższą kompilację aby zamiast czujników od 0 do 7 tworzyła czujniki od numeru 10 do 18?

#define MY_DEBUG
#define MY_GATEWAY_W5100 // Enable gateway ethernet module type
#define MY_PORT 5003
#define MY_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
#define MY_INCLUSION_MODE_FEATURE
#define MY_INCLUSION_MODE_DURATION 60
#define MY_DEFAULT_LED_BLINK_PERIOD 300

#include <Ethernet.h>
#include <SPI.h>
#include <MySensors.h>  
#include <DallasTemperature.h>
#include <OneWire.h>

#define COMPARE_TEMP 1 // Send temperature only if changed? 1 = Yes 0 = No
#define ONE_WIRE_BUS A9 // Pin where dallase sensor is connected 
#define MAX_ATTACHED_DS18B20 16

unsigned long SLEEP_TIME = 3000; // Sleep time between reads (in milliseconds)

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];
int numSensors=0;
bool receivedConfig = false;
bool metric = true;

// Initialize temperature message
MyMessage msg(0,V_TEMP);

void before()
{
  // Startup up the OneWire library
  sensors.begin();
}

void setup()  
{ 
  // requestTemperatures() will not block current thread
  sensors.setWaitForConversion(false);
}

void presentation() {
  // Send the sketch version information to the gateway and Controller
  sendSketchInfo("Temperature Sensor", "1.1");

  // Fetch the number of attached temperature sensors  
  numSensors = sensors.getDeviceCount();

  // Present all sensors to controller
  for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {   
     present(i, S_TEMP);
  }
}

void loop()     
{     
  // Fetch temperatures from Dallas sensors
  sensors.requestTemperatures();

  // query conversion time and sleep until conversion completed
  int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());
  // 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<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
 
    // Fetch and round temperature to one decimal
    float temperature = static_cast<float>(static_cast<int>((getControllerConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(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(msg.setSensor(i).set(temperature,1));
      // Save new temperatures for next compare
      lastTemperature[i]=temperature;
    }
  }
  sleep(SLEEP_TIME);
}

Nie udało mi się wygenerować stabilnego kodu, który umożliwiałby odczyt temperatury z czujników i sterowanie przekaźnikami podłączonymi do arduino. Ostatecznie zrezygnowałem ze sterowania przekaźnikami. HA odczytuje tylko temperatury z czujników podłączonych do arduino i steruje przekaźnikami Sonoff.
Teraz chciałbym uzyskać możliwość odczytu w HA czujnika temperatury spalin podłączonego do arduino poprzez sterownik MAX6675. Na stronie MySensors nic nie znalazłem. Ktoś z forumowiczów stosuje takie rozwiązanie?