Programmierung Temperatursensor / Steinhart Hart Gleichung

Hallo,

könnte mir mal bitte bei folgenden Code helfen, leider verstehe ich einige Funktion nicht. Es geht um die Auslesung der Temperatur von vier DS18B20 Sensoren am Bus 4. Deren Ausgabewert mit der Steinhart Hart Gleichung korrigiert wird.

Hier der komplette Code mit anderen Sensoren wie ein Volumenstromesser. Mich interessieren haber nur die Abschnitte bezüglich der Temperatursensoren:

#include <OneWire.h>
#include <DallasTemperature.h>
#include <FreqMeasure.h>

#define FREQUENCY_TO_LITERS_PER_HOUR 22.78481

#define ADC_PLUS_BITS 5

#define ONE_WIRE_BUS 4

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

DeviceAddress Probe1 = { 0x28, 0xFF, 0x2B, 0x2E, 0x72, 0x16, 0x03, 0xA1 }; // Intake air | Bottom radiator
DeviceAddress Probe2 = { 0x28, 0xFF, 0xE5, 0xBF, 0x71, 0x16, 0x04, 0x28 }; // Ambient air
DeviceAddress Probe3 = { 0x28, 0xFF, 0x06, 0x0D, 0x72, 0x16, 0x03, 0x4D }; // Intake air | External radiator
DeviceAddress Probe4 = { 0x28, 0xFF, 0xDC, 0x92, 0x71, 0x16, 0x05, 0xEC }; // Intake air | Top radiator

float read_analog(int pin) {
  unsigned long sum = analogRead(pin);
  for (int i = 1; i < pow(4, ADC_PLUS_BITS); i++) {
    sum += analogRead(pin);
  }
  return (sum >> ADC_PLUS_BITS) / pow(2, ADC_PLUS_BITS);
}

float get_analog_temp(int thermistor) {
  const float A[6] = {1.111424e-03, 1.537616e-03, 1.250530e-03, 1.512060e-03, 1.640398e-03, 1.613705e-03};
  const float B[6] = {2.225275e-04, 1.599151e-04, 2.081287e-04, 1.635745e-04, 1.472821e-04, 1.516699e-04};
  const float C[6] = {2.427177e-07, 4.491194e-07, 2.482240e-07, 4.305526e-07, 4.650907e-07, 4.490601e-07};
  const int SENSE_RESISTOR[] = {9963, 9970, 9946, 9988, 9964, 9962};

  float v = read_analog(thermistor);
  if (v < 263) return  0.0;
  if (v > 882) return 80.0;

  float r = SENSE_RESISTOR[thermistor] * ((1023.0 / v) - 1.0);
  return (1.0 / (A[thermistor] + B[thermistor] * log(r) + C[thermistor] * pow(log(r), 3))) - 273.15;
}

float get_digital_temp(int probe) {
  switch (probe) {
    case 1:
      return sensors.getTempC(Probe1);
    case 2:
      return sensors.getTempC(Probe2);
    case 3:
      return sensors.getTempC(Probe3);
    case 4:
      return sensors.getTempC(Probe4);
    default:
      return 0;
  }
}

float get_flow_rate() {
  float duration = 0;
  int samples = 0;
  while (FreqMeasure.available()) {
    duration += FreqMeasure.read();
    samples++;
  }
  return FreqMeasure.countToFrequency(duration) * FREQUENCY_TO_LITERS_PER_HOUR * samples;
}

void setup() {
  Serial.begin(9600);
  FreqMeasure.begin();

  analogReference(EXTERNAL);

  ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2));
  ADCSRA |= bit (ADPS1) | bit (ADPS2);

  sensors.begin();

  sensors.setResolution(Probe1, 12);
  sensors.setResolution(Probe2, 12);
  sensors.setResolution(Probe3, 12);
  sensors.setResolution(Probe4, 12);
  sensors.setWaitForConversion(false);
}

void loop() {
  //unsigned long timer = millis();
  sensors.requestTemperatures();
  String output = "";
  for (int i = 0; i < 6; i++) {
    output += get_analog_temp(i);
    output += " ";
  }
  delay(500);
  for (int i = 1; i < 5; i++) {
    output += get_digital_temp(i);
    output += " ";
  }
  output += get_flow_rate();
  Serial.println(output);
  //Serial.println(millis() - timer);
}

Nun zu meinen Fragen:

Was genau tut diese Funktion

#define ADC_PLUS_BITS 5
float read_analog(int pin) {
  unsigned long sum = analogRead(pin);
  for (int i = 1; i < pow(4, ADC_PLUS_BITS); i++) {
    sum += analogRead(pin);
  }
  return (sum >> ADC_PLUS_BITS) / pow(2, ADC_PLUS_BITS);

Welchen Sinn ergibt dieses Array, das ja aus sechs verschiedenen Koffizienten besteht, obwohl nur vier Sensoren angeschlossen sind und wie wird sicher gestellt, das für jeden Sensor die richtigen Koeffizienten zugewiesen werden

float get_analog_temp(int thermistor) {
  const float A[6] = {1.111424e-03, 1.537616e-03, 1.250530e-03, 1.512060e-03, 1.640398e-03, 1.613705e-03};
  const float B[6] = {2.225275e-04, 1.599151e-04, 2.081287e-04, 1.635745e-04, 1.472821e-04, 1.516699e-04};
  const float C[6] = {2.427177e-07, 4.491194e-07, 2.482240e-07, 4.305526e-07, 4.650907e-07, 4.490601e-07};
  const int SENSE_RESISTOR[] = {9963, 9970, 9946, 9988, 9964, 9962};

Was genau bewirkt diese Funktion?

float v = read_analog(thermistor);
  if (v < 263) return  0.0;
  if (v > 882) return 80.0;

Unterschiedliche Spannung des DS18B20 bei verschiedenen Temperaturen?
Danke

Der Code stammt anscheinend von hier.
Da Du ja dort auch schon gefragt hast, bin ich mal gespannt wo Du zuerst eine Antwort bekommst.

Dallas DS18B20 Sensoren liefern einen digitalen Messwert.
Eine Zahl die schon ganz direkt Grad Celsius sind.
Genauigkeit +-0,5 Grad Celsius. Man kann die Auflösung zwischen 9 und 12 bit einstellen.

Keine Ahnung was die Steinhart-Gleichung ist. Wenn die irgendwie zum linearisieren des Mess-Signals dient
macht das für Sensoren die eine digitale Zahl 21,0625 °C liefern keinen Sinn.

Wenn diese Steinhart-Gleichung einem anderen Zweck dient dann beschreibe mal wozu man die Gleichung benutzt.
Und einen Gesamtüberblick geben wäre auch nicht schlecht.

EDIT: So habe jetzt nachgelesen. Steinhardt-Gleichung ist was für NTC-Widerstände.
Wird bei DS18B20 Temperatursensoren nicht benötigt, weil DS18B20 schon einen digitalen Messwert liefern.
Das bedeutet auch das der Code angepasst werden muss.

Deshalb schreibe einen Gesamtüberblick was du mit dem Code steuern/regeln willst.
vgs

Hallo,

schau Dir noch mal ein paar Beispiele aus der Lib zum DS18B20 an. Da gibt es auch eines mit dem mehrere Sensoren eigelesen werden. Ich habe bei den DS18B20 Sensoren schon mal Offset Felhler festgestellt , die man leicht beheben kann in dem man eine Konstante vom Messwert abzieht. Das Du die Gleichung nicht benötgst wurde ja bereits gesagt.

Ansonsten emfehle ich Dir nochmals ein paar Grudlagen zu üben.

Heinz

Vielen Dank für eure Hilfe

Das wäre nun mein Code

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into digital pin 2 on the Arduino
#define ONE_WIRE_BUS 4

// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);	

// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);

// Addresses of 3 DS18B20s
uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27 };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6 };

float RawHigh = 99.6;
float RawLow = 0.5;
float ReferenceHigh = 99.1;
float ReferenceLow = 0;
float RawRange = RawHigh - RawLow;
float ReferenceRange = ReferenceHigh - ReferenceLow;

void setup(void)
{
  sensors.begin();	// Start up the library
  Serial.begin(9600);
}

void loop(void)
{ 
  // Send the command to get temperatures
  sensors.requestTemperatures();
  
   // 9 - 12 0,5 0,25 0,125 
  sensors.setResolution(12); 
  

  Serial.print("Sensor 1: ");
  printTemperature(sensor1);
  
  Serial.print("Sensor 2: ");
  printTemperature(sensor2);
  
  Serial.print("Sensor 3: ");
  printTemperature(sensor3);
  
  Serial.println();
  delay(1000);
}
  
void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  float CorrectedValue = (((tempC - RawLow) * ReferenceRange) / RawRange) + ReferenceLow;
  Serial.print(tempC);
  Serial.print((char)176);
  Serial.print("C  |  ");
  
}

Wie kann ich denn die unterschiedlichen Korrekturfaktoren

