Termostato con Arduino e sensore DS18B20

Termostato con Arduino e sensore DS18B20

In questo articolo ti mostro come ho realizzato un termostato con Arduino ed il sensore di temperatura DS18B20.

Tutto ha inizio con un piccolo scaldabagno di casa che smette di funzionare, dopo qualche indagine ed un pò di ricerche su internet capisco che quello che si è rotto è proprio il termostato.

Bene ho pensato, una decina di euro e lo scaldabagno tornerà a funzionare…
E no!
Non è proprio così.
Questo termostato non si trova facilmente, e se lo trovi costa pure un pochino. Onestamente non mi va di spendere più di 20€ per un termostato, quando con una sessantina di euro ricomprerei l’intero scaldabagno. Su Amazon supera i 30€.

Questo è il termostato in questione. Da quel che ho capito, rileva la temperatura stando a contatto con una parte non isolata termicamente dello scaldabagno.

Parte la sfida

A questo punto decido di realizzare un termostato con Arduino e quel po di materiale che mi trovo al seguito.

Anni fa in uno stock di componenti presi online mi sono ritrovato dei sensori di temperatura DS18B20 compresi di rivestimento impermeabile e cavetto in gomma resistente. Un po di ricerche e decido che potrebbe essere il sensore che fa al caso mio.

Sensore DS18B20

Il sensore DS18B20 è un termometro digitale con una risoluzione programmabile tra i 9 e i 12 bit. Consente di misurare con precisione temperature che vanno dai -55° ai + 125° in ambienti umidi e nel range -10° + 85° l’approssimazione è di ±0.5°.

Le informazioni vengono scambiate tramite un’interfaccia denominata 1-Wire, quindi su un solo filo. Per questo il sensore si compone da soli tre pin, due per l’alimentazione e uno per lo scambio dati.

L’alimentazione può variare tra i 3V e i 5.5V ed oltre alla rilevazione di temperatura è possibile settare degli allarmi direttamente sul dispositivo, in modo da far scattare procedure di sicurezza in caso di temperature oltre i range consentiti.

Una cosa molto interessante è che ogni dispositivo realizzato possiede un seriale univoco per cui sullo stesso bus dati possono coesistere teoricamente un numero illimitato di sensori.

Per maggiori dettagli ti invito a leggere il datasheet qui.

Libreria Arduino per sensore DS18B20

Esistono diversi modi per interfacciare Arduino con il sensore DS18B20, si potrebbe direttamente dialogare tramite la libreria OneWire (quindi armarsi di pazienza e seguire le istruzioni sul datasheet per lo scambio dei dati) o utilizzare una delle librerie dedicate presenti online.

Trattandosi nel mio caso di un applicazione abbastanza basilare, mi sono “accontentato” della libreria DS18B20_RT di RobTillaart. Si tratta di una versione semplificata ed alleggerita della più completa e ricca di metodi Arduino-Temperature-Control-Library di Miles Burton.

Se vorrai impiegare questo tipo di sensore in uno dei tuoi progetti, ti consiglio di dargli un’occhiata ad entrambe e valutare quale conviene in base alle tue esigenze.

Termostato con Arduino funzionamento iniziale

Inizialmente doveva essere una cosa da quattro linee di codice, ma poi man mano che scrivevo mi rendevo conto che le quattro linee non bastavano più.

In principio il film prevedeva questo:

  • Leggi la temperatura
  • Confrontala con quella impostata tramite potenziometro dall’utente
  • Se inferiore, attiva il relè (quindi dai corrente alla resistenza)
  • Se superiore disattiva il relè
  • Easy

Ma poi la cosa si è fatta più interessante… intanto devo mostrare in qualche modo il valore di temperatura che si sta cercando di impostare, quindi ho pensato ad un display e visto che ultimamente ho smanettato con degli OLED 128×64, ho optato per quelli.

Seconda cosa: se va via la corrente, la temperatura impostata dall’utente deve essere memorizzata da qualche parte, e per questo ho sfruttato l’EEPROM di Arduino come visto in un precedente articolo.

Cosa succede se per qualche motivo il sensore va in errore o Arduino si blocca ed in quel momento il relè è attivo? Riscaldo l’acqua ad oltranza? Credo non sia il caso… Quindi ho pensato anche a questa eventualità.

Durante le prime prove ho realizzato che il potenziometro non è un valido alleato in queste circostanze perché il valore della sua lettura è costantemente mutevole e a me serve invece una lettura stabile.

Quindi via il potenziometro e largo all’encoder 😎

Termostato con Arduino cosa è diventato

Come avrai notato sono partito da una cosuccia da niente per finire per realizzare qualcosa di ben diverso.

Il termostato che ho realizzato utilizza quindi un piccolo display OLED da 128×64 pixel per mostrare a video le informazioni, un encoder per ricevere le impostazioni dall’utente, la EEPROM di Arduino per salvare dati in memoria, il sensore DS18B20 per leggere la temperatura, un relè per comandare il carico e per essere sicuro che il programma non rimanga mai in stallo, ho utilizzato il watchdog come visto nell’ultimo articolo.

Il display del termostato

Ecco come si presenta il display del termostato:

Schema Termostato con Arduino

Schema termostato con Arduino
Schema elettrico termostato con Arduino

Lo schema è abbastanza semplice da replicare, da prestare attenzione sulla resistenza di pull-up da 4.7K collegata tra il bus del sensore e i +5V. Senza questa resistenza potresti avere problemi di comunicazione.

Codice Termostato con Arduino

Come potrai notare dalle quattro linee di codice preventivate all’inizio, se ne sono accumulate molte di più.

Per mantenere organizzato e più leggibile il codice, come buona norma, ho diviso in funzioni separate le varie parti del programma per arrivare ad ottenere un ciclo di loop che si presenta così:

void loop() {
  // Controlla pressione dello switch sull'encoder
  checkSwitch();

  // Leggi la temperatura
  readTemp();

  // Stampa dati sul display
  printData();

  // Gestisci relé
  controlRelay();

  // Resetta Watchdog
  wdt_reset();
}

Per non inondare di commenti l’intero listato, le parti di codice che si riferiscono ad argomenti che ho già trattato sono scarsamente descritte. Per qualsiasi dubbio dai un’occhiata agli articoli linkati nel post o utilizza la sezione commenti a fondo pagina.

/*
   Termostato con Arduino e sensore DS18B20

   Autore  : Andrea Lombardo
   Web     : http://www.lombardoandrea.com
   Post    : https://wp.me/p27dYH-RX
*/

// Inclusione librerie
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Bounce2.h>
#include <DS18B20.h>
#include <EEPROM.h>
#include <OneWire.h>
#include <avr/wdt.h>  //Whatchdog di sicurezza

// Definizione costanti
#define EEPROM_ADDRESS 0    // Indirizzo di memoria dove leggere e scrivere la temperatura impostata
#define REFRESH_RATE 5      // Ogni quanti secondi ripetere la lettura della temperatura
#define MIN_TEMP 5          // Temperatura minima impostabile
#define MAX_TEMP 180        // Temperatura massima impostabile
#define SCREEN_WIDTH 128    // Larghezza display in pixels
#define SCREEN_HEIGHT 64    // Altezza display in pixels
#define DEBOUNCE_DELAY 15   // Delay per il debounce del bottone sull'encoder

/*
  Info sulle dimensioni dei caratteri da utilizzare per il corretto
  posizionamento degli elementi sul display
*/
#define CHAR_SPACE 6
#define CHAR_HEIGHT 8
#define SM_FONT_SIZE 1
#define MD_FONT_SIZE 2
#define LG_FONT_SIZE 5

/*
  Per mostrare lo stato ON/OFF del relay sul display ho deciso di ricreare la grafica
  di un'interruttore simile a quelli che si trovano nelle impostazioni dei nostri cellulari.
  Ho definito delle costanti per faicilitarmi l'operazione di disegno e posizionamento
*/
#define TOGGLE_WIDTH 38         // Larghezza switch
#define TOGGLE_HEIGHT 20        // Altezza switch
#define PADDING 10              // Margine dai bordi del display
#define TOGGLE_RADIUS 5         // Arrotondamento rettangolo dello switch
#define TOGGLE_CIRCLE_RADIUS 7  // Raggio del pallino interno allo switch

// Definizione costanti pin
#define PIN_ENCODER_CLK 2  // Pin clk dell'encoder
#define PIN_ENCODER_DT 3   // Pin dt dell'encoder
#define PIN_ENCODER_SW 4   // Pin switch presente sull'encoder
#define PIN_TEMP_SENSOR 5  // Pin per il bus dati del sensore
#define PIN_RELAY 6        // Pin di comando per il relé

