Probleme mit Arduino Nano als Modbus Slave (Modbusino.h)

Guten Tag alle zusammen,

ich versuche mich gerade an einer Verbindung zwischen einem Arduino Nano und einem Raspberry Pi über Modbus. Die Verbindung wird über ein normales USB Kabel hergestellt, der USB MINI Stecker steckt also am Arduino (Slave), der normale am Raspberry Pi (Master). Auf diesem läuft Codesys, welches per Modbus bestimmte Werte aus dem Arduino ausliest.

Ich nutze die Modbusino.h Bibliothek für die Modbus Kommunikation, als auch die wire.h Bibliothek für das Auslesen der Sensoren (in diesem Fall sind es 4 kapazitive Sensoren, die die Erdfeuchte von Zimmer- oder Balkonpflanzen inkl. Temperatur messen). Es werden daher insgesamt 10 (Integer) Werte übertragen: Register 0-7 sind die Erdfeuchten und Temperaturen, Register 8 hat immer den Testwert "999" und Register 9 kann den Wert 0 oder 100 annehmen (eine Art Watchdog Signal für den Modbus Master).

Soweit funktioniert mein Arduino Programm eigentlich: Wenn ich den Arduino per USB an den PC anschließe und den Serial Monitor betrachte, erhalte ich genau die Sensor Werte, die ich erwarte (ausgegeben werden immer nur die Mittelwerte, sowohl beim SerialMonitor, also auch über Modbus).
Wenn ich den Arduino allerdings an den Raspberry Pi anschließe (Modbusverbindung über USB Kabel), bekomme ich nur die beiden letzten Werte richtig angezeigt (die "999", die nur zu Testzwecken übertragen wird und den xWatchdog).
Die 8 Sensorwerte sind immer nur "0" (Die Werte werden alle 6 Sekunden ausgelesen und mit je 10 Einzelwerten dann gemittelt - daher stehen die ersten Sensor Mittelwerte auch erst nach einer Minute bereit).

Es scheint so, als würden die onewire Sensoren im Arduino nicht ausgelesen, sobald der Raspberry per Modbus auf den Arduino zugreift.
Kann das sein? Und kann man das beheben bzw. optimieren?

Ich habe beim Modbus Master mit verschiedenen Abfrageintervallen herum probiert, zwischen 200ms und 75 Sekunden. Alle paar Sekunden bekommt der Master auch einen Timeout Fehler oder unbekannten Fehler. Die Abfrage funktioniert also auch nicht immer zuverlässig.

Könnt Ihr mir hier bitte helfen, ich finde ansonsten auch keine ähnlichen Probleme mit der Suchfunktion.

Vielen Dank im Voraus und eine schöne Woche!
Speedriff

#include <Wire.h>         // für Tindie Erdfeuchte Sensoren
#include <Modbusino.h>    // Modbus Bib

// Funktion zum Beschreiben bestimmter Sensor Register (aus Beispiel Sketch)
void writeI2CRegister8bit(int addr, int value) {
  Wire.beginTransmission(addr);
  Wire.write(value);
  Wire.endTransmission();
}

// Funktion um Sensoren auszulesen (aus Beispiel Sketch)
unsigned int readI2CRegister16bit(int addr, int reg) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.endTransmission();
  delay(20);
  Wire.requestFrom(addr, 2);
  unsigned int t = Wire.read() << 8;
  t = t | Wire.read();
  return t;
}

// Arrays für die Sensorwerte, die später gemittelt werden (interessant nur [1..10])
int T_Sensor_gelb[11];
int T_Sensor_rot[11];
int T_Sensor_blau[11];
int T_Sensor_gruen[11];
int Erde_Sensor_gelb[11];
int Erde_Sensor_rot[11];
int Erde_Sensor_blau[11];
int Erde_Sensor_gruen[11];

// Berechnete Mittelwerte aus 10 Einzelwerten
int T_Sensor_gelb_mittel;
int T_Sensor_rot_mittel;
int T_Sensor_blau_mittel;
int T_Sensor_gruen_mittel;
int Erde_Sensor_gelb_mittel;
int Erde_Sensor_rot_mittel;
int Erde_Sensor_blau_mittel;
int Erde_Sensor_gruen_mittel;

// Temporäre Variablen, die die Summen der 10 Einzelwerte enthalten
int T_Sensor_gelb_mittel_temp;
int T_Sensor_rot_mittel_temp;
int T_Sensor_blau_mittel_temp;
int T_Sensor_gruen_mittel_temp;
int Erde_Sensor_gelb_mittel_temp;
int Erde_Sensor_rot_mittel_temp;
int Erde_Sensor_blau_mittel_temp;
int Erde_Sensor_gruen_mittel_temp;

