01 февраля 2017
Кравченко Виктор

Arduino Uno: Герметичный датчик температуры DS18B20 по 1-Wire

Цифровые устройства Arduino Arduino Lang Умный дом Датчики, модули Протоколы Микроконтроллеры и мини ПК
01
Содержание:
03
04

Описание

Датчик температуры DS18B20 — первый серьезный датчик, который можно применять не только в образовательных целях, но и в реальных системах умного дома. Диапазон измеряемых температур составляет -55...+125°C, при этом точность в 0,5°C (полградуса) обеспечивается в диапазоне -10...+85°C. Такие характеристики и герметичность позволяют использовать данный датчик для измерения температуры в различных агрессивных для обычных микросхем средах, таких как жидкости и почва, служить уличным датчиком температуры и т. д.

Даташит производителя на датчик температуры DS18B20 — DS18B20.pdf (484 KB)
05

В то же время этот датчик не так прост как кажется — это программируемый цифровой термометр. Он снабжен энергонезависимой памятью EEPROM в которой хранятся значения триггеров срабатывания тревоги, а также данные регистра конфигурации.

06

Для организации использования нескольких датчиков с одним микроконтроллером (МК) в памяти ROM каждого из датчиков хранится уникальный 64-битный идентификатор. Именно по нему и происходит идентификация конкретного датчика и персональное взаимодействие с ним.

07

Датчик обладает программируемой точностью — 9, 10, 11, 12 бит. Протокол обмена данными 1-Wire, а это значит что для общения с микроконтроллером (МК) ему нужен всего один провод DQ — линия данных, остальные 2 подключаются к питанию (Vdd) и земле (GND). На одну линию данных можно вешать несколько 1-Wire-устройств.

08

Питание

Питание датчика осуществляется напряжением в диапазоне 3—5,5 В. Оно может осуществляться как в обычном режиме — по линии Vdd, так и в «паразитном» — без линии Vdd. Это такой режим при котором питание датчика берется с линии данных DQ, так как она по правилам подтянута к единице через резистор. По ней происходит заряд конденсатора CPP, обеспечивающего функционирование датчика:

09
10

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

11

При использовании «паразитного» режима подключения датчика, линия питания Vdd должна быть соединена с землей:

12
Слева схема подключения датчика в паразитном режиме, справа — в обычном
Слева схема подключения датчика в «паразитном» режиме, справа — в обычном
13 На заметку:
Производитель не рекомендует работу датчика в «паразитном» режиме при температуре выше +100°С.
14

В связи с тем, что во время осуществления измерений, либо во время копирования данных в/из EEPROM датчик потребляет больше тока (до 1,5 мА), в «паразитном» режиме, тока, запасенного в конденсаторе CPP может не хватить — в этом случае необходимо компенсировать просадку — это делается путем включения в схему МОСФЕТ-транзистора как показано на рисунке выше. Тем самым во время усиленного токопотребления линия данных будет притянута к питанию.

15

Принцип работы

По умолчанию датчик находится в состоянии покоя. Для осуществления измерения микроконтроллер должен отдать ему команду. Датчик умеет информировать микроконтроллер о том, что измерение происходит или завершено, делает он это по состоянию шины DQ: 0 — идет измерение, 1 — измерение завершено. По аналогии, состоянием шины датчик может говорить об осуществлении записи в/из EEPROM.

16 На заметку:
При «паразитном» режиме питания датчик не может информировать о статусе.
17

Данные о температуре хранятся в регистре температуры, который состоит из двух байт (16 бит) — LS Byte (LSB, Least Significant — англ. наименее значимый) и MS Byte (MSB, Most Significant — англ. наиболее значимый):

18
LS Byte бит 7 бит 6 бит 5 бит 4 бит 3 бит 2 бит 1 бит 0
23 22 21 20 2-1 2-2 2-3 2-4
MS Byte бит 15 бит 14 бит 13 бит 12 бит 11 бит 10 бит 9 бит 8
S S S S S 26 25 24
19

В битах S хранится информация о знаке (Sign), для положительных значений S=0, для отрицательных S=1. Например:

20
Температура (°C)Полученное измерение (BINARY)
+12500000111 11010000
+8500000101 01010000
+25.062500000001 10010001
+10.12500000000 10100010
+0.500000000 00001000
000000000 00000000
-0.511111111 11111000
-10.12511111111 01011110
-25.062511111110 01101110
-5511111100 10010000
21

При использовании меньшей точности измерений, биты 0-3 не используются: для 11-битной точности не используется 0 бит, для 10 — не используются 0-1 биты и т. д.

22

Для вычисления температуры необходимо:

  • при положительном значении (S=0) нужно 16 бит перевести в десятичный код и умножить на 0,0625°C (коррекция на десятичные $2^{-4}=0.0625$ — таким образом получается значение с заданным количеством знаков после запятой),
  • при отрицательном значении (S=1) нужно 16 бит сначала инвертировать и прибавить единицу, а затем перевести в десятичный код и умножить на 0,0625°C (или разделить на $2^{4}=16$).

23

При первом включении датчика значение температуры по умолчанию +85°С.

24

После того как измерение произведено, полученное значение сравнивается с установленными значениями триггеров тревоги, которые хранятся в регистрах TH и TL (High и Low). Доступ к этим регистрам осуществляется при обращении к 2 и 3 байтам памяти. Формат обоих регистров одинаков:

25
TH/TL бит 7 бит 6 бит 5 бит 4 бит 3 бит 2 бит 1 бит 0
S 26 25 24 23 22 21 20
26

При сравнении с регистрами TH и TL из регистра температуры сравниваются только 8 бит — с 11 по 4. Если полученное значение температуры будет равно или превышать TH, либо будет равно или ниже TL, то условие тревоги будет считаться выполненным и будет установлен соответствующий флаг. Этот флаг обновляется после каждого проведенного измерения. Управляющий МК может проверить статус флага и отреагировать на него. Если этот функционал не нужен, его можно просто игнорировать.

27

64-битный уникальный идентификатор ROM

Каждый датчик DS18B20 имеет присвоенный и доступный только для чтения (ROM — Read Only Memory, ПЗУ) уникальный 64-битный (8 байт) идентификатор. Он состоит из 3 частей:

28
MSBLSB MSBLSB MSBLSB
1 байт
8-битный код
CRC
6 байт
48-битный серийный номер
Serial Number
1 байт
8-битный код семейства 28h
Family Code
29

Младшие 8 бит содержат, одинаковый для всех датчиков DS18B20, код семейства — 28h (1-Wire DS18B20). Следующие 48 бит содержат серийный номер датчика. Заключительные старшие (Most Significant) 8-бит содержат код CRC (Cyclic Redundancy Check — англ. циклический избыточный контроль). CRC-значение является результатом расчета специального алгоритма нахождения контрольной суммы, предназначенного для проверки целостности данных, в котором участвуют предыдущие 56 бит.

30 На заметку:
Производитель гарантирует, что единожды использованный адрес повторится в другом устройстве (количество комбинаций в 48 битах: $2^{48} \approx 2.81 \times 10^{14}$) при ежегодном производстве 1000 млрд. устройств, не ранее чем через 281 год — и это только для одного семейства.
31

Структура памяти

Оперативная память датчика DS18B20 (SPM (ScratchPad Memory) — т. н. блокнотная память, высокоскоростная внутренняя память используется для временного хранения данных) состоит из 9 байт и её структура выглядит следующим образом:

32
Байты отмеченные звездочкой, при включении заполняются данными из EEPROMbrЖелтым выделены байты только для чтения — 1, 2 и 8
Байты отмеченные звездочкой, при включении заполняются данными из EEPROM
Желтым выделены байты только для чтения — 1, 2 и 8
33 На заметку:
Если в проекте регистры тревоги TH и TL не используются, то их можно использовать в качестве ячеек памяти общего назначения и хранить там произвольные данные.
34

Регистр конфигурации (Configuration Register)

Регистр конфигурации — четвертый байт в SPM-памяти. И по сути все что можно настроить при помощи этого регистра — точность измерений. Она задается комбинацией всего 2 битов — шестого R1 и пятого R0:

35
Configuration Register бит 7 бит 6 бит 5 бит 4 бит 3 бит 2 бит 1 бит 0
0 R1 R0 1 1 1 1 1
36

Возможны следующие варианты:

37

R1R0Точность (в битах)Максимальное время измерения
00993.75 мс(tCONV/8)
0110187.5 мс(tCONV/4)
1011375 мс(tCONV/2)
1112750 мс(tCONV)
* По умолчанию установлена максимальная 12-битная точность

38

Протокол 1-wire

Взаимодействие с датчиком происходит по протоколу 1-Wire. Здесь не будет описываться сам протокол. Подробно о нем можно почитать в статье 1-Wire: протокол обмена данными по одному проводу.

39

Датчик температуры DS18B20 в сети всегда выступает в роли ведомого (Slave). При передаче всех данных и команд первым отправляется младший бит (LSB, Least Significant Bit).

40

Весь процесс взаимодействия делится на 3 этапа:

  • инициализация
  • ROM-команда — обмен данными
  • функциональная команда — обмен данными

41

Все эти этапы обязательны к исполнению именно в таком порядке. Исключения составляют только ROM-команды Search ROM [F0h] и Alarm Search [ECh]. После исполнения этих команд необходимо вернуться к шагу №1 — инициализация.

42

Любая передача-транзакция начинается с последовательности инициализации — сначала микроконтроллер (мастер, ведущий) прижимает линию данных к нулю минимум на 480 мкс, затем мастер переходит в режим приема и следит, чтобы датчик ответно прижал линию данных к нулю на время 60-240 мкс, после этого подтягивающий резистор вернет линию в исходное положение HIGH:

43
bTsubX/sub/b — Transmit (передавать), bRsubX/sub/b — Recieve (получать)
TX — Transmit (передавать), RX — Recieve (получать)
44

После инициализации начинает происходит обмен данными. Единицей обмена является один бит, который передается/принимается в течение одного тайм-слота — временного отрезка, который не должен быть меньше 60 мкс. Пауза между тайм-слотами не должна быть менее 1 мкс — этого времени должно хватить, для подтягивания шины резистором к единице.

45

Существуют 4 вида тайм-слотов, при помощи которых происходит запись/чтение нулей и единиц в/из шины данных. Каждый из 4 видов начинается с притягивания мастером шины данных к нулю минимум на 1 мкс:

46
47

Для записи логической единицы HIGH мастер притягивает шину к нулю и держит до окончания тайм-слота, для записи логического нуля LOW, мастер начинает тайм-слот притягиванием линии данных к нулю, но тут же отпускает (не позже 15 мкс). Через 15 мкс с начала тайм-слота датчик считывает значения линии данных.

48

Для чтения данных мастер дергает линию данных к нулю на не менее, чем 1 мкс для начала тайм-слота и сразу отпускает и считывает состояние линии данных до истечения 15 мкс с начала тайм-слота.

49

Таким образом, когда осуществлять чтение или запись, ведущий знает исходя из отправляемых команд. Рассмотрим на примере как происходит взаимодействие с одним датчиком (напомню, TX — Запись (Write), RX — Чтение (Read)):

50
Мастер Данные Комментарии
TX Сброс (Reset) Мастер инициирует импульс сброса в линии
RX Присутствие (Presence) Датчик отвечает присутствием
TX 55h Команда Match ROM (55h) — начало передачи 64-битного уникального кода для идентификации конкретного датчика.
TX 64-битный идентификатор Мастер отправляет 64-битный код адресата. После отправки, на дальнейшие команды будет реагировать только один датчик, с указанным идентификатором
TX 44h Команда Convert T (44h) — запускает измерения
TX Проверка шины DQ После запуска измерения датчик притягивает линию данных к нулю 0, и держит её до окончания измерений. Мастер в это время контролирует, когда шина будет отпущена к единице 1. Это будет означать завершение измерений и можно будет считать полученное значение.
Новая партия обмена данными снова начинается с блока инициализации
TX Сброс (Reset) Повторяется процедура идентификации
RX Присутствие (Presence)
TX 55h
TX 64-битный идентификатор
TX BEh Команда Read Scratchpad (BEh) — после получения этой команды датчик начнет отправлять данные о своей SPM-памяти — все 9 байт
RX 9-байт памяти Мастер считывает все 9 байт памяти, включая 8 байт — CRC, далее производится проверка — рассчитывается CRC на основе первых восьми полученных байт и сравнивается с полученным CRC. Если они совпадают, то Мастер продолжает работу, если нет — процедура чтения повторяется.
51

ROM-Команды

  • Search Rom [F0h] (поиск ROM) — команда поиска всех 1-Wire-устройств на линии данных. Механизм поиска описан в спецификации iButton.
  • Read Rom [33h] (чтение ROM) — команда, после которой датчик передает 64-бита ROM. Нельзя использовать, если на линии большего одного датчика. Исполнение команды приводит к непредсказуемым результатам.
  • Match Rom [55H] (совпадение ROM) — команда сравнения идентификатора. После этой команды Мастер отправляет 64-битный идентификатор, после получения которого, на остальные команды будет реагировать только один датчик.
  • Skip Rom [CCh] (пропуск ROM) — команда пропуска процедуры идентификации конкретного датчика. После этой команды может быть отправлена команда запуска измерения температуры Convert T [44h] — все датчики находящиеся на линии начнут процедуру измерения.
    После этой команды применять команду Read Scratchpad [BEh] (чтения памяти SPM) можно только если на шине висит только один датчик. В противном случае исполнение команды приводит к непредсказуемым результатам. Также используется в случаях наличия на линии только одного датчика и идентификатор не нужен.
  • Alarm Search [ECh] (поиск тревоги) — процедура поиска устройств, идентична команде Search Rom [F0h] за тем лишь исключением, что будут найдены устройства только находящиеся в режиме тревоги.
52

Функциональные команды

  • Convert T [44h] (конвертация температуры) — команда запуска единичного измерения. После окончания измерения полученные данные будут помещены в первые два байта памяти SPM, и могут быть считаны при помощи команды Read Scratchpad [BEh].
  • Write Scratchpad [4Eh] (запись в блокнотную память) — команда записи данных в память SPM. После этой команды мастер передает 3 байта — байт регистра TH (второй байт памяти), байт регистра TL (третий байт памяти) и байт регистра конфигурации (четвертый байт). Данные передаются младшим битом вперед (LSB first).
  • Read Scratchpad [BEh] (чтение блокнотной памяти) — команда чтения памяти SPM. После этой команды датчик передает 9 байт временной памяти — последний байт передачи — контрольное значение CRC. Мастер может оборвать чтение в любом месте, если нужна только часть данных и она уже получена.
  • Copy Scratchpad [48h] (копирование блокнотной памяти) — команда копирования данных из блокнотной памяти (SPM) в EEPROM. Копируются только 3 байта — TH, TL и регистр конфигурации. На время копирования датчик притягивает линию данных к нулю.
  • Recall E2 [B8h] (запрос данных из EEPROM) — принудительное заполнение данных блокнотной памяти данными из EEPROM, копируются только 3 байта — TH, TL и регистр конфигурации
  • Read Power Supply [B4h] (чтение режима питания) — команда запроса режима питания. Если в ответ на команду на шине логический 0, значит используется «паразитный» режим питания, если 1 — то используется внешнее питание.
53

Практика

Подключается датчик следующим образом (на примере Arduino Uno):

54
55

Для работы с датчиком понадобится библиотека. Можно использовать только библиотеку OneWire — её лучше использовать для понимания механизмов взаимодействия с датчиками, так как это библиотека более низкого уровня. Она предназначена для работы со всеми устройствами, поддерживающими протокол 1-Wire. Все преобразования придется производить вручную — температура из байтов, установки точности, копирование и т. д. Заранее заготовленных функций по работе именно с датчиком DS18B20 в ней нет. Вот так например выглядит поиск и отображение идентификаторов всех устройств 1-Wire на всех подключенных шинах к плате Arduino Uno:

56 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <OneWire.h> void setup() { Serial.begin(9600); for (uint8_t pin = 2; pin < 13; pin++) // Перебираем все пины { findDevices(pin); // Запускаем процедуру поиска } } void loop() {} uint8_t findDevices(int pin) { OneWire ow(pin); // Создаем экземпляр объекта OneWire uint8_t address[8]; // Переменная для хранения идентификатора устройства uint8_t count = 0; // Переменная для хранения количества устройств if (ow.search(address)) // Если обнаружено устройство { // В переменную address записывается идентификатор Serial.print("\nuint8_t pin"); Serial.print(pin, DEC); // Отображаем пин Serial.println("[][8] = {"); { count++; // Приступаем к побайтовому выводу идентификатора Serial.print(" {"); for (uint8_t i = 0; i < 8; i++) // 8 байт идентификатора, младший - адрес семейства, 6 байт уникального кода { // 6 байт уникального кода и старший - CRC-код Serial.print("0x"); if (address[i] < 0x10) Serial.print("0"); Serial.print(address[i], HEX); if (i < 7) Serial.print(", "); } Serial.println(" },"); } while (ow.search(address)); // Делать до тех пор, пока поиск не вернет false - устройства закончатся Serial.println("};"); Serial.print("Devices found: " + String(count)); } return count; }
57

Результат — на 10 пине Arduino Uno найден один датчик:

1
2
3
4
uint8_t pin10[][8] = { {0x28, 0x51, 0x78, 0x26, 0x00, 0x00, 0x80, 0xA1 }, }; // Devices found: 1

58

На примере библиотеки OneWire можно посмотреть как происходит поэтапный процесс отправки команд и чтения данных, например:

59 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <OneWire.h> OneWire ow(10); // Шина на 10 пине void setup(void) { Serial.begin(9600); } void loop(void) { byte present = 0; byte data[12]; // 12 байт byte addr[8]; // 8 байт для получения 64-битного ROM-идентификатора float celsius, fahrenheit; // Переменные для хранения данных о температуре if (!ow.search(addr)) { // Если обнаружено первое устройство - переходим к отображению данных с него Serial.println("No more addresses.\n"); ow.reset_search(); // Если устройство больше не найдено обнуляем поиск и начинаем все сначала delay(250); return; } Serial.print("ROM ="); // Выводим идентификатор устройства for (int i = 0; i < 8; i++) { Serial.write(' '); Serial.print(addr[i], HEX); } if (OneWire::crc8(addr, 7) != addr[7]) { // Сверяем CRC - если совпадения нет - переходим к следующему датчику Serial.println("CRC is not valid!\n"); return; } switch (addr[0]) { // Проверяем принадлежит ли устройство семейству датчиков DS18B20 - 0x28 case 0x28: Serial.println(" Chip = DS18B20"); // Да принадлежит, продолжаем break; default: // Нет, не принадлежит, переходим к другому устройству Serial.println("Device is not a DS18B20 family device."); return; } ow.reset(); // Отправляем импульс сброса и ждем подтвержение присутствия ow.select(addr); // Match ROM + идентификатор - Отправляем идентификатор для определения устройства ow.write(0x44); // Convert T - Отправляем команду запуска измерений в режиме внешнего питания //ow.write(0x44, 1); // или Convert T - в режиме "паразитного" питания delay(1000); // Ждем максимум 750 мс пока завершится измерение ow.reset(); // Снова отправляем импульс сброса и ждем подтвержение присутствия ow.select(addr); // Отправляем идентификатор для определения устройства ow.write(0xBE); // Read Scratchpad - Команда чтения ScratchPad (блокнотной памяти) Serial.print(" Data = "); for (int i = 0; i < 9; i++) { // Нужно считать все 9 байт блокнотной памяти (0 и 1 байт - данные о температуре) data[i] = ow.read(); Serial.print(data[i], HEX); Serial.print(" "); } Serial.print(" CRC="); Serial.print(OneWire::crc8(data, 8), HEX);// Последний байт данных (8 байт) и сумма CRC должны совпадать, по нормальному это нужно проверять Serial.println(); // highByte lowByte int16_t raw = (data[1] << 8) | data[0]; // Конвертируем два байта температуры в 16-битное целое int sig = raw & 0x8000; // Знак значения, за него отвечают 5 старших бит MS-байта температуры, // нам достаточно одного бита. Serial.print("\n raw: "); Serial.print(raw, BIN); if (sig) { // Еси значение меньше нуля raw = (raw ^ 0xffff) + 1; // Инвертируем биты, если знак отрицательный и прибавляем единицу } byte cfg = (data[4] & 0x60); // Получаем значение точности // Необязательно // Обнулим ненужные младшие биты исходя из полученного значения точности if (cfg == 0x00) raw = raw & ~7; // 9 бит, к значению температуры применяем побитовое И с инверсией семерки, т.е. 00000111 -> 11111000 else if (cfg == 0x20) raw = raw & ~3; // 10 бит, к значению температуры применяем побитовое И с инверсией семерки, т.е. 00000011 -> 11111100 else if (cfg == 0x40) raw = raw & ~1; // 11 бит, к значению температуры применяем побитовое И с инверсией семерки, т.е. 00000001 -> 11111110 celsius = (float)raw * 0.0625; // Умножаем 2^(-4)=0.0625, тем самым получаем реальное значение температуры с десятичными //celsius = (float)raw / 16.0; // или делим на 2^4=16.0, тем самым получаем реальное значение температуры с десятичными fahrenheit = celsius * 1.8 + 32.0; // Конвертируем в Фаренгейты // Выводим информацию Serial.print("\n Temperature = " + String(celsius) + " Celsius, " + String(fahrenheit) + " Fahrenheit"); }
60

Результат, вывод данных только одного датчика подключенного к 10 пину:

1
2
3
4
ROM = 28 51 78 26 0 0 80 A1 Chip = DS18B20 Data = B9 1 FF FF 7F FF FF FF F1 CRC=F1 Temperature = 27.56 Celsius, 81.61 Fahrenheit No more addresses.

61

А можно использовать дополнительную надстройку к библиотеке OneWire — библиотеку DallasTemperature — эта библиотека уже является специализированной по работе именно с температурными датчиками DS18*, DS28*. Этой библиотекой можно пользоваться уже, как говорится, «из коробки». Обе библиотеки устанавливаются из среды Arduino IDE — в первом случае в окне Library Manager в поле поиска нужно набрать OneWire, во втором DallasTemperature:

В примерах к библиотеке DallasTemperature приведены примеры помимо стандартного функционала, описанного ниже, также и примеры редких ситуаций, например работа с датчиками расположенными на нескольких шинах.
62
63

Самый простой скетч по получению температуры с одного датчика выглядит следующим образом:

64 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <OneWire.h> // Библиотека протокола 1-Wire #include <DallasTemperature.h> // Библиотека для работы с датчиками DS* #define ONE_WIRE_BUS 10 // Шина данных на 10 пине OneWire oneWire(ONE_WIRE_BUS); // Создаем экземпляр объекта протокола 1-WIRE - OneWire DallasTemperature sensors(&oneWire); // На базе ссылки OneWire создаем экземпляр объекта, работающего с датчиками DS* void setup(void) { Serial.begin(9600); // Настраиваем Serial для отображения получаемой информации sensors.begin(); // Запускаем поиск всех датчиков } void loop(void) { Serial.println("Requesting temperatures..."); sensors.requestTemperatures(); // Запускаем измерение температуры на всех датчиках // Когда температура измерена её можно вывести // Поскольку датчик всего один, то запрашиваем данные с устройства с индексом 0 Serial.print("Temperature for the device 1 (index 0) is: "); Serial.println(sensors.getTempCByIndex(0)); }
65

Расширенный функционал библиотеки представлен ниже. Скетч включает самые распространенные виды взаимодействия с датчиком:

66 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <OneWire.h> // Библиотека протокола 1-Wire #include <DallasTemperature.h> // Библиотека для работы с датчиками DS* #define ONE_WIRE_BUS 10 // Шина данных на 10 пине #define TEMPERATURE_PRECISION 9 // Точность измерений в битах (по умолчанию 12) #define DEVICE_COUNT 10 // Количество устройств на шине. Необходимо объявить заранее, так как // среда Arduino не поддерживает динамические массивы, лучше указать заведомо // большее количество - все равно будет использоваться количество, которое // определил МК - в переменной int deviceCount OneWire oneWire(ONE_WIRE_BUS); // Создаем экземпляр объекта протокола 1-WIRE - OneWire DallasTemperature sensors(&oneWire); // На базе ссылки OneWire создаем экземпляр объекта, работающего с датчиками DS* DeviceAddress deviceAddresses[DEVICE_COUNT]; // Создаем массив для хранения адресов датчиков int deviceCount = 0; // Переменная для хранения количества датчиков void setup(void) { Serial.begin(9600); // Настраиваем Serial для отображения получаемой информации Serial.println("Locating devices..."); sensors.begin(); // Запускаем поиск всех датчиков на шине deviceCount = (int)sensors.getDeviceCount(); // Получаем количество найденных датчиков Serial.println("Found " + String(deviceCount) + " devices."); // Показываем количество найденных датчиков // Получаем режим питания датчиков - паразитное или обычное Serial.print("Parasite power is: "); Serial.println(sensors.isParasitePowerMode() ? "ON" : "OFF"); // Выводим режим питания for (int i = 0; i < deviceCount; i++) { // Заполняем массив адресами устройств if (!sensors.getAddress(deviceAddresses[i], i)) { // Если не удалось получить идентификатор Serial.println("Unable to find address for Device " + String(i)); // Выдаем сообщение об этом } else { // Все нормально - идентификатор получен // Основные данные Serial.print("Address for Device " + String(i+1) + ": "); printAddress(deviceAddresses[i]); // Выводим адрес каждого устройства Serial.print("\n"); sensors.setResolution(deviceAddresses[i], TEMPERATURE_PRECISION); // Для каждого датчика задаем точность TEMPERATURE_PRECISION (9 бит) Serial.print("Device " + String(i+1) + " Resolution: "); Serial.println(sensors.getResolution(deviceAddresses[i]), DEC); // Проверяем, удалось ли изменить точность // Тревога Serial.print("Device " + String(i + 1) + " Alarms: "); printAlarms(deviceAddresses[i]); // Вывод информации о параметрах тревоги Serial.println(); sensors.setHighAlarmTemp(deviceAddresses[i], 20); //Задаем новые пороговые значения тревоги sensors.setLowAlarmTemp(deviceAddresses[i], -10); //Задаем новые пороговые значения тревоги Serial.print("Device " + String(i + 1) + " New Alarms: "); printAlarms(deviceAddresses[i]); // Вывод информации о новой информации Serial.println(); } } Serial.println("================================================="); } void loop(void) { Serial.println("Requesting temperatures..."); sensors.requestTemperatures(); // Запускаем измерение температуры на всех датчиках //// Когда температура измерена её можно вывести //// Поскольку датчик всего один, то запрашиваем данные с устройства с индексом 0 //Serial.print("Temperature for the device 1 (index 0) is: "); //Serial.println(sensors.getTempCByIndex(0)); for (int i = 0; i < deviceCount; i++) { // Перебираем все устройства // Вывод температуры информации с каждого датчика float tempC = sensors.getTempCByIndex(i); // Получаем данные по порядковому номеру Serial.println("Temperature for the device " + String(i + 1) + " (by index " + String(i) + ") is: " + String(tempC)); tempC = sensors.getTempC(deviceAddresses[i]); // Либо по адресу устройства Serial.print("Temperature for the device " + String(i + 1) + " (by adress "); printAddress(deviceAddresses[i]); Serial.println(") is: " + String(tempC)); Serial.print("Complex info: "); printData(deviceAddresses[i]); // Можно воспользоваться функцией для вывода информации // Проверка состояния тревоги - если событие тревоги зафиксировано, // то появится уведомление об этом // базируется на функции sensors.hasAlarm(deviceAddress) checkAlarm(deviceAddresses[i]); // Проверяем каждый датчик на состояние тревоги Serial.print("\n\n"); } } //========================================================================================= //= Дополнительные функции = //========================================================================================= // Функция вывода идентификатора датчика void printAddress(DeviceAddress deviceAddress) { for (uint8_t i = 0; i < 8; i++) { if (deviceAddress[i] < 16) Serial.print("0"); Serial.print(deviceAddress[i], HEX); } } //========================================================================================= // Функция вывода температуры датчика по его идентификатору void printTemperature(DeviceAddress deviceAddress) { float tempC = sensors.getTempC(deviceAddress); Serial.print("Temp C: "); Serial.print(tempC); Serial.print(" Temp F: "); Serial.print(DallasTemperature::toFahrenheit(tempC)); } //========================================================================================= // Функция вывода точности датчика по его идентификатору void printResolution(DeviceAddress deviceAddress) { Serial.print("Resolution: "); Serial.print(sensors.getResolution(deviceAddress)); Serial.println(); } //========================================================================================= // Функция вывода всей информации о датчике по его идентификатору void printData(DeviceAddress deviceAddress) { Serial.print("Device Address: "); printAddress(deviceAddress); Serial.print(" "); printTemperature(deviceAddress); Serial.println(); } //========================================================================================= // Функция вывода информации о параметрах тревоги void printAlarms(uint8_t deviceAddress[]) { char temp; temp = sensors.getHighAlarmTemp(deviceAddress); Serial.print("High Alarm: "); Serial.print(temp, DEC); Serial.print("C/"); Serial.print(DallasTemperature::toFahrenheit(temp)); Serial.print("F | Low Alarm: "); temp = sensors.getLowAlarmTemp(deviceAddress); Serial.print(temp, DEC); Serial.print("C/"); Serial.print(DallasTemperature::toFahrenheit(temp)); Serial.print("F"); } //========================================================================================= // Функция вывода информации о состоянии тревоги void checkAlarm(DeviceAddress deviceAddress) { if (sensors.hasAlarm(deviceAddress)) { Serial.print("ALARM: "); printData(deviceAddress); } }
67

Результат:

1
2
3
4
5
6
7
8
9
10
11
12
13
Locating devices... Found 1 devices. Parasite power is: ON Address for Device 1: 28517826000080A1 Device 1 Resolution: 9 Device 1 Alarms: High Alarm: -1C/68.00F | Low Alarm: -1C/14.00F Device 1 New Alarms: High Alarm: 20C/68.00F | Low Alarm: -10C/14.00F ================================================= Requesting temperatures... Temperature for the device 1 (by index 0) is: 28.00 Temperature for the device 1 (by adress 28517826000080A1) is: 28.00 Complex info: Device Address: 28517826000080A1 Temp C: 28.00 Temp F: 82.40 ALARM: Device Address: 28517826000080A1 Temp C: 28.00 Temp F: 82.40

68

Без использования макетной платы датчик удобно подключать следующим образом (выводы датчика для придания им жесткости, необходимо предварительно залудить):

69
70
71

Об особенностях вывода данных на устройства отображения — сегментные дисплеи и светодиодные матрицы написано во второй части статьи.

73

Что почитать:

  • Даташит производителя — DS18B20.pdf (484 KB)
74

Похожие запросы:

  • Герметичный датчик температуры DS18B20
  • Arduino и цифровой датчик температуры DS18B20
  • Самодельный термозонд на базе DS18B20
  • Подключение датчика температуры DS18B20 к Arduino
  • DS18B20 – датчик температуры с интерфейсом 1-Wire
  • DS1820, DS18S20, DS18B20 — популярные цифровые термодатчики фирмы DALLAS-MAXIM с однопроводным интерфейсом 1-Wire
  • Датчик температуры DS18B20 (датчик, герметичный датчик, модуль датчика)
comments powered by HyperComments

Яндекс.Метрика