Наблюдаем за микроклиматом дома

Ни для кого не секрет, что поддержание определенной температуры и влажности воздуха дома способствует более комфортной и здоровой жизни. Если мы говорим о жилых помещениях, то большинство источников рекомендуют устанавливать температуру воздуха 18-22 градуса Цельсия и влажность воздуха 40-60%. Возможно, со временем, вы добавите к нашей системе управление такими гаджетами как, например, увлажнитель воздуха, но сейчас мы сконструируем установку, которая позволит нам измерять текущие температуру и влажность воздуха, а также выводить информацию на дисплей вместе с часами реального времени.

Итак, нам понадобятся:

  • Контроллер DaVinci
  • Шилд A050
  • Соединительные провода
  • Доска для прототипирования
  • Датчик температуры и влажности DHT-11
  • Часы реального времени RTC DS1387
  • OLED-дисплей 0.96 дюйма 128х32 пикселя

Рассмотрим датчики, входящие в систему, поподробнее.

Датчик температуры и влажности DHT-11

DHT-11 является популярным датчиком влажности и температуры с цифровым выходом. У датчика используются всего три контакта: питание (VCC), земля (GND) и сигнал (CH2).

Часы реального времени RTC DS1387

RTC DS1387 - часы реального времени с батарейкой, благодаря которой даже если от всей системы будет отключено питание, микросхема будет продолжать дальше отсчитывать время. Подключение модуля осуществляется по шине I2C:

OLED-дисплей

Монохромный OLED-дисплей позволяет сделать систему автономной и выводить данные непосредственно на свой собственный экран. Дисплей также подключается по шине I2C.

Схема

Теперь давайте соединим все модули вместе по схеме ниже и будем разбираться с тем, как настроить систему для корректной работы.

Программа

Для каждого модуля необходимо скачать и установить библиотеку. Инструкция по скачиванию и установке находится здесь: https://geegrow.ru/wiki/programming/libraries/manual-setup/lib-setup/

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

DHT-11: https://geegrow.ru/wiki/products/sensors/temp-and-hum/

RTC: https://geegrow.ru/wiki/products/sensors/rtc-module/

OLED: https://geegrow.ru/wiki/products/indicators/oled-display/

Перед сборкой всей системы воедино мы рекомендуем потренироваться и подключить сначала каждый модуль отдельно и научиться работать с ним. Так вам будет понятнее разобраться в коде, который мы представим ниже. Фактически эта программа объединяет в себе все три программы для каждого модуля. Но не все так просто.

Если вы проделали манипуляции с часами реального времени вы увидели, что сначала микросхеме нужно задать старт - текущие дату и время, откуда она будет вести свой счет. Для этого необходимо сначала загрузить в контроллер следующий код:

 

#include "Wire.h"
#include "TimeLib.h"
#include "DS1307RTC.h"

const char *monthName[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

tmElements_t tm;

void setup() {
  bool parse=false;
  bool config=false;

  // get the date and time the compiler was run
  if (getDate(__DATE__) && getTime(__TIME__)) {
    parse = true;
    // and configure the RTC with this info
    if (RTC.write(tm)) {
      config = true;
    }
  }

  Serial.begin(9600);
  while (!Serial) ; // wait for Arduino Serial Monitor
  delay(200);
  if (parse && config) {
    Serial.print("DS1307 configured Time=");
    Serial.print(__TIME__);
    Serial.print(", Date=");
    Serial.println(__DATE__);
  } else if (parse) {
    Serial.println("DS1307 Communication Error :-{");
    Serial.println("Please check your circuitry");
  } else {
    Serial.print("Could not parse info from the compiler, Time=\"");
    Serial.print(__TIME__);
    Serial.print("\", Date=\"");
    Serial.print(__DATE__);
    Serial.println("\"");
  }
}

void loop() {
}

bool getTime(const char *str)
{
  int Hour, Min, Sec;

  if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
  tm.Hour = Hour;
  tm.Minute = Min;
  tm.Second = Sec;
  return true;
}

bool getDate(const char *str)
{
  char Month[12];
  int Day, Year;
  uint8_t monthIndex;

  if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
  for (monthIndex = 0; monthIndex < 12; monthIndex++) {
    if (strcmp(Month, monthName[monthIndex]) == 0) break;
  }
  if (monthIndex >= 12) return false;
  tm.Day = Day;
  tm.Month = monthIndex + 1;
  tm.Year = CalendarYrToTm(Year);
  return true;
}

 

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

 

#include "GeeGrow_SSD1306_128x32.h"
#include "Wire.h"
#include "TimeLib.h"
#include "DS1307RTC.h"
#include "DHT.h"

#define DHTPIN 7     // Пин для DHT 11DHT 11 
#define DHTTYPE DHT11   // тип датчика - DHT 11 
#define FILL_SPEED  4

#define LUCASE_UPPER  1
#define LUCASE_LOWER  2

DHT dht(DHTPIN, DHTTYPE);

GeeGrow_SSD1306_128x32 *display;
uint8_t x = 0;
uint8_t y = 0;

void lettersABC_rus(uint8_t color, uint8_t LUcase);
void lines(uint8_t color);
void filling(uint8_t color);
void numbers123(uint8_t color);
void lettersABC(uint8_t color, uint8_t LUcase);
void specSymbols(uint8_t color);
void checkRotation();

void setup() {
   Wire.begin();
   display = new GeeGrow_SSD1306_128x32();
   dht.begin();
   display->attachLibs(LIB_LETTERS_ASCII | LIB_NUMBERS_ASCII | LIB_SYMBOLS_ASCII | LIB_LETTERS_CYRILLIC);
   display->initialize();
   display->fillDisplay(BLACK);
   display->refresh();
}

void loop() {
 tmElements_t tm;
 if (RTC.read(tm)) {
   }
    delay(500);
    //В переменной h - влажность
    float h = dht.readHumidity(); 
    //В переменной t - температура
    float t = dht.readTemperature(); 
  
  // Проверяем есть ли данные
  if (isnan(h) || isnan(t) ) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }
    display->fillDisplay(BLACK);
    char tt[6]; 
    char hh[4];
    char hourr[3];
    char mminutes[3];
    char dday[3];
    char mmonth [3];
    char yearr[5];
    itoa(h,hh,10);
    itoa(tm.Hour, hourr, 10);
    itoa(tm.Minute, mminutes, 10);
    itoa(tm.Day,dday,10);
    itoa(tm.Month,mmonth,10);
    itoa(tmYearToCalendar(tm.Year),yearr,10);
    
    tools_floatToStr(t, tt, 2);
    display->printStr(tt, 1, 0, 2, 1, WHITE);   
    display->drawCircle(38, 1, 1, WHITE);
    display->printStr("C", 44, 0, 2, 1, WHITE);   // OK
    display->printStr(hh, 107, 0, 2, 1, WHITE); 
    display->printStr("%", 122, 0, 2, 1, WHITE);
    display->printStr(hourr, 40, 8, 2, 2, WHITE); 
    display->printStr(":", 62, 8, 2, 2, WHITE);
    if (tm.Minute >= 0 && tm.Minute < 10) {
       display->printStr("0", 71, 8, 2, 2, WHITE);
       display->printStr(mminutes, 83, 8, 2, 2, WHITE);}
    else 
       display->printStr(mminutes, 71, 8, 2, 2, WHITE);
       display->printStr(dday, 14, 23, 2, 1, WHITE); //!!!!!
       
     switch (tm.Month ) {
          case 1:
          display->printStr("января", 39, 23, 2, 1, WHITE);
          break;
          case 2:
          display->printStr("февраля", 32, 23, 2, 1, WHITE);
          break;
          case 3:
          display->printStr("марта", 43, 23, 2, 1, WHITE);
          break;
          case 4:
          display->printStr("апреля", 30, 23, 2, 1, WHITE);
          break;
          case 5:
          display->printStr("мая", 52, 23, 2, 1, WHITE);
          break;
          case 6:
          display->printStr("июня", 45, 23, 2, 1, WHITE);
          break;
          case 7:
          display->printStr("июля", 45, 23, 2, 1, WHITE);
          break;
          case 8:
          display->printStr("августа", 30, 23, 2, 1, WHITE);
          break;
          case 9:
          display->printStr("сентября", 20, 23, 2, 1, WHITE);
          break;
          case 10:
          display->printStr("октября", 35, 23, 2, 1, WHITE);
          break;
          case 11:
          display->printStr("ноября", 39, 23, 2, 1, WHITE);
          break;
          case 12:
          display->printStr("декабря", 30, 23, 2, 1, WHITE);
          break;
          }

    display->printStr(yearr, 100, 23, 2, 1, WHITE);
    display->refresh();
  
  
  }
  
//Функции для вывода текста
void lettersABC_rus(uint8_t color, uint8_t LUcase) {
    uint8_t offset = 0;
    uint8_t x0 = 1;
    uint8_t y0 = 1;

    display->setRotation(ROTATION_UP);
    display->setEncoding(ENCODING_CP1251);

    if (LUcase == LUCASE_UPPER)
        offset = 192;
    if (LUcase == LUCASE_LOWER)
        offset = 224;

    if (color == WHITE)
        display->fillDisplay(BLACK);
    if (color == BLACK)
        display->fillDisplay(WHITE);

    for (uint8_t i = 0; i < 11; i++) {
        display->printChar((char)(offset+i), x0+8*i, y0, 1, color);
        display->printChar((char)(offset+11+i), x0+8*i, y0+9, 1, color);
        display->printChar((char)(offset+21+i), x0+8*i, y0+18, 1, color);
    }

  display->refresh();
}

