LED - LCD - Temperatura

La proiectul nostru precedent vom adăuga pe lângă afișorul de caractere, ledul ce se aprinde intermitent, un senzor de temperatură DS18B20 ce folosește comunicația 1-Wire.
Acest senzor se găsește de obicei în capsulă TO-92 sau într-o teacă de protecție.
Vom crește gradul de dificultate prin faptul că informațiile privind temperatura citite de la senzorul DS18B20 vor fi transmise comunicației seriale.

Conectarea senzorului am realizator la pinul digital 2 al plăcii Sanguino, ca în imaginea de mai jos.
Rezistența pull-up de 4k7 este obligatorie.
Afișarea temperaturii se va face pe comunicația seriala într-un terminal.
Astfel am introdus modul de comunicare serial.

Codul folosit este următorul:

/*
Conectarea LCD la Sanguino
 * LCD RS la pinul digital pin 22
 * LCD Enable la pinul digital pin 23
 * LCD D4 la pinul digital pin 24
 * LCD D5 la pinul digital pin 25
 * LCD D6 la pinul digital pin 26
 * LCD D7 la pinul digital pin 28
 * LCD R/W la masa
Conectare LED
* LED K la pinul digital 13
* LED A la +5V printr-o rezistenta de 1k
Conectare DS18B20 - senzor de temperatura capsula TO92
* DS18B20 DQ la pinul digital 2
* DS18B20 Vdd la +5V
* DS18B20 GND la masa
* DS18B20 o rezistenta pull-up de 4k7 intre Vdd si pinul DQ  
*/
#include <Arduino.h>
#include <LiquidCrystal.h> // incarc libraria pentru afisorul LCD
#include "OneWireNg_CurrentPlatform.h"

 // constantele sunt variabile cu valoare fixa
const int ledPin =  13; // pinul unde este conectat LED-ul
const long intervalBlink = 500; // intervalul de timp in care se va aprinde ledul (in milisecunde)
boolean started = false;

// variabile a caror valoare se va modifica
int ledState = HIGH;  // ledState utilzat pentru a seta LED-ul ON sau OFF (HiGH => LED=OFF)
unsigned long previousMillisBlink = 0; // variabila care va stoca timpul când starea LED-ului a fost actualizata

// intializez libraria si asociez pinii Sanguino la LCD
const int rs = 22, en = 23, d4 = 24, d5 = 25, d6 = 26, d7 = 27;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// Declaratiile necesare pentru senzorul DS18

#define OW_PIN  2 // pinul unde este conectat senzorul DS18 
// #define PARASITE_POWER // sterge comentarea pentru suport parasite 

#ifdef PARASITE_POWER
/* sterge comentarea pentru alimentarea tranzistorului
si controlul acestuia prin pinul 9 sau altul */
// # define PWR_CTRL_PIN   9
#endif

/* Comenzile pentru circuitele DS */
#define CMD_CONVERT_T           0x44
#define CMD_COPY_SCRATCHPAD     0x48
#define CMD_WRITE_SCRATCHPAD    0x4e
#define CMD_RECALL_EEPROM       0xb8
#define CMD_READ_POW_SUPPLY     0xb4
#define CMD_READ_SCRATCHPAD     0xbe

/* Tipurile de circuite DS suportate */
#define DS18S20     0x10
#define DS1822      0x22
#define DS18B20     0x28
#define DS1825      0x3b
#define DS28EA00    0x42

#define ARRSZ(t) (sizeof(t)/sizeof((t)[0]))

static struct {
  uint8_t code;
  const char *name;
  }
  DSTH_CODES[] = {
    { DS18S20, "DS18S20" },
    { DS1822, "DS1822" },
    { DS18B20, "DS18B20" },
    { DS1825, "DS1825" },
    { DS28EA00,"DS28EA00" }
    };

static OneWireNg *ow = NULL;

/* returneaza NULL daca nu este suportat */
static const char *dsthName(const OneWireNg::Id& id) {
  for (size_t i=0; i < ARRSZ(DSTH_CODES); i++) {
    if (id[0] == DSTH_CODES[i].code)
    return DSTH_CODES[i].name;
    }
    return NULL;
}

// sfarsitul declaratiilor necesare pentru circuitele DS

// declar functiile utilizate
void blinkLed(); // fuctia care aprinde un LED
void printSerialTemp(); // functia care va afisa temperatura la portul serial

// aceasta functie va fi rulata o data
void setup() { 
  #ifdef PWR_CTRL_PIN
  ow = new OneWireNg_CurrentPlatform(OW_PIN, PWR_CTRL_PIN, false);
  #else
  ow = new OneWireNg_CurrentPlatform(OW_PIN, false);
  #endif

  delay(500);

  #if (CONFIG_MAX_SRCH_FILTERS > 0)
  /* if filtering is enabled - filter to supported devices only;
  CONFIG_MAX_SRCH_FILTERS must be large enough to embrace all code ids */
  for (size_t i=0; i < ARRSZ(DSTH_CODES); i++)
  ow->searchFilterAdd(DSTH_CODES[i].code);
  #endif

  Serial.begin(9600); // intializez comunicatia seriala
  lcd.begin(16, 2); // setez nr. de coloane si de linii ale LCD-ului
  lcd.print("salut, lume!"); // Afisez un mesaj la LCD
  pinMode(ledPin, OUTPUT); // setez pinul digital aferent ledPin ca iesire

}

//acesta functie va fi rulata la infinit
void loop() { 

  lcd.setCursor(0, 1); // setez cursorul la coloana 0, linia 1 (pe randul 2)
  lcd.print(millis()/1000);  // afisez nr. de secunde de la ultimul reset
  blinkLed(); // apelez functia ce aprinde LED-ul
  printSerialTemp(); // apelez functia care afiseaza la portul serial temperatura

}

// acesta functie va aprinde un led
void blinkLed() { 
  // verifica daca e trecut timpul pentru a schimba satrea LED-ului (ON sau OFF)
  // daca diferenta dintre timpul curent (currentMillis) si ultimul timp (previosMillis) salvat
  // este mai mare decat timpul impus prin variabila (intervalBlink) 
  unsigned long currentMillisBlink = millis(); // stochez in curentMillis timpul curent
  if (currentMillisBlink - previousMillisBlink >= intervalBlink) {
    // verific daca s-a scurs intervalul de timp dorit
    // daca da, atunci,
    previousMillisBlink = currentMillisBlink; //salvez timpul current
    if (ledState == HIGH) {
      // daca LED-ul este OFF atunci schimb starea lui in ON si invers
      ledState = LOW;
      }
      else {
        ledState = HIGH;
        }
  digitalWrite(ledPin, ledState); // setez starea pinului cu variabila ledState stabilita anterior
  }

}

//afisare la portul serial a temperaturii
void printSerialTemp() {
  OneWireNg::Id id;
  OneWireNg::ErrorCode ec;

  ow->searchReset();
  do {
    ec = ow->search(id);
    if (!(ec == OneWireNg::EC_MORE || ec == OneWireNg::EC_DONE))
    break;

    /* porneste conversia temperaturii */
    ow->addressSingle(id);
    ow->writeByte(CMD_CONVERT_T);

    #ifdef PARASITE_POWER
    /* alimenteaza magistrala */
    ow->powerBus(true);
    #endif

    delay(500); // asteptam pentru realizarea conversiei

    uint8_t touchScrpd[] = {
      CMD_READ_SCRATCHPAD,
      /* datele citite scratchpad sunt plasate aici (9 bytes) */
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
      };

    ow->addressSingle(id);
    ow->touchBytes(touchScrpd, sizeof(touchScrpd));
    uint8_t *scrpd = &touchScrpd[1];  /* datele scratchpad */

    if (OneWireNg::crc8(scrpd, 8) != scrpd[8]) {
      Serial.println("  Invalid CRC!");
      continue;
      }

    long temp = ((long)(int8_t)scrpd[1] << 8) | scrpd[0];
    if (id[0] != DS18S20) {
      unsigned res = (scrpd[4] >> 5) & 3;
      temp = (temp >> (3-res)) << (3-res);  /* bitii nedefiniti devin zero */
      temp = (temp*1000)/16;
      }

    else if (scrpd[7]) {
      temp = 1000*(temp >> 1) - 250;
      temp += 1000*(scrpd[7] - scrpd[6]) / scrpd[7];
      }
      else {
        /* ar trebui sa nu se intample */
        temp = (temp*1000)/2;
        Serial.println("  Zeroed COUNT_PER_C detected!");
        }

  Serial.print("  Temperatura: ");
  if (temp < 0) {
    temp = -temp;
    Serial.print('-');
    }

  Serial.print(temp / 1000);
  Serial.print('.');
  Serial.print(temp % 1000);
  Serial.println(" C");
  } while (ec == OneWireNg::EC_MORE);

}

Librăriile folosite sunt:

Am încercat și librăria DallasTemperature dar a prezentat de multe ori erori de conversie/măsurare a temperaturii .

Funcționarea montajului în Proteus se poate vedea în videoclipul de mai jos:

Întreg proiectul (codul sursă și fișierul pentru Proteus ) se poate descărca de aici LCD_Blink_LED_DS18B20.zip