Vorstellung DanBus RS485

So nach langem kämpfen wenig Zeit und einiger Probleme kommt nun eine Teil-Vorstellung meines TeilHomeAutomation System bzw der ersten Teile die Funtkionieren

Ziel ist eine intelligenten Steuerung der Wärmepumpe bzw der Fußbodenheizung.
Da die Fußbodenheizung (in unserem Fall Niedertemperatur Heizung) extremst träge sind 2-4h kommt es an sonnigen Tagen in der "Übergangszeit" oft zum über heizen der Räume, da Raumregler nur auf aktuelle Raumtemperatur regeln. Ich will der Wärmepumpe schon mit Hilfe von Wettervorhersagen aus dem Netz dazu bringen an zu erwartenden sonnigen Tagen schon eben 4h früher anzufangen nicht so stark zu heizen da in 2-3h starker Sonneneinfall kommen wird und "mit heizt".

Die Kommunkation via RS232 und Wärmepumpe funktioniert schon Teilweise. Die Aufbreitung der Daten aus dem Internet für Wettervorhersage soll ein Raspberry Pi machen mit Phyton etc.

Die Fußbodenheizungsstellventile werden über DanBus-Nodes geregelt.

Als Haupt HMI steht mit ein Siemens KTP600 zur Verfügung das mit Hilfe von Modbus an einem MEGA2560 angeschlossen ist der mein Busmaster wird.
An diesem Arduino MEGA sind zwei MAX485 verbaut da das KTP600 Panel nur Modbus Master sein kann und ich nicht das ganze System auf Modbus laufen lassen wollte.
Der zweite Zweig basiert auf dem RS485 von Nick Gammon (DanBus).
Das System wird nach und nach erweitert incl Garagentor Steuerung (nur Remote schließen) Öffnen nur vor Ort mit NFC oder PIN. Geplant ist keine Anbindung ans Internet (ausser Wetter via RPI und NTP für die uhrzeit).

Teil 1

Teil 2
Die ersten Tests:
Das ist die HMI Basis ein Touch Display auf Siemens Basis mit WinCCflexible programmiert.:

20160820_224855.jpg
20160820_224859.jpg

An diesem Arduino MEGA sind zwei MAX485 verbaut da das KTP600 Panel nur Modbus Master sein kann und ich nicht das ganze System auf Modbus laufen lassen wollte.
Ich hatte lange Zeite Probleme mit einer zuverlässigen Kommunikation auf den beiden RS485 Bussen, was aber letzten Endes nur ein Problem der RS485 Wandlern SN75176 lag. Nachdem tausch auf MAX Wandlern funktioniert es. Warum? keine Ahnung habe leider keinen Logik Analyzer. Ich vermute das es timming Probleme im Chip während des Umschalten ist.
Der zweite Zweig basiert auf dem RS485 von Nick Gammon (DanBus).
Siehe hier:
20160820_224909.jpg

Der Code:

/*
Bus master for DBus System Connected over Modbus RTU(Slave for HMNI) on Siemens HMI System
Serial2 connected to MAX485 to DBus System
Serial1 connected via MAX 485 to MODBUS RTU to HMI
*/

#include <Streaming.h>
#include <RS485_non_blocking.h>
#include <ModbusRtu.h>
#include <INTERVAL.h>

#define TXENMODBUS  2  // TX Enable for MODBUS / Umschaltung fuer den RS485 Chip
#define TXENDBUS 3
#define LEDPin 13
#define interval 5000



// Holding Register fuer den modbus network sharing hier muessen alle Variablen rein fuer das Panel
// Holdinregister
uint16_t HoldingRegister[16] = {0,111,112,113, 114, 0, 0, 0, 9, 10, 11, 12, 13, 0, 1, 0};
unsigned long  lmillis = 0;
uint32_t lmillispoll = 0;
float temp = 0.0;
/**
*  Modbus object declaration
*  u8id : node id = 0 for master, = 1..247 for slave
*  u8serno : serial port (use 0 for Serial)
*  u8txenpin : 0 for RS-232 and USB-FTDI
*               or any pin number > 1 for RS-485
*/
Modbus slave(1, 1, TXENMODBUS); // this is slave @1 and RS-485

