Projekt Durchfluss- und Temperaturüberwachung mit einem Arduino Nano

0-8-15 User

Vice Admiral
Registriert
Jan. 2008
Beiträge
7.153
Das ursprüngliche Ziel des Projekts war die Erfassung der Vor- und Rücklauftemperatur sowie der aktuellen Raumtemperatur zur Weiterverwendung in einer softwarebasierten Regelung der Lüfterdrehzahlen (unter Linux).

Nach und nach kamen dann weitere Sensoren hinzu, die zwar (vorerst) keine Verwendung in der Lüfterregelung finden, aber dennoch aufschlussreiche Informationen liefern.


Die Steinhart-Hart Koeffizienten der NTC Widerstände (XSPC Temperatursensoren) wurden nach folgendem Prinzip: https://www.thinksrs.com/downloads/pdfs/applicationnotes/LDC Note 4 NTC Calculator.pdf mit dem SRS Thermistor Calculator (https://www.thinksrs.com/downloads/programs/therm calc/ntccalibrator/ntccalculator.html) bestimmt.

Die Impulsrate des Turbinendurchflussmessers wurde durch Auslitern bestimmt.

Die Sensordaten werden einmal pro Sekunde per USB (seriell) an den Computer übermittelt.

Sämtliche Lüfter sind direkt am Mainboard angeschlossen und werden per Software geregelt. Dabei werden die Radiatorlüfter nach der Wassertemperatur (unter Berücksichtigung der momentanen Vor-, Rücklauf- und Raumtemperatur) und die Gehäuselüfter nach der Temperatur des VRM Kühlkörpers geregelt.

Positionierung der Sensoren:

Die NTC Temperatursensoren TH1 bis TH5 messen die Wassertemperatur vor und nach jeder Komponente im Kreislauf.
Pumpe -> TH1 -> Bottom_Radiator -> TH2 -> Top_Radiator -> TH3 -> CPU -> TH4 -> GPU -> TH5 -> External_Radiator -> AGB

Der NTC Temperatursensor TH6 misst die Temperatur des VRM Kühlkörpers. Der Sensor wurde mit Wärmeleitpaste bestrichen und in die Mitte des Kühlkörpers eingeführt.

Die digitalen Temperatursensoren D1 bis D3 messen die Lufttemperatur im jeweiligen Ansaugbereich der drei Radiatoren.

Der digitale Temperatursensor D4 überwacht die Raumtemperatur (außerhalb des direkten Einzugbereichs der Radiatorlüfter).

Exemplarische Visualisierung der Sensordaten:


Quelltext:
C:
#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);
}

Schaltbild:

 
Zuletzt bearbeitet: (Syntax-Highlighting, Links korrigiert)
  • Gefällt mir
Reaktionen: Beitrag, Sinusspass, Baal Netbeck und 7 andere
Hallo 0-8-15 User!

ich hätte da einige Fragen zu deinem Programm. Habe so etwas ähnliches auch in meiner Wasserkühlung.
Will die Delta Temperatur von Radiator Ein-/Auslauf ausgeben und in dem Zuge auch die Genauigkeit der Thermistorberechnung erhöhen.

Wie genau hast du die Steinhart-Hart Koeffizienten ermittelt? Ohne genaues Temperaturmessgerät ist dies schwer möglich, oder?

Die DS18B20 hatte ich anfangs auch, da dauert die Berechnung so lange dass andere Berechnungen beeinflusst werden. Mit dem TMP36 hatte ich auch Probleme wenn mehrere verwendet werden.
Im Endeffekt bin ich bei den Thermistoren geblieben...
Beeinflussen die DS18B20 deinen Programmablauf nicht ?

Grüße
 
kauboy_kurt schrieb:
Ohne genaues Temperaturmessgerät ist dies schwer möglich, oder?
Wenn dich hauptsächlich die Temperaturdifferenz zwischen zwei Sensoren interessiert, dann ist die Genaugikeit des Referenzthermometers eigentlich gar nicht so wichtig. Ich hab zum Beispiel ein Brymen BM257s Multimeter verwendet.
kauboy_kurt schrieb:
Wie genau hast du die Steinhart-Hart Koeffizienten ermittelt?
Ich hab die Sensoren jeweils eine Minute lang in drei verschieden warme Wasserbehälter getaucht (0°C, 21°C, 50°C) und dann jeweils den durchschnittlichen Widerstandswert als Berechnungsgrundlage genommen. Wichtig ist vor allem eine gute Durchmischung des Wassers, damit sich wirklich alle Sensoren in gleich warmem Wasser befinden. Außerdem würde ich ein gut isoliertes Gefäß verwenden und auch nicht an der Wassermenge sparen.
kauboy_kurt schrieb:
Beeinflussen die DS18B20 deinen Programmablauf nicht ?
Die DS18B20 werden bei mir asynchron ausgelesen.
Code:
sensors.setWaitForConversion(false);
Also während die DS18B20 "rechnen", läuft das Programm ganz normal weiter und wartet dann noch eine Weile, bis die digitalen Sensoren bereit sind, ausgelesen zu werden. Die Wartedauer habe ich so gewählt, dass die Schleife einmal pro Sekunde durchlaufen wird.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Beitrag, Baal Netbeck und kauboy_kurt
Hey, Vielen Dank!
Du hast recht, es reicht wenn alle Thermistoren auf eine Referenztemperatur geregelt werden.
Dann werde ich es auch mal so probieren.
Ich hoffe es ist ok wenn ich mir 1-2 Zeilen aus deinem Code kopiere ;)

