LED - LCD - Temperature - Time
In this example, in addition to the LCD, LED, and temperature sensor DS18B20 peripherals, I have added a real-time clock (RTC - Real Time Clock) PCF8583, which is very useful when it is desired to record the time independently of the power supply/operation of the micro-controller.
The project will have a higher degree of difficulty than the previous ones by adding the serial communication necessary for setting the real-time clock and using a real terminal in the serial communications.
The electrical schematic of the assembly is presented below:
The source code used is as follows:
/*
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
Conectare RTC - PCF8583
* RTC A0 la +V adresa hardware 0xA2
* RTC SCL la pinul D16/SCL
* RTC SDA la pinul D17/SDA
* RTC OSC1 la oscilator quartz de 32678Hz
* RTC OSC0 la oscilator quartz de 32678HZ
* RTC GND la masa
* RTC OSC1 la +V printr-un condensator de 22pF
*/
// Librariile utilizate
#include <Arduino.h>
#include <LiquidCrystal.h> // load the library for the LCD display
#include <OneWireNg_CurrentPlatform.h> // load the library for 1-Wire communication (DS18B20)
#include <Wire.h> // load the library necessary for the RTC sensor
#include <PCF8583.h> // load the library for the RTC sensor
#include <stdio.h> // load the library necessary to create a string using sprintf
// 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 milliseconde)
int correct_address = 0; // necesar pentru PCF8583
// 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 cand 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 // the pin where the DS18 sensor is connected
// #define PARASITE_POWER // uncomment for parasite power support
#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-------------------------------
// construiesc un obiect p
// PCF8583 p(0xA0); // adresa pentru cazul in care A0 la GND
PCF8583 p(0xA2); // adresa pentru cazul in care A0 la +V
// declar functiile utilizate
void blinkLed(); // fuctia care aprinde un LED
long getTemp(); // functia pentru determinarea temperaturii
void printLcdTemp(); //functia pentru afisarea temperaturii
void printLcdTime(); //functia pentru afisarea ceasului
void setTime(); // functia pentru setarea datei si a orei
void clear_row_1(); // functia pentru stergerea randului 1 si pozitionare la inceputul liniei
void clear_row_2(); // functia pentru stergerea randului 2 si pozitionare la inceputul liniei
// aceasta functie va fi rulata o data
void setup() {
// -----------------configurez DS18B20------------------------------------
#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
// ---------------------sfarsit configurare DS18B20----------------------
Serial.begin(9600); // intializez comunicatia seriala
lcd.begin(16, 2); // setez nr. de coloane si de linii ale LCD-ului
lcd.print("Test..."); // Afisez un mesaj la LCD
Serial.print("Test...");
Serial.println(" gata");
delay(2000); // astept pentru ca mesajul de intampinare sa fie afisat
pinMode(ledPin, OUTPUT); // setez pinul digital aferent ledPin ca iesire
lcd.clear(); // sterg ecranul afisorului
}
//acesta functie va fi rulata la infinit
void loop() {
blinkLed(); // apelez functia ce aprinde LED-ul
setTime(); // apelarea functiei pentru setarea datei si a orei
printLcdTime(); //functia pentru afisarea ceasului
printLcdTemp(); // apelez functia care afiseaza temperatura
}
// functia ce va aprinde un led intermitent
// in acest moment intervalul de timp este afectat de toate delay-urile din program
// in viitor se va folosi task-uri pentru ca elimina acest neajuns
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
}
}
//functia pentru citirea temperaturii
long getTemp() {
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!");
}
return temp;
} while (ec == OneWireNg::EC_MORE);
}
// functia pentru afisare la LCD a temperaturii
void printLcdTemp() {
long temperatura = getTemp();
// afisarea temperaturii ca variabila de tip float (cast)
lcd.setCursor(9,1); // pozitionez cusorul pentru afisarea temperaturii
lcd.print(" "); // sterg temperatura anterioara
if (temperatura < 0) {
temperatura = -temperatura;
lcd.setCursor(9,1);
lcd.print('-');
}
lcd.setCursor(10,1); // pozitionez cursorul pentru afisarea valorii temperaturii
lcd.print((float)temperatura / 1000);
lcd.print('C');
/*
// afisarea temperaturii ca variabila de tip long
lcd.setCursor(9,1); // pozitionez cusorul pentru afisarea temperaturii
lcd.print(" "); // sterg temperatura anterioara
if (temperatura < 0) {
temperatura = -temperatura;
lcd.setCursor(9,1);
lcd.print('-');
}
lcd.setCursor(10,1);
lcd.print(temperatura / 1000);
lcd.print('.');
lcd.print(temperatura % 1000);
// lcd.print('C');
*/
}
// functia pentru setarea datei si a orei prin comunatia seriala
// se va transmite de forma: ZZLLAAAAhhmmss;
void setTime() {
if(Serial.available() > 0){ // daca a fost stabilta comunicatie seriala, atunci citesc caracterele primite
p.day = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
p.month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
p.year= (int) ((Serial.read() - 48) *1000 + (Serial.read() - 48) *100 + (Serial.read() - 48) *10 + (Serial.read() - 48)) ;
p.hour = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
p.minute = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
p.second = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48));
if(Serial.read() == ';'){ // daca intalnesc caracterul ; atunci setez ceasul
Serial.println("ceasul a fost setat");
clear_row_1(); // curat randul 1 al LCD-ului
clear_row_2(); // curat randul 2 al LCD-ului
p.set_time(); // setz ceasul
}
p.get_time(); // citesc ceasul
char time[50];
sprintf(time, "%02d/%02d/%04d %02d:%02d:%02d",
p.day, p.month, p.year, p.hour, p.minute, p.second);
Serial.println(time);
delay(1000);
}
}
// functia pentru afisarea datei si a orei la LCD
// se va afisa de forma: ZZ/LL/AAAA respectiv hh:mm:ss
void printLcdTime() {
p.get_time(); // citesc ceasul
char date[50];
char time[50];
sprintf(date, "%02d/%02d/%04d",
p.day, p.month, p.year);
sprintf(time, "%02d:%02d:%02d",
p.hour, p.minute, p.second);
lcd.setCursor(0,0);
lcd.print(date);
lcd.setCursor(0,1);
lcd.print(time);
delay(250);
}
// functia sterge randul 1 si pozitionez cursorul la inceputul liniei
void clear_row_1() {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
}
// functia sterge randul 2 si pozitionez cursorul la inceputul liniei
void clear_row_2() {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
}
The libraries used are:
The operation of the assembly in Proteus can be seen in the video below:
As can be seen from the video, I used serial communication to set the clock. For this, it was necessary to create a pair of serial ports com3 and com4 connected to each other (null modem).
I connected the microcontroller to port com3 via the virtual port COMPIM, and I connected the terminal CoolTerm to port com4 used in the serial communication.
The program used for creating the virtual ports com3 and com4 is com0com , which can be downloaded here .
For serial communication, I used the terminal CoolTerm .
WARNING: The RX/TX pins of the microcontroller should not be connected directly to a serial port in real life. Read below.
I connected the virtual port in Proteus COMPIM directly, to simplify the schematic and reduce the memory load on the computer's microprocessor, but in real life, it should be connected using a level-shifting circuit TTL-RS232 (for example, MAX232 or others that also use USB communication, such as FT232R).
The entire project (source code and the file for Proteus) can be downloaded from here LCD_Blink_LED_DS18B20_PCF8583.zip