int fAvailable ()
{
  return Serial2.available ();
}

int fRead ()
{
  return Serial2.read ();
}

size_t fWrite (const byte what)
{
  Serial2.write (what);
}


RS485 myChannel (fRead, fAvailable, fWrite, 20);


//unsigned long lmillis = 0;
byte runs = 0;




// definieren der Nachricht
struct {
  byte adress;
  byte function;
  byte wert[7];
} message;




void setup() {
  slave.begin(9600);
  pinMode (TXENDBUS, OUTPUT);
  pinMode (LEDPin, OUTPUT);
  myChannel.begin();
  Serial2.begin(9600);  // definieren der Serial1 Geschwindigkeit für die RS485

}

void loop() {
  INTERVAL(200UL) { // Poll alle 100ms sonst l�uft der Puffer voll
    slave.poll( HoldingRegister, 16 ); // Daten Array der vogehaltenen Daten
    lmillispoll = millis();
  }
  INTERVAL(3000UL) {             // readSTW(7) == HIGH Wenn das Sendebit aus der HMI gesetzt
    // Aufbau Daten
    message.wert[0] = HoldingRegister[0];  // Zuweisen der Werte der Holdingregister zu den Daten
    message.wert[1] = HoldingRegister[1];
    message.wert[2] = HoldingRegister[2];
    message.wert[3] = HoldingRegister[3];
	message.wert[4] = HoldingRegister[4];
	message.wert[5] = HoldingRegister[5];
	    sendMessage(2); // Senden der Daten
    setSTW(7,LOW);                    // Rücksetzten des Sendebits
  lmillis=millis();
  }
}

void setSTW( byte Pos, bool val) {
  constrain (Pos, 0, 15);
  bitWrite(HoldingRegister[0], Pos , val);
}

boolean readSTW( byte Pos) {
  constrain (Pos, 0, 15);
  return bitRead(HoldingRegister[0], Pos);
}


void sendMessage(byte ADR) {
  message.adress = ADR;
  digitalWrite (TXENDBUS, HIGH);  // enable sending
  myChannel.sendMsg ((byte *) &message, sizeof message); // Send my Message
  /* while (!(UCSR1A & (1 << UDRE1)))
  UCSR1A |= 1 << TXC1;
  while (!(UCSR1A & (1 << TXC1)));  // Warten bis der Puffer des Serial1 leer ist
  */
  Serial2.flush();
  digitalWrite (TXENDBUS, LOW);  // disable sending
}

Gruß
DerDani

Um seinen Bus zu testen und zu kontrollieren gibt es noch einen Busscanner aufgebaut mit einem Arudino Duemilanove und einm LCD1602 Shield von DFRobot. Er zeigt die Daten auf dem Bus an:20160820_224917.jpg

Hier der Code vom “Scanner”:

#include <SoftwareSerial.h>
#include <RS485_non_blocking.h>
#include <LiquidCrystal.h>
#include <LCDKeypad.h>
#include <Streaming.h>

#define VER 01

#define DriverPin 11
#define RS485TX 3
#define RS485RX 2

SoftwareSerial rs485 (RS485RX, RS485TX);  // receive pin, transmit pin

int runs = 0;
bool heart = false;


int fAvailable ()
{
  return rs485.available ();
}

int fRead ()
{
  return rs485.read ();
}

struct {        // Message struct ist 8 Bytes lang
  byte adress;
  byte function;
  byte wert[7];
} message;

RS485 myChannel (fRead, fAvailable, NULL, 20);


byte ANZEIGE = 0;
unsigned int interval = 2000;			// Intervall f�r den 500ms routine
unsigned long lmillis1, lmillis2  = 0;


LCDKeypad lcd;


void setup()
{
  pinMode (DriverPin, OUTPUT);
  digitalWrite(DriverPin, LOW);
  rs485.begin(9600);  // definieren der Softare Serial Geschwindigkeit
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("DanielBusCheck");
  lcd.setCursor(0, 1);
  lcd.print("zweite Zeile");
  delay(1500);
  lcd.clear();
  myChannel.begin();

}