Den DS18B20 werde ich mir wohl auch nochmal genauer ansehen, "sensors.setWaitForConversion(false);" habe ich noch nie gesehen...

Habe außerdem einen Aqua Computer Durchflusssensor aus meiner Arbeit bekommen, diesen werde ich demnächst auch verbauen. Dazu sehe ich mir dann deine Frequenzberechnung an. Dachte ursprünglich dass ich es mit pulseIn() auslese.

Grüße
 
kauboy_kurt schrieb:
Ich hoffe es ist ok wenn ich mir 1-2 Zeilen aus deinem Code kopiere
Nur zu, deshalb hab ich ihn reingestellt.
kauboy_kurt schrieb:
"sensors.setWaitForConversion(false);" habe ich noch nie gesehen
https://github.com/milesburton/Ardu...1d30005766a14/DallasTemperature.cpp#L370-L377
Hier das offizielle Beispiel: https://github.com/milesburton/Ardu...mples/WaitForConversion/WaitForConversion.ino
kauboy_kurt schrieb:
Dachte ursprünglich dass ich es mit pulseIn() auslese.
Ging mir genauso, funktioniert auch, ist aber leider ziemlich ungenau.

Nachtrag: Wenn du dir die Arbeit mit den Steinhart-Hart Koeffizienten sparen willst, dann könntest du auch dein Glück mit den Thermistoren von Aqua Computer (B-Wert: 3435) versuchen. Dann würde es vermutlich schon genügen, wenn du zu jedem Sensorwert einen festen Offset bestimmst, um die Abweichung untereinander (und vom Referenzthermometer) zu minimieren.
 
Zuletzt bearbeitet: (Links aktualisiert)
Mindestens genauso interessant wie die Erfassung der Sensordaten ist die anschließende Regelung der Lüfter auf Basis der erfassten Daten. Falls Interesse besteht, werde ich auch dazu einen Thread erstellen. Mit Schwerpunkt auf der Software Lüftersteuerung unter Linux.
 
Danke nochmal, du hast mir schon sehr viel nützliche Information gegeben.
Den B-Wert habe ich im Moment auch so ähnlich angegeben(Copy aus der Adafruit-Anleitung).

Ja Interesse besteht, auch wenn ich kein Linux User bin, aber deine Graphen sind schon was feines.
Du regelst die Lüfter dann per Software?

Habe 7 identische Noctua PWM Lüfter auf den Radiatoren und im Gehäuse und die werden linear über einen Temperaturbereich geregelt. Im Moment steckt ein Thermistor in dem Gehäuse des D5 Aqualis(Rücklauftemperatur).

C++:
void PWM_Regelung()
{

// Regelung Luefter Noctua NF14

if(Temp_Wasser < 23)
{
  OCR1A = 80;                                // 30% Tastgrad
}
else if(Temp_Wasser >= 23 && Temp_Wasser <= 47)
{
  //OCR1A_Wert = map(Temp_Wasser, 23, 47, 80, 640);
  OCR1A = 23.3333333 * Temp_Wasser - 456.6666667;
}
else if(Temp_Wasser > 47)
{
  OCR1A = 640;                                // 100% Tastgrad
}
}
 
kauboy_kurt schrieb:
Du regelst die Lüfter dann per Software?
Alle Lüfter (und die Pumpe) hängen direkt oder indirekt am Mainboard und werden, nachdem der Bootvorgang abgeschlossen ist, alle - bis auf einen - per Software geregelt. Die Pumpe, die jeweiligen Radiatorlüfter und die Gehäuselüfter haben jeweils ihren eigenen PWM Kanal.

Sollte der Arduino ausfallen, werden die Lüfter alle vom Mainboard gesteuert (ebenfalls abhängig von der Wassertemperatur).

Nachtrag: Mit den PWM Ausgängen des Arduinos habe ich bisher noch gar nicht rumgespielt. Sollte ich wohl mal nachholen.
 
Das Projekt hat nun das Endstadium erreicht. Bei Interesse stelle ich Gerberdateien und Quelltext zur Verfügung.
ArduinoSensors.jpg
 
  • Gefällt mir
Reaktionen: Beitrag
Mal ausgraben, gute Arbeit. Da ich leider eine Niete im coden bin, hätte ich mal eine Frage zu deinem Code


#define ADC_PLUS_BITS 5

Was ist das für eine Funktion?



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};


Wenn ich das richtig verstehe, ist das ein Array aus sechs Koeffizienten. Was ich aber leider nicht verstehe, warum sechs, wenn nur vier Sensoren angeschlossen sind? Wie wird sichergestellt das für jeden Sensor der richtige Koeffizient zu gewiesen wird?


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


V steht ja für die Voltage, was aber bewirkt diese Funktion, unterschiedliche Spannung des DS18b20 bei verschiedenen Temperaturen?


float r = SENSE_RESISTOR[thermistor] * ((1023.0 / v) - 1.0);

Habe ich jetzt schön öfter gesehen, aber immer noch nicht verstanden.


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


Welche Funktion hat das?


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 += " ";


Was ist der Unterschied zwischen Analog und Digital, geben die DS18b20 zwei unterschiedliche Signale aus?
 
Zurück
Oben