// Variabili di appoggio
long lastTempReadMills; // Appoggio ultima volta che è stata fatta una lettura
int sensorTemp;         // Parte intera della temperatura restituita dal sensore
int sensorTempDecimals; // I decimali della temperatura che serviranno per disegnare i trattini a fondo display
int setTemperature;     // Temperatura desiderata
int prevClk;            // Gestione movimenti dell'encoder
bool error;             // Se il sistema è in errore (determinato dal sensore di temperatura)
bool relayStatus;       // Status del relé
bool modEdit;           // Quando true siamo in modalità modifica

// Istanze librerie
OneWire oneWire(PIN_TEMP_SENSOR);
DS18B20 sensor(&oneWire);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT);
Bounce btnMode = Bounce();


/*
  Comanda il relè
*/
void controlRelay() {
  // Se il sistema è in errore stacca a prescindere il relè ed esci dalla funzione
  if (error) {
    relayStatus = false;
    digitalWrite(PIN_RELAY, relayStatus);
    return;
  }

  if (sensorTemp < setTemperature) {
    relayStatus = true;
  } else {
    relayStatus = false;
  }

  // Aggiorna valore sul pin del relè sol se non sono in modalità impostazione
  if (!modEdit) {
    digitalWrite(PIN_RELAY, relayStatus);
  }
}

/*
  Interpreta i movimenti dell'encoder.
  Ha effetto solo se siamo in modalità impostazione
*/
void checkEncoder() {
  if (!modEdit)
    return;

  int currClk = digitalRead(PIN_ENCODER_CLK);
  int currDt = digitalRead(PIN_ENCODER_DT);

  if (currClk != prevClk) {
    if (currDt == currClk) {
      setTemperature--;
    } else {
      setTemperature++;
    }
    prevClk = currClk;

    if (setTemperature > MAX_TEMP) {
      setTemperature = MIN_TEMP;
    } else if (setTemperature < MIN_TEMP) {
      setTemperature = MAX_TEMP;
    }
  }
}

/*
  "Ascolta" i cambiamenti dello switch per passare dalla modalità normale alla
  modalità impostazione
*/
void checkSwitch() {
  btnMode.update();

  if (btnMode.fell()) {
    modEdit = !modEdit;
    /*
       Se stiamo uscendo dalla modalità impostazione
       salviamo il valore sulla eeprom
    */
    if (!modEdit) {
      // Il metodo .update aggiorna il valore solo se diverso da quello
      // registrato in precedenza
      EEPROM.update(EEPROM_ADDRESS, setTemperature);
    }
  }
}

/*
  Esegue la lettura della temperatura ogni REFRESH_RATE secondi e solo se non
  siamo in modalità impostazione.
*/
void readTemp() {
  if ((millis() - lastTempReadMills >= (REFRESH_RATE * 1000)) && (!modEdit)) {
    // Richiedo lettura al sensore
    sensor.requestTemperatures();
    /*
      Nell'attesa che avvenga la lettura e la conversione,
      creo un ulteriore controllo per eivtare che a causa di qualche problema
      con il sensore, il codice rimanga nel loop in attesa di un valore
    */
    unsigned long timeout = millis();
    while (!sensor.isConversionComplete()) {
      if (millis() - timeout >= 800) {
        // Se il sensore è andato in timeout
        error = true;
        break;
      } else {
        error = false;
      }
    }

    // Recupero temperatura dalla libreria
    float t = sensor.getTempC();

    // Confronto il valore restituito con le costanti di errore della libreria
    if (t == DEVICE_CRC_ERROR || t == DEVICE_DISCONNECTED) {
      // Se il sensore restituisce un errore
      error = true;
    } else {
      // Altrimenti aggiorno la variabile sensorTemp
      
      sensorTemp = (int) t;                     // Prelevo solo la parte intera del valore restituito dal sensore
      float _decimals = ( t - sensorTemp) * 10; // Estraggo la parte decimale
      sensorTempDecimals = round(_decimals);    // E la arrotondo per ottenere un numero che vada da 0 a 9 che sarà il numero di trattini a fondo display

      error = false; // resetto eventuale stato di errore
    }

    // Aggiorna lastTempReadMills per tenere traccia dell'ultima lettura
    lastTempReadMills = millis();
  }
}

/*
  Stampa sul display un messaggio durante la fase di setup.
*/
void printInitScreen() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(MD_FONT_SIZE);
  display.setCursor(0, SCREEN_HEIGHT / 2 - ((MD_FONT_SIZE * CHAR_HEIGHT) / 2));
  display.print("Avvio...");
  display.display();
}

/*
  Stampa la parte superiore del display.
  Il contenuto cambierà in base allo stato della variabile modEdit.
*/
void drawHeader() {
  display.fillRect(0, 0, SCREEN_WIDTH, 16, WHITE);
  display.setTextColor(BLACK, WHITE);
  display.setTextSize(SM_FONT_SIZE);
  display.setCursor(0, 4);

  // Se sono in modalità impostazione
  if (modEdit) {
    display.print("Imposta temperatura");
  } else {
    // Altrimenti stampo riepilogo temperatura impostata
    display.print("Temp. impostata");

    String tempToDisplay = "";
    // Se il valore è inferiore a 10 aggiungo lo zero iniziale per avere
    // sempre una cifra a due caratteri
    if (setTemperature < 10) {
      tempToDisplay = "0";
      tempToDisplay.concat(setTemperature);
    } else {
      tempToDisplay = "";
      tempToDisplay.concat(setTemperature);
    }

    display.setCursor(SCREEN_WIDTH - ((SM_FONT_SIZE * CHAR_SPACE) * 2), 4);
    display.print(tempToDisplay);
  }
}

/*
  Disegna il pallino dei gradi
*/
void drawPallinoGradi() {
  unsigned int pallinoPositionX = ((LG_FONT_SIZE * CHAR_SPACE) * 2) + 5;

  display.drawCircle(pallinoPositionX, SCREEN_HEIGHT / 3, 5, WHITE);
  display.fillCircle(pallinoPositionX, SCREEN_HEIGHT / 3, 5, WHITE);
  display.drawCircle(pallinoPositionX, SCREEN_HEIGHT / 3, 3, BLACK);
  display.fillCircle(pallinoPositionX, SCREEN_HEIGHT / 3, 3, BLACK);
}

/*
  Stampa i gradi rilevati dal sensore
*/
void drawTemp() {
  display.setTextColor(WHITE);
  display.setTextSize(LG_FONT_SIZE);
  display.setCursor(0, SCREEN_HEIGHT / 3);

  String tempToDisplay = "";
  // Se il valore è inferiore a 10 aggiungo lo zero iniziale per avere
  // sempre una cifra a due caratteri
  if (sensorTemp < 10) {
    tempToDisplay = "0";
    tempToDisplay.concat(sensorTemp);
  } else if (sensorTemp > 99) {
    // Se il valore dovesse superare le due cifre, rimpicciolisco il carattere
    display.setTextSize(MD_FONT_SIZE);
    tempToDisplay = "  ";
    tempToDisplay.concat(sensorTemp);
  } else {
    tempToDisplay = "";
    tempToDisplay.concat(sensorTemp);
  }
  display.print(tempToDisplay);

  // Disegna pallino dei gradi
  drawPallinoGradi();
}

/*
  Stampa i gradi da settare quando si è in modalità impostazione
*/
void drawEditTemp() {
  display.setTextColor(WHITE);
  display.setTextSize(LG_FONT_SIZE);
  display.setCursor(0, SCREEN_HEIGHT / 3);

  String tempToDisplay = "";
  // Se il valore è inferiore a 10 aggiungo lo zero iniziale per avere
  // una cifra a due caratteri
  if (setTemperature < 10) {
    tempToDisplay = "0";
    tempToDisplay.concat(setTemperature);
  } else if (setTemperature > 99) {
    // Se il valore dovesse superare le due cifre, rimpicciolisco il carattere
    display.setTextSize(MD_FONT_SIZE);
    tempToDisplay = "  ";
    tempToDisplay.concat(setTemperature);
  } else {
    tempToDisplay = "";
    tempToDisplay.concat(setTemperature);
  }

  display.print(tempToDisplay);

  // Disegna pallino dei gradi
  drawPallinoGradi();
}

/*
  Disegna l'interruttore.
  Riempimento e posizione cambiano in base al parametro "on".
*/
void drawToggleSwitch(bool on) {
  int toggleX = (SCREEN_WIDTH - TOGGLE_WIDTH - PADDING);
  int toggleY = (SCREEN_HEIGHT - TOGGLE_HEIGHT - PADDING);

  display.setTextColor(WHITE);
  display.setTextSize(MD_FONT_SIZE);

  if (on) {
    display.setCursor( toggleX + ((CHAR_SPACE * MD_FONT_SIZE) / 2) + (TOGGLE_RADIUS / 2), toggleY - (CHAR_HEIGHT * MD_FONT_SIZE));

    display.print("ON");

    display.fillRoundRect(toggleX, toggleY, TOGGLE_WIDTH, TOGGLE_HEIGHT, TOGGLE_RADIUS, WHITE);

    display.fillCircle((toggleX + TOGGLE_WIDTH) - (TOGGLE_CIRCLE_RADIUS + 2), toggleY + (TOGGLE_HEIGHT / 2), TOGGLE_CIRCLE_RADIUS, BLACK);
  } else {
    display.setCursor(toggleX + (TOGGLE_RADIUS / 2), toggleY - (CHAR_HEIGHT * MD_FONT_SIZE));

    display.print("OFF");

    display.drawRoundRect(toggleX, toggleY, TOGGLE_WIDTH, TOGGLE_HEIGHT, TOGGLE_RADIUS, WHITE);

    display.fillCircle(toggleX + TOGGLE_CIRCLE_RADIUS + 2, toggleY + (TOGGLE_HEIGHT / 2), TOGGLE_CIRCLE_RADIUS, WHITE);
  }
}

/*
  Disegna un'icona da mostrare quando siamo in modalità impostazione.
  Dimensioni e posizionamento rimangono le stesse dell'icona interruttore
*/
void drawEditIcon() {
  int toggleX = (SCREEN_WIDTH - TOGGLE_WIDTH - PADDING);
  int toggleY = (SCREEN_HEIGHT - TOGGLE_HEIGHT - PADDING);

  display.setTextColor(WHITE);
  display.setTextSize(MD_FONT_SIZE);

  display.setCursor(toggleX + (TOGGLE_RADIUS / 2), toggleY - (CHAR_HEIGHT * MD_FONT_SIZE));

  display.print("MOD");

  display.drawRoundRect(toggleX, toggleY, TOGGLE_WIDTH, TOGGLE_HEIGHT, TOGGLE_RADIUS, WHITE);

  display.setTextSize(SM_FONT_SIZE);

  display.setCursor(toggleX + 8, toggleY + (CHAR_HEIGHT * SM_FONT_SIZE) - 2);

  display.print("TEMP");
}

/*
  Disegna la scritta ERR!
*/
void drawError() {
  display.setTextColor(WHITE);
  display.setTextSize(LG_FONT_SIZE);
  display.setCursor(0, SCREEN_HEIGHT / 3);
  display.print("ERR!");
}

/*
  Disegna i trattini a fondo display.
  I trattini rappresentano la parte decimale della lettura del sensore.
  Supponendo un valore di 37,38 gradi il numero dei trattini sarà 4
  ovvero la parte decimale .38 arrotondata a 4.
*/
void drawDashes() {
  const int N_DASHES = 9;                                   // Numero di trattini massimo per calcolarne la larghezza
  const int DASH_GAP = 4;                                   // Spazio tra un trattino e l'altro
  const int DASH_H = 3;                                     // Altezza di ogni trattino
  const int DASH_Y = SCREEN_HEIGHT - DASH_H;                // Posizione Y di ogni trattino (fondo display - l'altezza del trattino)
  const int DASH_W = (SCREEN_WIDTH / N_DASHES) - DASH_GAP;  // Larghezza di ogni trattino tenendo conto dello spazio tra uno e l'altro

  //Disegna i trattini
  for (int i = 0; i < sensorTempDecimals; i++) {
    int DASH_X  = i * (DASH_W + DASH_GAP); // Calcola la posizione x di ogni trattino da disegnare
    display.fillRect(DASH_X, DASH_Y, DASH_W, DASH_H, WHITE);
  }
}

/*
  Raggruppa tutte le funzioni che disegnano sul display
*/
void printData() {
  // Pulisco buffer del display
  display.clearDisplay();

  drawHeader();

  if (error) {
    drawError();
  } else {
    if (modEdit) {
      drawEditTemp();
      drawEditIcon();
    } else {
      drawTemp();
      drawToggleSwitch(relayStatus);
      drawDashes();
    }
  }

  // Mando in stampa i dati sul display
  display.display();
}


