Oled часы с выводом температуры на Arduino своими руками.

.Oled часы с выводом температуры на Arduino своими руками.

Я люблю разрабатывать различные часы на Arduino . Свои первые часы делал на сдвиговых регистрах, светодиодах и Arduino. Вторая версия светодиодных часов уже была сделана на адресных светодиодах (данные часы до сих пор работают и висят у меня в коридоре). С ребёнком делали часы из Лего, семисегментного индикатора и digispark. Последние, четвёртые, реализованы на Arduino и дисплее Nextion. Подписчики написали мне, что часы получаются достаточно дорогие и собирать их нецелесообразно. Согласен, что дисплей Nextion дорогой, поэтому решил собрать часы на OLED дисплее и Arduino.

Для реализации данного проекта нам понадобится:

Электронную часть OLED часов собрал на макетной плате вот по такой схеме.

Электронную часть OLED часов собрал на макетной плате вот по такой схеме.

Описание работы часов OLED часы на Arduino.

Часы имеют стандартный функционал. Вывод текущего дня недели, даты, времени и температуры. Две тактовые кнопки помогают нам настраивать дату и время.

Часы имеют стандартный функционал

Как видим, дни недели у нас выводятся на русском языке. Вы скажете, что в интернете достаточно много проектов с данным дисплеем и с выводом информации на русском языке. Да, так оно и есть, информации много, но в 90% случаев русские буквы выводятся с помощью перекодировки символов. И просматривая код непонятно, какое слово будет на дисплее. В данном примере русский текст прописан в коде в точно таком же виде, как он выводится на дисплей.

Устанавливаем и настраиваем библиотеки.

1. Установить библиотеки Adafruit : библиотеку Adafruit_SSD1306 и библиотеку Adafruit_GFX . Как их установить, я рассматривал в уроке.

2. Скачать файл glcdfont.c и заменить его в библиотеке Adafruit-GFX. Библиотека находится C:\Users\USER\Documents\Arduino\libraries\Adafruit_GFX_Library. У вас путь до библиотеки может отличаться. Вы можете скачать, библиотеку подготовленную для вывода русского текста, внизу статьи в разделе материалы для скачивания.

3. В скетч нужно добавить функцию перекодировки русских букв из UTF-8 в Win-1251 . Добавить функцию можно внизу кода.

/* Функция перекодировки русских букв из UTF-8 в Win-1251 */
String utf8rus(String source)
{
  int i,k;
  String target;
  unsigned char n;
  char m[2] = { '0', '\0' };
  k = source.length(); i = 0;
  while (i < k) {
    n = source[i]; i++;
    if (n >= 0xC0) {
      switch (n) {
        case 0xD0: {
          n = source[i]; i++;
          if (n == 0x81) { n = 0xA8; break; }
          if (n >= 0x90 && n <= 0xBF) n = n + 0x30;
          break;
        }
        case 0xD1: {
          n = source[i]; i++;
          if (n == 0x91) { n = 0xB8; break; }
          if (n >= 0x80 && n <= 0x8F) n = n + 0x70;
          break;
        }
      }
    }
    m[0] = n; target = target + String(m);
  }
return target;
}

4. Добавить в блок void setup() {} следующую строчку кода.

display.cp437(true);

5. При выводе русского текста использовать функцию utf8rus() .

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


Код OLED часы на Arduino.

Рассказывать, как устроена программа часов полностью не буду. Тем более у меня на сайте достаточно уроков и примеров по реализации функционалов на дисплеях и работы с часами реального времени. Рассмотрим только основную особенность данного кода - это вывод русского языка в том виде, как он прописан в самом коде. Библиотеку для вывода символов на дисплей мы установили и настроили. Как же сейчас её использовать так, чтобы постоянно не прописывать перекодировку символов? Для этого сделаем функцию, которая будет выводить наш текст с нужным размером шрифта по указанным координатам.

void draw_text(byte x_pos, byte y_pos, char *text, byte text_size) {
  display.setCursor(x_pos, y_pos);
  display.setTextSize(text_size);
  display.print(utf8rus(text));
  display.display();
}

В данной функции и укажем перекодировку символов. Благодаря этому можно вывести информацию на дисплей на русском языке.

void display_day() { // вывод дня недели
  switch (day) {
    case 1:  draw_text(30, 0, "ВОСКРЕСЕНЬЕ", 1); break;
    case 2:  draw_text(30, 0, "ПОНЕДЕЛЬНИК", 1); break;
    case 3:  draw_text(30, 0, "  ВТОРНИК  ", 1); break;
    case 4:  draw_text(30, 0, "   СРЕДА   ", 1); break;
    case 5:  draw_text(30, 0, "  ЧЕТВЕРГ  ", 1); break;
    case 6:  draw_text(30, 0, "  ПЯТНИЦА  ", 1); break;
    default: draw_text(30, 0, "  СУББОТА  ", 1);
  }
}

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

/*
   Arduino часы с выводом температуры на DS3231 и SSD1306 OLED
   Сайт https://portal-pk.ru/
   Проекты на Arduino https://portal-pk.ru/page-17/proekty-na-arduino.html
   Проекты на ESP https://portal-pk.ru/page-18/proekty-na-nodemcu-esp8266-esp-01.html
*/
#include <Wire.h>                        // I2C 
#include <Adafruit_GFX.h>                // Включить графическую библиотеку Adafruit
#include <Adafruit_SSD1306.h>            // Включить драйвер OLED Adafruit SSD1306
#define SCREEN_WIDTH 128 // Ширина OLED-дисплея, в пикселях
#define SCREEN_HEIGHT 64 // Высота OLED-дисплея в пикселях
// SSD1306, подключенного к I2C (выводы SDA, SCL)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define button1    9                       // Кнопка B1 подключена к контакту Arduino 9
#define button2    8                       // Кнопка B2 подключена к контакту Arduino 8
void setup(void) {
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  delay(1000);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // инициализировать с помощью I2C адресс 0x3C (для 128x64)
  // Очистить дисплей.
  display.clearDisplay();
  display.display();
  display.cp437(true);
  display.setTextColor(WHITE, BLACK);
  display.drawRect(117, 56, 3, 3, WHITE);     // Поставить символ степени ( ° )
  draw_text(0, 56, "ТЕМПЕРАТУРА =", 1);
  draw_text(122, 56, "C", 1);
}
char Time[]     = "  :  ";
char Calendar[] = "  /  /20  ";
char temperature[] = " 00.00";
char temperature_msb;
byte i, second, minute, hour, day, date, month, year, temperature_lsb;
void display_day() { // вывод дня недели
  switch (day) {
    case 1:  draw_text(30, 0, "ВОСКРЕСЕНЬЕ", 1); break;
    case 2:  draw_text(30, 0, "ПОНЕДЕЛЬНИК", 1); break;
    case 3:  draw_text(30, 0, "  ВТОРНИК  ", 1); break;
    case 4:  draw_text(30, 0, "   СРЕДА   ", 1); break;
    case 5:  draw_text(30, 0, "  ЧЕТВЕРГ  ", 1); break;
    case 6:  draw_text(30, 0, "  ПЯТНИЦА  ", 1); break;
    default: draw_text(30, 0, "  СУББОТА  ", 1);
  }
}
void DS3231_display() {
  // Преобразовать в десятичную
  minute = (minute >> 4) * 10 + (minute & 0x0F);
  hour   = (hour >> 4)   * 10 + (hour & 0x0F);
  date   = (date >> 4)   * 10 + (date & 0x0F);
  month  = (month >> 4)  * 10 + (month & 0x0F);
  year   = (year >> 4)   * 10 + (year & 0x0F);
  Time[4]     = minute % 10 + 48;
  Time[3]     = minute / 10 + 48;
  if (second % 2)
    Time[2]   = 32;
  else
    Time[2]   = 58;
  Time[1]     = hour   % 10 + 48;
  Time[0]     = hour   / 10 + 48;
  Calendar[9] = year   % 10 + 48;
  Calendar[8] = year   / 10 + 48;
  Calendar[4] = month  % 10 + 48;
  Calendar[3] = month  / 10 + 48;
  Calendar[1] = date   % 10 + 48;
  Calendar[0] = date   / 10 + 48;
  if (temperature_msb < 0) {
    temperature_msb = abs(temperature_msb);
    temperature[0] = '-';
  }
  else
    temperature[0] = ' ';
  temperature_lsb >>= 6;
  temperature[2] = temperature_msb % 10  + 48;
  temperature[1] = temperature_msb / 10  + 48;
  if (temperature_lsb == 0 || temperature_lsb == 2) {
    temperature[5] = '0';
    if (temperature_lsb == 0) temperature[4] = '0';
    else                     temperature[4] = '5';
  }
  if (temperature_lsb == 1 || temperature_lsb == 3) {
    temperature[5] = '5';
    if (temperature_lsb == 1) temperature[4] = '2';
    else                     temperature[4] = '7';
  }
  draw_text(4,  12, Calendar, 2);                     // Показать дату (формат: дд / мм / гггг)
  draw_text(20, 30, Time, 3);                         // Показать время
  draw_text(80, 56, temperature, 1);                  // Показать температуру
}
void blink_parameter() {
  byte j = 0;
  while (j < 10 && digitalRead(button1) && digitalRead(button2)) {
    j++;
    delay(25);
  }
}
byte edit(byte x_pos, byte y_pos, byte parameter, byte text_size) {
  char text[3];
  sprintf(text, "%02u", parameter);
  while (!digitalRead(button1));                     // Подождите, пока кнопка B1 отпущена
  while (true) {
    while (!digitalRead(button2)) {                  // Если кнопка B2 нажата
      parameter++;
      if (i == 0 && parameter > 31)                  // Если date > 31 ==> date = 1
        parameter = 1;
      if (i == 1 && parameter > 12)                  // Если  month > 12 ==> month = 1
        parameter = 1;
      if (i == 2 && parameter > 99)                  // Если year > 99 ==> year = 0
        parameter = 0;
      if (i == 3 && parameter > 23)                  // Если hours > 23 ==> hours = 0
        parameter = 0;
      if (i == 4 && parameter > 59)                  // Если minutes > 59 ==> minutes = 0
        parameter = 0;
      sprintf(text, "%02u", parameter);
      draw_text(x_pos, y_pos, text, text_size);
      delay(200);                                    // Ждем 200ms
    }
    draw_text(x_pos, y_pos, "  ", text_size);
    blink_parameter();
    draw_text(x_pos, y_pos, text, text_size);
    blink_parameter();
    if (!digitalRead(button1)) {                     // Если кнопка B1 нажата
      i++;                                           // Увеличение «i» для следующего параметра
      return parameter;                              // Вернуть значение параметра и выйти
    }
  }
}
void draw_text(byte x_pos, byte y_pos, char *text, byte text_size) {
  display.setCursor(x_pos, y_pos);
  display.setTextSize(text_size);
  display.print(utf8rus(text));
  display.display();
}
void loop() {
  if (!digitalRead(button1)) {                       // Если кнопка B1 нажата
    i = 0;
    while (!digitalRead(button1));                   // Дождитесь отпускания кнопки B1
    while (true) {
      while (!digitalRead(button2)) {                // Пока кнопка B2 нажата
        day++;                                       // Инкремент для деня
        if (day > 7) day = 1;
        display_day();                               // Вызовите функцию display_day
        delay(200);                                  // Ждем 200 ms
      }
      draw_text(30, 0, "           ", 1);
      blink_parameter();                             // Вызов функции blink_parameter
      display_day();                                 // Вызовите функцию display_day
      blink_parameter();                             // Вызов функции blink_parameter
      if (!digitalRead(button1))                     // Если кнопка B1 нажата
        break;
    }
    date   = edit(4, 12, date, 2);                      // Изменить дату
    month  = edit(40, 12, month, 2);                    // Изменить month
    year   = edit(100, 12, year, 2);                    // Изменить year
    hour   = edit(20, 30, hour, 3);                     // Изменить hours
    minute = edit(74, 30, minute, 3);                   // Изменить minutes
    // Преобразовать десятичную в BCD
    minute = ((minute / 10) << 4) + (minute % 10);
    hour = ((hour / 10) << 4) + (hour % 10);
    date = ((date / 10) << 4) + (date % 10);
    month = ((month / 10) << 4) + (month % 10);
    year = ((year / 10) << 4) + (year % 10);
    // End conversion
    // Записать данные в DS3231 RTC
    Wire.beginTransmission(0x68);               // Запустите протокол I2C с адресом DS3231
    Wire.write(0);                              // Отправить адрес регистрации
    Wire.write(0);                              // Сбросьте sesonds и запустите генератор
    Wire.write(minute);                         // Запишем минуту
    Wire.write(hour);                           // Запишем  hour
    Wire.write(day);                            // Запишем  day
    Wire.write(date);                           // Запишем  date
    Wire.write(month);                          // Запишем  month
    Wire.write(year);                           // Запишем  year
    Wire.endTransmission();                     // Остановите передачу и отпустите шину I2C
    delay(200);                                 // Ждем 200ms
  }
  Wire.beginTransmission(0x68);                 // Запустите протокол I2C с адресом DS3231
  Wire.write(0);                                // Отправить адрес регистрации
  Wire.endTransmission(false);                  // Перезапуск I2C
  Wire.requestFrom(0x68, 7);                    // Запросите 7 байтов от DS3231 и освободите шину I2C
  second = Wire.read();                         // Считать секунды из регистра 0
  minute = Wire.read();                         // Считать minuts из регистра 1
  hour   = Wire.read();                         // Считать hour из регистра 2
  day    = Wire.read();                         // Считать day из регистра 3
  date   = Wire.read();                         // Считать date из регистра 4
  month  = Wire.read();                         // Считать month из регистра 5
  year   = Wire.read();                         // Считать year из регистра 6
  Wire.beginTransmission(0x68);                 // Запустите протокол I2C с адресом DS3231
  Wire.write(0x11);                             // Отправить адрес регистрации
  Wire.endTransmission(false);                  // Перезапуск I2C
  Wire.requestFrom(0x68, 2);                    // Запрос 2 байта от DS3231 и освобождение шины I2C
  temperature_msb = Wire.read();                // Чтение температуры MSB
  temperature_lsb = Wire.read();                // Считать температуру LSB
  display_day();
  DS3231_display();                             // Время и календарь
  delay(50);                                    // Ждем 50ms
}
/* Функция перекодировки русских букв из UTF-8 в Win-1251 */
String utf8rus(String source)
{
  int i, k;
  String target;
  unsigned char n;
  char m[2] = { '0', '\0' };
  k = source.length(); i = 0;
  while (i < k) {
    n = source[i]; i++;
    if (n >= 0xC0) {
      switch (n) {
        case 0xD0: {
            n = source[i]; i++;
            if (n == 0x81) {
              n = 0xA8;
              break;
            }
            if (n >= 0x90 && n <= 0xBF) n = n + 0x30;
            break;
          }
        case 0xD1: {
            n = source[i]; i++;
            if (n == 0x91) {
              n = 0xB8;
              break;
            }
            if (n >= 0x80 && n <= 0x8F) n = n + 0x70;
            break;
          }
      }
    }
    m[0] = n; target = target + String(m);
  }
  return target;
}

Пишите в комментариях, какие проекты на данном дисплее вы бы хотели видеть.


Возможности OLED часов на arduino.

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

Возможности OLED часов на arduino.

В связи с тем, что используем модуль реального времени ds3231 , в котором встроен датчик температуры, мы можем вывести температуру в помещении. Самая нижняя строчка на дисплее выводит данную информацию.

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

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


На этом всё. Не забывайте подписываться на мой YouTube канал, вступать в группу Вконтакте, в группу на Facebook.

И всем пока-пока. До встречи в новом проекте.


Понравилась статья? Поделитесь ею с друзьями:

Файлы для скачивания

Adafruit_GFX_Library русифицированная Adafruit_GFX_Library русифицированная.zip310 Kb 2113 Скачать
glcdfont.c glcdfont.c.zip2 Kb 1728 Скачать
Adafruit_SSD1306 Adafruit_SSD1306.zip34 Kb 1736 Скачать
cloсk_oled cloсk_oled.ino12 Kb 2061 Скачать
Вернуться в раздел: Проекты на Arduino Дата публикации 6 февраля , 2020

Комментарии

Войдите или Зарегистрируйтесь И Вы сможете общаться на форуме и оставлять комментарии без капчи.

dDeny
#7 dDeny (4 февраля, 2023 в 23:48)
В скетче есть недочет. Если отключить мк от питания, но оставить запитанным от штатной батарейки модуль ds3231, то при включении мы увидим то время, когда выключился мк и оно дальше продолжит отсчитываться от этой точки... и наверно у такой логики может быть применение, но теряется смысл в использовании модуля часов реального времени...
boev
#5 boev (13 февраля, 2021 в 18:54)
ошибка IDE        Arduino: 1.5.7 (Windows 7), Board: "Arduino Pro or Pro Mini, ATmega328 (5V, 16 MHz)"In file included from sketch_feb13a.ino:4:0:C:\Users\Андрей\Documents\Arduino\libraries\Adafruit_SSD1306/Adafruit_SSD1306.h:40:17: fatal error: SPI.h: No such file or directory #include <SPI.h>                 ^compilation terminated.  This report would have more information with  "Show verbose output during compilation"  enabled in File > Preferences.
vfksi713
#4 vfksi713 (13 января, 2021 в 14:18)
Не компилируется строка display.cp437(true);
Alexandr
#3 Alexandr (31 мая, 2020 в 16:26)
Прекрасно работают часы. Запустились с первого раза. Пробовал на ардуино уно и нано. Без разницы. К автору этой замечательной конструкции большая просьба. Можно сделать тоже самое, но с большим дисплеем oled? Дисплей в схеме мелкий, чтобы использовать эти часы в качестве настольных. Ждём следующую конструкцию автора. Успехов.
Виталий
#2 Гость: Виталий (19 февраля, 2020 в 12:14)

Будильника в часах не хватает.

Djek
#1 Гость: Djek (6 февраля, 2020 в 16:10)

А что на русском часов на Ардуино не сделать? Много же разных библиотек. Вчем проблема?


Cлучайные товары

Посмотреть товар в Магазине »

Мы в соц сетях

Подпишись на канал Telegram

Portal-PK (Портал ПК) © 2015 - 2024
Разработка проектов
AT Block
DWIN Box