Используя раннюю разработку программы Барометр c разными системами измерения давления создаю небольшую, можно сказать, карманную погодную станцию.
Основа погодной станции – датчик BME280. В этом датчике, в отличие от датчика BMP280, имеется чувствительный элемент для измерения ещё и относительной влажности.
Добавив цифровой датчик ф. Даллас DS18B20 к уже имеющемуся датчику BME280 получаем, что наше устройство может измерять, обрабатывать и показывать:
Для смены единиц измерения давления необходимо удержание кнопки Установка уровня 0 метров
2...3 секунды, после отпускания кнопки – давление будет измеряться альтернативными единицами измерения.
Схема погодной станции была создана на основе: Arduino Pro Mini (можно использовать любую: Arduino UNO, Arduino Nano и подобные им), датчике атмосферного давления и влажности BME280 (не путать с BMP280) и цифровом датчике температуры семейства DS1820.
Датчик ф. Даллас семейства DS1820 и ему подобные имеет достаточную точность в широком диапазоне температур. Такой датчик я уже использовал при разработке МТР-1.2 – хороший, надёжный датчик. Если использовать герметичное исполнение, то можно измерять и температуру жидкостей, к примеру, воды.
Погодная станция может работать от встроенного аккумулятора Li-Ion +3.7 В, или от внешнего источника напряжением 6...9 В, тогда, в этом случае, напряжение питания необходимо подавать на контакт RAW. При работе от аккумулятора, используется повышающий DC-DC преобразователь MT3608 (можно любой повышающий). На его выходе установлено напряжение 4.5...5 В. Во избежании повреждения Ардуино, BME280, TFT экрана – следует обратить внимание, что перед подключением преобразователя к устройству, необходимо заранее выставить на его выходе указанное напряжение.
Если у вас напряжение питания не точно +5.0 В, а, к примеру, +4.5 В, то для точного измерения напряжения питания аккумулятора необходимо в строке [462] указать ваше напряжение питания на контакте VCC Ардуино.
Используемый мной образец BME280 работает в диапазоне напряжений 1.8...5 В, что избавляет от необходимости применять стабилизатор на 3.3 В. Если у вас образец BME280 на напряжение 3.3 В, то добавьте для него стабилизатор на требуемое напряжение.
Скетч имеет подробные пояснения, надеюсь будет легко понять как работает программа. Основа скетча – предыдущие разработки и опыты по использованию датчиков BME280 и DS1820.
Опубликованый скетч является последней версией v3.13. Историю доработок смотрите в подразделе Доработки. Предыдущие версии и последнюю версию и используемые библиотеки можно скачать в подразделе Приложение.
/********************************************************* 2018-05-27 Mr.ALB Тренировка в программировании Ардуино BME280 I2C: SCL - A5 SDA - A4 VCC - 3.3V GND - GND 2018-07-17 v1.2 Добавлен датчик влажности DHT22: между контактом OUT и VCC подключить 10к 2018-08-24 v3.0 Подключение дисплея TFT ST7735 128*160 1,8 дюйма 2018-09-05 v3.1 Автономная работа от батареии Li-Ion +3.7В 2018-09-08 v3.2 Перерисовка только меняющихся значений 2018-11-02 v3.2.1 Минимальная яркость = 1 2018-11-11 v3.2.2 Прогрессбар подсветки на главной странице 2019-05-09 v3.3 Использование модуля BME280 2019-12-27 v3.4 Разные системы измерения давления (мм | кПа) 2019-12-29 v3.5 Использование датчика температуры DS18B20 2020-01-01 v3.5 Незначительное улучшение 2020-01-16 v3.6 Усреднение измерение напр. питания 2020-01-25 v3.7 Корректное измерение напр. питания + + Вывод мин/макс темпер. с DS1820 + + Вывод на экран подключенного типа DS18xx 2020-02-02 v3.8 Управление яркостью экрана + + изменение функции центрирования строки + + оптимизация кода 2020-02-16 v3.9 Уменьшение яркости экрана через 30 секунд 2020-05-16 v3.10 Сохранение предыдущего давления, сравнение с текущим давлением 2020-06-24 v3.11 Оптимизация в значениях знаков после запятой 2020-07-09 v3.12 Доработка функции dark_light + оптимизация подсветки 2020-08-31 v3.13 Использование циклического буфера для компенсации колебания значений высоты и напряжения батареи; + подключение DS18B20 на паразитное питание, т.е.: VCC DS18B20 (3) соединить с GND(1), а выход (2) через 4.7к на VCC Arduino; + обнуление высоты при включении + вывод высоты относительно уровня моря Аппаратное подключение TFT: TFT ST7735 ARDUINO Pin 1 RST -[R 1k]- PIN 10 Pin 2 CS -[R 1k]- PIN 9 Pin 3 DC -[R 1k]- PIN 8 Pin 4 DIN -[R 1k]- PIN 11 Pin 5 CLK -[R 1k]- PIN 13 Pin 6 VCC - PIN VCC +5V Pin 7 BL - PIN 3~ - для регулировки подсветки использовать ШИМ Pin 8 GND - PIN GND Контакты TFT 1-5 подключать через резисторы 1к...2к **********************************************************/ // Программа: Автор, Версия, Дата, Название const char* prog[] = {"Mr.ALB", "v3.13", "2020-08-31", "BME280 "}; #include <EEPROM.h> // для EEPROM // Подключение библиотеки для I2C #include <Wire.h> /* Графический дисплей TFT ST7735 1.8" */ #include <Adafruit_GFX.h> // Ядро графической библиотеки #include <Adafruit_ST7735.h>// Библиотека для ST7735 #define TFT_RST 10 #define TFT_CS 9 #define TFT_DC 8 // Аппаратное подключение TFT // Создаём объект tft Adafruit_ST7735 tft = Adafruit_ST7735 ( TFT_CS, TFT_DC, TFT_RST ); // Подключение библиотеки BME280 #include <Adafruit_BME280.h> /* Чтобы всё заработало необходимо 1. Сканером I2C найти адрес BME280 или в библиотеке <Adafruit_BME280.h>: 1) Изменить адрес #define BME280_ADDRESS (0x77) на адрес #define BME280_ADDRESS (0x76) 2) Закоментировать библиотеку #include <Adafruit_Sensor.h> */ // Создаём объект барометра Adafruit_BME280 bme; /* мБар - мм рт. ст. 1013.2472 - 760 k=1.33322 1005.24788 - 754 */ // Уровень моря #define SEALEVELPRESSURE_HPA 1013.2472 // В мБар // Нулевой уровень для давления 754 мм рт.ст. // Задайте значение соответствующее вашему положению float null_level = 1005.24788; // В мБар // Коэффициент для перевода Па в мм рт.ст. const float kp = 133.322; // переменные давления float press_now, press_last, press_tmp, press_prev, altit; // переменные температуры float temp, temp_last, temp_correct = -1; // переменные влажности float humidity_now, humidity_last, humidity_correct = 0; #include <Buttons.h> // Подключение библиотеки кнопок #define BUTTON_SET_LEVEL 6 // Кнопка "установка уровня 0 м" #define BUTTON_LIGHT 5 // Кнопка подсветки дисплея #define LIGHT_PIN 3 // Вывод PWM для подсветки дисплея #define VBAT_PIN A0 // Ввод VBAT батареи питания, // через резистор 1к #define DARK_LIGHT 30000 // Задержка гашения экрана 30 секунд // Создание объекта для кнопки "установка уровня 0 м" Buttons buttonSetLevel(BUTTON_SET_LEVEL, 21); // Создание объекта для кнопки подсветки Buttons buttonLight(BUTTON_LIGHT, 21); // Подключение библиотеки OneWire #include <OneWire.h> // Установка OneWire на Pin4 #define ONE_WIRE_BUS 4 // Номер пина к которому подключен DS18B20 OneWire oneWire(ONE_WIRE_BUS); // Подключение библиотеки Dallas Temperature #include <DallasTemperature.h> // Установка DallasTemperature для работы по OneWire DallasTemperature sensors(&oneWire); // Переменные для DS1820 float temperature, fahrenheit, last_temperature; float temperatureMax = 0, temperatureMin = 0; byte addr[8]; byte type_s; byte resolution; // Название типа датчика DS18хх const char* type_ds[] = { "DS1820", "DS18S20", "DS18B20", "DS1822", "DS1825" }; // Чип датчика DS18хх const char* type_text[] = { "Chip = DS1820", //0 "Chip = DS18S20 | old DS1820",//1 "Chip = DS18B20", //2 "Chip = DS1822", //3 "Chip = DS1825", //4 "is not family of DS18x20", //5 "Resolution = " //6 }; // Переменные для экрана const uint8_t byinfo = 24; // Позиция вывода информации по y const uint8_t bxinfo = 10 * 6; // Позиция вывода информации по х // Уровень подсветки 0...250; int level_light = 25; // Строка вывода прогресс-бара подсветки const uint8_t light_str = 15; // Переменные для обновления экрана по времени unsigned long time_now, time_check, time_check_display, time_button, time_button_now; const unsigned long time_delta = 3000; #define SCREEN_W 160 // Ширина экрана в рх #define SCREEN_H 128 // Высота экрана в рх uint8_t font_size = 1; // Размер шрифта /* Флаги */ // Для обновления экрана по времени boolean flagcheck = false; // Для вывода надписей после установки яркости boolean flagSetLight = true; // Для измерения в кило Паскалях (kPa) byte flagPa = 0; // Для DS1820 boolean flagDS = false; // Для снижения яркости экрана byte flag_dark_light = false; const float vcc = 4.51; // vcc - Напряжение питания Ардуино. // Реальное напряжение после // преобразователя, для точного // измерения батареи питания //Адреса хранения системы измерения и давления int addrSYSPR = 1, addrPR = 2; // Переменные и постоянные для циклического буфера #define BUFF_SIZE 16 // Циклический буфер для батареи int buffer1[BUFF_SIZE]; byte index1 = 0; // индекс циклического буфера // Циклический буфер для высоты int buffer2[BUFF_SIZE]; byte index2 = 0; // индекс циклического буфера /***********************/ /* Настройка программы */ /***********************/ void setup() { //Serial.begin(9600); для отладки // Порт управления подсветкой экрана pinMode(LIGHT_PIN, OUTPUT); // Порт для измерения батареи питания pinMode(VBAT_PIN, INPUT); // Опорный источник - внутренний //analogReference(DEFAULT); // Инициируем работу с дисплеем 1.8" TFT tft.initR(INITR_BLACKTAB);// initialize a ST7735S chip, black tab fnClrScreen(); // Очистить экран tft.setRotation(1); // Поворот экрана: // 0 - 0° книжный // 1 - 90° альбомный // 2 - 180° книжный // 3 - 270° альбомный // Установка начальной яркости analogWrite(LIGHT_PIN, level_light); if (!bme.begin(0x76)) // Старт датчика давления { //Если ошибка запуска BME280 char* bme_error[] = { "Error!", "Not find BME280 sensor!", "Check contacts", "Click Reset/off" }; tft.setTextColor(ST7735_RED); tft.setTextSize(2); fnCenter(bme_error[0], 2, 1, 0); tft.print(bme_error[0]); tft.setTextSize(1); fnCenter(bme_error[1], 1, 7, 0); tft.print(bme_error[1]); fnCenter(bme_error[2], 1, 9, 4); tft.print(bme_error[2]); tft.setTextColor(ST7735_YELLOW); fnCenter(bme_error[3], 1, 14, 0); tft.print(bme_error[3]); while (1); } // Старт датчика DS1820 sensors.begin(); if (!sensors.getAddress(addr, 0)) flagDS = false; // Нет датчика DS1820 else { flagDS = true; /* Определяем устройство и его параметры */ //Считываем установленную точность resolution = sensors.getResolution(); if(resolution<10) sensors.setResolution(10); // Проверка точности resolution = sensors.getResolution(); // Первый байт ROM указывает, какой чип switch (addr[0]) { case DS18S20MODEL: type_s = 1; break; case DS18B20MODEL: type_s = 2; break; case DS1822MODEL: type_s = 3; break; case DS1825MODEL: type_s = 4; break; default: type_s = 5; } // Обновление значения датчика DS1820 sensors.requestTemperatures(); temperature = sensors.getTempC(addr); temperatureMax = temperature; temperatureMin = temperature; } // Заставка fnZastavka(); // Считывание системы измерения давления из EEPROM flagPa = EEPROM.read(addrSYSPR); // Считывание предыдущего давления из EEPROM press_prev = EEPROM_float_read(addrPR); // Измерение текущего давления press_now = bme.readPressure(); // Установка параметров давления и высоты if (flagPa) { press_now /= 1000; // Давление в кПа // обнуление текущей высоты null_level = press_now * 10; } else { press_now /= kp; // Давление в мм рт. ст. // обнуление текущей высоты null_level = press_now * kp / 100; } // Запись текущего давления в EEPROM EEPROM_float_write(addrPR, press_now); // Вывод основного текста на экран fnPrintText(); } /***********************/ /* Рабочая программа */ /***********************/ void loop() { // Текущее время работы программы в мс time_now = millis(); // Проверка интервала времени для обновления экрана if (time_now - time_check > time_delta) { time_check = time_now; flagcheck = true; } // Энергосбережение: яркость экрана на минимум if (time_now - time_check_display > DARK_LIGHT) { time_check_display = time_now; // Установка минимальной яркости через 30 секунд analogWrite(LIGHT_PIN, 3); flag_dark_light = true; } buttonSetLevel.scanState();// Считывание состояния кнопки уровня buttonLight.scanState(); // Считывание состояния кнопки подсветки // Было нажатие кнопки подсветки if (buttonLight.flagPress == true) { time_check_display = time_now; //Задержка гашения экрана time_button = millis(); // Пока кнопка нажата while (buttonLight.flagPress) buttonLight.scanState(); time_button_now = millis(); if (time_button_now - time_button >= time_delta / 2) { // Если нажатие больше time_delta/2 сек, // то... // ...измерение и запись текущего давления в EEPROM press_now = bme.readPressure(); if (flagPa == 0) press_now /= kp; // Давление из мБар в мм рт.ст. else press_now /= 1000; // Давление из мБар в кПа press_prev = press_now; // Смена предыдущего давления EEPROM_float_write(addrPR, press_prev);//Запись давления // ...запись текущей системы измерения давления в EEPROM EEPROM.write(addrSYSPR, flagPa); } else { // 2020-07-09 v3.12 Настройка яркости экрана if (flag_dark_light == false) { if (level_light == 3)level_light = 25; else level_light += 25; // Шаг яркости if (level_light == 250) { level_light = 3; // Минимальная яркость tft.fillRect(0, light_str * 8 + 2, 160, 3, ST7735_BLACK); } } // Установка яркости // если было гашение экрана через dark_light, // то устанавливаем ранее выбранную яркость analogWrite(LIGHT_PIN, level_light); // сброс флага dark_light flag_dark_light = false; /* Прогресс бар яркости подсветки */ uint8_t bar_step = 0; const uint8_t bar_step_const = 8 * 2; tft.setTextSize(1); for (uint8_t ip = 0; ip < level_light / 25; ip++) { tft.fillRect (1 + bar_step, light_str * 8 + 2, bar_step_const - 2, 3, ST7735_WHITE ); bar_step += bar_step_const; } } flagSetLight = true; } // Было нажатие кнопки "Установка уровня 0" if (buttonSetLevel.flagPress == true) { time_check_display = time_now; //Задержка гашения экрана time_button = millis(); // Пока кнопка нажата while (buttonSetLevel.flagPress) buttonSetLevel.scanState(); time_button_now = millis(); if (time_button_now - time_button >= time_delta / 2) { // Если нажатие больше time_delta/2 сек, // то смена размерности давления flagPa = !flagPa; // Инверсия флага Па fnPrintText(); // Вывод основных надписей // Смена у предыдущего давления if (flagPa == 0) { // Из кПа в мм рт. ст. press_prev /= kp; press_prev *= 1000; } else { // Из мм рт. ст. в кПа press_prev *= kp; press_prev /= 1000; } } else { // Если нажатие меньше time_delta/2 сек, // то обнуление высоты if (flagPa) null_level = press_now * 10; else null_level = press_now * kp / 100; // Обнуление значений цикл.буфера for (byte i = 0; i < BUFF_SIZE; i++) { buffer2[i] = 0; } } flagcheck = true; } // Измерение параметров с BME280********************* press_now = bme.readPressure(); // Давление в мБар if (flagPa == 0) press_now /= kp; // Давление в мм рт. ст. else press_now /= 1000; // Давление в кПа temp = bme.readTemperature(); // Температура в °С humidity_now = bme.readHumidity();// Влажность в % // Конец измерения параметров с BME280*************** //Применение коррекции влажности if (humidity_correct != 0) humidity_now += humidity_correct; //Применение коррекции температуры if (temp_correct != 0) temp += temp_correct; /* Если давление изменилось больше чем на 0,1 мм рт.ст., или на 0,01 кПа, то выводим на дисплей... или если температура изменилась больше чем на 0,1°С или если нажата кнопка обнуления высоты... или если прошёл интервал времени >= 2 сек */ if ( (flagPa == 0) && ((press_now < (press_last - .1)) || (press_now > (press_last + .1))) || (flagPa == 1) && ((press_now < (press_last - .01)) || (press_now > (press_last + .01))) || (temp < (temp_last - .1)) || (temp > (temp_last + .1)) || flagcheck == true || flagDS && ((last_temperature > temperature + .1) || (last_temperature < temperature - .1)) ) { flagcheck = false; // Сброс флага проверки по времени if (flagSetLight) // Если была настройка яркости { tft.fillRect(0, light_str * 8 + 2, 160, 3, ST7735_BLACK); flagSetLight = false; } // Приблизительная высота в метрах altit = bme.readAltitude(null_level); int altit_buf = altit * 100; // Значение для цикл.буфера //Serial.println(index2); //Serial.print("altit = "); Serial.println(altit); //Serial.print("altit_buf = "); Serial.println(altit_buf); // Записываем в циклический буфер fnAddReading(altit_buf, buffer2, index2); // Вывод значения давления tft.setTextColor(ST7735_YELLOW); tft.fillRect(bxinfo, byinfo - 3, 6 * 10, 16, ST7735_BLACK); tft.setTextSize(2); tft.setCursor(10 * 6, byinfo - 2); tft.print(press_now, 1); // Вывод значения предыдущего давления tft.setTextSize(1); tft.fillRect(bxinfo, byinfo + 16, 6 * 10, 8, ST7735_BLACK); tft.setCursor(bxinfo, byinfo + 16); tft.print(press_prev, 1); tft.print("/"); // Дельта текущего давления к предыдущему float fdpress = press_now - press_prev; // Если дельта меньше нуля, то голубым if (fdpress < 0)tft.setTextColor(ST7735_CYAN); // Если дельта больше 0, то красным else tft.setTextColor(ST7735_RED); tft.print(press_now - press_prev, 1); // Вывод высоты tft.setTextColor(ST7735_WHITE); tft.fillRect(bxinfo, byinfo + 28, 6 * 12, 8, ST7735_BLACK); tft.setTextSize(1); tft.setCursor(bxinfo, byinfo + 28); // Вывод усреднённого значения altit = (float)fnAverage(buffer2) / 100; //Serial.print("altit_avr = "); //Serial.println(altit, 2); //Serial.println(); tft.print(altit, 2); tft.print("/"); // Вывод абсолютной высоты от уровня моря tft.print(bme.readAltitude(SEALEVELPRESSURE_HPA), 1); // Вывод значения температуры с BME280 tft.setTextColor(ST7735_ORANGE); tft.fillRect(bxinfo, byinfo + 40, 6 * 6, 8, ST7735_BLACK); tft.setCursor(bxinfo, byinfo + 40); if (temp > 0) tft.print("+"); tft.print(temp, 1); /* Вывод влажности */ /* Раскрашиваем значение в зависимости от % влажности */ if (humidity_now < 30.0) // Сушь tft.setTextColor(ST7735_RED); if (humidity_now >= 30.0 && humidity_now < 60.0)// Сухо tft.setTextColor(ST7735_YELLOW); if (humidity_now >= 60.0 && humidity_now < 75.0)// Влажно tft.setTextColor(ST7735_GREEN); if (humidity_now >= 75.0) // Сыро tft.setTextColor(ST7735_BLUE); //Очистка места под значение tft.fillRect(bxinfo, byinfo + 52, 6 * 8, 16, ST7735_BLACK); tft.setTextSize(2); tft.setCursor(bxinfo, byinfo + 52); tft.print(humidity_now, 0); if (flagDS) { // Обновление значения датчика DS1820 sensors.requestTemperatures(); temperature = sensors.getTempC(addr); // Вывод значения температуры с DS1820 tft.setTextColor(ST7735_WHITE); tft.fillRect(bxinfo, byinfo + 74, 6 * 11, 16, ST7735_BLACK); tft.setTextSize(2); tft.setCursor(bxinfo, byinfo + 74); if (temperature > 0) tft.print("+"); tft.print(temperature, 1); // Записываем максимальное значение if (temperature > temperatureMax) temperatureMax = temperature; // Записываем минимальное значение if (temperature < temperatureMin) temperatureMin = temperature; // Вывод максимального и минимального значений tft.setTextColor(ST7735_LIGHTGRAY); tft.fillRect(bxinfo, byinfo + 90, 6 * 12, 8, ST7735_BLACK); tft.setTextSize(1); tft.setCursor(bxinfo, byinfo + 90); if (temperatureMin > 0) tft.print("+"); tft.print(temperatureMin, 1); tft.print("/"); if (temperatureMax > 0) tft.print("+"); tft.print(temperatureMax, 1); } /******************************* Проверка напряжения питания *******************************/ // Чтение напряжения питания из порта VBAT_PIN // v3.13 Добавление значения в циклический буфер fnAddReading(analogRead(VBAT_PIN), buffer1, index1); // Перевод бит в вольты vcc=4.51V float vbat; // v3.13 Усреднение значений циклического буфера vbat = (float)fnAverage(buffer1) * vcc / 1024.0; // Выводим значение напряжения питания батареи // если запитываемся от батареи if (vbat > 2.5) { uint8_t x_vbat = 20 * 6 + 3, y_vbat = 14; tft.fillRect(x_vbat, y_vbat, 6 * 6, 8, ST7735_BLACK); tft.setCursor(x_vbat, y_vbat); tft.setTextSize(1); if (vbat < 3.7) tft.setTextColor(ST7735_YELLOW); if (vbat < 3.3) tft.setTextColor(ST7735_RED); if (vbat >= 3.7) tft.setTextColor(ST7735_GREEN); tft.print("+"); tft.print(vbat); tft.print("V"); fnBattary(23 * 6, 3, vbat); //Вывод пиктограммки } /* Конец проверки питания */ // Перезаписываем бывшие значения temp_last = temp; //BMP280 press_last = press_now; humidity_last = humidity_now; last_temperature = temperature; //DS1820 delay(5); } } /* Функция - Вывод °С */ void fnChrDegree ( int16_t x, int16_t y, uint16_t color ) { uint8_t radius = 2; tft.drawCircle(x += radius * 2, y += radius * 2 - 1, radius, color); tft.setCursor(x + 3, y); tft.setTextColor(color); tft.print("C"); } /* Функция символа батарейки */ void fnBattary ( int16_t x, int16_t y, float vbat ) { int16_t w = 19, h = 8; // Контур батарейки tft.drawRect(x, y, w, h, ST7735_WHITE); // Пимпочка tft.fillRect(x + w, y + 2, 2, 4, ST7735_WHITE); // Вывод квадратиков наполнения if (vbat < 3.3) fnPrintFillRect(x, y, 1, ST7735_RED); if (vbat >= 3.3 && vbat < 3.5) fnPrintFillRect(x, y, 1, ST7735_YELLOW); if (vbat >= 3.5 && vbat < 3.7) fnPrintFillRect(x, y, 2, ST7735_YELLOW); if (vbat >= 3.7 && vbat < 3.9) fnPrintFillRect(x, y, 3, ST7735_GREEN); if (vbat >= 3.9) fnPrintFillRect(x, y, 4, ST7735_GREEN); } /* Функция вывода квадратиков для символа батарейки*/ void fnPrintFillRect ( int16_t x, int16_t y, uint8_t max_i, int16_t color ) { tft.fillRect(x + 1, y + 1, 17, 6, ST7735_BLACK); for (uint8_t i = 0; i < max_i; i++) { tft.fillRect(x + 2 + i * 4, y + 2, 3, 4, color); } } /* Функция - Заставка */ void fnZastavka() { fnClrScreen(); // Очистить экран tft.setTextWrap(false); tft.setTextColor(ST7735_ORANGE); font_size = 3; tft.setTextSize(font_size); fnCenter(prog[3], font_size, 1, 0); tft.print(prog[3]); font_size = 2; tft.setTextSize(font_size); fnCenter(prog[0], font_size, 4, 0); tft.setTextColor(ST7735_DARKGREEN); tft.print(prog[0]); font_size = 1; tft.setTextSize(font_size); fnCenter(prog[1], font_size, 11, 0); tft.setTextColor(ST7735_GRAY); tft.println(prog[1]); fnCenter(prog[2], font_size, 12, 2); tft.print(prog[2]); delay(3600); if (flagDS) { fnClrScreen(); // Очистить экран tft.setTextColor(ST7735_ORANGE); font_size = 3; tft.setTextSize(font_size); // Вывод названия fnCenter(type_ds[0], font_size, 1, 0); tft.print(type_ds[0]); font_size = 1; tft.setTextSize(font_size); // Вывод чипа DS18xx fnCenter(type_text[type_s], font_size, 7, 0); tft.print(type_text[type_s]); // Вывод точности изменения DS18xx fnCenter(type_text[6], font_size, 9, 0); tft.print(type_text[6]); tft.print(resolution); } delay(3000); } /* Функция - Вывод основных надписей на экран */ void fnPrintText() { fnClrScreen(); // Очистить экран tft.setTextSize(1); tft.setCursor(0, 3); tft.setTextColor(ST7735_GREEN); tft.print(prog[3]); tft.print(prog[1]); if (flagDS) { tft.print(" "); tft.print(type_ds[type_s]); } /* Вывод давления */ tft.setTextColor(ST7735_LIGHTGRAY); tft.setCursor(0, byinfo);//24 tft.print("Pressure:"); tft.setCursor(bxinfo + 6 * 11, byinfo); tft.setTextSize(1); // Вывод размерности давления if (flagPa) tft.print("kPa"); else tft.print("mm"); tft.setCursor(0, byinfo + 15); tft.print("Pres_pre:"); tft.setCursor(bxinfo + 6 * 11, byinfo + 15); // Вывод размерности давления if (flagPa) tft.print("kPa"); else tft.print("mm"); /* Вывод высоты */ tft.setCursor(0, byinfo + 28); tft.print("Altitude:"); tft.setCursor(bxinfo + 6 * 12, byinfo + 28); // Вывод размерности высоты tft.print("m"); /* Вывод температуры */ tft.setCursor(0, byinfo + 40); tft.print("Temper. :"); // Вывод размерности температуры fnChrDegree(bxinfo + 6 * 7, 61, ST7735_LIGHTGRAY); /* Вывод влажности */ tft.setCursor(0, byinfo + 52); tft.print("Humidity:"); tft.setTextSize(1); tft.setCursor(bxinfo + 6 * 8, byinfo + 54); // Вывод размерности влажности tft.print("%"); /* Вывод температуры с DS1820 */ if (flagDS) { //Разделитель влажности и DS1820 tft.drawFastHLine(0, byinfo + 70, 160, ST7735_DARKGRAY); /* Вывод температуры c DS1820 */ tft.setCursor(0, byinfo + 78); tft.print("Temper. :"); // Вывод размерности температуры fnChrDegree(bxinfo + 6 * 12, 100, ST7735_LIGHTGRAY); // Вывод размерности температуры для Min/Max fnChrDegree(bxinfo + 6 * 12, 112, ST7735_LIGHTGRAY); /* Вывод минимальной и максимальной температуры c DS1820 */ tft.setCursor(0, byinfo + 88); tft.print("Min/Max :"); } } /* Функция - центрирование строки */ void fnCenter ( char* string, uint8_t fntsize, uint8_t row, uint8_t offset ) { // Подсчёт количества символов в строке boolean flag = true; byte i = 0; while (flag) { if (string[i] != '\0')i++; else flag = false; } // Установка позиции вывода строки tft.setCursor ( (SCREEN_W - i * fntsize * 6) / 2 + fntsize * 6 / 2, fntsize * 8 * row + offset ); } /* Функция - Cтирание экрана чёрным цветом */ void fnClrScreen() { // Очистить экран, т.е. закрасить чёрным tft.fillScreen(ST7735_BLACK); // Курсор в начало экрана tft.setCursor(0, 0); } /******************************** Функции для работы с EEPROM *******************************/ /* Чтение float из EEPROM */ float EEPROM_float_read(int addr) { byte raw[4]; for (byte i = 0; i < 4; i++) { raw[i] = EEPROM.read(addr + i); } float &num = (float&)raw; return num; } /* Запись float в EEPROM */ void EEPROM_float_write(int addr, float num) { byte raw[4]; (float&)raw = num; for (byte i = 0; i < 4; i++) { EEPROM.write(addr + i, raw[i]); } } /***************************************** Функции для работы с циклическим буфером *****************************************/ /* Функция заполнения циклического буфера 2020-08-31 v3.13 Mr.ALB */ void fnAddReading(int reading, int *buffer, byte &ind) { // Запись в ячейку буфера buffer[ind] = reading; ind++; // Инкремент индекса буфера //Обнуление индекса буфера if (ind >= BUFF_SIZE)ind = 0; } /* Функция усреднения значений != 0 2020-08-31 v3.13 Mr.ALB */ int fnAverage(int *buffer) { long sum = 0; byte count = 0; byte i = 0; while (i < BUFF_SIZE) { // если значение != 0 // то суммируем if (buffer[i] != 0) { // суммирование значений sum += buffer[i]; // инкремент счётчика // значений != 0 count++; } i++; } // Возвращение среднего // из значений != 0 return (int)(sum / count); }
Программа написана, отлажена, пришло время всё изготовить как единый модуль.
Хотелось собрать в минимальных размерах, но так, чтобы был лёгкий доступ к любым частям устройства. Поэтому сделал как модуль, в который втыкаются основные платы-части устройства.
Основой послужила монтажная плата с металлизированными отверстиями, что удобно для двухстороннего монтажа. Печатную плату разрабатывать не стал, сберёг своё личное время . Это устройство единичное и не имеет смысла делать какие-то печатные платы. Размер монтажной платы 63 * 50 мм. Куплена в Китае.
С нижней стороны платы установлена панелька на 24 контакта для Arduino Pro Mini. Там же смонтирован преобразователь DC-DC MT3608, и плата для зарядки литиевого аккумулятора на основе TP4056. Преобразователь использовал покупной, какой был под рукой, но можно использовать любой, хоть бы и самодельный, который выдаёт на выходе напряжение от 4.5 В до 5 В.
На нижней стороне платы смонтированы конденсаторы С1 и С2.
С верхней стороны платы размещены: датчик BME280, TFT экран 1"8, аккумулятор 3,7 В 400 мАч.
Вся разводка выполнена цветными одножильными монтажными проводками.
Напоминаю, что перед установкой платы Ардуино, BME280 и TFT экрана, заранее проверьте и выставите напряжение на выходе преобразователя DC-DC, чтобы случайно не повредить компоненты устройства.
Когда всё проверено, установите в панельки все компоненты. Подключите Ардуино к компьютеру и залейте в него программу.
При включении устройства на экране выводится заставка, где указывается дата и версия прошивки. Последняя версия v3.13 2020-08-31.
Если к погодной станции подключен датчик температуры DS1820, то на второй странице заставки выводится сообщение о его подключении. Определяется чип датчика и выводится точность измерения. Максимальная точность – 12 бит. Если внешний датчик температуры не подключен, то после вывода заставки программа переходит к измерениям текущих параметров с датчика BME280.
По просьбам читателей, добавил возможность измерения давления в кПа. У нас в РФ атмосферное давление измеряется в мм ртутного столба, а в Европейских странах, в основном, в Барах или Паскалях. Эти единицы измерения подобны и отличаются лишь десятичным разрядом. При измерении в кПа на экране выводится число по размерности соответствующее нашему значению, то есть три десятичных разряда перед запятой и два после.
Смена единиц измерения давления происходит при удержании кнопки Установка уровня 0 метров
. Удерживать 2...3 секунды. При кратком нажатии на кнопку, происходит установка нулевого уровня для текущего атмосферного давления.
Основной режим измерения задан в мм рт. ст. Если вам необходимо чтобы погодная станция по умолчанию измеряла в кПа, то в программе в строке [184] установите флаг измерения в кПа flagPa=true;.
Если подключен внешний датчик температуры DS1820, то на экране внизу дополнительно выводится строчка с измеренным значением с этого датчика. Дополнительно в верху экрана указывается, что используется датчик DS1820 (левее пиктограммы батарейки). Значение температуры с датчика BME280 выводится мелким шрифтом, перед строкой со значением влажности (Humidity).
Модуль закончен и вполне функционален. Удобен в использовании и легко можно его модернизировать, к примеру добавить ещё кнопочку, или обновить прошивку.
Для полного завершения этой конструкции требуется изготовить корпус. Как и прежде, буду его делать из пластика ABS. Когда корпус будет изготовлен, то добавлю фото, а пока так как есть.
Размеры получившегося модуля: 64 * 50 * 32 мм (ширина * высота * глубина). Потом размеры несколько увеличатся за счёт толщины пластика корпуса.
2020-01-02
Закончил изготовление корпуса. Теперь погодная станция обрела законченный вид. Ниже на фото этапы изготовления корпуса. Хотелось его изготовить как можно меньше по габаритам. В итоге получился размерами 70 * 63 * 38 мм.
Прежде чем что-то сделать, приходится рассчитывать, проектировать на бумаге, выверять размеры. Корпус состоит из двух частей: нижней и верхней.
Модуль погодной станции вставлен в нижнюю часть. Пока примерка. Не стал устанавливать защитное стекло перед экраном. Решил на экран наклеить защитную плёнку, которая используется для экранов смартфонов.
Закончено изготовление частей корпуса и толкателей для кнопок управления. В нижней крышке есть отверстие-окно для индикации процесса зарядки аккумулятора. Когда аккумулятор зарядится, то включается синий светодиод, а пока идёт процесс заряда, в этом окне видно свечение красного светодиода.
Всё собрано вместе. Подключен внешний датчик температуры DS1820. На экране видны все надписи.
Подключен блок питания для зарядки аккумулятора. Идёт процесс заряда.
Подсветка/Lightпроисходит принудительная запись в EEPROM текущего давления и текущей системы измерения. При последующем включении, на экране будет выводиться давление в сохранённой системе измерения, что удобно для тех, кто предпочитает смотреть давление в кПа.
Обновлённый скетч можно скачать ниже.
Используемые библиотеки и программы: