В статье автор рассказывает об изобретении электронного термометра.
Ключевые слова: термометр, Arduino Nano, схема подключения, Энкодер, авторское изобретение.
Каждый день людям надо измерять температуру чего-либо: тела, воды, еды, поверхности, да даже просто воздуха. Для каждого перечисленного объекта измерения требуется свой измерительный прибор. А если использовать один единый универсальный прибор, который имеет большой диапазон температуры, компактный и практичный? Это же намного удобнее и выгоднее. К сожалению, таких готовых термометров я не нашел. Поискав аналоги среди самодельных устройств, я понял, что эта тема не сильно развита, в сети много одинаковых, очень простых и незавершенных проектов.
Заинтересовавшись этой темой, я решил создать свой собственный электронный термометр, который подходил бы под все мои потребности и запросы, а также пригодился бы моей семье
Вдобавок к этому, мне очень нравится работать с электроникой (из нее можно сделать много интересных и полезных вещей), изобретать различные вещи и паять.
Итак, цель определена, я делаю самодельный термометр для измерения всего.
Технология изготовления термометра:
— нарисовать схему подключения;
— подобрать необходимые материалы;
— приобрести необходимые материалы;
— собрать схему на макетной плате;
— написать программу для термометра;
— загрузить и проверить работу программы;
— собрать всё в корпус;
— проверить работу готового изделия;
— подсчитать стоимость потраченного материала;
Изделие, которое я решил создать, должно быть функциональным, качественным и автономным.
Корпус будет сделан из ABS пластика, потому что он легок в обработке, он стойкий к низким и высоким температурам, а также такие готовые коробки 100x60x25(ДxШxВ) продаются по недорогой цене.
Данное творчество требует усидчивости, упорства, что важно кроме самого материала и инструмента.
Для изготовления понадобятся инструменты:
— Дрель-шуруповерт
— Паяльник
— Линейка
— Бормашина
— Скальпель или модельный нож
— Клеевой пистолет
Расходные материалы:
— Сверла
— Фрезы
— Флюс
— Припой
— Провода
— Термоусадка
— Стержни для клеевого пистолета
Материалы:
— ABS коробка 100x60x25
— Arduino Nano
— Энкодер
— 7-сегментный 4-значный белый дисплей на сдвиговом регистре TM1637
— Аккумулятор типа 14500 на 1А/ч
— Отсек для аккумулятора
— Термистор 10к 3950
— Разъём для термистора
— Buzzer
— Транзистор IRF 3205 (или аналог)
— Резисторы на 100 Ом и 10 кОм
— USB ASP (ICSP программатор)
— Плата зарядки аккумулятора на TP4056
— Микро повышающий модуль до 5 вольт
Для качественного выполнения мною составлена технологическая карта. Последовательное выполнение всех этапов поможет без ошибок выполнить работу.
Технологическая карта.
Нарисовать схему подключения (рис.1) — Fritzing
Запрограммировать Arduino Nano — USB ASP
Спаять схему — Паяльник, припой, флюс
Подготовить корпус (вырезать необходимые отверстия) — Шуруповерт, бормашина, скальпель, линейка
Сборка схемы в корпус (рис.2) — Клеевой пистолет
Рис. 1 Схема подключения
Рис. 2 Сборка в корпус
Прилагаю код программы, написанную для данного термометра.
код программы
/**********НАСТРОЙКИ**********/
#define S1 4 //Пин S1 энкодера
#define S2 3 //Пин S2 энкодера
#define KEY 2 //Пин KEY энкодера (! ТОЛЬКО 2 ИЛИ 3 ДЛЯ ATMega328P !)
#define EB_HOLD_TIME 1000 //Сколько надо удерживать кнопку энкодера, чтобы она считалась удержанной (в мс)
#define BUZZ 5 //Пин пищалки
#define BUZZ_TYPE 0 //Тип пищалки: 0 - пассивная; 1 - активная
#define HZ 1000 //Частота для пассивной пищалки (в герцах)
#define NTC A0 //Аналоговый пин NTC термистора
#define DSP 13 //Пин питания дисплея
#define CLK 11 //Пин CLK дисплея
#define DIO 12 //Пин DIO дисплея
#define SLP_TIME 60 //Время ухода в режим сна(секунды)
#define MIDDLE 2 //Фильтрация: 0 - отсутствует, 1 - усреднение библиотеки, 2 - экспоненциальное скользящее среднее, 3 - среднее арифметическое
#define AVERAGE 40 //Кол-во усреднений для среднего арифметического
/**********НАСТРОЙКИ**********/
/**********millis() и delay()**********/
//#define millis() millis() * 2 //Для корректной работы millis
//#define delay(x) delay(x / 2) //Для корректной работы delay
/**********millis() и delay()**********/
/**********БИБЛИОТЕКИ**********/
#include
#include
#include
#include
#include
#include
GyverNTC ntc(NTC, 10000, 3950);
EncButton enc(S1, S2, KEY);
EndTM1637 disp(CLK, DIO);
/**********БИБЛИОТЕКИ**********/
/**********ПЕРЕМЕННЫЕ**********/
bool slp, ton;
volatile bool icr, stat;
byte hih = eeprom_read_byte(4), mode, tks;
float lst = eeprom_read_float(0), vcc_const, nov, vcc, nvcc;
uint32_t tmr2, tmrslp, btmr;
/**********ПЕРЕМЕННЫЕ**********/
/**********SETUP**********/
void setup() {
power.autoCalibrate();
//power.setSystemPrescaler(PRESCALER_2);
power.setSleepMode(POWERDOWN_SLEEP);
power.hardwareDisable(PWR_SPI | PWR_I2C | PWR_UART0);
attachInterrupt(digitalPinToInterrupt(KEY), isr, FALLING);
Timer1.setPeriod(60);
Timer1.enableISR();
disp.setBright(hih);
mode = 1;
pinMode(BUZZ, OUTPUT); //Пищалка
pinMode(DSP, OUTPUT);
disp.clear();
if (enc.pressing()) {
for (byte i = 0; i <= 8; i += 4) eeprom_update_dword(i, 0);
digitalWrite(DSP, 1);
enc.clear();
eeprom_update_float(5, 1.1);
vcc_const = 1.1;
disp.setBytes(_C, _A, _L, _b);
delay(1000);
vcc = readVcc();
disp.manualFloat(vcc, 2);
while (!enc.hold()) calibration();
vcc_const = (float)1.1 * vcc / readVcc();
eeprom_update_float(5, vcc_const);
disp.clear();
byte nc[11] = {_U, _c, _c, _empty, _C, _O, _n, _S, _t, _empty, _defis};
disp.runningString(nc, sizeof(nc), 300);
disp.manualFloat(vcc_const, 3);
delay(1000);
disp.setBytes(_d, _O, _n, _E);
enc.clear();
tmr2 = millis() - 4000;
} else {
vcc_const = eeprom_read_float(5);
}
readVcc();
nvcc = readVcc();
tmrslp = millis();
}
/**********SETUP**********/
/**********WAKE UP**********/
void isr() {
if (stat == 1) {
stat = 0;
icr = 1;
}
}
/**********WAKE UP**********/
/**********LOOP**********/
void loop() {
if (enc.right()) {
if (++mode > 4) mode = 1;
tmr2 = millis();
digitalWrite(DSP, 1);
delay(1);
mod(mode);
slp = 1;
tmrslp = millis();
}
if (enc.left()) {
if (--mode < 1) mode = 4;
tmr2 = millis();
digitalWrite(DSP, 1);
delay(1);
mod(mode);
slp = 1;
tmrslp = millis();
}
if (mode == 1) {
if (enc.hold()) {
uint32_t tmr = millis();
uint32_t tmr1 = millis();
bool bye = 0;
digitalWrite(BUZZ, 0);
noTone(BUZZ);
digitalWrite(DSP, 1);
delay(1);
disp.clear();
while (millis() - tmr < 180000) {
if (millis() - tmr1 > 200) {
nov = temp();
disp.manualFloat(nov);
tmr1 = millis();
}
if (enc.hold()) {
bye = 1;
break;
}
}
lst = nov;
eeprom_update_float(0, lst);
bool byze = 0;
uint32_t tmr5 = millis();
while (bye == 0) {
if (millis() - tmr5 > 400) {
if (BUZZ_TYPE == 1) {
digitalWrite(BUZZ, !digitalRead(BUZZ));
}
if (BUZZ_TYPE == 0) {
byze = !byze;
(byze == 1) ? (tone(BUZZ, HZ)) : (noTone(BUZZ));
}
tmr5 = millis();
}
if (enc.hold()) {
bye = 1;
break;
}
}
digitalWrite(BUZZ, 0);
noTone(BUZZ);
disp.clear();
enc.clear();
tmr2 = millis();
btmr = millis();
tks = 0;
ton = 0;
tmrslp = millis();
slp = 1;
}
}
if (mode == 2) {
if (enc.hold()) {
digitalWrite(DSP, 1);
delay(1);
disp.clear();
disp.manualFloat(lst);
enc.clear();
tmr2 = millis();
tmrslp = millis();
slp = 1;
}
}
if (mode == 3) {
if (enc.rightH()) {
digitalWrite(DSP, 1);
delay(1);
if (++hih > 7) hih = 0;
disp.setBright(hih);
disp.displayInts(hih);
eeprom_update_byte(4, hih);
tmr2 = millis();
tmrslp = millis();
slp = 1;
}
if (enc.leftH()) {
digitalWrite(DSP, 1);
delay(1);
if (--hih >= 250) hih = 7;
disp.setBright(hih);
disp.displayInts(hih);
eeprom_update_byte(4, hih);
tmr2 = millis();
tmrslp = millis();
slp = 1;
}
}
if (mode == 4) {
if (enc.hold()) {
digitalWrite(DSP, 1);
delay(1);
disp.clear();
nvcc = readVcc();
disp.manualFloat(nvcc, 2);
enc.clear();
tmr2 = millis();
tmrslp = millis();
slp = 1;
}
}
if (millis() - tmr2 > 5000) {
digitalWrite(DSP, 1);
delay(1);
disp.clear();
if (slp == 1) {
slp = 0;
tmrslp = millis();
}
}
if ((millis() - tmrslp) / 1000 >= SLP_TIME) {
digitalWrite(BUZZ, 0);
noTone(BUZZ);
digitalWrite(DSP, 1);
delay(1);
stat = 1;
disp.setBytes(_empty, _b, _y, _E);
delay(1000);
disp.clear();
digitalWrite(DSP, 0);
power.hardwareDisable(PWR_TIMER2);
delay(100);
power.sleep(SLEEP_FOREVER);
}
if (nvcc <= 3.0 && millis() - btmr >= 20000) {
static uint32_t btmr2 = millis();
byte cl = 0;
if (nvcc <= 3.0 && nvcc >= 2.85) cl = 1;
else if (nvcc < 2.85 && nvcc >= 2.7) cl = 2;
else cl = 3;
if (millis() - btmr2 >= 300) {
btmr2 = millis();
ton = !ton;
if (BUZZ_TYPE == 1) {
digitalWrite(BUZZ, ton);
}
if (BUZZ_TYPE == 0) {
(ton == 1) ? (tone(BUZZ, HZ)) : (noTone(BUZZ));
}
if (ton) tks++;
if (tks == cl && !ton) {
tks = 0;
btmr = millis();
nvcc = readVcc();
}
}
}
if (icr == 1) {
power.hardwareEnable(PWR_TIMER2);
digitalWrite(DSP, 1);
delay(1);
icr = 0;
disp.setBytes(_empty, _empty, _h, _i);
tmrslp = millis();
readVcc();
nvcc = readVcc();
btmr = millis();
tks = 0;
ton = 0;
slp = 1;
mode = 1;
tmr2 = millis() - 4000;
}
}
/**********LOOP**********/
/**********ВЫВОД РЕЖИМА**********/
void mod(byte m) {
switch (m) {
case 1: {
disp.setBytes(_t, _E, _S, _t);
break;
}
case 2: {
disp.setBytes(_L, _A, _S, _t);
break;
}
case 3: {
disp.setBytes(_L, _i, _G, _t);
break;
}
case 4: {
disp.setBytes(_b, _A, _t, _t);
break;
}
}
}
/**********ВЫВОД РЕЖИМА**********/
/**********ФИЛЬТРАЦИЯ**********/
float temp() {
float out = 0.0;
switch (MIDDLE) {
case 0: out = ntc.getTemp(); break;
case 1: out = ntc.getTempAverage(); break;
case 2: {
static float filT = 0;
filT += (ntc.getTempAverage() - filT) * 0.1;
out = filT;
}
break;
case 3: {
long aver = 0;
for (int i = 0; i < AVERAGE; i++) aver += ntc.getTempAverage() * 10;
aver /= AVERAGE;
out = (float)aver / 10;
}
break;
}
return out;
}
/**********ФИЛЬТРАЦИЯ**********/
/**********КАЛИБРОВКА**********/
void calibration() {
if (enc.right()) {
vcc += 0.01;
disp.manualFloat(vcc, 2);
}
if (enc.left()) {
vcc -= 0.01;
disp.manualFloat(vcc, 2);
}
}
/**********КАЛИБРОВКА**********/
/**********ИЗМЕРЕНИЕ НАПРЯЖЕНИЯ**********/
float readVcc() {
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA, ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high << 8) | low;
result = vcc_const * 1023 * 1000 / result; // расчёт реального VCC
return result / 1000.0; // возвращает VCC
}
/**********ИЗМЕРЕНИЕ НАПРЯЖЕНИЯ**********/
/**********tick ЭНКОДЕРА**********/
ISR(TIMER1_A) {
enc.tick();
}
/**********tick ЭНКОДЕРА**********/
Также в данном проекте используется моя библиотека для дисплея, которую я выложил на сайт https://github.com/EndLibs/EndTM1637 (я периодически пишу и выкладываю библиотеки для разных датчиков).
Мною рассчитано, что на одном заряде термометр проработает год.
Кратное описание функций термометра:
1) поворотами энкодера можно перемещаться по пунктам меню. После минуты бездействия, термометр перейдет в режим сна, показав на дисплее «bye».
2) выйти из режима сна можно нажатием на кнопку энкодера (на дисплее будет показано «hi»), а пункт меню будет изменен на (temp).
3) в меню есть 4 пункта — temp (тест температуры), last (последняя температура), ligt (яркость дисплея), batt (напряжение аккумулятора).
4) тест температуры («temp») — при удержании кнопки энкодера, начнется тест температуры, при котором на дисплее будет показываться текущая температура. Если ничего не делать, то тест прекратится через три минуты, издавая писк, и показывая последнюю измеренную температуру. Чтобы выключить писк, надо удержать кнопку энкодера. Если во время теста удержать кнопку энкодера, то тест прекратится, а писка не будет.
5) последняя температура («last») — при удержании кнопки энкодера, на дисплее 5 секунд будет показываться последняя температура.
6) яркость дисплея («ligt») — если при нажатой кнопке энкодера его вращать, то яркость дисплея будет меняться от 0 до 7, при этом на дисплее будет показываться уровень яркости.
7) напряжение аккумулятора («batt») — при удержании кнопки энкодера, на дисплее будет показано напряжение аккумулятора на 5 секунд.
8) если при включении зажать кнопку энкодера, то термометр перейдет в режим калибровки, одновременно очистив данные о последней температуре и яркости дисплея. На дисплее на секунду будет показано «calb», после будет показано измеренное напряжение аккумулятора. Вращением энкодера надо сопоставить его с реальным напряжением аккумулятора, которое надо изменить вручную. После надо удержать энкодер. На дисплее будет показана константа напряжения и надпись «done».
9) все настройки сохраняются в энергонезависимую помять.
10) если заряд аккумулятора от 20 до 30 процентов, то термометр будет издавать 1 писк, раз в 20 секунд. Если заряд аккумулятора от 10 до 20 процентов, то термометр будет издавать 2 писка, раз в 20 секунд. Если заряд аккумулятора менее 10 процентов, то термометр будет издавать 3 писка раз в 20 секунд.
Диапазон измеряемой температуры от -40°С до +125°С.
Данный термометр (рис. 3) можно назвать универсальным и многофункциональным. В нем есть настраиваемое ограничение по времени для измерения температуры, что полезно для бытового использования. Также есть возможность просмотреть измеренные температуры. Для вывода различных показаний стоит яркий дисплей, который видно даже на солнце, яркость дисплея можно регулировать для комфортной работы в темноте. А для экономии на батарейках внутри стоит аккумулятор, за его напряжение можно следить на дисплее (напряжение можно откалибровать), если он будет почти разряжен, то электроника будет напоминать писком. Если надо срочно что-то измерить, а аккумулятор разрядился и прибор не работает (внутри стоит плата защиты-зарядки, которая отключает аккумулятор при разрядке), то можно его достать и поменять, а свободный — поставить на зарядку. Благодаря удобной эргономике, термометром можно управлять одной рукой. Под универсальностью я имел в виду то, что в термометр можно вставлять разные по форме датчики температуры (термисторы) для разной по назначению работы. А учитывая, что внутри стоит система энергосбережения, термометр на одном заряде проработает 1 год. Также не стоит забывать о бюджетности — из-за того, что используются дешевые модули, цена небольшая для такого количества задач.
Рис. 3. Электронный термометр
Так как термометр создан на основе Arduino Nano, его можно постоянно совершенствовать, добавляя новые датчики и функции. Данный проект был создан для участия в региональном этапе ВСОШ Ростовской области в 2023/2024 уч.году.
Литература:
- Технология: 8–9 классы: учебник / Е. С. Глозман, О. А. Кожина, Ю. Л. Хотунцев и др. — 2-е изд., стереотип. — М.: Просвещение, 2021. — 380, [4] с.: ил.
- https://alexgyver.ru/lessons/
- https://kit.alexgyver.ru/tutorials/