Найти тему
Реальная Сталь

Ардуино с Нуля до ЧПУ. Работа с Датчиком Температуры DS18x20 без Библиотеки. Термостат.

Выводить показания датчика DS18x20 будем на знаковый LCD 2004, затем на семисегментный индикатор с точкой и десятой долью градуса

Открываем документацию(Datasheet),читаем хотя бы поверхностно.Выясняем:

DS18х20 это цифровой измеритель температуры, с  разрешением преобразования 9 - 12 разрядов и функцией тревожного сигнала контроля за температурой. Параметры контроля могут быть заданы пользователем и сохранены в энергонезависимой памяти датчика.

DS18B20 обменивается данными с микроконтроллером, а следовательно и с Ардуино по однопроводной линии связи, используя протокол интерфейса 1-Wire.

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

Диапазон измерения температуры составляет от -55 до +125 °C. Для диапазона от -10 до +85 °C погрешность не превышает 0,5 °C.

У каждого DS18B20 есть уникальный серийный номер 64 разряда, который позволяет нескольким датчикам подключаться на одну общую линию связи. То есть на один пин Ардуино можно подключить несколько датчиков, распределенных на значительном расстоянии.

Пока в протокол 1-Wire вникать не будем, а будем использовать предназначенную для этого библиотеку OneWire.h. С великой благодарностью к создателям качаем ТУТ. Устанавливаем

Смотрим в документации распиновку датчика. Собираем схему. Нужно будет установить резистор 3.1к - 5к(обычно 4.7к), соединяющий пин питания и пир данных датчика. Он участвует в формировании логической 1 для протокола.

.
.

Схему для LCD берем с прошлого занятия (работа с LCD по I2C). Подключаем датчик.

.
.

Сколько ни пытался загрузить скетч из просторов сети , ни один не заработал. Ваяем свой. В библиотеке OneWire.h есть пример работы с DS18x20, обычно по пути C:\Users\ИМЯ\Documents\Arduino\libraries\OneWire\examples\DS18x20_Temperature , файл DS18x20_Temperature.pde.

Открываем Temperature.pde с помощью Ардуино ИДЕ, грузим в плату Ардуино, исполняем монитор порта, видим следующее

Да, не забываем изменить пин подключения датчика, в нашем случае А0

.
.

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 или датчиков.

.
.

Функция search(addr) ищет датчик или очередной датчик и заносит его уникальный HEX адрес в массив addr . Если датчик один можно жестко прописать адрес в скетч или перенести функцию в Setup, но при замене датчика придется переписывать код и перешивать плату.

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

ROM = 28 A8 3E F9 5 0 0 12  —  Адрес датчика на шине в HEX формате.

.
.

Можно или даже нужно все удалить или закоментить.

Следующий фрагмент кода по ранее полученному адресу (первая цифра) выводит в монитор порта тип датчика

Chip = DS18B20  —  Тип датчика, вычисляется из адреса датчика.

и определяет type_s для дальнейших расчетов.

.
.

Все Serial.println удаляем или коментим. Если один датчик можно все удалить, а type_s жестко прописать.

Далее начинаем преобразование температуры (природного явления) в цифровое значение, читаем это значение, выводим в монитор порта в HEX формате. Все Serial.print_ы удаляем или коментим.

.
.

Начинаем процесс преобразования

.
.

полученных данных в фактическую температуру, которая храниться в 0 и 1 байтах считанной памяти. Для этого объединяем эти два байта в одно 16-ти битное число

int16_t raw = (data[1] << 8) | data[0];

Перед дальнейшим преобразованием, на потребуется определить семейство, к которому относится данный датчик (ранее мы сохраняли результат в переменной type_s). В зависимости от семейства, вычисление температуры будет проходить по-разному, так как DS18B20 и DS1822 возвращают 12-ти битное значение, а DS18S20 - 9-ти битное

if (type_s) { // Если датчик относится к семейству DS18S20

raw = raw << 3; // разрешение по умолчанию равно 9 бит

if(data[7] == 0x10) {

raw = (raw & 0xFFF0) + 12 - data[6]; } }

else {

Определяем на какую точность измерения сконфигурирован данный датчик

byte cfg = (data[4] & 0x60);

При более низких разрешениях можно обнулять младшие биты, так как они всё-рано не определены

if (cfg == 0x00) raw = raw & ~7; 9 бит преобразование занимает 93.75 ms

else if (cfg == 0x20) raw = raw & ~3; 10 бит преобразование занимает 187.5 ms

else if (cfg == 0x40) raw = raw & ~1; 11 бит преобразование занимает 375 ms

По умолчанию установлена точность 12 бит, преобразование занимает 750 ms.

Далее ведем подсчет согласно системе измерения и выводим на экран

.
.
.
.

Ну и обещанный термостат. При менее 28 градусах цельсия будем зажигать встроенный светодиод, имитируя включенный нагреватель а при 29 отключать. Разница между включением и выключением 1 градус называется гистерезис.

.
.
.НАГРЕВ
.НАГРЕВ
ОТКЛЮЧАЕМ НАГРЕВ
ОТКЛЮЧАЕМ НАГРЕВ

Используя например транзисторный ключ, подключенный в данном случае к 13 пину нашего Ардуино можно включать и отключать настоящий нагреватель, хоть на 100000 ватт.

Конечно наш термостат прописан "жестко". Что бы изменить условия нужно переписать код и перешить плату.

Давайте пропишем гистерезис между 37.4 и 37.9 градусов цельсия. Вполне сойдет для инкубатора. Я пробовал, вывод 85 процентов. Для 99 процентов лучше применять ПИД (пропорционально-интегро-дифференциальный регулятор) совместно с ШИМ (широтно импульсная модуляция) . Этим мы в ближайшее время займемся.

Проверенный скетч наших трудов для копирования

#include <OneWire.h> // библиотека протокола 1-Wire для ds18х20

#include <LiquidCrystal_I2C.h> // Подключаем библиотеку для работы с LCD дисплеем по шине I2C

LiquidCrystal_I2C lcd(0x27, 20, 4);// ОБЪЕКТ ( АДРЕС_I2C , КОЛ_СТОЛБЦОВ , КОЛ_СТРОК );

OneWire ds(A0); // Вход датчика

void setup() {

lcd.init(); // инициализация дисплея

lcd.backlight(); // включаем подсветку

pinMode(13, OUTPUT); // 13 пин на вывод

}

void loop(void) {

byte i; // Вспомогательная переменная для циклов

byte present = 0; //Переменная готовности датчика к общению

byte type_s; // Переменная для определения типа термодатчика на шине

byte data[12]; // Массив для хранения принятой от датчика информации

byte addr[8]; // Массив для хранения 64-битного адреса датчика

float celsius, fahrenheit; // Переменные для вычисления температуры

if ( !ds.search(addr)) { // функция поиска очередного датчика

//Serial.println("No more addresses."); // нам не нужен

// Serial.println(); // нам не нужен

ds.reset_search();

delay(250); //коварное делей потом заменим на милис

return;

}

// the first ROM byte indicates which chip

switch (addr[0]) {

case 0x10:

type_s = 1;

break;

case 0x28:

type_s = 0;

break;

case 0x22:

type_s = 0;

break;

default:

return;

}

ds.reset();

ds.select(addr);

ds.write(0x44);// начнаем преобразование,при поразитном питании вкл 1

delay(1000); //может быть, 750 мс достаточно, а может быть, и нет

present = ds.reset();

ds.select(addr);

ds.write(0xBE); // Читать

for ( i = 0; i < 9; i++) { // нам нужно 9 байт

data[i] = ds.read();

}

// Преобразование данных в фактическую температуру

// поскольку результатом является 16-битное целое число со знаком, оно должно

// будет сохранен в виде типа "int16_t", который всегда равен 16 битам

// даже при компиляции на 32-разрядном процессоре.

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;

lcd.setCursor(0,0); // первая строка 0,первый знак 0

lcd.print("Temperature celsius "); // пишем

lcd.setCursor(6,1); // вторая строка, шестой знак

lcd.print(celsius); // выводим температуру на ЛСД

lcd.setCursor(0,2); // третья строка, первый знак

lcd.print("Tempture fahrenheit"); // пишем

lcd.setCursor(6,3); // выводим температуру на ЛСД

lcd.print(fahrenheit); // выводим температуру на ЛСД

if (celsius < 28) digitalWrite(13, 1);//нагреватель включен

if (celsius > 29) digitalWrite(13, 0);//нагреватель отключен

}

Если использовать этот код как термометр или термостат коварный delay(1000); нам не помеха, но если мы захотим вставить меню для изменения параметров термостата или выводить температуру на семисегментный индикатор, то придется от delay(1000); избавиться.

Вывод температуры на семисегментный индикатор с десятой долей и меню для термостата будет ТУТ