Habe einen einfachen Framepool und eine Solarmatte 0,68x4m.
Die Solarmatte ist über ein einfaches Bypass-System angeschlossen:
2 T Stücke zur Solarmatte, dazwischen ein 2 Wege Ventil
Wenn man das Ventil teilweise oder ganz zudreht wird das Wasser durch die Solarmatte geleitet. Ist das Ventil offen geht es den direkten Weg und vermeidet wegen des Durchflusswiderstandes die Solarmatte
==> Gibt es fertig auf Ebay oder Amazon.
Problem bei dem Ding: Heizt wunderbar.
Nur wenn gerade ein kälterer Tag oder gar Regen ist, kühlt es den Pool wunderbar.
Abhilfe ist eine elektronische Regelung damit man nicht selber andauernd manuell nachstellen muss.
Nur kostet ein simples Motorventil 200€ und mehr, eine fertige Solarsteuerung noch mehr.

Mein Aufbau besteht aus einem Arduino Uno, ein LCD-Keypad Shield, 2x ds18b20 Temperatursensoren, einem kräftigen Modellbauservo und ein 6V Netzteil. Programmiert auch in Arduino weil es da die fertigen libs für die ds18b20 gibt.
die Datenleitung der 2 Temperatursensoren werden an PIN 2 angeschlossen, der PWM Ausgang für das Servo an PIN 3.

Der Temperatursensor für Solar ist auf die Rückseite der Solarmatte aufgeklebt, so dass halbwegs die reale Wassertemperatur und nicht nur die Temperatur in der Sonne gemessen wird.
Der Temperatursensor für den Pool ist am Wasserschlauch zum Bypassventil befestigt. An den Schlauch angelegt, Klebeband drüber, noch mit Schaumstoff umwickelt, die gemessenen Werte passen ganz gut mit einem anderen Poolthermometer das im Wasser hängt überein.

Mit den 2 linken Tasten stellt man die Pool Solltemperatur ein.
Mit den 2 Tasten auf/ab die Hysterese: ab 1-10°C an der Solarmatte über aktueller Pooltemperatur wird das Servo betätigt um das Ventil zu drosseln. Wenn die Temperatur an der Solarmatte nur mehr 0.2°C über der Pooltemperatur liegt wird mit dem Servo das Ventil geöffnet um ein auskühlen des Pools über die Solarmatte zu verhindern.
Bei erreichen der Pool Solltemperatur bleibt das Ventil ebenfalls offen.

Die Werte werden gleich im EEPROM gespeichert, dh Strom an und die Regelung funktioniert.
Das Servo wird nur bei Änderungen für 2 Sekunden angesteuert, danach ist der PWM Pin aus und das Servo macht nichts und verbraucht auch keinen Strom.

Man könnte mit dem Servo auch auf eine bestimmte Temperaturdifferenz hinregeln, also den Durchfluss so regeln das an der Solarmatte immer 1°C mehr als im Pool ist.
Aber bisher ist nur die primitive ein-aus Regelung verbaut wie man sie auch kommerzielle Regler haben

Kostenpunkt für den ganzen Spass ca 30€ + Kosten für das Bypasssystem ( ab ca 20€)
Wenn das Ventil zu schwergängig ist, aufschrauben und den Einsatz leicht herausdrehen.
Die Kunst ist es den Einsatz so weit herauszudrehen das es leichtgängig und trotzdem halbwegs dicht ist.

Hier in einer alten Süßigkeitendose wasserdicht reingeschmissen:
Klicke auf die Grafik für eine größere Ansicht

Name:	20190628_1.jpg
Hits:	34
Größe:	58,4 KB
ID:	34238


Ansteuerng vom Ventil mit Servo. Alles will ich gar nicht durch die Matte leiten, die schafft nicht den ganzen Durchfluss der Pumpe.
Klicke auf die Grafik für eine größere Ansicht

Name:	20190628_2.jpg
Hits:	31
Größe:	63,4 KB
ID:	34239

Code, schnell hingepfuscht und sicher verbesserungswürdig:
Code:
#include <EEPROM.h>

#include <LiquidCrystal.h>

#include <OneWire.h>

#include <DallasTemperature.h>



LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5



#define ein 1
#define aus 2
#define Servopin_high  (PORTD |= (1<<DDD3))
#define Servopin_low  (PORTD &= ~(1<<DDD3))

int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor
 // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
 // we add approx 50 to those values and check to see if we are close
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 // For V1.1 us this threshold
 if (adc_key_in < 50)   return btnRIGHT;
 if (adc_key_in < 250)  return btnUP;
 if (adc_key_in < 450)  return btnDOWN;
 if (adc_key_in < 650)  return btnLEFT;
 if (adc_key_in < 850)  return btnSELECT;
}

//-------------------------------
#define ONE_WIRE_BUS 2               //Pin 17 als OneWire 
OneWire ourWire(ONE_WIRE_BUS);        /* Ini oneWire instance */
DallasTemperature sensors(&ourWire); 



float Pooltemp,Solartemp,Hys; 
char buffer[20];
int dallas_sensor_counter,Poolsoll,Hysterese,servo,servo_alt;
unsigned long millis_alt,millis_altx;
volatile char servopin;

// Servosteuerung von https://www.pololu.com/docs/0J57/8.a
// This line specifies what pin we will use for sending the
// signal to the servo.  You can change this.
//#define SERVO_PIN 3  ==> direkter Befehl an PIN
 
// This is the time since the last rising edge in units of 0.5us.
uint16_t volatile servoTime = 0;
 
// This is the pulse width we want in units of 0.5us.
uint16_t volatile servoHighTime = 3000;
 
// This is true if the servo pin is currently high.
boolean volatile servoHigh = false;



void Anzeige()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Pool");
lcd.setCursor(0,1);
lcd.print("Solar");
}
//---------------------------------------------
void setup() {
lcd.begin(16, 2); // start the library  
Anzeige();

sensors.begin();
sensors.setResolution(12); 
 
 
//pinMode(3, OUTPUT);   // sets the pin3 as output
DDRD |=(1<<DDD3); // servo pin3 as output



Poolsoll=EEPROM.read(10);
Hysterese=EEPROM.read(12);
if (Poolsoll<20) Poolsoll=20;
if (Poolsoll>32) Poolsoll=32;
if (Hysterese<1) Hysterese=1;
if (Hysterese>10) Hysterese=10;


 millis_alt=0;
 millis_altx=0;
 
   
  // Turn on CTC mode.  Timer 2 will count up to OCR2A, then
  // reset to 0 and cause an interrupt.
  TCCR2A = (1 << WGM21);
  // Set a 1:8 prescaler.  This gives us 0.5us resolution.
  TCCR2B = (1 << CS21);
   
  // Put the timer in a good default state.
  TCNT2 = 0;
  OCR2A = 255;
   
  TIMSK2 |= (1 << OCIE2A);  // Enable timer compare interrupt.
  sei();   // Enable interrupts.

}

void servoSetPosition(uint16_t highTimeMicroseconds)
{
  TIMSK2 &= ~(1 << OCIE2A); // disable timer compare interrupt
  servoHighTime = highTimeMicroseconds * 2;
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}

