Блок с датойБлок с временемБлок с возрастом сайта
Mr.ALB

    Анатолий Беляев (aka Mr.ALB). Персональный сайт

    Да пребудут с вами Силы СВЕТА!

     

    Ардуино (Arduino). #12

    Погодная станция на BME280 + DS1820

    Используя раннюю разработку программы Барометр c разными системами измерения давления создаю небольшую, можно сказать, карманную погодную станцию.

    Подразделы


     

    Основа погодной станции – датчик BME280. В этом датчике, в отличие от датчика BMP280, имеется чувствительный элемент для измерения ещё и относительной влажности.

    Добавив цифровой датчик ф. Даллас DS18B20 к уже имеющемуся датчику BME280 получаем, что наше устройство может измерять:

    • Абсолютное атмосферное давление (в мм рт. ст. или в кПа).
    • Высоту (относительную, в метрах).
    • Внутреннюю температуру с BME280 (в °С).
    • Относительную влажность (в %).
    • Внешнюю температуру (в °С, через DS18В20) + показывать минимальное и максимальное измеренное значение.

    Для смены единиц измерения давления необходимо удержание кнопки Установка уровня 0 метров 2...3 секунды, после отпускания кнопки – давление будет измеряться альтернативными единицами измерения.

     

    Схема погодной станции

    Схема погодной станции была создана на основе: Arduino Pro Mini (можно использовать любую: Arduino UNO, Arduino Nano и подобные им), датчике атмосферного давления и влажности BME280 (не путать с BMP280) и цифровом датчике температуры семейства DS1820.

    Схема погодной станции
    Pic 1. Схема погодной станции

    Датчик ф. Даллас семейства 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.12. Историю доработок смотрите в подразделе Доработки. Предыдущие версии и последнюю версию и используемые библиотеки можно скачать в подразделе Приложение.

    /*********************************************************
      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 + оптимизация подсветки
    
      Аппаратное подключение 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.12", "2020-07-09", "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
    */
    
    // Нулевой уровень для давления 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;
    
    const float vcc = 4.51; // vcc - Напряжение питания Ардуино
    
    //Адреса хранения системы измерения и давления
    int addrSYSPR = 1, addrPR = 2;
    
    byte flag_dark_light = false;
    
    /***********************/
    /* Настройка программы */
    /***********************/
    void setup()
    {
      // Порт управления подсветкой экрана
      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)) // Старт датчика давления
      {
        //Если ошибка запуска DS18xx
        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();
    
        // Первый байт 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();
      // Запись текущего давления в EEPROM
      if (flagPa == 0)
        press_now /= kp;      // Давление в мм рт. ст.
      else press_now /= 1000; // Давление в кПа
      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;
        }
        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);
    
        // Вывод значения давления
        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 * 7, 8, ST7735_BLACK);
        tft.setTextSize(1);
        tft.setCursor(bxinfo, byinfo + 28);
        tft.print(altit);
    
        // Вывод значения температуры с 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);
        }
    
        /* Проверка напряжения питания */
        float vbat = 0;
        for (int i = 0; i < 10; i++)
        { // 10 измерений напряжения аккумулятора
          // Перевод бит в вольты vcc=4.51V
          vbat += (float)analogRead(VBAT_PIN) * vcc / 1024.0;
        }
        // среднее арифметическое из 10 измерений
        vbat = vbat / 10.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 * 10, 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 * 10, byinfo + 15);
      // Вывод размерности давления
      if (flagPa)
        tft.print(" kPa");
      else
        tft.print(" mm");
    
      /* Вывод высоты */
      tft.setCursor(0, byinfo + 28);
      tft.print("Altitude:");
      tft.setCursor(bxinfo + 6 * 8, 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]);
      }
    }
    
    

     

    Реализация

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

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

    Основой послужила монтажная плата с металлизированными отверстиями, что удобно для двухстороннего монтажа. Печатную плату разрабатывать не стал, сберёг своё личное время улыбка. Это устройство единичное и не имеет смысла делать какие-то печатные платы. Размер монтажной платы 63 * 50 мм. Куплена в Китае.

    С нижней стороны платы установлена панелька на 24 контакта для Arduino Pro Mini. Там же смонтирован преобразователь DC-DC MT3608, и плата для зарядки литивого аккумулятора на основе TP4056. Преобразователь использовал покупной, какой был под рукой, но можно использовать любой, хоть бы и самодельный, который выдаёт на выходе напряжение от 4.5 В до 5 В.

    На нижней стороне платы смонтированы конденсаторы С1 и С2.

    Нижняя сторона платы
    Pic 1. Нижняя сторона платы

    С верхней стороны платы размещены: датчик BME280, TFT экран 1"8, аккумулятор 3,7 В 400 мАч.

    Вся разводка выполнена цветными одножильными монтажными проводками.

    Верхняя сторона платы
    Pic 2. Верхняя сторона платы

    Напоминаю, что перед установкой платы Ардуино, BME280 и TFT экрана, заранее проверьте и выставите напряжение на выходе преобразователя DC-DC, чтобы случайно не повредить компоненты устройства.

    Когда всё проверено, установите в панельки все компоненты. Подключите Ардуино к компьютеру и залейте в него программу.

    Сборка верхней стороны платы
    Pic 3. Сборка верхней стороны платы

    При включении устройства на экране выводится заставка, где указывается дата и версия прошивки. Последняя версия, на момент написания статьи, v3.5 2020-01-01.

    Заставка при включении
    Pic 4. Заставка при включении

    Если к погодной станции подключен датчик температуры DS1820, то на второй странице заставки выводится сообщение о его подключении. Определяется чип датчика и выводится точность измерения. Максимальная точность – 12 бит. Если внешний датчик температуры не подключен, то после вывода заставки программа переходит к измерениям текущих параметров с датчика BME280.

    Заставка при подключенном DS1820
    Pic 5. Заставка при подключенном DS1820

    По просьбам читателей, добавил возможность измерения давления в кПа. У нас в РФ атмосферное давление измеряется в мм ртутного столба, а в Европейских странах, в основном, в Барах или Паскалях. Эти единицы измерения подобны и отличаются лишь десятичным разрядом. При измерении в кПа на экране выводится число по размерности соответствующее нашему значению, то есть три десятичных разряда перед запятой и два после.

    Смена единиц измерения давления происходит при удержании кнопки Установка уровня 0 метров. Удерживать 2...3 секунды. При кратком нажатии на кнопку, происходит установка нулевого уровня для текущего атмосферного давления.

    Измерение давления в кПа
    Pic 6. Измерение давления в кПа

    Основной режим измерения задан в мм рт. ст. Если вам необходимо чтобы погодная станция по умолчанию измеряла в кПа, то в программе в строке [184] установите флаг измерения в кПа flagPa=true;.

    Измерение давления в мм рт. ст.
    Pic 7. Измерение давления в мм рт. ст.

    Если подключен внешний датчик температуры DS1820, то на экране внизу дополнительно выводится строчка с измеренным значением с этого датчика. Дополнительно в верху экрана указывается, что используется датчик DS1820 (левее пиктограммы батарейки). Значение температуры с датчика BME280 выводится мелким шрифтом, перед строкой со значением влажности (Humidity).

    Вывод температуры с внешнего датчика
    Pic 8. Вывод температуры с внешнего датчика

    Модуль закончен и вполне функционален. Удобен в использовании и легко можно его модернизировать, к примеру добавить ещё кнопочку, или обновить прошивку.

    В процессе разработки соединений модуля
    Pic 9. В процессе разработки соединений модуля

    Для полного завершения этой конструкции требуется изготовить корпус. Как и прежде, буду его делать из пластика ABS. Когда корпус будет изготовлен, то добавлю фото, а пока так как есть.

    Размеры получившегося модуля: 64 * 50 * 32 мм (ширина * высота * глубина). Потом размеры несколько увеличатся за счёт толщины пластика корпуса.

    2020-01-02

     

    Корпус

    Закончил изготовление корпуса. Теперь погодная станция обрела законченный вид. Ниже на фото этапы изготовления корпуса. Хотелось его изготовить как можно меньше по габаритам. В итоге получился размерами 70 * 63 * 38 мм.

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

    Начало изготовления
    Pic 10. Начало изготовления

    Модуль погодной станции вставлен в нижнюю часть. Пока примерка. Не стал устанавливать защитное стекло перед экраном. Решил на экран наклеить защитную плёнку, которая используется для экранов смартфонов.

    Модуль в нижней части корпуса
    Pic 11. Модуль в нижней части корпуса
    Вид слева на разъём для DS1820
    Pic 12. Вид слева на разъём для DS1820
    Вид спереди на разъём для зарядки аккумулятора
    Pic 13. Вид спереди на разъём для зарядки аккумулятора
    Вид справа
    Pic 14. Вид справа

    Закончено изготовление частей корпуса и толкателей для кнопок управления. В нижней крышке есть отверстие-окно для индикации процесса зарядки аккумулятора. Когда аккумулятор зарядится, то включается синий светодиод, а пока идёт процесс заряда, в этом окне видно свечение красного светодиода.

    Вид на части корпуса и сам модуль
    Pic 15. Вид на части корпуса и сам модуль

    Всё собрано вместе. Подключен внешний датчик температуры DS1820. На экране видны все надписи.

    Вид общий
    Pic 16. Вид общий
    Вид на клавишу включения
    Pic 17. Вид на клавишу включения
    Вид на разъём для внешнего датчика температуры DS1820
    Pic 18. Вид на разъём для внешнего датчика температуры DS1820

    Подключен блок питания для зарядки аккумулятора. Идёт процесс заряда.

    Вид снизу. Идёт зарядка аккумулятора
    Pic 19. Вид снизу. Идёт зарядка аккумулятора
    2020-02-07

     

    Доработки

    • Версия v3.7 Доделал немного программу. Теперь на экран выводится минимальная и максимальная температура с датчика DS1820. Также на главный экран выводится конкретный тип подключенного датчика DS18xx. Немного улучшил возможность для точного измерения напряжения аккумулятора.
    • Версия v3.8 Доработка по оптимизации кода. Добавил гашение экрана на минимум через минуту с момента включения или после любого нажатия любой кнопки, что позволяет экономить энергию аккумулятора. Переделал функцию центрирования строки.
    • Версия v3.9 Модернизация. Уменьшение яркости экрана через 30 секунд.
    • Версия v3.10 Программа переделана. Добавлено:
      • Сохранение в EEPROM предыдущего давления.
      • Сравнение предыдущего давления с текущим. Если разность между текущим давлением и предыдущим положительная – идёт рост давления, то значение разности выводится красным цветом. Если разность между текущим давлением и предыдущим отрицательная – идёт падение давления, то значение разности выводится голубым цветом. По цвету разности легко ориентироваться, что происходит с атмосферным давлением – рост или падение.
      • При включении погодной станции происходит считывание предыдущего сохранённого давления и запись текущего давления.
      • При смене системы измерения, значение предыдущего давления выводится в установленной системе измерения.
      • При длительном (2-3 секунды) нажатии на кнопку Подсветка/Light происходит принудительная запись в EEPROM текущего давления и текущей системы измерения. При последующем включении, на экране будет выводиться давление в сохранённой системе измерения, что удобно для тех, кто предпочитает смотреть давление в кПа.
      Версия v3.10. Давление в кПа
      Pic 20. Версия v3.10. Давление в кПа
      Версия v3.10. Давление в мм рт.ст.
      Pic 21. Версия v3.10. Давление в мм рт.ст.
    • Версия v3.11 Оптимизация в значениях знаков после запятой при выводе на экран.
    • Версия v3.12 Доработка функции dark_light (яркость экрана на минимум через 30 секунд). Теперь после того, как экран был переведён в сберегающий режим, по нажатии на кнопку SB2 (Light) происходит возврат в яркий режим подсветки с сохранением ранее выбранной настройки яркости. Дополнительно в вызове функции bme.begin(0x76)) адресс платы 0x76 указан явно.

    Обновлённый скетч можно скачать ниже.


     

    Приложение

    Используемые библиотеки и программы:


    Yandex.Money
    Или другим способом оказать помощь сайту


    . Mr.ALB
    Предыдущая страница Страница 13