Automatische Temperaturregelung mit Servomotor - Jeep Wrangler 1997

Hi Leute,
Ich bin gerade dabei an meinem Jeep Wrangler TJ zu schrauben und kam jetzt auf die Idee, die alte Temperatursteuerung für die Heizung, durch eine automatisierte Variante mittels Arduino zu ersetzen.

Das Projekt beinhaltet einen Arduino Nano, 3 DS18B20 Temperatursensoren, einen 20kg Servomotor und ein OLED-Display.

Aktuell wird die Temperatur mit einem Schieberegler gesteuert, der über einen Seilzug mit einer Klappe verbunden ist, die sich in einem Bereich von ca. 90° bewegt. An diesem "Drehpunkt" will ich einen kleinen Servomotor anbringen, der das manuelle Umstellen automatisieren soll.
Ich habe aktuell schon ein bisschen was an Code geschrieben, der soweit auch schon einigermaßen gut funktioniert, allerdings komme ich mittlerweile so langsam an's Ende meiner Programmierfähigkeiten :confused:

Ziel ist es, den Servomotor schrittweise in Richtung Kalt (135°), bzw. Warm (45°) zu bewegen, bis die Temperatur im Innenraum (in_temp) mit der gesetzten Temperatur (set_temp) übereinstimmt.
(Der Sensor für den Außenbereich dient nur zur Anzeige und ist für die Regelung nicht relevant)

Dabei hätte ich gerne einen gewissen Toleranzbereich (ca. +- 1°C), damit der Servomotor nicht dauerhaft Nachregeln muss.
Außerdem sollte es zwischen den Versuchen die Temperatur anzupassen, eine gewisse Verzögerung geben, da ich ein ständiges Voll-AUF/Voll-ZU der Klappe vermeiden möchte :slight_smile:

Delays würde ich gerne vermeiden und auf millis() zurückgreifen, allerdings weiß ich gerade nicht so genau wie ich das am besten angehen sollte...

Ich hoffe Ihr könnt mir weiterhelfen!
Für Verbesserungsvorschläge am vorhandenen Code bin ich immer offen :smiley:

#include <Servo.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
#include <Wire.h>

#define ONE_WIRE_BUS_OUT 2    // Pins für Temperatursensoren definieren
#define ONE_WIRE_BUS_IN_L 3
#define ONE_WIRE_BUS_IN_R 4

const int servopin = 8;     //Pin für Servomotor
const int btn_minus = 6;    // Pins für die Taster zur Temperatureinstellung (- / +)
const int btn_plus = 7;

int out_temp;   //Festlegen der Variablen für die Temperaturwerte
int inl_temp;
int inr_temp;
int in_temp;
int set_temp = 24;

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 2000;

int angle = 90;   // Startwinkel für Servo (Mitte)
int state_minus;
int state_plus;
int val_minus;
int val_plus;

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

Servo servo;    // Namen für Servomotor definieren

OneWire temp_outside(ONE_WIRE_BUS_OUT);
OneWire temp_inside_left(ONE_WIRE_BUS_IN_L);
OneWire temp_inside_right(ONE_WIRE_BUS_IN_R);

DallasTemperature temp_out(&temp_outside);
DallasTemperature temp_in_l(&temp_inside_left);
DallasTemperature temp_in_r(&temp_inside_right);

void setup()

{
  servo.attach(servopin);   // Initialisierung Servomotor
  Serial.begin(115200);
  temp_out.begin();
  temp_in_l.begin();
  temp_in_r.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);    // Initialisierung OLED-Display
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(1, 0);
  display.println("TEMP-Test");
  display.display();
  delay(2000);
  display.clearDisplay();
  display.display();
  pinMode(btn_minus, INPUT_PULLUP);   // Taster als Eingänge definieren
  pinMode(btn_plus, INPUT_PULLUP);
  state_minus = digitalRead(btn_minus);   // Startzustand der Taster auslesen
  state_plus = digitalRead(btn_plus);
}

void loop() {
  currentMillis = millis();   // Start des millis-Timers
  val_minus = digitalRead(btn_minus);
  val_plus = digitalRead(btn_plus);

  if (val_minus != state_minus) {
    if (val_minus == LOW) {
      set_temp--;               // Bei Tastendruck -> Temperatur um 1 verringern
    }
  }
  state_minus = val_minus;

  if (val_plus != state_plus) {
    if (val_plus == LOW) {
      set_temp++;               // Bei Tastendruck -> Temperatur um 1 erhöhen
    }
  }
  state_plus = val_plus;

  if (currentMillis - startMillis >= period) {
    temp_out.requestTemperatures();     // Abfrage der Temperatursensoren
    temp_in_l.requestTemperatures();
    temp_in_r.requestTemperatures();

    display.clearDisplay();
    display.setTextColor(WHITE);
    display.setTextSize(0.5);
    display.setCursor(1, 0);

    out_temp = temp_out.getTempCByIndex(0);   // Speichern der Temperaturen in Variablen ohne Nachkommastellen
    inl_temp = temp_in_l.getTempCByIndex(0);
    inr_temp = temp_in_r.getTempCByIndex(0);
    in_temp = ((inl_temp + inr_temp) / 2);    // Mittelwert der beiden Temperatursensoren für den Innenbereich

    display.print("Out: ");     // Ausgabe der Werte auf dem OLED-Display
    display.print(out_temp);
    display.print(" IN: ");
    display.println(in_temp);
    display.print("In-L: ");
    display.print(inl_temp);
    display.print(" SET: ");
    display.println(set_temp);
    display.print("In-R: ");
    display.println(inr_temp);
    display.print("Angle: ");  // Aktueller Servo-Winkel
    display.println(angle);
    display.display();

    startMillis = millis();    // millis-Timer zurücksetzen
  }

  if (in_temp > set_temp && angle < 135) {    // Anpassen des Servowinkels bei Temperaturabweichung (135° = Kalt)
    angle++;
    servo.write(angle);
    delay(200);
  }
  if (in_temp < set_temp && angle > 45) {     // Anpassen des Servowinkels bei Temperaturabweichung (45° = Warm)
    angle--;
    servo.write(angle);
    delay(200);
  }

  if (angle < 45) {       // Servo-Winkel begrenzen
    angle = 45;
  }
  if (angle > 135) {
    angle = 135;
  }
}

(deleted)

Das hast du ja schon drin.
Das wichtige ist übrigens nicht der millis() Aufruf, sondern die Variable (unsigned long startmillis bei dir).
Da brauchst du für jeden gleichzeitigen und unabhängigen Zyklus eine eigene.

michael_x:
Das hast du ja schon drin.
Das wichtige ist übrigens nicht der millis() Aufruf, sondern die Variable (unsigned long startmillis bei dir).
Da brauchst du für jeden gleichzeitigen und unabhängigen Zyklus eine eigene.

Das habe ich zwar schon drin, allerdings bekomme ich trotzdem jedes mal ne Verzögerung von ca. 1 Sekunde, wenn die Abfrage der Temperaturen erfolgt und die Werte auf das Display geschrieben werden.
In der Zeit bleibt der Servo dann auch immer kurz stehen, obwohl er eigentlich regelmäßig laufen sollte... :confused:

Hat denn jemand ne Idee wie ich das mit den Temperatur-Toleranzen einrichten könnte, so dass der Servo nicht ständig nach regelt und gleichzeitig auch eine Verzögerung bis zum nächsten Angleichungsversuch zu programmieren?

(deleted)

Peter-CAD-HST:
Moin

probier es mal mit einem Mittelwert der Temperatur aus.

Gruss Peter

und gesund bleiben

Hi,
Danke erst mal für die Antwort!

Wie genau meinst du das mit dem Mittelwert?
Hast du vielleicht ein Beispiel für mich?

(deleted)

Hi

Die Sekunde könnten auch gute Zwei sein?
3x DS18B20 abfragen, blockierend (?) zu je 750ms macht 2250ms.
Schau Dir die Beispiele zum DS18B20 an - man kann die Temperatur auch anfragen und NACH den 750ms abholen (und erneut anfragen).
Dann hast Du alle 750ms eine aktuelle Zemperatur (bei 12 Bit) - wenn Dir auch halbe Grad reichen. die Auflösung auf 9 Bit umstellen - dort sollte die Blockade kaum stören (meine 97ms? müsste nachschauen/rechnen).

MfG

postmaster-ino:
....müsste nachschauen/rechnen).

Auflösung einstellen 9, 10, 11, 12 --> 0.5°C, 0.25°C, 0.125°C, 0.0625°C --> 93.75 ms, 187.5 ms, 375 ms, 750 ms

Gruß Fips

Hallo,

bau Dir doch eine vernünftige Hysrese ein bei der nichts passiert.

if ( istwert > (Sollwert+hysterese))
kühlen

if ( istwert < (Sollwert -hysterese))
heizen

wenn ich das richtig gesehen habe arbeitest Du mit Integer Werten. da gibts nur 24 oder 25 Grad Damit kannst Du schlecht auf +-1Grad regeln. Die Temperatur liegt doch eigendlich als float vor , dann nimm das doch auch.

Heinz

...hatte es auch schon wieder vergessen :slight_smile: Danke Dir

Damit kannst Du schlecht auf +-1Grad regeln

Selbst mit einem optimal eingestellten PID Regler wäre das sehr anspruchsvoll.

Für den Programmierer des Servo ist es evtl. hübsch, wenn man ihn eifrig "regeln" sieht, für alle anderen sieht es mehr nach instabilem oder mindestens überempfindlichem Regler aus.

Miss erstmal die Zeit und die EndtemperaturÄnderung bei einer einzelnen Verstellung um x Winkelgrad (bei wechselnden Bedingungen, wenn du soviel Zeit hast), das hilft beim Abschätzen der erforderlichen Wartezeiten und der möglichen Regelgüte.

michael_x:
Das wichtige ist übrigens nicht der millis() Aufruf, sondern die Variable (unsigned long startmillis bei dir).
Da brauchst du für jeden gleichzeitigen und unabhängigen Zyklus eine eigene.

Danke dir noch mal für den Hinweis, ich glaube ich habe es jetzt einigermaßen sinnvoll eingebaut.

postmaster-ino:
Die Sekunde könnten auch gute Zwei sein?
3x DS18B20 abfragen, blockierend (?) zu je 750ms macht 2250ms.

Ja, da hast du wohl recht, danke für die Info!
Habe die Auflösung jetzt auf 0.25° eingestellt und es läuft etwas flüssiger.

Rentner:
Hallo,
bau Dir doch eine vernünftige Hysrese ein bei der nichts passiert.

if ( istwert > (Sollwert+hysterese))

kühlen

if ( istwert < (Sollwert -hysterese))
heizen

Danke für den Tipp. Manchmal können die Dinge so einfach sein :smiley:
Ich habe die Werte angepasst und es läuft soweit ganz gut, allerdings habe ich jetzt noch das Problem, dass der Servo immer mal wieder zuckt, auch wenn er schon an seiner Endposition (135°/45°) angelangt ist. Das ist zwar nicht wirklich schlimm, aber trotzdem irgendwie nervig und es wäre schöner wenn man es irgendwie vermeiden könnte.
Außerdem habe ich das Problem, dass der Servo jedes mal beim Start mit voller Geschwindigkeit auf seine Startposition von 90° fährt. Besser wäre, der würde die letzte Position beibehalten, allerdings weiß ich nicht ob das irgendwie machbar ist...

Hier noch mal der angepasste Code:

#include <Servo.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
#include <Wire.h>

#define ONE_WIRE_BUS_OUT 2    // Define Pins for Temp-Sensors
#define ONE_WIRE_BUS_IN_L 3
#define ONE_WIRE_BUS_IN_R 4

const int servopin = 8;     //Pin for Servo
const int btn_minus = 6;    // Pins to Buttons for Temperature Control (- / +)
const int btn_plus = 7;

float out_temp;   //Set Variables for Temp-Values
float inl_temp;
float inr_temp;
float in_temp;
int set_temp = 25;

unsigned long currentMillis;
unsigned long startMillisDisplay;
unsigned long startMillisRegulate;
const unsigned long periodDisplay = 3000;
const unsigned long periodRegulate = 300;

int angle = 90;   // Starting angle for Servo
int state_minus;
int state_plus;
int val_minus;
int val_plus;

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

Servo servo;    // Define Name for Servo

OneWire temp_outside(ONE_WIRE_BUS_OUT);
OneWire temp_inside_left(ONE_WIRE_BUS_IN_L);
OneWire temp_inside_right(ONE_WIRE_BUS_IN_R);

DallasTemperature temp_out(&temp_outside);
DallasTemperature temp_in_l(&temp_inside_left);
DallasTemperature temp_in_r(&temp_inside_right);

void setup()

{
  servo.attach(servopin);   // Initialize Servo
  Serial.begin(115200);
  temp_out.begin();
  temp_in_l.begin();
  temp_in_r.begin();
  temp_out.setResolution(10);
  temp_in_l.setResolution(10);
  temp_in_r.setResolution(10);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);    // Initialize OLED-Diplay
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(1, 0);
  display.println("TEMP-Test");
  display.display();
  delay(2000);
  display.clearDisplay();
  display.display();
  pinMode(btn_minus, INPUT_PULLUP);   // Define Buttons as Inputs
  pinMode(btn_plus, INPUT_PULLUP);
  state_minus = digitalRead(btn_minus);   // Read starting state of Buttons
  state_plus = digitalRead(btn_plus);
}

void loop() {
  currentMillis = millis();   // Start of the millis-Timer
  val_minus = digitalRead(btn_minus);
  val_plus = digitalRead(btn_plus);

  if (val_minus != state_minus) {
    if (val_minus == LOW) {
      set_temp--;               // Button press -> Temperature -1
    }
  }
  state_minus = val_minus;

  if (val_plus != state_plus) {
    if (val_plus == LOW) {
      set_temp++;               // Button press -> Temperature +1
    }
  }
  state_plus = val_plus;

  if (currentMillis - startMillisDisplay >= periodDisplay) {
    temp_out.requestTemperatures();     // Read out Temperatures
    temp_in_l.requestTemperatures();
    temp_in_r.requestTemperatures();

    out_temp = temp_out.getTempCByIndex(0);   // Saving Temperatures in Variables
    inl_temp = temp_in_l.getTempCByIndex(0);
    inr_temp = temp_in_r.getTempCByIndex(0);
    in_temp = ((inl_temp + inr_temp) / 2);    // Average Temperature of both Sensors in the Inside

    display.clearDisplay();
    display.setTextColor(WHITE);
    display.setTextSize(0.5);
    display.setCursor(1, 0);

    display.print("Out: ");     // Write values to OLED-Display
    display.print(out_temp);
    display.print(" IN: ");
    display.println(in_temp);
    display.print("In-L: ");
    display.print(inl_temp);
    display.print(" SET: ");
    display.println(set_temp);
    display.print("In-R: ");
    display.println(inr_temp);
    display.print("Angle: ");  // Actual Servo angle
    display.println(angle);
    display.display();

    startMillisDisplay = millis();    // Reset millis-Timer
  }
  if (currentMillis - startMillisRegulate >= periodRegulate) {
    if (in_temp > (set_temp + 1) && angle < 135) { // Adjusting servo angle in the event of temperature deviations (135° = Coldest setting)
      angle++;
      servo.write(angle);
    }
    if (in_temp < (set_temp - 1) && angle > 45) {  // Adjusting servo angle in the event of temperature deviations (45° = Warmest setting)
      angle--;
      servo.write(angle);
    }

    if (angle < 45) {       // Limit Servo angle
      angle = 45;
    }
    if (angle > 135) {
      angle = 135;
    }
    startMillisRegulate = millis();
  }
}

Ridgeback09:
(Der Sensor für den Außenbereich dient nur zur Anzeige und ist für die Regelung nicht relevant)

Eine "Automatische Heizungsregelung" ohne die Aussentemperatur mit einzubeziehen?
Warum den das. Die nötige Vorlauftemperatur stelle ich abhängig von der Aussentemperatur.

Franz

Edit: Kommando zurück. Ich habe die Anforderung misstverstanden. Ich dachte das wird eiene Gebäudeheuzung die "Jeep Wrangler <JP" heißt, dabei ist es eiene Autoheizung. Aber auch da ist es wichtig dorch die Aussentemperatur, erst mal eine Hausnummer als Vorgabe für die vorlauftemperatur nach Aussentemperatur

Franz54:
Eine "Automatische Heizungsregelung" ohne die Aussentemperatur mit einzubeziehen?
Warum den das. Die nötige Vorlauftemperatur stelle ich abhängig von der Aussentemperatur.

Franz

Edit: Kommando zurück. Ich habe die Anforderung misstverstanden. Ich dachte das wird eiene Gebäudeheuzung die "Jeep Wrangler <JP" heißt, dabei ist es eiene Autoheizung. Aber auch da ist es wichtig dorch die Aussentemperatur, erst mal eine Hausnummer als Vorgabe für die vorlauftemperatur nach Aussentemperatur

Und wie genau würdest du die mit einbeziehen?
Wenn ich Innen die Temperatur messe und die Klappe entsprechend einstelle, bis die Temperatur passt, ist die Außentemperatur doch völlig egal, oder sehe ich das falsch?
Bei -10°C außen, werden es im Auto wohl kaum 20°C sein, wenn ich den Motor starte :wink:

Hat sich schon jemand zum Thema „Zulassung“ geäußert? Oder ist das hier nicht relevant?

Gruß

Gregor

gregorss:
Hat sich schon jemand zum Thema „Zulassung“ geäußert? Oder ist das hier nicht relevant?

Gruß

Gregor

Was hat das Thema "Innenraumheizung" mit der Zulassung zu tun??
Im Innenraum eines Fahrzeugs kannst du so gut wie alles machen wie du willst, solange es nichts mit den sicherheitsrelevanten Teilen des Autos zu tun hat.

Ridgeback09:
Was hat das Thema "Innenraumheizung" mit der Zulassung zu tun??

Keine Ahnung. Deshalb frage ich ja. Ich kenne mich null damit aus, bekomme aber immer wieder mit, was hierzuforum an Bedenken geäußert wird.

Gruß

Gregor

backlinkpro1x:
Thank you!!!

You're welcome but for what? :smiley:

Ridgeback09:
Und wie genau würdest du die mit einbeziehen?
Wenn ich Innen die Temperatur messe und die Klappe entsprechend einstelle, bis die Temperatur passt, ist die Außentemperatur doch völlig egal, oder sehe ich das falsch?
Bei -10°C außen, werden es im Auto wohl kaum 20°C sein, wenn ich den Motor starte :wink:

Man kann Vorgabewerte benutzen. Also wenn es draussen -10 Grad sind, wird eine Klappenstellung von sagen wir mal 10 Grad Öffnung sicher zu wenig sein. Da kann man gleich mal auf vielleicht 70 Grad Klappenstellung gehen. Also man braucht sich dann nicht langsam rantasten, sondern kann gleich mal auf eine Stellung gehen, die bei der Aussentemperatur wohl annähernd hinkommt. Und dann die feinarbeit. Also das wäre wohl mein Weg.