float RawHigh = 99.6;
float RawLow = 0.5;
float ReferenceHigh = 99.1;
float ReferenceLow = 0;
float RawRange = RawHigh - RawLow;
float ReferenceRange = ReferenceHigh - ReferenceLow;

in einer Gleichung für alle Sensoren mit unterschiedlichen Variablen definieren?

float CorrectedValue = (((tempC - RawLow) * ReferenceRange) / RawRange) + ReferenceLow;

#define ADC_PLUS_BITS 5
ist keine Funktion sondern eine "textersetzung" vor dem Kompilieren.
"ADC_PLUS_BITS" wird mit "5" ersetzt.

erst einmal die Frage: würde denn eine Genauigkeit von 0,5°C ausreichen für deine noch nicht beschriebene Anwendung?

Stammt diese Korrekturformel aus einem Programm das im Original mit DS18B20-Sensoren arbeitet?

Offsetfehler heißt der Sensor gibt einen konstant zu hohen / zu niedrigen Messwert

Beispiele
Messwert 21,5 real 21,1
Messwert 18,5 real 18,1
Messwert -7,2 real 6,8

Für so einen Offsetfehler reicht es einfach den Offsetwert zu addieren/subtrahieren

Wenn du eine höhere Genauigkeit brauchst dann muss ein Sensor vewendet werden der eine höhere
Genauigkeit bietet.

Oder du müsstest umfangreiche Vergleichsmessungen machen und dir die Messergebnisse anschauen
Wenn die Abweichung nicht konstant ist dann müsstest du eine komplette Umrechnungstabelle verwenden

vgs

StefanL38:
erst einmal die Frage: würde denn eine Genauigkeit von 0,5°C ausreichen für deine noch nicht beschriebene Anwendung?

Ich möchte gerne an einem Referenzthermometer mit Prüfzertifikat eine Studie über die Abweichung dieser Sensoren durchführen.

StefanL38:
Stammt diese Korrekturformel aus einem Programm das im Original mit DS18B20-Sensoren arbeitet?

Quelle https://www.instructables.com/Calibration-of-DS18B20-Sensor-With-Arduino-UNO/

StefanL38:
Offsetfehler heißt der Sensor gibt einen konstant zu hohen / zu niedrigen Messwert

Beispiele
Messwert 21,5 real 21,1
Messwert 18,5 real 18,1
Messwert -7,2 real 6,8

Für so einen Offsetfehler reicht es einfach den Offsetwert zu addieren/subtrahieren

Stimmt die o.g. Gleichung macht nichts anderes und bildet aus der unteren Abweichung und der oberen Abweichung nur einen linearen Durchschnittswert. Ich möchte aber gerne im weiteren Verlauf der Studie nicht lineare Gleichungen anwenden.
Da ich leider bezüglich Coding kaum Ahnung habe ,geht es mir eigentlich nur um diese Zeilen:

{
  float tempC = sensors.getTempC(deviceAddress);
  float CorrectedValue = (((tempC - RawLow) * ReferenceRange) / RawRange) + ReferenceLow;
  
  
}

Mit der ersten Syntax ruft man am selben Bus ja alle Daten sämtlicher Sensoren ab. Und mit der nachfolgenden Gleichung wird diese korrigiert. Nur ist die Variable für jeden Sensor anderes. Ich dachte ich könnte das z.B. über ein Array lösen, in dem für jeden Sensor mit der selben Gleichung unterschiedliche Variable multipliziert etc werden, also wie eine Matrizenrechnung in Matlab und co. Aber vielleicht denke ich einfach viel zu kompliziert und sollte das ganz einfach so lösen:

{
  float tempC1 = sensors1.getTempC(deviceAddress);
  float CorrectedValue1 = (((tempC1 - RawLow1) * ReferenceRange1) / RawRange1) + ReferenceLow1;
   
  float tempC2 = sensors2.getTempC(deviceAdress);
  float CorrectedValue2 = (((tempC2 - RadLow2) * ReferenceRange2) / RawRange1) + ReferenceLow2;

Also ganz klassisch seriell? Funktioniert der Befehl float tempC = sensors.getTempC(deviceAdress) bezüglich der Device Adress für jeden Sensor separat?

Du könntest eine Struktur mit den Konstanten zusammen mit einer Funktion verwenden.

oldboy65:
Also ganz klassisch seriell?

Im Prinzip wäre sowas möglich, aber es ist nicht schön.

Ich würde dann eher dazu raten, überall da, wo jetzt 1, 2, 3 und 4 stehen, jeweils Arrays zu verwenden (Index 0...3).
Noch besser wäre ein Array of struct - wobei in der Struktur alle Werte (temp, RawLow1, ReferenceRange, ReferenceLow...) versammelt sind.
Und die Krönung wäre dann eine Klasse mit Methoden für das Auslesen, Rechnen und Ergebnisse abholen. Davon dann vier Instanzen - möglicherweise wieder in einem Array.

Ist nur so eine Idee, mit lauffähigem Code kann ich mangels Sensoren nicht dienen.

Neuer Vorschlag:

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into digital pin 2 on the Arduino
#define ONE_WIRE_BUS 4

// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);	

// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);

// Addresses of 3 DS18B20s
DeviceAddress Probe01 = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC };
DeviceAddress Probe02 = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27 };
DeviceAddress Probe03 = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6 };