void setup() {
  // Disabilito Watchdog per riabilitarlo dopo il setup
  wdt_disable();

  // inizializzo variabili
  sensorTemp = 0;
  sensorTempDecimals = 0;
  prevClk = 0;
  error = false;
  relayStatus = false;
  modEdit = false;
  // Forzerà lettura già al primo ciclo di loop
  lastTempReadMills = -(REFRESH_RATE * 1000);

  // Leggo il valore della temperatura precedentemente impostata sulla EEPROM
  setTemperature = EEPROM.read(EEPROM_ADDRESS);
  // Se il valore all'indirizzo EEPROM_ADDRESS è uguale a 255 vuol dire che non
  // è mai stato settato
  if (setTemperature == 255) {
    // Quindi metto il sistema in modalità impostazione e setto momentaneamente
    // la temperatura al minimo
    modEdit = true;
    setTemperature = MIN_TEMP;
  }

  // setto il pin del relé come output
  pinMode(PIN_RELAY, OUTPUT);

  // Setto lo status iniziale del relè (spento di default)
  digitalWrite(PIN_RELAY, relayStatus);

  // Setto i pin dell'encoder come input e input_pullup per lo switch
  pinMode(PIN_ENCODER_CLK, INPUT);
  pinMode(PIN_ENCODER_DT, INPUT);
  pinMode(PIN_ENCODER_SW, INPUT_PULLUP);

  // Eseguo prima lettura del valore del pin Clk dell'encoder
  prevClk = digitalRead(PIN_ENCODER_CLK);

  /*
    Per essere sicuro che vengano intercettati tutti i cambiamenti di stato
    dell'encoder sfrutto gli interrupt di Arduino.
  */
  attachInterrupt(digitalPinToInterrupt(PIN_ENCODER_CLK), checkEncoder, CHANGE);

  // Setto switch su istanza debounce
  btnMode.attach(PIN_ENCODER_SW);
  btnMode.interval(DEBOUNCE_DELAY);

  // Provo ad inizializzare il display all'indirizzo 0x3C (il tuo potrebbe
  // essere diverso, 0x3D per esempio)
  while (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    // Fin quando il display non è pronto rimango nel while
  }

  /*
    Stampo a video il messaggio iniziale.
    Se il messaggio non scompare per lasciare posto al resto dei dati, è sintomo
    di qualche problema.
  */
  printInitScreen();

  /*
     Attendi che il sensore sia pronto.
     Se il sensore da problemi rimarremo nel loop e il display sarà bloccato
     sulla scritta iniziale. Ciò ci farà intuire che qualcosa non va con
     l'inizializzazione del sensore
  */
  while (!sensor.begin()) {
    // Fin quando il sensore non è pronto rimango nel while
  }

  // Imposto controllo di errore su sensore
  sensor.setConfig(DS18B20_CRC);

  // Abilito il Watchdog.
  // Il sistema non potrà rimanere in blocco per più di due secondi
  wdt_enable(WDTO_2S);
}

// Lavora!
void loop() {
  // Controlla pressione dello switch sull'encoder
  checkSwitch();

  // Leggi la temperatura
  readTemp();

  // Stampa dati sul display
  printData();

  // Gestisci relé
  controlRelay();

  // Resetta Watchdog
  wdt_reset();
}

Sembra molto più complesso di quello che è ma sono più i commenti che il resto. Naturalmente come sempre se qualcosa non ti è chiara puoi utilizzare i commenti a fondo pagina.

Conclusioni

In conclusione, il termostato è pronto e funzionante ma non l’ho ancora testato sullo scaldabagno perché il modulo relè che ho a casa supporta fino a 10A mentre quello originale ne riporta 15A. Non so ancora come finirà la storia, ma di sicuro ho imparato e messo assieme un sacco di cose, dalla gestione grafica del display al watchdog al salvataggio dei dati in memoria.

Nato come progetto di un termostato da scaldabagno, in realtà potrà essere applicato da qualsiasi parte. In futuro se recupererò altri tipi di sensore proverò a riscrivere il codice secondo le loro librerie.

Come sempre spero possa esserti utile…

Video

A fine pagina trovi il link per scaricare il pacchetto con codici e schemi di collegamento. Come sempre ti ricordo che acquistando prodotti Amazon passando attraverso i link del mio sito, io percepisco una piccola commissione (parliamo di centesimi) in buoni regalo. Questi buoni sommati alle eventuali donazioni PayPal, servono a mantenere attivo il sito web e ad acquistare nuovi componenti.






Potrebbero interessarti anche...

Available for Amazon Prime