void lines(uint8_t color) {
    display->setRotation(ROTATION_UP);
    display->fillDisplay(BLACK);
    for (uint8_t i = 0; i < 13; i++) {
        display->drawLine(0, 0, i*10, 31, color);
        display->refresh();
        delay(300);
    }
    display->fillDisplay(BLACK);
    for (uint8_t i = 0; i < 13; i++) {
        display->drawLine(0, 31, i*10, 0, color);
        display->refresh();
        delay(300);
    }
    display->fillDisplay(BLACK);
    for (int8_t i = 12; i >= 0; i--) {
        display->drawLine(127, 0, i*10, 31, color);
        display->refresh();
        delay(300);
    }
    display->fillDisplay(BLACK);
    for (int8_t i = 12; i >= 0; i--) {
        display->drawLine(127, 31, i*10, 0, color);
        display->refresh();
        delay(300);
    }
}

void specSymbols(uint8_t color) {
  uint8_t x0 = 0;
  uint8_t y0 = 0;

  display->setRotation(ROTATION_UP);
  display->fillDisplay(BLACK);

  for (uint8_t i = 0; i < 15; i++)
    display->printChar((char)(33+i), x0+8*i, y0, 1, color);
  for (uint8_t i = 0; i < 7; i++)
    display->printChar((char)(58+i), x0+8*i, y0+8, 1, color);
  for (uint8_t i = 0; i < 6; i++)
    display->printChar((char)(91+i), x0+8*i, y0+16, 1, color);
  for (uint8_t i = 0; i < 4; i++)
    display->printChar((char)(123+i), x0+8*i, y0+24, 1, color);

  display->refresh();
}

void lettersABC(uint8_t color, uint8_t LUcase) {
  uint8_t offset = 0;
  uint8_t x0 = 1;
  uint8_t y0 = 0;

  display->setRotation(ROTATION_UP);

  if (LUcase == LUCASE_UPPER)
    offset = 65;
  if (LUcase == LUCASE_LOWER)
    offset = 97;

  if (color == WHITE)
    display->fillDisplay(BLACK);
  if (color == BLACK)
    display->fillDisplay(WHITE);

  for (uint8_t i = 0; i < 13; i++) {
      display->printChar((char)(offset+i), x0+8*i, y0, 1, color);
      display->printChar((char)(offset+13+i), x0+8*i, y0+9, 1, color);
  }

  display->refresh();
}

void numbers123(uint8_t color) {
    uint8_t x0 = 1;
    uint8_t y0 = 0;

    display->setRotation(ROTATION_UP);

    if (color == WHITE)
        display->fillDisplay(BLACK);
    if (color == BLACK)
        display->fillDisplay(WHITE);

    for (uint8_t i = 0; i < 10; i++)
        display->printChar((char)(48+i), x0+8*i, y0, 1, color);
    display->refresh();
}


void checkRotation() {
  display->setRotation(ROTATION_LEFT);
  filling(WHITE);
  display->fillDisplay(BLACK);
  display->refresh();

  display->setRotation(ROTATION_UP);
  filling(WHITE);
  display->fillDisplay(BLACK);
  display->refresh();

  display->setRotation(ROTATION_DOWN);
  filling(WHITE);
  display->fillDisplay(BLACK);
  display->refresh();

  display->setRotation(ROTATION_RIGHT);
  filling(WHITE);
  display->fillDisplay(BLACK);
  display->refresh();
}

void filling(uint8_t color) {
  uint8_t height = display->getHeight();
  uint8_t width = display->getWidth();

  for (uint8_t j = 0; j < height; j+=8)
    for (uint8_t i = 0; i < width; i+=FILL_SPEED) {
      for (uint8_t s = 0; s < FILL_SPEED; s++)
        for (uint8_t k = 0; k < 8; k++)
          display->drawPixel(i+s, j+k, color);
      display->refresh();
      delay(30);
    }
}
//Перевод int в string str[]. 
// Количество символов после запятой - d.
int tools_intToStr(int x, char* str, int d) { 
    int i = 0; 
    while (x) { 
        str[i++] = (x % 10) + '0'; 
        x = x/10; 
    } 
  
    // Если d больше, добавляем 0 перед числом  
    while (i < d) 
        str[i++] = '0'; 
  
    tools_reverse(str, i); 
    str[i] = '\0'; 
    return i; 
} 
// Переворачивает строчку 'str' длины 'len' 
void tools_reverse(char *str, int len){
    int i = 0, j = len - 1, temp; 
    while (i < j) { 
        temp = str[i]; 
        str[i] = str[j]; 
        str[j] = temp; 
        i++; j--; 
    }
}
// Конвертирует число типа float в string. 
int tools_floatToStr(float n, char *res, int afterpoint){
    // Выделяем целую часть 
    int ipart = (int)n; 
    // Выделяем дробную часть
    float fpart = n - (float)ipart; 
    int i = tools_intToStr(ipart, res, 1); 
    if (afterpoint != 0) { 
        res[i] = '.';  // Добавить точку 
        fpart = fpart * pow(10, afterpoint); 
        int j = tools_intToStr((int)fpart, res + i + 1, afterpoint);
        return i+1+j;
    }
    return i;
}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

После того, как загружена вторая программа в контроллер, на дисплее должны отображаться текущее время и дата, а также влажность в процентах и температура в градусах Цельсия, как на рисунке ниже.