Hallo zusammen,
ich habe ein Problem mit der zuverlässigen Antwort meiner ModbusRTU-Slaves.
In unserem Wohnhaus sind in jedem Raum in einer Unterputzdose proMinis untergebracht die dort mit je einem DHT22 die Lufttemperatur und die relative Luftfeuchte messen, sie optional auf einem OLED Display darstellen und per ModbusRTU einem Master zur Archivierung zur Verfügung stellen sollen.
Die letzten sechs Jahre werkelte ein selbst geschriebenes Python-Skript auf einem Raspi 3B um die Slaves anzusprechen und die Messwerte in eine MySQL Datenbank zu schreiben. Und auch damals schon kam es immer wieder mal vor, dass bei Abfrage diverser Slaves ein Timeout auftrat. Die Fehlerhäufigkeit ist dabei relativ gleichmäßig auf alle Slaves verteilt. Im Python-Skript hatte ich dazu einen „faulen“ Workaround geschaffen der einfach jeden Slave bis zu drei Mal angesprochen hat.
Vor kurzem habe ich nun auf Iobroker umgestellt und lasse die Slaves mit einem ModbusRTU Adapter abfragen. Hier ist natürlich kein Workaround möglich und das ist auch gut so – ich denke mittlerweile, dass es sinnvoller ist die Ursache für die Timeouts zu beheben, wenn denn möglich.
Es kommt allerdings auch mit dem schlankestmöglichen Code immer wieder zu Timeouts und mir gehen langsam die Ideen aus woran das noch liegen könnte.
Der aktuelle Testaufbau auf dem Schreibtisch umfasst einen Slave mit dem unten angehängten Code. Also keine langen Leitungen, selbst die OLED Ansteuerung habe ich schon mal auskommentiert um zu sehen ob der OLED-Code zu irgendwelchen Latenzen führt. Aber, keine Besserung. Selbst wenn wirklich nur der DHT abgefragt wird, kommt es immer wieder mal zu Timeouts. Master ist in diesem Fall der PC mit der QModMaster Software zum Abfragen der Leseregister.
#include <Arduino.h>
#include <DHT_Async.h>
#include <ModbusRTUSlave.h>
// #include <U8g2lib.h>
/*
/*-----( Declare Constants and Pin Numbers )-----*/
#define RELEASE 3.01 //NICHT KOMPATIBEL MIT LOCHRASTER-PROTOTYP!!!
#define BAUD 19200
#define RX_PIN 12 // Serial Receive pin RO on MAX485-Board
#define TX_PIN 10 // Serial Transmit pin DI on MAX485-Board
#define DE_RE_PIN 11 // MAX485-Board Direction control DE_RE
#define DHT_SENSOR_PIN 2 // Sensor angeschlossen an pin
#define DHT_TYPE DHT_TYPE_22 // Sensortyp = DHT22
#define OLED_5V 3 //Spannungsversorgung OLED +5V fuer Abschaltung OLED ### Aktuell nicht verwendet ###
#define ID_Bit0 4 //mit 6Bit wird der ProMini codiert --> ID vergeben
#define ID_Bit1 5 //mit 6Bit wird der ProMini codiert --> ID vergeben
#define ID_Bit2 6 //mit 6Bit wird der ProMini codiert --> ID vergeben
#define ID_Bit3 7 //mit 6Bit wird der ProMini codiert --> ID vergeben
#define ID_Bit4 8 //mit 6Bit wird der ProMini codiert --> ID vergeben
#define ID_Bit5 9 //mit 6Bit wird der ProMini codiert --> ID vergeben
#define VCC A0 //Messe Versorgungsspannung
SoftwareSerial mySerial(RX_PIN, TX_PIN);
ModbusRTUSlave modbus(mySerial, DE_RE_PIN); // serial port, driver enable pin for rs-485
uint16_t inputRegisters[6];
// Initialization DHT22
DHT_Async dht_sensor(DHT_SENSOR_PIN, DHT_TYPE);
// Initialization OLED Display
// U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
void setup() {
//Define all needed Digital IN/OUT´s
pinMode(OLED_5V, OUTPUT); // Vcc for OLED Display
pinMode(DE_RE_PIN, OUTPUT); // Modbus direction switch
pinMode(ID_Bit0, INPUT_PULLUP); // SlaveID 6bit in summary max 63 slaves
pinMode(ID_Bit1, INPUT_PULLUP); // SlaveID 6bit in summary max 63 slaves
pinMode(ID_Bit2, INPUT_PULLUP); // SlaveID 6bit in summary max 63 slaves
pinMode(ID_Bit3, INPUT_PULLUP); // SlaveID 6bit in summary max 63 slaves
pinMode(ID_Bit4, INPUT_PULLUP); // SlaveID 6bit in summary max 63 slaves
pinMode(ID_Bit5, INPUT_PULLUP); // SlaveID 6bit in summary max 63 slaves
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
//Initialize all needed OUTPUT´s
digitalWrite(OLED_5V, HIGH); // switches the OLED constantly on
delay(150);
// Initialize modbus instance
uint8_t SlaveAdress = getSlaveAdress();
modbus.configureInputRegisters(inputRegisters, 6); // unsigned 16 bit integer array of input register values, number of input registers
modbus.begin(SlaveAdress, BAUD, SERIAL_8N1); // ModbusRTUSlave::begin(uint8_t unitId, unsigned long baud, uint32_t config, unsigned long preDelay; unsigned long postDelay)
// Start OLED Driver instance
// u8g2.begin();
Serial.begin(9600);
// OLED_StartScreen();
}
void loop() {
static float temperature;
static float humidity;
static byte keepAliveFeedback;
static byte dht_error;
int voltage = CheckVoltage(VCC);
bool sampled = measure_environment(&temperature, &humidity, &dht_error);
// --- Modbus Communication ---
inputRegisters[0] = (int)(temperature * 10);
inputRegisters[1] = (int)(humidity * 10);
inputRegisters[2] = keepAliveFeedback; // Send keep-alive frmo 0 to 99
inputRegisters[3] = voltage;
inputRegisters[4] = dht_error; //DHT Error incremented in error case
inputRegisters[5] = (int)(RELEASE * 100);
modbus.poll();
// OLED Data
// keepAliveFeedback = OLED_Measured_Values(temperature, humidity, 0);
keepAliveFeedback = 123;
}
/*
byte OLED_Measured_Values( float Val_1, float Val_2, int ERR_ALL) {
//TXT_1 Benennung des Messwerts
//Val_1 Temperaturwert
//Val_2 Lufteuchtigkeitswert
static byte ScreenDelay = 1;
static byte keepAlive = 0;
static unsigned long flap_screen = millis();
// Flap the screen after a defined time
if (millis() - flap_screen > 1000ul) {
flap_screen = millis();
++ScreenDelay;
//Wechsel nach X-Sekunden wieder auf den Screen1
if (ScreenDelay >= 17) {
ScreenDelay = 5;
}
//Zaehle einen keep-alive-timer hoch
++keepAlive;
}
//Bedingung zur Anzeige des ersten Screens
if (ScreenDelay == 5) {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_helvB10_tf);
u8g2.setCursor(2, 12);
u8g2.print("Temperatur:");
u8g2.setFont(u8g2_font_fub35_tn);
u8g2.setCursor(0, 64);
u8g2.print(Val_1, 1);
} while (u8g2.nextPage());
++ScreenDelay;
}
if (ScreenDelay == 11) {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_helvB10_tf);
u8g2.setCursor(1, 12);
u8g2.print("Luftfeuchtigkeit:");
u8g2.setFont(u8g2_font_fub35_tn);
u8g2.setCursor(0, 64);
u8g2.print(Val_2, 1);
} while (u8g2.nextPage());
++ScreenDelay;
}
return keepAlive;
}
*/
/*
void OLED_StartScreen(){
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_helvB10_tf);
u8g2.setCursor(2, 12);
u8g2.print("Slave-ID: "); u8g2.print(getSlaveAdress());
u8g2.setCursor(2, 30);
u8g2.print("Release: "); u8g2.print(RELEASE);
u8g2.setCursor(2, 48);
u8g2.print("Voltage: "); u8g2.print(CheckVoltage(VCC)/100); u8g2.print("V");
} while ( u8g2.nextPage() );
delay(2000);
return;
}
*/
static bool measure_environment(float *temperature, float *humidity, byte *dht_error) {
static unsigned long measurement_timestamp = millis();
static float tmp_temp = 99.9;
static float tmp_humi = 99.9;
static bool firstRun = true;
/* Measure once every four seconds. */
if (millis() - measurement_timestamp > 4000ul || firstRun) {
if (dht_sensor.measure(temperature, humidity)) {
measurement_timestamp = millis();
if(isnan(*temperature) || isnan(*humidity)){
*temperature = tmp_temp; // 99.9 means, we never saw a real measure but NAN was delivered
*humidity = tmp_humi; // 99.9 means, we never saw a real measure but NAN was delivered
*dht_error = 10; // error-code 10 means that delivered value is NAN
}else{
// eine Messung temporär zwischenspeichern
tmp_temp = *temperature;
tmp_humi = *humidity;
*dht_error = 0; // error-code 0 means everything is fine
}
firstRun = false;
return (true);
}
}
return (false);
}
byte getSlaveAdress() {
byte ID_Byte = 0;
//Codiereingänge lesen und entsprechende Bits im Adress_Byte setzen
bitWrite(ID_Byte, 0, digitalRead(ID_Bit0)); //least-significant (rightmost) bit
bitWrite(ID_Byte, 1, digitalRead(ID_Bit1));
bitWrite(ID_Byte, 2, digitalRead(ID_Bit2));
bitWrite(ID_Byte, 3, digitalRead(ID_Bit3));
bitWrite(ID_Byte, 4, digitalRead(ID_Bit4));
bitWrite(ID_Byte, 5, digitalRead(ID_Bit5));
bitWrite(ID_Byte, 6, 0); //Ungenutzes Nibble initialisieren
bitWrite(ID_Byte, 7, 0); //Ungenutzes Nibble initialisieren
return ID_Byte;
}
int CheckVoltage(byte pin) {
float rawVoltage = map(analogRead(pin), 0, 1023, 0, 500);
int Voltage = (int)rawVoltage;
return Voltage;
}
Vielleich kann mir hier jemand helfen?