ISR(TIMER2_COMPA_vect)
{
  // The time that passed since the last interrupt is OCR2A + 1
  // because the timer value will equal OCR2A before going to 0.
  servoTime += OCR2A + 1;
   
  static uint16_t highTimeCopy = 3000;
  static uint8_t interruptCount = 0;
   
  if(servoHigh)
  {
    if(++interruptCount == 2)
    {
      OCR2A = 255;
    }
 
    // The servo pin is currently high.
    // Check to see if is time for a falling edge.
    // Note: We could == instead of >=.
    if(servoTime >= highTimeCopy)
    {
      // The pin has been high enough, so do a falling edge.
      Servopin_low;
      servoHigh = false;
      interruptCount = 0;
    }
  } 
  else
  {
    // The servo pin is currently low.
     
    if(servoTime >= 40000)
    {
      // We've hit the end of the period (20 ms),
      // so do a rising edge.
      highTimeCopy = servoHighTime;
      if (servopin==ein) Servopin_high; // nur einschalten wenn nötig
      servoHigh = true;
      servoTime = 0;
      interruptCount = 0;
      OCR2A = ((highTimeCopy % 256) + 256)/2 - 1;
    }
  }
}
//---------------------------------------------
//------------------------------------------------
void loop() {


  {
if (millis()-millis_alt>=1000UL)
 {
    sensors.requestTemperatures();  // Temperaturen abfragen
      if (dallas_sensor_counter==0) Pooltemp = sensors.getTempCByIndex(0)-0.9;
      if (dallas_sensor_counter==1) Solartemp = sensors.getTempCByIndex(1);
      dallas_sensor_counter++;if (dallas_sensor_counter>=2)dallas_sensor_counter=0;


    millis_alt=millis();
 }

  
    dtostrf( Pooltemp, 5, 1, buffer );  
     lcd.setCursor(6,0);
     lcd.print(buffer);
    dtostrf( Solartemp, 5, 1, buffer );  
     lcd.setCursor(6,1);
     lcd.print(buffer);
  
     if (Solartemp>=Pooltemp+(float)Hysterese) 
     {
     servo=ein;  
      }
     if (Solartemp<=Pooltemp+0.2) 
     {
     servo=aus;  
      }
    
    if (Pooltemp>(float)Poolsoll) 
     {
       servo=aus;     
      }

    if (Solartemp<10) servo=aus; // bei Fehler im Sensor ausschalten
    if (Pooltemp<10) servo=aus;  
    
  }

  

if (servo!=servo_alt) // nur Servo einschalten wenn sich der Status ändert.
{
lcd.setCursor(13,0); lcd.print("x");
millis_altx=millis();

    if (servo== ein)
    {
    lcd.setCursor(13,1);
    servoSetPosition(1900);                      
    lcd.print("Ein");
    }
    if (servo== aus)
    {
     lcd.setCursor(13,1);     
     servoSetPosition(1100); 
     lcd.print("Aus");
    }
    
servo_alt= servo; 
servopin=ein; 
}
if (millis()-millis_altx>=2000UL)  {servopin=aus;lcd.setCursor(13,0); lcd.print(" ");} // nach ca 2s Servo ausschalten

read_LCD_buttons();
lcd_key = read_LCD_buttons();
if (lcd_key==btnSELECT) 
  {
  Poolsoll=Poolsoll-1; if (Poolsoll<20) Poolsoll=20;
  EEPROM.update(10, Poolsoll);
  dtostrf( Poolsoll, 5, 1, buffer ); 
    lcd.clear(); 
    lcd.setCursor(0,0);
   lcd.print("Pool Soll Temp");
   lcd.setCursor(0,1);
   lcd.print(buffer); 
   _delay_ms(1500);Anzeige();
  
  }
if (lcd_key==btnLEFT) 
  {
   Poolsoll=Poolsoll+1; if (Poolsoll>32) Poolsoll=32;
  EEPROM.update(10, Poolsoll);
   dtostrf( Poolsoll, 5, 1, buffer ); 
    lcd.clear(); 
   lcd.setCursor(0,0);
   lcd.print("Pool Soll Temp");
   lcd.setCursor(0,1);
   lcd.print(buffer); 
   _delay_ms(1500);Anzeige();
  }
if (lcd_key==btnUP) 
  {
  Hysterese=Hysterese+1; if (Hysterese>10) Hysterese=10;
  EEPROM.update(12, Hysterese);
   dtostrf( Hysterese, 5, 1, buffer ); 
   lcd.clear();  
   lcd.setCursor(0,0);
   lcd.print("Hysterese");
   lcd.setCursor(0,1);
   lcd.print(buffer); 
   _delay_ms(1500);Anzeige();
  }
if (lcd_key==btnDOWN) 
  {
  Hysterese=Hysterese-1; if (Hysterese<1) Hysterese=1;
  EEPROM.update(12, Hysterese);
   dtostrf( Hysterese, 5, 1, buffer ); 
    lcd.clear(); 
   lcd.setCursor(0,0);
   lcd.print("Hysterese");
   lcd.setCursor(0,1);
   lcd.print(buffer); 
   _delay_ms(1500);Anzeige();
  }
if (lcd_key==btnRIGHT) {}