// Timer Variablen: Alle 6000ms (6s) finden die Sensorabfragen statt. Nach 10 Abfragen werden die Mittelwerte gebildet und über SerialPrint ausgegeben
unsigned long previousMillis = 0;
const long interval_readingSensors = 6000;
int icounter = 1;
bool xAusgabe;    // Wenn true, dann Ausgabe über SerialPrint

//////////////////////   MODBUS   /////////////////////////

/* Initialize the slave with the ID 1 */
ModbusinoSlave modbusino_slave(1);
/* Allocate a mapping of 10 values */
uint16_t tab_reg[10];


uint16_t xWatchdog;
uint16_t counter;


//////////////////////   VOID SETUP    /////////////////////////
void setup() {
  Wire.begin();
  Serial.begin(9600);

  writeI2CRegister8bit(0x21, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x22, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x23, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x24, 6); //reset Sensor
  delay(1000);

  // Modbus slave configuration parameters
  modbusino_slave.setup(9600);

}

//////////////////////   VOID LOOP    /////////////////////////
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval_readingSensors) {      // Abfrage aller Sensoren alle 6 Sekunden
    previousMillis = currentMillis;

    T_Sensor_gelb[icounter] = readI2CRegister16bit(0x21, 5);
    T_Sensor_rot[icounter] = readI2CRegister16bit(0x22, 5);
    T_Sensor_blau[icounter] = readI2CRegister16bit(0x23, 5);
    T_Sensor_gruen[icounter] = readI2CRegister16bit(0x24, 5);

    Erde_Sensor_gelb[icounter] = readI2CRegister16bit(0x21, 0);
    Erde_Sensor_rot[icounter] = readI2CRegister16bit(0x22, 0);
    Erde_Sensor_blau[icounter] = readI2CRegister16bit(0x23, 0);
    Erde_Sensor_gruen[icounter] = readI2CRegister16bit(0x24, 0);

    icounter = icounter + 1;    // Counter beginnt bei 1 und zählt nacht jedem Auslesevorgang um 1 hoch

    if (icounter == 11) {       // Wenn 10 Abfragen abgearbeitet
      T_Sensor_gelb_mittel_temp = 0;  // Init der Summenvariablen
      T_Sensor_rot_mittel_temp = 0;
      T_Sensor_blau_mittel_temp = 0;
      T_Sensor_gruen_mittel_temp = 0;

      Erde_Sensor_gelb_mittel_temp = 0;
      Erde_Sensor_rot_mittel_temp = 0;
      Erde_Sensor_blau_mittel_temp = 0;
      Erde_Sensor_gruen_mittel_temp = 0;

      // Bildung der Mittelwerte aus je 10 Einzelwerten
      for (int i = 1; i < 11; i++) {
        T_Sensor_gelb_mittel_temp = T_Sensor_gelb_mittel_temp + T_Sensor_gelb[i];
        T_Sensor_rot_mittel_temp = T_Sensor_rot_mittel_temp + T_Sensor_rot[i];
        T_Sensor_blau_mittel_temp = T_Sensor_blau_mittel_temp + T_Sensor_blau[i];
        T_Sensor_gruen_mittel_temp = T_Sensor_gruen_mittel_temp + T_Sensor_gruen[i];

        Erde_Sensor_gelb_mittel_temp = Erde_Sensor_gelb_mittel_temp + Erde_Sensor_gelb[i];
        Erde_Sensor_rot_mittel_temp = Erde_Sensor_rot_mittel_temp + Erde_Sensor_rot[i];
        Erde_Sensor_blau_mittel_temp = Erde_Sensor_blau_mittel_temp + Erde_Sensor_blau[i];
        Erde_Sensor_gruen_mittel_temp = Erde_Sensor_gruen_mittel_temp + Erde_Sensor_gruen[i];
      }

      T_Sensor_gelb_mittel = T_Sensor_gelb_mittel_temp / 10;
      T_Sensor_rot_mittel = T_Sensor_rot_mittel_temp / 10;
      T_Sensor_blau_mittel = T_Sensor_blau_mittel_temp / 10;
      T_Sensor_gruen_mittel = T_Sensor_gruen_mittel_temp / 10;

      Erde_Sensor_gelb_mittel = Erde_Sensor_gelb_mittel_temp / 10;
      Erde_Sensor_rot_mittel = Erde_Sensor_rot_mittel_temp / 10;
      Erde_Sensor_blau_mittel = Erde_Sensor_blau_mittel_temp / 10;
      Erde_Sensor_gruen_mittel = Erde_Sensor_gruen_mittel_temp / 10;

      icounter = 1;   // Zurücksetzen des Counters
      xAusgabe = HIGH;  // Auslöser für die Ausgabe der Sensorwerte über SerialPrint
    }
  }

  if (xAusgabe == HIGH) {
    Serial.print("Feuchtigkeit Erde gelb: ");
    Serial.print(Erde_Sensor_gelb_mittel); //read capacitance register
    Serial.print(", Feuchtigkeit Erde rot: ");
    Serial.print(Erde_Sensor_rot_mittel); //read capacitance register
    Serial.print(", Feuchtigkeit Erde blau: ");
    Serial.print(Erde_Sensor_blau_mittel); //read capacitance register
    Serial.print(", Feuchtigkeit Erde grün: ");
    Serial.print(Erde_Sensor_gruen_mittel); //read capacitance register

    Serial.print("  ||  T gelb: ");
    Serial.print(T_Sensor_gelb_mittel / float(10)); //temperature register
    Serial.print(", T rot: ");
    Serial.print(T_Sensor_rot_mittel / (float)10); //temperature register
    Serial.print(", T blau: ");
    Serial.print(T_Sensor_blau_mittel / float(10)); //temperature register
    Serial.print(", T gruen: ");
    Serial.print(T_Sensor_gruen_mittel / (float)10); //temperature register

    Serial.println();
    xAusgabe = LOW;   // Auslöser zurücksetzen
  }

  counter = counter + 1;    // Counter zählt jeden Programmdurchlauf

  // Bedingung für den Watchdog (der ist nur für den ModBus Master interessant)
  if (counter <= 32000)
  {
    xWatchdog = 0;
  }
  else
  {
    xWatchdog = 100;
  }

  if (counter > 64000)
  {
    counter = 0;
  }

  // Array befüllen, welches über Modbus bereitgestellt wird
  /* Initialize the first register to have a value to read */
  tab_reg[0] = Erde_Sensor_gelb_mittel;          // #0 Sensor gelb Erdfeuchtigkeit
  tab_reg[1] = Erde_Sensor_rot_mittel;           // #1 Sensor rot Erdfeuchtigkeit
  tab_reg[2] = Erde_Sensor_blau_mittel;          // #2 Sensor blau Erdfeuchtigkeit
  tab_reg[3] = Erde_Sensor_gruen_mittel;         // #3 Sensor weiss Erdfeuchtigkeit
  tab_reg[4] = T_Sensor_gelb_mittel;             // #4 Sensor gelb Temperatur
  tab_reg[5] = T_Sensor_rot_mittel;              // #5 Sensor rot Temperatur
  tab_reg[6] = T_Sensor_blau_mittel;             // #6 Sensor blau Temperatur
  tab_reg[7] = T_Sensor_gruen_mittel;            // #7 Sensor weiss Temperatur
  tab_reg[8] = 999;                              // #8 999 - Testwert
  tab_reg[9] = xWatchdog;                        // #9 Watchdog Blinker

  /* Launch Modbus slave loop with:
     - pointer to the mapping
     - max values of mapping */
  modbusino_slave.loop(tab_reg, 10);      // Modbus Slave jeden Programm Durchgang aufrufen

}

Wie soll das funktionieren? Serial soll doch schon Monitorausgaben schreiben, wie kann dann gleichzeitig Modbus darüber laufen?

ich würde einen ganz kleinen Server / Slave sketch machen

die 10 Register zur Verfügung stellen
die 10 Register im setup mit Testwerten befüllen

Im ersten Schritt einfach mal versuchen ob die 10 Register richtig ausgelesen werden können.

Solange das nicht klappt, kann man sich das ganze I2C auslesen sparen. Das lenkt nur ab.

Die Monitorausgaben habe ich nur für den Testbetrieb am PC geschrieben, der Raspberry bzw. Modbus Master braucht die eigentlich nicht.
Meinst Du, ich habe hier ein Problem mit "doppelter Ressourcen Verwendung"?

Du willst einen Modbus RTU-Server betreiben (Slave ist als Begriff out...). Da ist das Timing auf dem Bus ziemlich kritisch und nur protokollgerechte Responses werden von einem Server erwartet, keine sonstigen Daten. Wenn der Client tolerant genug ist, wirft er die Texte achselzuckend weg, aber wenn er gerade auf eine Modbus-Response von Deinem Server wartet, könnte das zum Verpassen der eigentlichen Antwort führen.

Das habe ich nun gemacht:

#include <Modbusino.h>    // Modbus Bib

//////////////////////   MODBUS   /////////////////////////

/* Initialize the slave with the ID 1 */
ModbusinoSlave modbusino_slave(1);
/* Allocate a mapping of 10 values */
uint16_t tab_reg[10];

//////////////////////   VOID SETUP    /////////////////////////
void setup() {
// Modbus slave configuration parameters
modbusino_slave.setup(9600);
}

//////////////////////   VOID LOOP    /////////////////////////
void loop() {
  // Array befüllen, welches über Modbus bereitgestellt wird
  /* Initialize the first register to have a value to read */
  tab_reg[0] = 123; //Erde_Sensor_gelb_mittel;          // #0 Sensor gelb Erdfeuchtigkeit
  tab_reg[1] = 456; //Erde_Sensor_rot_mittel;           // #1 Sensor rot Erdfeuchtigkeit
  tab_reg[2] = 789; //Erde_Sensor_blau_mittel;          // #2 Sensor blau Erdfeuchtigkeit
  tab_reg[3] = 666; //Erde_Sensor_gruen_mittel;         // #3 Sensor weiss Erdfeuchtigkeit
  tab_reg[4] = 1010; //T_Sensor_gelb_mittel;             // #4 Sensor gelb Temperatur
  tab_reg[5] = 1; //T_Sensor_rot_mittel;              // #5 Sensor rot Temperatur
  tab_reg[6] = 10; //T_Sensor_blau_mittel;             // #6 Sensor blau Temperatur
  tab_reg[7] = 100; //T_Sensor_gruen_mittel;            // #7 Sensor weiss Temperatur
  tab_reg[8] = 1000; //999;                              // #8 999 - Testwert
  tab_reg[9] = 10000; //xWatchdog;                        // #9 Watchdog Blinker

  /* Launch Modbus slave loop with:
     - pointer to the mapping
     - max values of mapping */
  modbusino_slave.loop(tab_reg, 10);      // Modbus Slave jeden Programm Durchgang aufrufen

}

Das funktioniert, alle Werte werden ausgelesen, keine Timeout Probleme..

Als nächstes habe ich dann das ursprüngliche Programm wieder benutzt und alles mit "Serial" herausgelöscht (da wohl der Serial-Part von Modbus UND SerialMonitor benutzt wird?):

#include <Wire.h>         // für Tindie Sensor
#include <Modbusino.h>    // Modbus Bib

// Funktion zum Beschreiben bestimmter Sensor Register
void writeI2CRegister8bit(int addr, int value) {
  Wire.beginTransmission(addr);
  Wire.write(value);
  Wire.endTransmission();
}

// Funktion um Sensoren auszulesen
unsigned int readI2CRegister16bit(int addr, int reg) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.endTransmission();
  delay(20);
  Wire.requestFrom(addr, 2);
  unsigned int t = Wire.read() << 8;
  t = t | Wire.read();
  return t;
}

// Arrays für die Sensorwerte, die später gemittelt werden
int T_Sensor_gelb[11];
int T_Sensor_rot[11];
int T_Sensor_blau[11];
int T_Sensor_gruen[11];
int Erde_Sensor_gelb[11];
int Erde_Sensor_rot[11];
int Erde_Sensor_blau[11];
int Erde_Sensor_gruen[11];

// Berechnete Mittelwerte aus 10 Einzelwerten
int T_Sensor_gelb_mittel;
int T_Sensor_rot_mittel;
int T_Sensor_blau_mittel;
int T_Sensor_gruen_mittel;
int Erde_Sensor_gelb_mittel;
int Erde_Sensor_rot_mittel;
int Erde_Sensor_blau_mittel;
int Erde_Sensor_gruen_mittel;

// Temporäre Variablen, die die Summen der Einzelwerte enthalten
int T_Sensor_gelb_mittel_temp;
int T_Sensor_rot_mittel_temp;
int T_Sensor_blau_mittel_temp;
int T_Sensor_gruen_mittel_temp;
int Erde_Sensor_gelb_mittel_temp;
int Erde_Sensor_rot_mittel_temp;
int Erde_Sensor_blau_mittel_temp;
int Erde_Sensor_gruen_mittel_temp;

// Timer Variablen: Alle 6000ms finden die Sensorabfragen statt. Nach 10 Abfragen werden die Mittelwerte gebildet und über SerialPrint ausgegeben
unsigned long previousMillis = 0;
const long interval_readingSensors = 6000; 
int icounter = 1;
bool xAusgabe;    // Wenn true, dann Ausgabe über SerialPrint

//////////////////////   MODBUS   /////////////////////////

/* Initialize the slave with the ID 1 */
  ModbusinoSlave modbusino_slave(1);
/* Allocate a mapping of 10 values */
  uint16_t tab_reg[10];


  uint16_t xWatchdog;
  uint16_t counter;

    
//////////////////////   VOID SETUP    /////////////////////////
void setup() {
  Wire.begin();
  writeI2CRegister8bit(0x21, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x22, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x23, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x24, 6); //reset Sensor
  delay(1000);

// Modbus slave configuration parameters
  modbusino_slave.setup(9600);

}

//////////////////////   VOID LOOP    /////////////////////////
void loop() { 
  unsigned long currentMillis = millis(); 
  if (currentMillis - previousMillis >= interval_readingSensors) {      // Abfrage aller Sensoren alle 6 Sekunden
        previousMillis = currentMillis;
        
        T_Sensor_gelb[icounter] = readI2CRegister16bit(0x21, 5);
        T_Sensor_rot[icounter] = readI2CRegister16bit(0x22, 5);
        T_Sensor_blau[icounter] = readI2CRegister16bit(0x23, 5);
        T_Sensor_gruen[icounter] = readI2CRegister16bit(0x24, 5);
        
        Erde_Sensor_gelb[icounter] = readI2CRegister16bit(0x21, 0);
        Erde_Sensor_rot[icounter] = readI2CRegister16bit(0x22, 0);
        Erde_Sensor_blau[icounter] = readI2CRegister16bit(0x23, 0);
        Erde_Sensor_gruen[icounter] = readI2CRegister16bit(0x24, 0);
        
        icounter = icounter + 1;    // Counter beginnt bei 1 und zählt nacht jedem Auslesevorgang um 1 hoch
        
        if(icounter == 11){         // Wenn 10 Abfragen abgearbeitet
          T_Sensor_gelb_mittel_temp = 0;  // Init der Summenvariablen
          T_Sensor_rot_mittel_temp = 0;
          T_Sensor_blau_mittel_temp = 0;
          T_Sensor_gruen_mittel_temp = 0;

          Erde_Sensor_gelb_mittel_temp = 0;
          Erde_Sensor_rot_mittel_temp = 0;
          Erde_Sensor_blau_mittel_temp = 0;
          Erde_Sensor_gruen_mittel_temp = 0;

          // Bildung der Mittelwerte aus je 10 Einzelwerten
          for(int i = 1; i < 11; i++){
              T_Sensor_gelb_mittel_temp = T_Sensor_gelb_mittel_temp + T_Sensor_gelb[i];
              T_Sensor_rot_mittel_temp = T_Sensor_rot_mittel_temp + T_Sensor_rot[i];
              T_Sensor_blau_mittel_temp = T_Sensor_blau_mittel_temp + T_Sensor_blau[i];
              T_Sensor_gruen_mittel_temp = T_Sensor_gruen_mittel_temp + T_Sensor_gruen[i];

              Erde_Sensor_gelb_mittel_temp = Erde_Sensor_gelb_mittel_temp + Erde_Sensor_gelb[i];
              Erde_Sensor_rot_mittel_temp = Erde_Sensor_rot_mittel_temp + Erde_Sensor_rot[i];
              Erde_Sensor_blau_mittel_temp = Erde_Sensor_blau_mittel_temp + Erde_Sensor_blau[i];
              Erde_Sensor_gruen_mittel_temp = Erde_Sensor_gruen_mittel_temp + Erde_Sensor_gruen[i];
              }
          
          T_Sensor_gelb_mittel = T_Sensor_gelb_mittel_temp / 10; 
          T_Sensor_rot_mittel = T_Sensor_rot_mittel_temp / 10;
          T_Sensor_blau_mittel = T_Sensor_blau_mittel_temp / 10;
          T_Sensor_gruen_mittel = T_Sensor_gruen_mittel_temp / 10;
          
          Erde_Sensor_gelb_mittel = Erde_Sensor_gelb_mittel_temp / 10; 
          Erde_Sensor_rot_mittel = Erde_Sensor_rot_mittel_temp / 10; 
          Erde_Sensor_blau_mittel = Erde_Sensor_blau_mittel_temp / 10; 
          Erde_Sensor_gruen_mittel = Erde_Sensor_gruen_mittel_temp / 10; 
          
          icounter = 1;   // Zurücksetzen des Counters
          }
        }
  
counter = counter + 1;    // Counter zählt jeden Programmdurchlauf

// Bedingung für den Watchdog (der ist nur für den ModBus Master interessant)
if(counter <= 32000) 
  {xWatchdog = 0;}
else
  {xWatchdog = 100;}

if(counter > 64000)
  {counter = 0;}

// Array befüllen, welches über Modbus bereitgestellt wird
  /* Initialize the first register to have a value to read */
    tab_reg[0] = Erde_Sensor_gelb_mittel;          // #0 Sensor gelb Erdfeuchtigkeit
    tab_reg[1] = Erde_Sensor_rot_mittel;           // #1 Sensor rot Erdfeuchtigkeit
    tab_reg[2] = Erde_Sensor_blau_mittel;          // #2 Sensor blau Erdfeuchtigkeit
    tab_reg[3] = Erde_Sensor_gruen_mittel;         // #3 Sensor weiss Erdfeuchtigkeit
    tab_reg[4] = T_Sensor_gelb_mittel;             // #4 Sensor gelb Temperatur
    tab_reg[5] = T_Sensor_rot_mittel;              // #5 Sensor rot Temperatur
    tab_reg[6] = T_Sensor_blau_mittel;             // #6 Sensor blau Temperatur
    tab_reg[7] = T_Sensor_gruen_mittel;            // #7 Sensor weiss Temperatur
    tab_reg[8] = 999;                              // #8 999 - Testwert
    tab_reg[9] = xWatchdog;                        // #9 Watchdog Blinker
    
    /* Launch Modbus slave loop with:
       - pointer to the mapping
       - max values of mapping */
    modbusino_slave.loop(tab_reg, 10);      // Modbus Slave jeden Programm Durchgang aufrufen

}