float coeffA[4]  =  {99.6, 99.4, 99.2, 99.5};
float coeffB[4]  =  {0.3, 0.1, 0.2, 0.4};
float coeffC[4]  =  {99.1, 99.1, 99.1, 99.1};
float coeffD[4]  =  {0, 0, 0, 0};


void setup(void)
{
  sensors.begin();	// Start up the library
  Serial.begin(9600);
     
     // 9 - 12 0,5 0,25 0,125 
  sensors.setResolution(Probe01, 10);
  sensors.setResolution(Probe02, 10);
  sensors.setResolution(Probe03, 10);
  sensors.setResolution(Probe04, 10);
  sensors.setResolution(Probe05, 10);
  
  
}

void loop()
{
  delay(4000);
  Serial.println();
  Serial.print("Number of Devices found on bus = "); 
  Serial.println(sensors.getDeviceCount());   
  Serial.print("Getting temperatures... "); 
  Serial.println();   
 
  // Command all devices on bus to read temperature 
  sensors.requestTemperatures(); 
 
  Serial.print("Probe 01 temperature is:   ");
  printTemperature(Probe01);
  Serial.println();

  Serial.print("Probe 02 temperature is:   ");
  printTemperature(Probe02);
  Serial.println();
 
  Serial.print("Probe 03 temperature is:   ");
  printTemperature(Probe03);
  Serial.println();
   
  Serial.print("Probe 04 temperature is:   ");
  printTemperature(Probe04);
  Serial.println();
 
  Serial.print("Probe 05 temperature is:   ");
  printTemperature(Probe05);
  Serial.println();
 
}
  
void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = float tempC = (sensors.getTempC(deviceAddress) - ceffB) * (coeffC - coeffD) / (coeffA - coeffB) + ceffD;
  Serial.print(tempC);
  Serial.print((char)176);
  Serial.print("C  |  ");
  
}

Denn dieser Befehl

{
  float tempC1 = sensors1.getTempC(deviceAddress);

müsste ja ein Vektor sein, es werden alle Werte in einem Signal aufgereiht, dann könnte man doch einfach die Koeffizienten ebenfalls als Vektor bzw. 1 D Array schreiben:

float coeffA[4]  =  {99.6, 99.4, 99.2, 99.5};
float coeffB[4]  =  {0.3, 0.1, 0.2, 0.4};
float coeffC[4]  =  {99.1, 99.1, 99.1, 99.1};
float coeffD[4]  =  {0, 0, 0, 0};

Dann den eben nur noch Vektor mit Vektor rechnen:

float tempC = (sensors.getTempC(deviceAddress) - ceffB) * (coeffC - coeffD) / (coeffA - coeffB) + ceffD;

Oder habe ich hier einen Denkfehler? Bzw. eigentlich müssten die Werte ja nur als row aufgereiht sein -> also sensor 1, sensor2, ....

oldboy65:

{

float tempC1 = sensors1.getTempC(deviceAddress);



müsste ja ein Vektor sein,

Der liefert genau einen Wert zurück - die Temperatur in °C

Wo willst Du da Vektoren sehen?
Ich glaube, Du bist völlig neben der Realität.

Gruß Tommy

Ich habe mit der DallasTemperature-Lib nie gearbeitet. Es scheint mir aber so, dass es nur ein "sensors" geben muss und beim getTempC dann einer der vier dann durch die Device-Adresse (Index) ausgewählt wird. Siehe Beispiel "Multiple.ino"

Wenn das mal verstanden ist, sollte sich die Vektor-Verwirrung lösen lassen.

Sprachlich muß man zwischen den mathematischen Zeigern (Vektoren) und den Zeigern in C++ unterscheiden.

oldboy65:
Dann den eben nur noch Vektor mit Vektor rechnen:

So wie Mathe eine "exakte" Wissenschaft ist, muß man sich auch in C++ an Regeln halten, die es zu erlernen gilt. Dein Vorschlag sieht eher nach Phantasie-C aus.

Wenn Du nach Zeigern bzw. Referenzen für die Parameterübergabe an Funktionen suchst, dann eventuell so (nach einer Vorlage von combie; getestet mit UNO):

using MyCoeff = float[4];
MyCoeff coeffA {99.6, 99.4, 99.2, 99.5};
MyCoeff coeffB {0.3, 0.1, 0.2, 0.4};
MyCoeff coeffC {99.1, 99.1, 99.1, 99.1};
MyCoeff coeffD {0, 0, 0, 0};

void setup(void)
{
  Serial.begin(9600);
  Serial.println(F("\nStart"));
}

void loop()
{
  for (byte probe = 0; probe < 4; probe++)
  {
    Serial.print(F("Probe "));
    Serial.print(probe + 1);
    Serial.print(F(" temperature is:   "));
    Serial.print(printTemperature(coeffA, coeffB, coeffC, coeffD, probe));
    Serial.println(F("°C"));
  }
  Serial.println();
  delay(4000);
}

float printTemperature(MyCoeff &coeffA, MyCoeff &coeffB, MyCoeff &coeffC, MyCoeff &coeffD, byte probe)
{
  float temp = 1.0 * random(15, 25); // mangels Sensor
  return (temp - coeffB[probe]) * (coeffC[probe] - coeffD[probe]) / (coeffA[probe] - coeffB[probe]) + coeffD[probe];
}

Etwas übersichtlicher sieht es mit einer Klasse aus (nach einer Vorlage von combie; getestet mit UNO):

class Temperatur
{
    const float coeffA, coeffB, coeffC, coeffD;
  public:
    Temperatur(const float coeffA, const float coeffB, const float coeffC, const float coeffD):
      coeffA(coeffA), coeffB(coeffB), coeffC(coeffC), coeffD(coeffD) {}

    float messen()
    {
      float temp = 1.0 * random(15, 25);
      return (temp - coeffB) * (coeffC - coeffD) / (coeffA - coeffB) + coeffD;
    }
};

Temperatur temperatur []
{ // coeffA,coeffB,coeffC,coeffD
  {99.6, 0.3, 99.1, 0}, // Sensor 1
  {99.4, 0.1, 99.1, 0}, // Sensor 2
  {99.2, 0.2, 99.1, 0}, // Sensor 3
  {99.5, 0.4, 99.1, 0}  // Sensor 4
};

void setup(void)
{
  Serial.begin(9600);
  Serial.println(F("\nStart"));
}

void loop()
{
  byte probe = 1;
  for (Temperatur &t : temperatur)
  {
    Serial.print(F("Probe "));
    Serial.print(probe);
    Serial.print(F(" temperature is:   "));
    Serial.print(t.messen());
    Serial.println(F("°C"));
    probe++;
  }
  Serial.println();
  delay(4000);
}

Vielen Dank @agmue

Ich habe von C und co. einfach keine Ahnung und hoffe das Du es mir nicht böse nimmst. Auch hat man nicht immer die Zeit um alles zu lernen, gerade Programmiersprachen wie C, sind ja nicht besonders für ihre Einfachheit bekannt.

oldboy65:
Ich habe von C und co. einfach keine Ahnung und hoffe das Du es mir nicht böse nimmst.

Ich bin vollkommen entspannt und möchte Dir nur aufzeigen, woran es hapert.

Das Stückchen Programm habe ich als Anfänger in C++ geschrieben, weil ich etwas lernen möchte. Wenn es Dir irgendwie hilft, wäre das natürlich noch besser :slight_smile:

{

float tempC = sensors.getTempC(deviceAddress);
float CorrectedValue = (((tempC - RawLow) * ReferenceRange) / RawRange) + ReferenceLow;
}



Mit der ersten Syntax ruft man am selben Bus ja alle Daten sämtlicher Sensoren ab. Und mit der nachfolgenden Gleichung wird diese korrigiert.

Ich habe die ganz starke Vermutung, dass diese Korrekturrechnung nicht aus einem Code stammt der für DS18B20-Sensoren geschrieben wurde. Sondern von irgendwelchen anderen analogen Temperatursensoren.

Und wenn es darum geht

Ich möchte gerne an einem Referenzthermometer mit Prüfzertifikat eine Studie über die Abweichung dieser Sensoren durchführen.

Warum willst du die Werte die die DS18B20-Sensoren liefern noch einmal korrigieren????
vgs