void loop() {
  if (myChannel.update ()) {
    memset (&message, 0, sizeof message);  // Wird alles mit nullen gfeüllt
    int len = myChannel.getLength ();
    if (len > sizeof message)
      len = sizeof message;
    memcpy (&message, myChannel.getData (), len); // kopieren der der Speicherbereiche in das Messags struct
    lcd.clear();
    lcd.print("DataInput");

  }  // end of message completely received
  if (millis() - lmillis1 >= interval) {
    if ((runs % 2) == 0) {
      PrintLCD(1);
    }
    else
      PrintLCD(2);
    lmillis1 = millis();
    ++runs;
  }


}

void PrintLCD(byte Seite) {
  lcd.clear();
  switch (Seite) {
    case 1:
      lcd.setCursor(0, 0);
      lcd.print( "ADR_FNC_VA0_VA1_");
      lcd.setCursor(2, 1);
      lcd.print (message.adress);
      lcd.setCursor(4, 1);
      lcd.print (message.function);
      lcd.setCursor(8, 1);
      lcd.print(message.wert[0]);
      lcd.setCursor(12, 1);
      lcd.print(message.wert[1]);
      break;
    case 2:
      lcd.setCursor(0, 0);
      lcd.print("VA2_VA3_VA4_VA5_");
      lcd.setCursor(0, 1);
      lcd.print(message.wert[2]);
      lcd.setCursor(4, 1);
      lcd.print(message.wert[3]);
      lcd.setCursor(8, 1);
      lcd.print(message.wert[4]);
      lcd.setCursor(12, 1);
      lcd.print(message.wert[5]);
      break;
  }
}

Gruß
DerDAni

Und hier ist der zweite Node ein Arduino MEGA1280 der nur mit einem Blinkenlight Shield versehen ist um die Zustände sichtbar zumachen die über das HMI gemacht werden. Natürlich auch mit einem RS485 Wandler
Die Ausgänge 3-9 sind Digital und 10-13 PWM:
20160820_224913.jpg

Die hier ist der Code vom Node:

/* These Code for recieveing Data form DBus System
this sketch is for Arduino Mega2560/1280 with more Hardware Serial Connections


*/


#include <RS485_non_blocking.h>
#include <Streaming.h>



#define DriverPinDB 22
#define LedPin 13
#define interval 2000
#define myAdress 2

#define debug

unsigned long lmillis = 0;
#ifdef debug
unsigned long lmillisDebug = 0;
#endif


// SoftwareSerial rs485 (RS485RX, RS485TX);  // receive pin, transmit pin only for Software Serial Port on Uno or equal


int fAvailable ()
{
  return Serial1.available();
}

int fRead ()
{
  return Serial1.read();
}



RS485 myChannel (fRead, fAvailable, NULL, 20);

// definieren Nachricht
struct {
  byte adress;
  byte function;
  byte wert[7];
} message;




void setup() {
  pinMode (DriverPinDB, OUTPUT);
  digitalWrite(DriverPinDB, LOW);
  myChannel.begin();
  for (int i=2; i<=12;i++){
    pinMode(i,OUTPUT);
  }
  Serial1.begin(9600);  // define Speed of Serial1 on Arduino MEGA
  #ifdef debug
  Serial.begin(9600);  // only for debug
  #endif
  // put your setup code here, to run once:
  Serial << "Init" << endl;

}

