Датчик температуры DS18B20, подключение к Arduino.

Подробно про датчики температуры DS18B20 и работу с библиотекой DallasTemperature.

Характеристики датчика:

  • Диапазон температур:  –55 … 125°C  ±2.0, –10 … 85°C  ±0.5
  • Разрешение: от 9 до 12 Бит, до 0.0625 °C
  • Напряжение питания: от 3.0 В до 5.5 В. Возможно фантомное питание (питание по линии данных)
  • Связь по 1-Wire. Каждый датчик имеет уникальный 64 битный серийный номер, по которому происходит общение с датчиком на шине.
  • Тревожный сигнал, передает адрес датчика, если температуры вышла за заданные пределы.


Схема подключения датчиков:

Назначение выводов:

Также существует герметичная версия DS18B20, в таких датчиках смотрим на цвета проводов.

PIN Цвет TO-92 8-PIN SOIC ОПИСАНИЕ
GND Черный 1 5 Масса
DQ Желтый, Белый или Синий 2 4 Линия данных интерфейса 1-Wire.
VDD Красный 3 3 Вход внешнего питания.

Подключение одного датчика:

Датчик (ногу DQ) можно подключать на любой свободный выход arduino, в данном случаи датчик подключен к аналоговому А1 (он же 15 цифровой). В коде, при необходимости, можно задать любой другой, на котором будет сконфигурирована шина 1-Wire.

Также необходимо притянуть линию данных к питанию резистором на 4,7к. Питание у датчика 5 вольт.


Подключение нескольких датчиков:

 

Дополнительные датчики подключаются параллельно.


Подключение датчика с фантомным питанием:

Не рекомендуется, без крайней необходимости подключать датчик подобным образом, это плохо сказывается на быстродействии и стабильности работы датчика.


Софт:

Для работы с датчиками необходима библиотека OneWire, скачать можно тут или тут, благодаря которой можно работать со всей линейкой устройств от Maxim/Dallas с однопроводной шиной (1-Wire), включая DS18B20.

Также, для удобства работы с датчиками DS18B20, рекомендуется использовать библиотеку DallasTemperature, особенно если датчиков на шине несколько, можно скачать тут или тут, она работает поверх библиотеки OneWire. Библиотека обширная, возможно будет тяжела для освоения начинающим, особенно по примерам из комплекта.


Пример работы с одним датчиком, без DallasTemperature:

Открываем пример DS18x20_Temperature.pde из библиотеки OneWire.

OneWire  ds(10);  // on pin 10 (a 4.7K resistor is necessary)

Далее в 10 строке указываем пин к которому подключен датчик, изначально там указан 10 пин, а в нашем случаи это A1, то есть, меняем «OneWire  ds(10);» на «OneWire  ds(A1);» и загружаем в arduino. Открываем «монитор порта» в мониторе каждую секунду приходят следующие данные.

ROM = 28 A8 3E F9 5 0 0 12  —  Адрес датчика на шине в HEX формате.
Chip = DS18B20  —  Тип датчика, вычисляется из адреса датчика.
Data = 1 C0 1 4B 46 3F FF 10 10 6F CRC=6F  —  Данные о температуре в HEX формате.
Temperature = 28.00 Celsius, 82.40 Fahrenheit  —  Температура в двух системах.


Алгоритм получения данных с датчика:

Получение температуры в примере DS18x20_Temperature.pde, происходит в три этапа.

Первым нужно узнать адрес датчика на шине и подключен ли он вообще,

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

за это отвечает код выше и функция ds.search(addr), которая, если найдет устройство, положит его адрес в массив addr. Можно эту процедуру опустить, если адрес датчика заранее известен.

Вторым отправляется команда на датчик, чтобы он прочитал температуру и положил данные в регистр.

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end

на это датчику требуется относительно много времени, порядка 750мс.

Третьем даем команду на чтение данных из регистра и в цикле считываем ответ в массив

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }

и попутно отправляем его в «монитор порта» для отладки, ниже конвертируем в привычные нам Цельсии или Фаренгейты.

Пример целиком, на всякий случай

DS18x20_Temperature.pde
#include <OneWire.h>

// OneWire DS18S20, DS18B20, DS1822 Temperature Example
//
// http://www.pjrc.com/teensy/td_libs_OneWire.html
//
// The DallasTemperature library can do all this work for you!
// http://milesburton.com/Dallas_Temperature_Control_Library