Diesmal wird nichts mehr ausgelesen, der Master bekommt immer den Timeout Fehler oder auch einen "UNDEFINED" Error.

Ich habe die Bedenken, dass die Modbusino den Arduino "völlig in Beschlag" nimmt, also der I2C Teil nicht mehr ausgeführt wird, wenn der Arduino angesprochen wird.
Wie ja schon erwähnt, funktioniert das Programm am PC, dort werden die Werte richtig angezeigt und ausgelesen. Allerdings versucht in dem Moment ja auch keine Modbus Master darauf zuzugreifen.

Wie kann ich hier weiter vorgehen? Ich probiere mich hier schon ziemlich lange, habe das Projekt gerade wieder einmal aufgenommen, das liegt schon lange wegen dieser Probleme in der Schublade :wink:

Klingt logisch was Du schreibst. In einem weiteren Beitrag habe ich mal die Serial Monitor Funktionen rausgelöscht. Die brauche ich ja ohnehin nicht unbedingt.
Leider habe ich nun wieder dieselben Timeout Probleme wie vorher.
Ich könnte mir daher schon vorstellen, dass es Timing Probleme sind, die aber wohl auf den I2C Auslesevorgang zurück zu führen sind. Ich habe nur keine Idee, wie ich das beheben kann...

weiterhin schrittweise vorgehen.

nim den nackten Server Sketch und implementiere nun EINEN Sensor.
Lies den einen Sensor nur alle 6 Sekunden aus.

Klatsche nicht alles in den Loop.
Mach dir eine Funktion die den "Timer" bzw. das Auslesen macht.

nur mal ein Sensor.
einmal lesen, keine Wiederholungen.

Ohne Serieller zum Debuggen ist das ein Krampf, ergänze halt noch einige LEDs und schalte die LEDs je nach dem Ein / Aus wo du gerade bist.

Ich würde als erstes die Klammern anders setzen.
Dann ist mir nicht klar, warum Du mit 1 ... 11 arbeitest anstelle 0 ... 10. Das verwirrt und kostet Dich zusätzlöcihen Speicherpaltz für ein ungenutztes Array-Element.
Und warum benutzt Du noch für alle Werte Transfervariablen?

Und in der Modbusino.cpp ist nen Osterei drin:

1 Like

Ok Danke, das werde ich mal so machen!

Schaltplan und Bild der Verkabelung hätte ich auch gern gesehen.... nicht dass dir da der I2C Bus einfriert...

Danke für die Antwort, aber ich habe da noch ein paar Fragen dazu:
Welche Klammern soll ich anders setzen und aus welchem Grund`

1..11 deshalb, damit man mit dem Zählen bei 1 anfangen kann. Sicherlich wird hier Speicher verschwendet, aber das Programm ansich leidet darunter ja nicht.

Bei den Transfer Variablen bin ich auch nicht ganz sicher was Du meinst. Man könnte das schon knapper programmieren, aber dafür ist eben jeder Schritt klar und eindeutig.

Das mit dem Osterrei hab ich jetzt hingegen garnicht verstanden, was Du mir damit sagen willst :wink: Ich kenn den Begriff als "zusätzliche, versteckte Funktion eines Programms", weiß aber jetzt nicht, worauf Du hinaus willst...

Da es in der IT üblich ist, von 0 anzufangen, machst Du es damit möglichen Helfenden unnötig schwer und bekommst evtl. weniger Antworten.

Gruß Tommy

1 Like

Das auslesen der Sensoren einzeln und das abarbeiten der Funktion ab counter == auch einzeln.
Spart Verschachtelungstiefen und wird übersichtlicher, was zusammen gehört. So lassen sich einzelne Teile später auch komplett einfacher als Funktionen auslagern.

Und ich hab Das mal umgestellt. Bitte beachte, was die Variable numWerte macht.

Ich habe mal

// Berechnete Mittelwerte aus 10 Einzelwerten

komplett weg gelassen.

Und dann zum auslesen der Sensoren... Was ist da für ein I2C-Chip dran, den Du da ausliest?
Mit Deiner Schleife, alle acht Register auszulesen hast Du schon 160ms allein durch das delay drin.
ICH würde in dieser Konstellation mit jedem Umlauf genau ein Register lesen.

Aber hier mal was umgeschriebenes.

#include <Wire.h> // für Tindie Sensor
#include <Modbusino.h> // Modbus Bib

// Funktion zum Beschreiben bestimmter Sensor Register
void writeI2CRegister8bit(int addr, int value)
{
  Wire.beginTransmission(addr);
  Wire.write(value);
  Wire.endTransmission();
}

// Funktion um Sensoren auszulesen
unsigned int readI2CRegister16bit(int addr, int reg)
{
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.endTransmission();
  delay(20);
  Wire.requestFrom(addr, 2);
  unsigned int t = Wire.read() << 8;
  t = t | Wire.read();
  return t;
}

// Arrays für die Sensorwerte, die später gemittelt werden
const byte numWerte = 10;
int T_Sensor_gelb[numWerte];
int T_Sensor_rot[numWerte];
int T_Sensor_blau[numWerte];
int T_Sensor_gruen[numWerte];
int Erde_Sensor_gelb[numWerte];
int Erde_Sensor_rot[numWerte];
int Erde_Sensor_blau[numWerte];
int Erde_Sensor_gruen[numWerte];

// Temporäre Variablen, die die Summen der Einzelwerte enthalten
int T_Sensor_gelb_mittel_temp;
int T_Sensor_rot_mittel_temp;
int T_Sensor_blau_mittel_temp;
int T_Sensor_gruen_mittel_temp;
int Erde_Sensor_gelb_mittel_temp;
int Erde_Sensor_rot_mittel_temp;
int Erde_Sensor_blau_mittel_temp;
int Erde_Sensor_gruen_mittel_temp;


// Timer Variablen: Alle 6000ms finden die Sensorabfragen statt. Nach 10 Abfragen werden die Mittelwerte gebildet und über SerialPrint ausgegeben
unsigned long previousMillis = 0;
const long interval_readingSensors = 6000;
int icounter = 1;
bool xAusgabe; // Wenn true, dann Ausgabe über SerialPrint

////////////////////// MODBUS /////////////////////////

/* Initialize the slave with the ID 1 */
ModbusinoSlave modbusino_slave(1);
/* Allocate a mapping of 10 values */
uint16_t tab_reg[10];
uint16_t xWatchdog;
uint16_t counter;


////////////////////// VOID SETUP /////////////////////////
void setup()
{
  Wire.begin();
  writeI2CRegister8bit(0x21, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x22, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x23, 6); //reset Sensor
  delay(1000);
  writeI2CRegister8bit(0x24, 6); //reset Sensor
  delay(1000);
  /* Initialize the first register to have a value to read */
  tab_reg[0] = 0; // #0 Sensor gelb Erdfeuchtigkeit
  tab_reg[1] = 0; // #1 Sensor rot Erdfeuchtigkeit
  tab_reg[2] = 0; // #2 Sensor blau Erdfeuchtigkeit
  tab_reg[3] = 0; // #3 Sensor weiss Erdfeuchtigkeit
  tab_reg[4] = 0; // #4 Sensor gelb Temperatur
  tab_reg[5] = 0; // #5 Sensor rot Temperatur
  tab_reg[6] = 0; // #6 Sensor blau Temperatur
  tab_reg[7] = 0; // #7 Sensor weiss Temperatur
  tab_reg[8] = 999; // #8 999 - Testwert
  tab_reg[9] = xWatchdog; // #9 Watchdog Blinker
  // Modbus slave configuration parameters
  modbusino_slave.setup(9600);
}

////////////////////// VOID LOOP /////////////////////////
void loop()
{
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval_readingSensors) // Abfrage aller Sensoren alle 6 Sekunden
  {
    previousMillis = currentMillis;
// *INDENT-OFF*
    T_Sensor_gelb[icounter]     = readI2CRegister16bit(0x21, 5);
    T_Sensor_rot[icounter]      = readI2CRegister16bit(0x22, 5);
    T_Sensor_blau[icounter]     = readI2CRegister16bit(0x23, 5);
    T_Sensor_gruen[icounter]    = readI2CRegister16bit(0x24, 5);
    Erde_Sensor_gelb[icounter]  = readI2CRegister16bit(0x21, 0);
    Erde_Sensor_rot[icounter]   = readI2CRegister16bit(0x22, 0);
    Erde_Sensor_blau[icounter]  = readI2CRegister16bit(0x23, 0);
    Erde_Sensor_gruen[icounter] = readI2CRegister16bit(0x24, 0);
// *INDENT-ON*    
    icounter++; // Counter zählt nacht jedem Auslesevorgang um 1 hoch
  }
  if (icounter == numWerte) // Wenn 10 Abfragen abgearbeitet
  {
    // Bildung der Mittelwerte aus je 10 Einzelwerten
    // *INDENT-OFF*
    for (int i = 0; i < numWerte; i++)
    {
      T_Sensor_gelb_mittel_temp      += T_Sensor_gelb[i];
      T_Sensor_rot_mittel_temp       += T_Sensor_rot[i];
      T_Sensor_blau_mittel_temp      += T_Sensor_blau[i];
      T_Sensor_gruen_mittel_temp     += T_Sensor_gruen[i];
      Erde_Sensor_gelb_mittel_temp   += Erde_Sensor_gelb[i];
      Erde_Sensor_rot_mittel_temp    += Erde_Sensor_rot[i];
      Erde_Sensor_blau_mittel_temp   += Erde_Sensor_blau[i];
      Erde_Sensor_gruen_mittel_temp  += Erde_Sensor_gruen[i];
    }
    // Array befüllen, welches über Modbus bereitgestellt wird
    tab_reg[0] = T_Sensor_gelb_mittel_temp     / 10;
    tab_reg[1] = T_Sensor_rot_mittel_temp      / 10;
    tab_reg[2] = T_Sensor_blau_mittel_temp     / 10;
    tab_reg[3] = T_Sensor_gruen_mittel_temp    / 10;
    tab_reg[4] = Erde_Sensor_gelb_mittel_temp  / 10;
    tab_reg[5] = Erde_Sensor_rot_mittel_temp   / 10;
    tab_reg[6] = Erde_Sensor_blau_mittel_temp  / 10;
    tab_reg[7] = Erde_Sensor_gruen_mittel_temp / 10;
    // *INDENT-ON*
    icounter = 0; // Zurücksetzen des Counters
  }
  counter++; // Counter zählt jeden Programmdurchlauf
  // Bedingung für den Watchdog (der ist nur für den ModBus Master interessant)
  if (counter <= 32000)
  {xWatchdog = 0;}
  else
  {xWatchdog = 100;}
  if (counter > 64000)
  {counter = 0;}
  /* Launch Modbus slave loop with:
    - pointer to the mapping
    - max values of mapping */
  modbusino_slave.loop(tab_reg, 10); // Modbus Slave jeden Programm Durchgang aufrufen
}

Ein Server darf bei Modbus nur IDs zwischen 1 und 247 haben, die 0 gibt es nicht!

Noch schlimmer: der Compiler meint..

warning: comparison is always true due to limited range of data type [-Wtype-limits]

Das erste = muss weg, dann stimmts.

I²C erlaubt es, bis zu 32 Byte in einem Rutsch zu lesen. Du könntest also mit einem Read die Register 0 bis 5 lesen und selber auseinanderklamüsern. Spart 4*20ms.

Danke für die Optimierung!
Einzige Änderung: "icounter" muss entsprechend mit 0 initialisiert werden, es stand noch von meiner Version auf 1.

Leider hat das insgesamt keine Änderung gebracht. Ich bekomme immer noch Timeout- und UNDEFINDED Meldungen beim Master.

Zum Sensor:

Das ist er, ich habe die rugged version, dabei ist die ganze Platine mit Kunstharz vergossen und ein großer Schrumpfschlauch liegt über den Bauteilen.

Ich kann leider nicht sagen, welcher I2C-Chip drauf ist, die Infos finde ich nicht.

Zum Osterei: Das mit der Adress Beschränkung hatte ich zwar zuvor nicht registriert, aber ich hatte ja ohnehin die Adresse 1 eingestellt, von daher hatte ich ja keinen Fehler zu erwarten.

Kannst Du beim Client auf dem Raspi die Timeoutzeit einstellen? Vielleicht wartet der nur ein bisschen zu wenig auf eine Antwort?

Einen Schaltplan hatte ich vorher nicht, den müsste ich bei Bedarf nachreichen, erst mal ein Programm suchen. Daher erst mal so:

  • Stromversorgung des Arduino Nano über USB Kabel
  • +5V (rotes Kabel) von Arduino Pin an Schraubklemme
  • GND (schwarzes Kabel) von Arduino Pin an Schraubklemme
  • SDA (blaues Kabel) von Arduino Pin A4 an Schraubklemme
  • SCK (gelbes Kabel) von Arduino Pin A5 an Schraubklemme
  • Jeweils einen PULLUP Widerstand zwischen A4 und +5V, sowie zwischen A5 und +5V

Zusätzlich sind D2 und D3 vom Arduino an je eine Schraubklemme geführt, auch jeweils mit PULLUP Widerständen zwischen den Pins und +5V. Da sollen dann noch DHT22 Sensoren angeschlossen werden, die sind im Moment aber nicht dran.

Hier noch 3 Fotos vom Aufbau: Leider ist das ganze schon auf einer Platine verlötet und nicht mehr auf dem Breadboard.