Arduino v příkladech - XII. díl - Displej a měření teploty poprvé
Už podle názvu zbytečný článek. O měření teploty je toho na webu až dost. My se na to podíváme úplně jinak a mohu Vám slíbit, že toto na webu nikde nenajdete.
Když jsem poprvé měřil teploty s čidly DS 18B20, pěkně jsem si nejdříve nahrál program na zjišťování adres, ty zapsal jako pevnou proměnnou pak nahrál program měření teplot s knihovnou DallasTemperature a měřil. Nějak zlobily tlačítka a i rozlišení čidla si tak nějak dělalo co chtělo. Tak si zase prošlapeme jednu slepou uličku.
Zabývat se budeme pouze čidlem DS18B20, dnes jiná ani pro běžné měření ani nepoužívám. Jedná se o kalibrované čidlo teploty s nízkou spotřebou, pro běžné účely s dostačujícím rozsahem měření, dostatečně rychlou odezvou, existují verze co můžete strčit i do vody a měřit teplotu vody v kotli, kabel lze vést bez problému desítky metrů, nastavit rozlišení až 12 bit, vejde se do hodinek a cena je v řádech desetikorun. Čidla od stejného výrobce, např. DS 18S20 a tomu podobná, oproti DS 18B20 nepřinášejí žádnou výhodu, tedy vyjma pár ušetřených korun.
Měření teploty
To divné chování tlačítek a rozlišení je způsobeno použitou knihovnou. Když pošleme čidlu příkaz k měření teploty, probíhá konverze, která může podle nastavení rozlišení trvat až 750 millisekund. Podle mého programu trvá 657 millisekund. Knihovna to řeší příkazem delay(), tedy čekáme. V rozlišení je drobná chybička ve výpočtu po bitovém posunu a tak si to dělá co chce. A´t dělám co dělám, můj program funguje správně i po bitovém posunu.
Vzorový program z knihovny vypadá takto:
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// arrays to hold device address
DeviceAddress insideThermometer;
/* * Setup function. Here we do the basics */
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// locate devices on the bus
Serial.print("Locating devices...");
sensors.begin();
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors.isParasitePowerMode()) Serial.println("ON");
else Serial.println("OFF");
// Assign address manually. The addresses below will beed to be changed
// to valid device addresses on your bus. Device address can be retrieved
// by using either oneWire.search(deviceAddress) or individually via
// sensors.getAddress(deviceAddress, index)
// Note that you will need to use your specific address here
//insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
// Method 1:
// Search for devices on the bus and assign based on an index. Ideally,
// you would do this to initially discover addresses on the bus and then
// use those addresses and manually assign them (see above) once you know
// the devices on your bus (and assuming they don't change).
if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
// method 2: search()
// search() looks for the next device. Returns 1 if a new address has been
// returned. A zero might mean that the bus is shorted, there are no devices,
// or you have already retrieved all of them. It might be a good idea to
// check the CRC to make sure you didn't get garbage. The order is
// deterministic. You will always get the same devices in the same order
//
// Must be called before search()
//oneWire.reset_search();
// assigns the first address found to insideThermometer
//if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
// show the addresses we found on the bus
Serial.print("Device 0 Address: ");
printAddress(insideThermometer);
Serial.println();
// set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
sensors.setResolution(insideThermometer, 9);
Serial.print("Device 0 Resolution: ");
Serial.print(sensors.getResolution(insideThermometer), DEC);
Serial.println();
}
// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
// method 1 - slower
//Serial.print("Temp C: ");
//Serial.print(sensors.getTempC(deviceAddress));
//Serial.print(" Temp F: ");
//Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit
// method 2 - faster
float tempC = sensors.getTempC(deviceAddress);
Serial.print("Temp C: ");
Serial.print(tempC);
Serial.print(" Temp F: ");
Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}
/* * Main function. It will request the tempC from the sensors and display on Serial. */
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
// It responds almost immediately. Let's print out the data
printTemperature(insideThermometer); // Use a simple function to print out the data
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
Vlastní program, který nečeká lze přitom napsat úplně snadno, stačí použít ty správné příkazy. Ukážeme si příklad, který využijeme i dále. Při spuštění programu si načteme adresy připojených čidel, uložíme je do paměti EEPROM a pak každých 5 vteřin budeme číst teplotu. Je to napsané tak, že se na nic nečeká. Program samostatně popisovat nebudu, je podrobně okomentovaný a je spíše na ukázku. Využít ho samozřejmě můžete kde se Vám zachce.
//------------------------------------------------------------------
// inicializace zakladnich knihoven
#include <OneWire.h> // knihovna pro komunikaci 1-wire
#include <EEPROM.h> // knihovna pro zapis do pameti eeprom
//------------------------------------------------------------------
// nastaveni nazvu pinu
#define DS_18B20 3 // vstup pro cidla DS 18B20
//------------------------------------------------------------------
// pocet cidel
#define numberOfSensors 8 // maximalni pocet cidel DS 18B20
//------------------------------------------------------------------
// Nastaveni komunikace cidel DS 18B20
OneWire ds18B20(DS_18B20); // nastaveni komunikace cidel po sbernici oneWire
//------------------------------------------------------------------
// promenne teploty
float teplota[numberOfSensors];
unsigned long casCteniTeplot; // cas posledniho cteni
boolean povolCteniTeplot = false;
byte ID_teploty; // ID pro aktualizaci teploty v pripade nastaveni teplot
unsigned long prodlevaCteniTeplot = 5000; // nastaveni periody cteni teploty
byte resolutionDS = 12; // nastaveni rozliseni cidla
//------------------------------------------------------------------
// pole adres cidel DS 18B20 - 8 pozic dalsi volna adresa 624
//const int cidlaDS_i[] = {560, 568, 576, 584, 592, 600, 608, 616};
const int cidlaDS_i = 560; // prvni adresa EEPROM pro zapis adres cidel
//------------------------------------------------------------------
void setup() {
//------------------------------------------------------------------
Serial.begin(9600); // seriova komunikace
//------------------------------------------------------------------
// vyhleda a ulozi adresy teplotnich cidel
hledaniDS18B20(); // vyhleda a ulozi adresy teplotnich cidel
}
//------------------------------------------------------------------
void loop() {
//------------------------------------------------------------------
// cast programu pro cteni teplot
if (!povolCteniTeplot) { // kdyz je zakazano cteni teplot
if ((unsigned long)(millis() - casCteniTeplot) >= prodlevaCteniTeplot) { // cekej na uplynuti prodlevz
spustKonverzi();
}
}
else { // jinak je povoleno cteni teplot
if (ds18B20.read()) { // zkontroluj zda jsou data k dispozici
cteniTeplot(); // precist hodnoty cidel teploty
}
}
}
//------------------------------------------------------------------
// cast programu pro cteni teplot
void cteniTeplot() { // precte teploty ulozenych cidel
byte data[12];
byte address[8];
byte count;
Serial.println(F("Dalsi cteni teploty"));
Serial.println("Doba konverze: " + (String)(millis() - casCteniTeplot) + " millis");
ds18B20.depower();
for (int id = 0; id < numberOfSensors; id++) { // nastav odresu EEPROM cidla
count = 0;
for (int i = cidlaDS_i + (id * 8); i < cidlaDS_i + (id * 8) + 8; i++) { // precti adresy
address[count] = EEPROM.read(i);
count++;
}
ds18B20.reset(); // resetuj nastaveni oneWire
ds18B20.select(address); // vyber adresu aktualniho cidla
ds18B20.write(0xBE); // cti zasobnik cidla
for (int i = 0; i < 9; i++) { // precti prvnich 9 bitu, bez CRC kodu
data[i] = ds18B20.read();
}
if (address[0] == 255 || data[0] == 255) teplota[id] = NULL; // kdyz neni cidlo ulozeno, nebo nejsou data k dispozici
else {
uint16_t temp;
if (resolutionDS == 12) temp = 127; // 12 bit res, 750 ms
else if (resolutionDS == 11) temp = 95; // 11 bit res, 375 ms
else if (resolutionDS == 10) temp = 63; // 10 bit res, 187.5 ms
else temp = 31; // 9 bit res, 93.75 ms
if (data[4] != temp) { // kdyz neni nastaveno pozadovane rozliseni
uint16_t tmpResolution;
tmpResolution = (resolutionDS - 9) * 32;
ds18B20.reset();
ds18B20.select(address);
ds18B20.write(0x4E); // zapis do zasobniku
ds18B20.write(0x00); // TL
ds18B20.write(0x00); // TH
ds18B20.write(tmpResolution); // Nastav nove rozliseni
ds18B20.write(0x48); // kopiruj zasobnik
ds18B20.reset();
}
int16_t raw = (data[1] << 8) | data[0];
Serial.print("RAW: " + String(raw) + " ");
teplota[id] = (float)raw / 16;
Serial.print("Cidlo " + (String)(id + 1) + ": ");
Serial.print(teplota[id], 4);
Serial.println(" C");
}
}
povolCteniTeplot = false;
Serial.println();
}
//------------------------------------------------------------------
// konverze teplot cidel
void spustKonverzi() {
casCteniTeplot = millis(); // nastav novy cas cteni
ds18B20.reset(); // resetuj nastaveni oneWire
ds18B20.write(0xCC); // nastav konverzi pro vsechny cidla
ds18B20.write(0x44); // start interni konverze teploty
povolCteniTeplot = true;
}
//------------------------------------------------------------------
// hleda adresy teplotnich cidel DS 18B20
void hledaniDS18B20() {
byte address[8];
byte id;
ds18B20.reset_search(); // resetuj hledani
if (ds18B20.search(address)) { // hledej adresy
for (id = 0; id < numberOfSensors; id++) { // zapis data do displeje
String stringText;
for (byte i = 0; i < 8; i++) { // precti a uloz adresy
EEPROM.write(cidlaDS_i + (id * 8) + i, address[i]);
stringText += address[i], DEC;
if (i < 8 - 1) stringText += ",";
}
Serial.println("Cidlo " + (String)(id + 1) + ": " + stringText);
if (!ds18B20.search(address)) {
ds18B20.reset_search();
Serial.println(F("Zadne dalsi cidlo nenalezeno"));
break;
}
}
Serial.println("Nalezeno " + (String)(id + 1) + " cidel.");
Serial.println();
}
}
Klíčové věci se Vám vypisují na Serial monitor. Jenom si dejte pozor při práci s pamětí EEPROM, ať si počtem čidel nepřepíšete jiná data.
POZOR – program je napsán tak že nereflektuje parazitní napájení. Jen to zapojte pěkně poctivě, po třech drátech.
V dalším díle si to vše hezky zaplikujeme.
JB
Přidat komentář
Přehled komentářů
-
Vyšší delkarovaná teplota
(Jiří, 13. 10. 2020 12:03:52)
Odpovědět
Dobrý den, měl bych dotaz k DS18B20. Mám jej připojeno k ESP8266 D1 mini v třídrátovém zapojení s odporem 4K7. Zařízení však vrací hodnotu o cca 5° vyšší. Poradíte prosím, kde by mohl být problém? PS: Čidla DS18B20 mám z Aliexpresu.