void loop() {
  // put your main code here, to run repeatedly:
  if (myChannel.update ())
  {
    Serial << "DataInput" << endl;
    memset (&message, 0, sizeof message);  // Wird alles mit nullen gfeüllt
    int len = myChannel.getLength();
    if (len > sizeof message){
      len = sizeof message;
    }
    memcpy (&message, myChannel.getData (), len); // kopieren der der Speicherbereiche in das Messags struct
    if (message.adress==2){
      digitalWrite(3, bitRead(message.wert[0], 0)); // Digital schreiben der einzelenen Daten auf die Ausg�nge des Display
      digitalWrite(4, bitRead(message.wert[0], 1));
      digitalWrite(5, bitRead(message.wert[0], 2));
      digitalWrite(6, bitRead(message.wert[0], 3));
      digitalWrite(7, bitRead(message.wert[0], 4));
      digitalWrite(8, bitRead(message.wert[0], 5));
      digitalWrite(9, bitRead(message.wert[0], 6));
      analogWrite(10, message.wert[1]);     // Schreiben der Analogwerte auf das Display
      analogWrite(11, message.wert[2]);
      analogWrite(12, message.wert[3]);
      analogWrite(13, message.wert[4]);
      #ifdef debug
      debugSerialPrint();
      #endif

    } // end of message completely received
  }
}


void debugSerialPrint() {  // Testausgabe der Empfangenen Daten
  Serial << "Adresse: " << message.adress << " Funktion:" << message.function << " Wert1:" << message.wert[0] <<
  " Wert2:" << message.wert[1] << " Wert3:" << message.wert[2] << " Wert4:" << message.wert[3] << " Wert5:" << message.wert[4] <<
  " Wert6:" << message.wert[5] <<" Wert7:" << message.wert[6] <<" Wert3:" << message.wert[7] << endl;
}

Das ist meine aktuelle Teil Vorstellung da ich seit langer Zeit dran bin und mir heute der durchburch mit RS485 “Chips” gelungen ist wollte ich es hier schonmal einstellen.

Der Nächste Schritt ist die “aktuellen” Einstellungen der HMI in einem I2C NV-RAM sichern und bei neustart laden. Desweitern kommt noch eine Einbindung eine NEXTION LCD Display als “wireless” Node via ESP8266 Bridge.
Und noch vor Weihnachten eine Schaltuhr für die ganzen Lämpchen mit Hilfe von RC Steckdosen die über einen Node gesteuert werden.
Ich werde hier dann bei weitern Schritten weiter berichten.
Gruß
DerDani

Das sieht klasse aus, bist ja schon sehr weit gekommen! Respekt! :o
Woher weißt du wie du mit dem HMI kommunizieren musst?

ModbusRTU kann auch ein Siemens Panel aber wie gesagt nur als Master. Ich arbeite nur mit Holding Register
Gruß
DerDani

Musst du da im WinCC was spezielles beachten?

Es sind ein paar Kleinigkeiten zu beachten. Es muss eine AEG Modicon vorgewählt werden zuverlassig lief nur 9600baud und die Variablen nur bei bedarf aktualisiern und nicht Zyklisch weil der Arduino Puffer im HardwareSerial sonst vollläuft. Und ich habe nur mit Holdingregister gearbeitet bzw da nur die besten Ergebnisse gehabt. Beim Entwickeln der Kommunikation zwischen Arduino und HMI habe ich geschätzte 100 Male das Projekt übertragen um alles richtig zu etablieren
Ich kann das Projekt mal irgendwo hosten weil hier ja Größenbeschränkung der Attachments ist.
Gruß
DerDani

Das glaub ich sofort, auf Anhieb funktioniert da sicher nichts, hast sicher schon einige Stunden verbraten. :confused:

Fürs hosten wäre ich sehr dankbar, ich hätte noch ein TP177 zum testen evtl lässt sich das so übertragen.

Danke und Gruß

Und warum kein Modbus?

Das KTP600 läßt sich ohne Probleme in das TP177 Portieren. Ich habe ja mit dem TP177 angefangen.

Das Panel Modbus Master sein muss und ich so die Funktionen selber bestimmen kann. So muss ich überall Modubs Register vorhalten etc. Auxh bei den eignen Nodes.

Gruß
DerDani

Hallo Dani,

haste es jetzt vollenden können. Sehr schön. :slight_smile:

@Doc
Vollenden ist noch etwas zu optimistisch aber riesige Schritte vorwärts.....

Hallo Dani,

ich glaube "fertig" wird man nie ganz werden :slight_smile: Hauptsache es geht vorwärts und in die richtige Richtung.

Fertig ist man nie, aber ich würde dein Projekt schon als Meilenstein betrachten! :slight_smile: