PWM Lüftersteuerung Hilfe für Map alternative

Hallo zusammen,

ich habe folgendes Problem. Ich steuere zwei Lüfter zur Kühlung eines Serverschranks über PWM. Als Signalgeber habe ich einen NTC der mir gemeinsam mit einem Widertsand zum Nullabgleich die Temperatur liefert an A0.

Jetzt habe ich mich an einem Code Beispiel von einer Anleitung die ich im www gefunden habe entlang gehangelt.

Mein Problem ist das mein Regelbereich relativ klein ist. Ich möchte zwischen 30 und 45 grad die Lüfter Stückweise langsam hochdrehen lassen je nach Temperatur.

In dem Programm wird dies über folgendes Mapping gemacht

  // Lüftergeschwindigkeit über Temperaturbereich einstellen
  // TMin->0% PWM | TMax->100% PWM
 fanSpeed = map(temp, tMin, tMax, 0, 255);

Das funktioniert auch allerdings sehr Sprunghaft da hier die Werte natürlich auf nur 15 Schritte aufgeteilt werden.

Hat jemand einen vorschlag wie ich in dem Temperaturbereich 30-45 Grad den Lüfter ganz "Sanft" hochdrehen lasse?

da hier die Werte natürlich auf nur 15 Schritte aufgeteilt werden.

Warum tust du das?

Map ist bei den richtigen Eingangsdaten sehr gut für deine Zwecke geeignet.

Leider ist geheim, was temp ist.
Da ist der Knackpunkt.

 fanSpeed = map(temp*100L, tMin*100L, tMax*100L, 0, 255);

Ohne jede Gewähr

Wenn Du die Temperatur in Einer-Schritten mißt dann könnendie Lüfter auch nur in großen Schritten variieren.
Die Lüfter laufen unter einer gewissen Spannung/PWM nicht bzw nicht an. Das mußt Du auch bedenken.
Grüße Uwe

was du lesen sollst ist
https://www.arduino.cc/reference/en/language/functions/math/map/
https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/

und bedenke:
map ...
does not constrain values to within the range, because out-of-range values are sometimes intended and useful.

daher vorsicht:
In welchem Bereich misst du temp?
Was hast du als tMin und tMax eingetragen?

in anderen Worten: bis welchem tMin soll 0 ermittelt werden, ab welchem tMax soll 255 rauskommen?

Danke für eure schnellen Antworten

void temperaturberechnung()
{
  // Nimmt N Abfragen in einer Reihe, mit einem kurzen delay
  for (int i=0; i < abfrageZahl; i++)
  {
    abfrage[i] = analogRead(ntc);
    delay(10);
  }
  
  // Mittelt alle Abfragen
  durchschnitt = 0;
  for (int i=0; i < abfrageZahl; i++)
  {
    durchschnitt += abfrage[i];
  }
  durchschnitt /= abfrageZahl;
  
  // Umwandlung des Wertes in Wiederstand
  durchschnitt = 1023 / durchschnitt - 1;
  durchschnitt = serienWiederstand / durchschnitt;
  
  // Umrechnung aller Ergebnisse in die Temperatur mittels einer Steinhard Berechnung
  temp = durchschnitt / ntcNominal;     // (R/Ro)
  temp = log(temp);                     // ln(R/Ro)
  temp /= bCoefficient;                 // 1/B * ln(R/Ro)
  temp += 1.0 / (tempNominal + 273.15); // + (1/To)
  temp = 1.0 / temp;                    // Invertieren
  temp -= 273.15;                       // Umwandeln in °C

so kommt temp zustande
@uwefed wie meinst du in einer schritten das könnte ein interessanter anzastz sein. Kannst du mir das kurz erklären wie ich das realisiere?

Satalaner:
@uwefed wie meinst du in einer schritten das könnte ein interessanter anzastz sein. Kannst du mir das kurz erklären wie ich das realisiere?

Ich heiße zwar nicht Uwe, aber ich vermute, dass er auf den Datentyp der Variable temp anspielt. Wenn Du für den Bereich, den Du überblicken/messen möchtest, nur Ganzzahlwerte herausbekommst, kann map() kaum etwas Anderes machen als große Schritte.

Wie ist temp denn deklariert?

Gruß

Gregor

Schade dass du nicht die Fragen beantwortest, die man dir stellt.

Das macht imho was, ist vollständig und kompiliert:

float temp = 0;
byte pwm = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
}

void loop() {
  // simulation messen
  temp = random(0, 400) / 10.0;
  // umrechnen 
  pwm = temp2pwm(temp, 20.0, 30.0);
  // Ausgeben
  Serial.print(F("temp="));
  Serial.print(temp);
  Serial.print(F("   pwm="));
  Serial.print(pwm);
  Serial.println();
  delay(1000);// dirty delay
}

byte temp2pwm(float in, float tMin, float tMax)
{
  if (in < tMin) return 0;
  if (in > tMax) return 255;
  byte pwm = 0;
  pwm = in / (tMax - tMin) * 255.0;
  return pwm;
}

temp=17.70 pwm=0
temp=20.80 pwm=18
temp=22.80 pwm=69
temp=13.30 pwm=0
temp=29.80 pwm=247
temp=18.10 pwm=0
temp=23.50 pwm=87
temp=1.30 pwm=0
temp=26.50 pwm=163
temp=21.40 pwm=33
temp=26.30 pwm=158
temp=13.60 pwm=0
temp=22.50 pwm=61

Hi

Schaue Dir die Beispiele zur PID-Regelung an.
Damit (also mit einem Selbst-bau) läuft der Schieber meiner Öl-Heizung (und hält den Heizungsvorlauf auf Soll 55°).
Naja - fast :wink:
Da ich bei diesem sehr trägem System zu wenig Lebenszeit habe, um Das auszutesten, ist wohl P zu niedrig und I zu hoch gewählt - habe also wohl 6...8K Schwankungsbereich (also +/-4 Grad).

MfG

noiasca:
Das macht imho was, ist vollständig und kompiliert:

.

.
.
.
byte temp2pwm(float in, float tMin, float tMax)
{
 if (in < tMin) return 0;
 if (in > tMax) return 255;
 byte pwm = 0;
 pwm = in / (tMin - tMax) * 255.0;
 return pwm;
}

Hallo,

Bitte um Entschuldigung für die Frage, falls ich den Wald vor lauter
Bäumen nicht sehe:

für tMin 20 und tMax 30 ergibt der Ausdruck in der Klammer (tMin - tMax) -10.
Ich bekomme beim Nachrechnen nicht die Werte wie angegeben.
Habe gerade keinen Arduino zum Probieren hier.

mfg

Frank

@noiasca sry habe deine Fragen übersehen :frowning:

In welchem Bereich misst du temp? 28.0 und momentan 53.5 Grad (wollte Linear von 0-255 einen Bereich haben)
Was hast du als tMin und tMax eingetragen?
float tMin = 28.0; // Untere Grenze des
float tMax = 53.5; // Obere Grenze des Temperaturbereichs

Also mein Programm sieht folgendermaßen aus:

/*Fangen wir mit den Konstanten an. Hier haben wir diesmal ein paar für die Berechnung wichtigen Werte, die ich jetzt erläutere.

    NTCNominal = 10000 - Damit ist der Wiederstand des NTC bei Nominaltemperatur gemeint. Dieser Wert wird immer im Namen schon angegeben. Ein 10kO NTC hat einen Wiederstand von 10000 Ohm.
    TempNominal = 25 - Das ist die Nominaltemperatur. Diese ist im Normalfall 25°.
    BCoefficient = 3977 - Der Beta Coefficient ist eine Materialkonstante und ist im Datenblatt des NTC zu finden und wird mit B25 bezeichnet.
    SerienWiederstand = 10000 - Das ist der Wert in Ohm, des Wiederstand, der zusammen mit dem NTC Wiederstand verbaut wird. In unserem Fall auch ein 10kO Wiederstand.

Bei den Variablen gibt es ein paar Einstellmöglichkeiten, die je nach Geschmack oder Anwendungsfall zu wählen sind. Ich habe diese Werte nur exemplarisch gewählt.

    FanMin = 60 - Das ist der kleinste PWm Wert den der Lüfter macht. Fällt der PWM Wert durch die Temperatursteuerung darunter, schält der Lüfter ab. Wählt diesen Wert so, dass euer Lüfter damit noch läuft.
    TMin = 20 - Untere Tegeltemperaturgrenze
    TMax = 60 - Obere Regeltemperaturgrenze

Über den Temperaturbereich zwischen TMin und TMax wird im Code der PWM Wert linear verteilt. Sprich 20° (TMin) = 0 PWM und 60° (TMax) = 255 PWM. Ich habe hier mit FinMin festgelegt das der Lüfter erst bei einem PWM Wert von 60 startet. Das entspricht ca. 30°. Alles was darunter liegt resultiert in einem stehenden Lüfter.

    AbfrageZahl = 5
    Abfrage[5]

AbfrageZahl und Abfrage hängen zusammen und bestimmen mit wievielen Messwerten des NTC gemittelt wird. Je höher der Wert, desto weniger sprunghaft ist der Temperaturwert, allerdings dauert die Erfassung auch länger.
*/

// Konstanten
const int fanPin = 9;                 // Pin für den Lüfter
const int ntc = A0;                   // Pin für den 10kO NTC Wiederstand
const int ntcNominal = 10000;         // Wiederstand des NTC bei Nominaltemperatur
const int tempNominal = 25;           // Temperatur bei der der NTC den angegebenen Widerstand hat
const int bCoefficient = 3977;        // Beta Coefficient(B25 aus Datenblatt des NTC)
const int serienWiederstand = 10000;  // Wert des Wiederstands der mit dem NTC in Serie geschalten ist
const int abfrageZahl = 50;            // Je mehr abfragen, desto stabiler isr das Ergebnis, dauert aber länger

// Variablen
int fanSpeed = 0;          // Variable für die Lüftergeschwindigkeit
int fanMin = 25;          // Kleinster PWM Wert für den Lüfter befor er abschält
int fanOut = 1;            // Variable zum pürfen ob der Lüfter aus war
float tMin = 28.0;             // Untere Grenze des Temperaturbereichs
float tMax = 53.5;             // Obere Grenze des Temperaturbereichs
int abfrage[abfrageZahl];  // Array Variable für das Mitteln der Temperatur
float durchschnitt = 0;    // Variable für das Mitteln der Temperatur
float temp;                // Variable für die Berechnung der temperatur nach Steinhart
int index;

void setup()
{
  TCCR1B = TCCR1B & 0b11111000 | 0x01;   // Setzt Timer1 (Pin 9 und 10) auf 31300Hz
  Serial.begin(9600);             // Baudrate für die Ausgabe am Serial Monitor
  pinMode(fanPin, OUTPUT);        // Setzt den Pin des Lüfters als Ausgang
  pinMode(ntc, INPUT);            // Setzt den Pin des NTC Wiederstands als Eingang
}


void loop()
{
  temperaturberechnung();      // Startet die Temperaturerfassungsroutine
  
  // Lüftergeschwindigkeit über den Temperaturbereich einstellen
  // TMin->0% PWM | TMax->100% PWM
 fanSpeed = map(temp, tMin, tMax, 0, 255);    

  
  // Wenn der PWM Wert unter den van FanMin fällt, schält der Lüfter ab
  if (fanSpeed < fanMin)
  {
    fanSpeed = 0;
    fanOut = 1;
  }
  
  // Hysterese
  if (fanOut == 1)
  {
    fanSpeed = 0;
  }
  
  if(temp >= 33)
  {
    if(fanOut == 1)
    {
      fanOut = 0;
      analogWrite(fanPin, 255);
    }
  }
  
  // PWM Wert auf 255 begerenzen  
  if (fanSpeed > 255)
  { 
    fanSpeed = 255;
  }
  
  // Lüftergeschwindigkeit über den Seriellen Monitor ausgeben
   // Ausgabe an den Seriellen Monitor
  Serial.print("Temperatur ");
  Serial.print(temp);
  Serial.print(" *C  ");
  Serial.print("Lueftergeschwindigkeit: ");          
  Serial.print(fanSpeed);
  Serial.print(" PWM"); 
  Serial.println();
  analogWrite(fanPin, fanSpeed);      // Den Lüfter mit dem PWM Wert ansteuern
  delay(1000);  
} 



void temperaturberechnung()
{
  // Nimmt N Abfragen in einer Reihe, mit einem kurzen delay
  for (int i=0; i < abfrageZahl; i++)
  {
    abfrage[i] = analogRead(ntc);
    delay(10);
  }
  
  // Mittelt alle Abfragen
  durchschnitt = 0;
  for (int i=0; i < abfrageZahl; i++)
  {
    durchschnitt += abfrage[i];
  }
  durchschnitt /= abfrageZahl;
  
  // Umwandlung des Wertes in Wiederstand
  durchschnitt = 1023 / durchschnitt - 1;
  durchschnitt = serienWiederstand / durchschnitt;
  
  // Umrechnung aller Ergebnisse in die Temperatur mittels einer Steinhard Berechnung
  temp = durchschnitt / ntcNominal;     // (R/Ro)
  temp = log(temp);                     // ln(R/Ro)
  temp /= bCoefficient;                 // 1/B * ln(R/Ro)
  temp += 1.0 / (tempNominal + 273.15); // + (1/To)
  temp = 1.0 / temp;                    // Invertieren
  temp -= 273.15;                       // Umwandeln in °C
  //temp =temp*100;
  
 
  delay(1000);
}

Ausgabe serieller Monitor:
Temperatur 36.10 *C Lueftergeschwindigkeit: 81 PWM
Temperatur 35.92 *C Lueftergeschwindigkeit: 71 PWM
Temperatur 35.08 *C Lueftergeschwindigkeit: 71 PWM
Temperatur 34.42 *C Lueftergeschwindigkeit: 61 PWM
Temperatur 34.36 *C Lueftergeschwindigkeit: 61 PWM
Temperatur 33.83 *C Lueftergeschwindigkeit: 51 PWM
Temperatur 33.69 *C Lueftergeschwindigkeit: 51 PWM
Temperatur 32.82 *C Lueftergeschwindigkeit: 40 PWM
Temperatur 33.13 *C Lueftergeschwindigkeit: 51 PWM
Temperatur 33.50 *C Lueftergeschwindigkeit: 51 PWM
Temperatur 32.44 *C Lueftergeschwindigkeit: 40 PWM
Temperatur 32.99 *C Lueftergeschwindigkeit: 40 PWM
Temperatur 32.44 *C Lueftergeschwindigkeit: 40 PWM
Temperatur 32.22 *C Lueftergeschwindigkeit: 40 PWM
Temperatur 32.35 *C Lueftergeschwindigkeit: 40 PWM
Temperatur 31.65 *C Lueftergeschwindigkeit: 30 PWM
Temperatur 31.96 *C Lueftergeschwindigkeit: 30 PWM
Temperatur 31.54 *C Lueftergeschwindigkeit: 30 PWM
Temperatur 31.72 *C Lueftergeschwindigkeit: 30 PWM
Temperatur 31.61 *C Lueftergeschwindigkeit: 30 PWM
Temperatur 31.59 *C Lueftergeschwindigkeit: 30 PWM
Temperatur 30.98 *C Lueftergeschwindigkeit: 0 PWM

Der Tipp mit float war echt super die Frage ist nur wie bekomme ich es hin das der PWM nicht in 10er schritten läuft sondern in 1er?

Und nochmal danke das ihr so schnell geantwortet habe echt Klasse Support :slight_smile:

Ok hab ich gepennt, MAP kann nur Ganzzahlig also für jeden der es nutzen möchte einfach die Formel von MAP nutzen. In meinem Fall

fanSpeed = (temp-tMin) * (255-0) / (tMax-tMin) + 0;

Danke euch allen

noiasca:
du hast das sehr gut erkannt.
+Karma.

Zur Hilfe gekommen ist mir nur der eingeschränkte Zahlenraum vom byte pwm.
Ich wollte eigentlich die Differenz der beiden Grenzen ermitteln. Habs nun umgedreht.

Danke.
Jetzt bin ich durchgestiegen."byte pwm" wird durch das Ergebnis der Formel
mehrfach komplett überschrieben wenn der Wert z.B. 530,4 ist.
Eigentlich ein klassischer Overflow.
Habe das mal auf einem Uno laufenlassen...

mfg

Frank

Satalaner:
nur wie bekomme ich es hin das der PWM nicht in 10er schritten läuft sondern in 1er?

Eine der möglichen Lösungen habe ich dir schon im ersten Posting (#1) überreicht.
Schlüsselfertig!
(inclusive Hinweis auf die Fehlerquelle)

War aber wohl da noch nicht wichtig genug für dich....
(Manchmal frage ich mich, wofür ich mir überhaupt die Arbeit mache?)

Meine güte combie und ich frage mich warum du autamtisch voraussetzt das man deine Lösung nicht getestet hast und direkt ein solch kindlicher post "war wohl nicht wichtig genug" nötig ist.

Tut mir leid das ich nicht gleich aus "Warum tust du das?

Map ist bei den richtigen Eingangsdaten sehr gut für deine Zwecke geeignet."

ableiten konnte das hier der MAP Befehl, NICHT der Richtige ist aufgrund der Nachkommastellen die missachtet werden.

Ich musste mich da auch erstmal reindenken und wie du siehst habe ich die Grundsätzliche Formel des MAP Befehls auch genutzt!

Hi

MAP IST dafür geeignet - Das ist ja der Knackpunkt an der Aussage!

mfG

postmaster-ino:
MAP IST dafür geeignet - Das ist ja der Knackpunkt an der Aussage!

Wollte ich auch gerade sagen....

Map ist bei den richtigen Eingangsdaten sehr gut für deine Zwecke geeignet.

Das Problem liegt nicht in map(), sondern bei den Eingangsdaten.
Und genau das, wie man diese transformiert, habe ich versucht dir zu zeigen.