В прошлых статьях я рассказал как выводить четырех разрядное число на семисегментный индикатор и как работать с датчиком температуры DS18x20 и выводить значение на LCD дисплей. Ссылки в конце каждого кода.
Давайте объединим эти два кода воедино, то есть выведем значение температуры с помощью DS18x20 на семисегментный индикатор с точкой и десятой долью.
ВИДЕО о чем пойдет речь ТУТ
Вот схема и код семисегментного индикатора. Выводит значение, присвоенное глобальной переменной int g = 8127;, любое четырех разрядное.
Рабочий код для копирования
int g = 8127;
byte segment[8] = {0, 1, 2, 3, 4, 5, 6, 7};
byte razrad[4] = {8, 9, 10, 11};
byte cifri[12][7] = { // маска цифр
0, 0, 0, 0, 0, 0, 1, //0
1, 0, 0, 1, 1, 1, 1, //1
0, 0, 1, 0, 0, 1, 0, //2
0, 0, 0, 0, 1, 1, 0, //3
1, 0, 0, 1, 1, 0, 0, //4
0, 1, 0, 0, 1, 0, 0, //5
0, 1, 0, 0, 0, 0, 0, //6
0, 0, 0, 1, 1, 1, 1, //7
0, 0, 0, 0, 0, 0, 0, //8
0, 0, 0, 0, 1, 0, 0, //9
0, 0, 1, 1, 1, 0, 0, // знак градус
0, 1, 1, 0, 0, 0, 1, // С символ цельсий
};
void setup() { // настройка портов
for (byte i = 0; i < 12; i++) {
pinMode(i, OUTPUT);
}
digitalWrite(segment[7], 0);// 1 гасим точку, 0 горит
}
void loop() {
// здесь потом напишем любой другой код
//например термометр
Vivod_Seg(); // вызываем функцию вывода
}
void Vivod_Seg() {
static unsigned long timer; // статическая для организации таймера
static byte r = 0; //статическая для внутренней работы функции
byte d; //для внутренней работы функции
if (timer > millis()) return; // если не прошло 5 мс, ничего не делаем
switch (r) { // вычисляем цифру активного разряда
case 0:
d = g / 1000; // вычисляем количество тысяч g=8127 цифра 8 первый разряд
break;
case 1:
d = (g / 100) % 10; // вычисляем колличество сотен 1 во второй разряд
break;
case 2:
d = (g / 10) % 10; // количество десятков 2 в третий разряд
break;
case 3:
d = g % 10; // количество едениц 7 в четвертый разряд
break;
}
for (byte i = 0; i < 4; i++) { // выставляем разряд
digitalWrite(razrad[i], (r == i));
}
for (byte i = 0; i < 7; i++) { // выставляем сегменты
digitalWrite(segment[i], cifri[d][i]);
}
r = r == 4 ? 0 : r + 1; // если r равно 4, то r в ноль,иначе увеличиваем на 1
timer = millis() + 5;
}
ОПИСАНИЕ ТУТ
Вот схема и код DS18x20. Выводит температуру на дисплей, термостат на 28 градусов цельсия.
#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);//нагреватель отключен
}
ОПИСАНИЕ ТУТ
ОБЪЕДИНЯЕМ СХЕМУ, ардуино можно взять любое, выводы любые.
ОБЪЕДИНЯЕМ СКЕТЧИ. Здесь придется потрудится, взять код из loop одного скетча и дополнить в другой не получиться. Во первых нужно разобраться с переменными, во вторых в коде DS18x20 две коварных
функции delay(), которые сразу остановят весь скетч, то есть прекратится динамическая индикация, вывод на семисегментный индикатор. В третьих получится длинный, запутанный код.
Давайте воспользуемся системой вкладок в Arduino IDE, Разделим наш код на вкладки. Вкладки относятся к файлам, находящимся в одной папке со скетчем, с расширением .ino , старая версия .pde
Создаем в Ардуино ИДЕ Новый файл, называем например Termostat. Добавляем к нему две, СОВЕРШЕННО ЧИСТЫЕ вкладки, например 7_Seg и ds1820 с помощью меню или вставляем напрямую в папке. На все вкладки один SETUP, один LOOP, у нас в Termostat.
Во вкладку Termostat помещаем переменные, в сетап инициализируем порты согласно схеме, в луп вызываем функцию вывода на семисегментик. Во вкладке 7_Seg помещаем функцию вывода на семисегментный индикатор
Вывели наше число int g = 8127; с помощью двух вкладок. Теперь нужно подключить наш датчик температуры и переменную температуры float celsius "подсунуть" вместо int g = 8127; Здесь сложнее, переменные имеют разный тип и нужно избавиться от ДЕЛЕЙ_ев, да еще и поставить ТОЧКУ на семисегментике, отделив целое от десятых долей градуса.
Если вы изучали код работы с датчиком DS18x20, то знаете, что для считывания уникального адреса требуется 250 мс, delay(250);. а для считывания данных температуры delay(1000) 1 секунда. Динамическая индикация семисегментика не потерпит такой задержки. Придется параллелить работу.
Придется подключать таймер, хотя бы косвенно, с помощью функции millis();
В loop добавляем код счета секунд с помощью миллис и будем вызывать определенные функции. Определять очередность функций будем с помощью флага byte flag;.
static uint32_t timer; //статическая переменная для таймера
if (millis() - timer >= 1000) { //сравниваем,1000 таймер 1 секунда
flag ++; // увеличиваем флаг на еденицу
if (flag == 1)ReadDsAddr();//вызов функции считывания адреса
if (flag == 2)ReadDsTemp();//вызов функции считывания температуры 2 сек
timer = millis(); // текущее время
}
ДАЛЕЕ во вкладке ds_1820 исполняем эти функции, секунду одну функцию, секунду другую.
void ReadDsAddr(){
if ( !ds.search(addr)) { // функция поиска очередного датчика
ds.reset_search();
// delay(250); // можно удалить
return;
}}
void ReadDsTemp(){
здесь остальной код
celsius = (float)raw / 16.0;
// lcd.setCursor(0,0); // первая строка 0,первый знак 0
// lcd.print("Temperature celsius "); // пишем
// lcd.setCursor(6,1); // вторая строка, шестой знак
// lcd.print(celsius); // выводим температуру на ЛСД
if (celsius < 28) digitalWrite(13, 1);//нагреватель включен
if (celsius > 29) digitalWrite(13, 0);//нагреватель отключен
flag = 0; // обнуляем флаг
}
ТЕПЕРЬ ДЕЛЕИ и lcd.print_ы можно (нужно) удалить.
Подсовываем переменную float celsius нашей динамической индикации, то есть вместо int g = 8127.
g = celsius * 100.0f; , Умножаем на 100 , что бы получить из температуры например 36,66 ЧЕТЫРЕХ ЗНАЧНОЕ ЧИСЛО 3666, вывести на индикатор и зажечь точку, в нашем случае во втором разряде.
ДАЛЕЕ ТЕРМОСТАТ
if (celsius < 28) digitalWrite(13, 1);//нагреватель включен
if (celsius > 29) digitalWrite(13, 0);//нагреватель отключен
Подключаем транзистор в режиме ключа (общий эмиттер) , в нашем случае к ножке 13, он будет включать или отключать реле, которое управляет нагревателем согласно заданной температуре.
Код не само совершенство, но начало есть. Слегка подсвечиваются сегменты по мимо цифр. Их нужно принудительно гасить. Иногда слегка подмигивают цифры, если сильно присмотреться. Желательно применять таймер микроконтроллера вместо миллис. Над этим мы поработаем в дальнейшем Вот полный код всех вкладок для копирования и в конце ВИДЕО
РАБОЧИЕ , ПРОВЕРЕНО
Termostat.ino
int g = 0;
byte segment[8] = {0, 1, 2, 3, 4, 5, 6, 7};
byte razrad[4] = {8, 9, 10, 11};
byte cifri[12][7] = { // ДЛЯ ОБЩЕГО КАТОДА
1, 1, 1, 1, 1, 1, 0, //0
0, 1, 1, 0, 0, 0, 0, //1
1, 1, 0, 1, 1, 0, 1, //2
1, 1, 1, 1, 0, 0, 1, //3
0, 1, 1, 0, 0, 1, 1, //4
1, 0, 1, 1, 0, 1, 1, //5
1, 0, 1, 1, 1, 1, 1, //6
1, 1, 1, 0, 0, 0, 0, //7
1, 1, 1, 1, 1, 1, 1, //8
1, 1, 1, 1, 0, 1, 1, //9
1, 1, 0, 0, 0, 1, 1, // знак градуса
1, 0, 0, 1, 1, 1, 0, // знак цельсия
};
float celsius; // для вычисления температуры
byte flag = false; // Вспомогательная переменная для секундомера
byte addr[8]; // Массив для хранения 64-битного адреса датчика
#include <OneWire.h> // библиотека протокола 1-Wire для ds18х20
OneWire ds(A0); // Вход датчика
void setup() { // настройка портов
for (byte i = 0; i < 12; i++) {
pinMode(i, OUTPUT);
}
pinMode(13, OUTPUT); // 13 пин на вывод для термостата
}
void loop() {
Vivod_Seg(); // вызываем функцию вывода семисегментика
static uint32_t timer; //статическая переменная для таймера
if (millis() - timer >= 1000) { //сравниваем,1000 таймер 1 секунда
flag++ ; // увеличиваем флаг на еденицу
if (flag == 1)ReadDsAddr();//вызов функции считывания адреса
if (flag == 2)ReadDsTemp();//вызов функции считывания температуры
timer = millis(); // текущее время
}}
7_Seg.ino
void Vivod_Seg() {
static unsigned long timer; // статическая для организации таймера
static byte r = 0; //статическая для врутренней работы функции
byte d, x; //для внутренней работы функции
if (timer > millis()) return; // если не прошло 3 мс, ничего не делаем
switch (r) { // вычисляем цифру активного разряда
case 0:
d = g / 1000; // вычисляем колличество тысяч g=8127 цифра 8 первый разряд
break;
case 1:
d = (g / 100) % 10; // вычисляем колличество сотен 1 во второй разряд
break;
case 2:
d = (g / 10) % 10; // колличество десятков 2 в третий разряд
break;
case 3:
d = g % 10; // колличество едениц 7 в четвертый разряд
break;
}
for (byte i = 0; i < 4; i++) { // выставляем разряд
x = r == i ? 0 : x = 1;
digitalWrite(razrad[i],x); //(r == i));для общего анода
// 0 гасим точку, 1 горит .. для общего анода наоборот
if(r == 1)digitalWrite(segment[7], 1); else digitalWrite(segment[7], 0);
}
for (byte i = 0; i < 7; i++) { // выставляем сегменты
digitalWrite(segment[i], cifri[d][i]);
}
r = r == 4 ? 0 : r + 1; // если r равно 4, то r в ноль,иначе увеличиваем на 1
timer = millis() + 3;
}
ds_1820.ino
void ReadDsAddr(){
if ( !ds.search(addr)) { // функция поиска очередного датчика
ds.reset_search();
// delay(250); // можно удалить
return;
}}
void ReadDsTemp(){
byte i; // Вспомогательная переменная для циклов
byte present = 0; //Переменная готовности датчика к общению
byte type_s; // Переменная для определения типа термодатчика на шине
byte data[12]; // Массив для хранения принятой от датчика информации
// 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;
g = celsius * 100.0f;
if (celsius < 28.5) digitalWrite(13, 1);//нагреватель включен
if (celsius > 29.7) digitalWrite(13, 0);//нагреватель отключен
flag = 0;
}
Удачи...
ВИДЕО ТУТ
Транзистор в режиме в режиме ключа БУДЕТ ТУТ