OneWire  ds(A1);  // on pin 10 (a 4.7K resistor is necessary)

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
  
  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }
  
  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
 
  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
  
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
}


Работа с библиотекой DallasTemperature:

Получать температуру с датчиков библиотекой DallasTemperature можно разными методами, у каждого метода есть свои особенности, но по порядку.


Работа с датчиками по индексу:

Родными примерами из библиотеки пользоваться не будем, разберем упрощенную версию, что ниже. Датчик температуры все также подключен ко входу А1.

#include <OneWire.h>
#include <DallasTemperature.h>
 
OneWire oneWire(15);                                       // вход датчиков 18b20, аналоговый А1, он же 15 цифровой 
DallasTemperature ds(&oneWire);    

void setup() {
  Serial.begin(9600);
  ds.begin();                                             // инициализация
}

void loop() {
  ds.requestTemperatures();                               // считываем температуру с датчиков, на это требуется 750мс
  
  Serial.print("Sensor 0: ");
  Serial.print(ds.getTempCByIndex(0));                    // отправляем температуру
  Serial.println("C");   
}

Пример крайне упрощен и по сути не отличается от кода из примера «DS18x20_Temperature.pde» библиотеки OneWire.

При инициализации просиходит поиск устройств на шине и каждому устройству назначается свой «индекс» по номеру которого, функцией getTempCByIndex(0), можно достать температуру из датчика. Перед считыванием нужно вызвать функцию requestTemperatures() которая дает команды на подключенные датчики считать температуру и положить её в регистр для считвания.

Метод не стабилен при работе с несколькими датчиками, ибо если будут проблемы с обнаружением одного из нескольких датчиков на шине, индексы перестроятся и будем получать ошибочные показания. Также такой метод, не всегда имеет смысл использовать при работе с одним датчиком, зачем использовать тяжелую библиотеку, если достаточно одной функции в коде?

ds18b20_Index - код из видео
#include <OneWire.h>
#include <DallasTemperature.h>
 
OneWire oneWire(15);// вход датчиков 18b20
DallasTemperature ds(&oneWire);

byte qty; // количество градусников на шине 

void setup() {
  Serial.begin(9600);
  ds.begin();
  
  qty = ds.getDeviceCount(); 
  Serial.print("Found ");
  Serial.print(qty);
  Serial.println(" devices.");
    //
  
}

void loop() {
  ds.requestTemperatures(); // считываем температуру с датчиков
  
  for (int i = 0; i < qty; i++){ // крутим цикл 
    Serial.print("Sensor ");
    Serial.print(i);
    Serial.print(": ");
    Serial.print(ds.getTempCByIndex(i)); // отправляем температуру
    Serial.println("C"); 
  } 
  Serial.println();
}

Еще один пример работы с датчиками по индексу, этот пример использоватся в видео. Он отправляет в «монитор порта» температуру со всех подключенных датчиков. Для получения количества подключенных датчиков, вызывается функция getDeviceCount(), которая возвращает значение в переменную qty. В loop, также даем команду датчикам, вызывая функцию requestTemperatures(), а ниже, в цикле for, используя количество подключенных датчиков, отпраляем в монитор порта температуру со всех подключенных датчиков.


Работа с датчиками по ID:

В данном случаи обращаемся к датчику не по назначенному библиотекой «индексу», а по серийному номеру датчика, заданного в коде,

ds18b20_ID

#include <OneWire.h>
#include <DallasTemperature.h>
 
OneWire oneWire(15); // вход датчиков 18b20
DallasTemperature ds(&oneWire);

DeviceAddress sensor1 = {0x28, 0xA8, 0x3E, 0xF9, 0x05, 0x0, 0x0, 0x12};
DeviceAddress sensor2 = {0x28, 0xE6, 0xBD, 0x3B, 0x05, 0x0, 0x0, 0xCF};

void setup() {
  Serial.begin(9600);
  ds.begin();
 
}

void loop() {
  ds.requestTemperatures(); // считываем температуру с датчиков
     
    Serial.print("Sensor 1: ");
    Serial.print(ds.getTempC(sensor1)); // отправляем температуру
    Serial.println("C");
    Serial.print("Sensor 2: ");
    Serial.print(ds.getTempC(sensor2));
    Serial.println("C");
    Serial.println();
    
    delay(1000);
}

и в данном случаи мы исключаем возможность считать температуру с неправельного датчика, но нужно зарание задать серийный номер. Если задавать его в коде, то устройство будет привязано к конкретному датчику, что осложнит замену датчика при его неисправности.

В коде объяевленны два массива sensor1 и sensor2  в которых хранятся 8 битный серийный номер датчика. В loop после вызова функции requestTemperatures() считываем температуру функцией getTempC(sensor1) передавая ей массив с серийным номером датчика, функция возвращает значение температуры с датчика, которую отправляем в «монитор порта», в случаи если датчика с таким серийным номером на шине не окажится, функция вернет -127.0

Еще один пример из видео, работающий по такому принципу ниже. Он отображает полученную температуру с датчика на LCD сшилде.

ds18b20_ID_lcd - код из видео
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>

OneWire oneWire(15); // вход датчиков 18b20
DallasTemperature ds(&oneWire);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

DeviceAddress sensor1 = {0x28, 0xA8, 0x3E, 0xF9, 0x05, 0x0, 0x0, 0x12};
DeviceAddress sensor2 = {0x28, 0xE6, 0xBD, 0x3B, 0x05, 0x0, 0x0, 0xCF};

void setup() {
//  Serial.begin(9600);
  ds.begin();
  lcd.begin(16, 2);
  lcd.clear(); 
}

void loop() {
  ds.requestTemperatures(); // считываем температуру с датчиков
    
    lcd.setCursor(0, 0);  
    lcd.print("Sensor 1: ");
    lcd.print(ds.getTempC(sensor1)); // отправляем температуру
    lcd.print("C");
    lcd.setCursor(0, 1);  
    lcd.print("Sensor 2: ");
    lcd.print(ds.getTempC(sensor2));
    lcd.print("C");

}


Работа с ID:
#include <OneWire.h>
#include <DallasTemperature.h>
 
OneWire oneWire(15); // вход датчиков 18b20
DallasTemperature ds(&oneWire);

DeviceAddress sensor0;

void setup() {
  Serial.begin(9600);
  ds.begin();
  
    // показываем сколько датчиков нашли на шине
  Serial.print("Found ");
  Serial.print(ds.getDeviceCount(), DEC);
  Serial.println(" devices.");

    // достаем адрес датчика с индесом 0 
  if (!ds.getAddress(sensor0, 0)){ 
    Serial.println("Unable to find address for Device 0"); 
  } 

    // отпаравляем адрес из массива в монитор порта
  Serial.print("address sensor 0: ");   
  for (uint8_t i = 0; i < 8; i++)  {  
    Serial.print("0x");   
    Serial.print(sensor0[i], HEX);
    Serial.print(", ");
  }
  Serial.println();
 
    // устанавливаем разрешение датчика 11 бит (может быть 9, 10, 11, 12) 
    // на точность измерения температуры показатель не влияет.
 ds.setResolution(sensor0, 11); 

 
}

void loop() {
   ds.requestTemperatures();                  // считываем температуру с датчиков
     
    Serial.print("Sensor 0: "); 
    Serial.print(ds.getTempC(sensor0));      // отправляем температуру
    Serial.println("C  ");                    
 
    
 //   delay(1000);
}


И заключительный простой пример, в данном случаи, при включении микроконтроллера и всего прочего, вызываем функцию getDeviceCount() которая возвращает количество найденых датчиков на шине, это количество отправляем в «монитор порта» но правельее сделать проверку на наличие датчиков и остановку устройства если датчики не найдены.

Следом вызываем функцию getAddress(sensor0, 0), в функцию передаем массив sensor0 который в нашем случаи объявлен в 7 строке кода и индекс датчика, адрес которого функция присвоит в sensor0.

Следом, в цикле for отправляем в монитор порта содержимое массива sensor0, в котором должен содержатся серийный номер датчика, если все прошло успешно

В функции setResolution(sensor0, 11) устанавливаем разрешение получаемой с датчика sensor0 температуры по его серийному номеру, может быть 9, 10, 11, 12 бит, данный параметр не влияет на точность датчика.

в loop, как и в примере выше, отправляем значение температуры с датчика в «монитор порта» по серийному номеру

Зачем нужен этот пример? если с таким же успехом, можно написать ds.getTempCByIndex(0), с одним датчиком так и нужно, а если их много? серийный номер датчика можно записать в EEPROM с привязкой к конкретной задачи, отслеживать получение верных данных с конкретного датчика, все это повышает надежность устройства.


Купить DS18B20:

можно на али, тут, в герметичном исполнении тут.

Видео: