// Часы на WS2812
// Версия 1.8
// Дата написания 23.04.2021
// Сборка проршивки [email protected]
/* Последоватеольность соединения сигментов индикатора
{G B A F E D C} {G B A F E D C} {верхняя точка} {нижняя точка} {G B A F E D C} {G B A F E D C}
Количество светодиодов светодиодов выберается относительно количества светотдиодов на сегмент
При первом запуске выбрать режим "----" для обнуления памяти
ВОЗМОЖНОСТИ
Отображают время, температуру, дату, 255 цетов (смена каждую минуту)
В режиме часов нажатие на "+" отображает температуру, а на "-" дату
Цвет отображении температуры изменяется от температуры
/* ИЗМЕНЕНИЯ *поправил смену цвета
добавил настройку задержку анимации пока с ограничением в 30 миллисекунд
НАСТРОЙКИ в часах (листаются кнопкой "настройки" по кругу) менять кнопками "+" и "-":
1. (H) установка часов 0...23
2. (A) установка минут 0...59
3. (S) сброс секунд на 0 кнопками ("+" - сбрасывает сек. на 0 добавляет 1 мин.) или ("-" - сбрасывает сек. на 0)
4. (d) установка дня 1...31
5. (b) установка месяца 1...12
6. (отображается полностью) установка года
7. (C) настройка цвета (если 00 то автоматическая) 0...25
8. (L) настройка яркости (если 00 то автоматическая) 0...25
9. (t) включение датчиков температуры (0 - комнатный, 1 - уличный, 2 - оба)
10. (tt) установка времени отображения температуры 0...5 сек.
11. (td) установка времени отображения даты 0...5 сек.
12. (SH) установка отображениея анимации при смене минут (0 - отк. 1 - вкл.)
13. (tH) установка количества отображения температуры и даты в минуту 1...6 раз (период 60,30,20,15,12,10 сек. соответственно)
14. (----) сброс настроек кнопками "+" или "-"
*/
#include <DS3232RTC.h>
#include <FastLED.h>
#include <EEPROMex.h>
#include <microDS18B20.h>
// Настройки пинов подключения к Ардуино +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define DATA_PIN 6 // pin вывод данных на светодиоды
#define LIGHT_PIN A0 // pin датчика освещенности
#define TEMP_INSIDE_PIN 10 // pin комнатного датчика температуры DS18B20 только плюсовая до десятых
#define TEMP_OUTSIDE_PIN 11 // pin уличного датчика температуры DS18B20 плюсовая и минусовая до целых
#define PIN_UP 3 // pin кнопки прибавить
#define PIN_ST 4 // pin кнопки выбор
#define PIN_DW 5 // pin кнопки убавить
// Настройки +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define LED_TYPE WS2812 // Тип светодиодов (WS2812, WS2811 и т.д.)
#define COLOR_ORDER GRB // Порядок цветов (если при включении часы не красные менять буквы местами)
#define SEGMENT_LEDS 1 // Количество светодиодов на сегмент (точки по 1 светодиоду)
#define LIGHT_MIN 4 // Минимальный уровень яркости (0...255) должно быть меньше максимального
#define LIGHT_MAX 250 // Максимальный уровень яркости (0...255) должно быть больше минимального
#define TIME_EXIT 4 // Время выхода из меню после последнего нажатия кнопок (секунд)
#define ANIME_DELAY 20 // Замедление анимации при смемене цвета в миллисекундах (не более 30)
// Настройки отображения температуры ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define TEMP_INSIDE 10 // Знак градусов комнатной температуры (10 - "0" 11 - "С")
#define TEMP_OUTSIDE 10 // Знак градусов уличной температуры (10 - "0" 11 - "С")
#define TEMP_INSIDE_COLOR 1 // Вариант цвета комнатной (0 - как часы, 1 - кр-ор-ж-зел-гол-с, 2 - кр-роз-фиол-с)
#define TEMP_OUTSIDE_COLOR 1 // Вариант цвета уличной (0 - как часы, 1 - кр-ор-ж-зел-гол-с, 2 - кр-роз-фиол-с)
#define TEMP_INSIDE_MIN 20 // Минимальная комнатная температура (отображается синим)
#define TEMP_INSIDE_MAX 30 // Максимальная комнатная температура (отображается красным)
#define TEMP_OUTSIDE_MIN -20 // Минимальная уличная температура (отображается синим)
#define TEMP_OUTSIDE_MAX 35 // Максимальная уличная температура (отображается красным)
// Defauil настройки в часах устанавливаются режимом "----" +++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define TIME_TEMP 4 // Установка времени отображения температуры 0...5 секунд (0 - отк.)
#define TIME_DATE 0 // Установка времени отображения даты 0...5 секунд (0 - отк.)
#define SET_COLOR 0 // Настройка цвета (если 0 то автоматическая) 0...20
#define SET_LIGHT 0 // Настройка яркости (если 0 то автоматическая) 0...25
#define SET_TEMPS 2 // Включение отображения температуры (0 - комнатный, 1 - уличный, 2 - оба)
#define SET_SHOWS 1 // Включение отображения анимации при смене минут (0 - отк. 1 - вкл.)
#define TIME_TIME 3 // Установка количества отображения температуры и даты в минуту 1...6 раз
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MicroDS18B20 sensor1(TEMP_INSIDE_PIN);
MicroDS18B20 sensor2(TEMP_OUTSIDE_PIN);
#define NUM_LEDS ((SEGMENT_LEDS * 28) + 2)
#define POINT_UP (14 * SEGMENT_LEDS)
#define POINT_DW ((14 * SEGMENT_LEDS) + 1)
CRGB leds[NUM_LEDS];
CRGB temp_color;
CRGB led_color;
byte digits[21] = {0b01111110, // 0
0b01000010, // 1
0b00110111, // 2
0b01100111, // 3
0b01001011, // 4
0b01101101, // 5
0b01111101, // 6
0b01000110, // 7
0b01111111, // 8
0b01101111, // 9
0b00001111, // º 10
0b00111100, // C 11
0b01011011, // H 12
0b01011111, // A 13
0b01101101, // S 14
0b00000001, // - 15
0b01110011, // d 16
0b01111001, // b 17
0b00000000, // 18
0b00111001, // t 19
0b00111000}; // L 20
boolean eeprom_flag;
boolean temp_flag = false;
boolean read_eeprom = true;
boolean mode_flag = true;
unsigned long key_pressed;
unsigned long t;
byte color;
byte old_color = 0;
byte mode = 55; // Режим работы
byte brightness; // Яркость
byte znak; // Для вывода знака минуса
byte points = 0; // Состотяние точек
byte figure[4]; // Цыфры часов
byte setups[8]; // 0, TIME_TEMP, TIME_DATE, SET_COLOR, SET_LIGHT, SET_TEMPS, SET_SHOWS, TIME_TIME
int temp1; // Комнатная температура
int temp2; // Уличная температура
int time_tp; // Промежуточное время вывода температуры
class button { // Работа с кнопками
public:
button (byte pin) {
_pin = pin;
pinMode(_pin, INPUT_PULLUP);}
static byte key;
boolean click() {
boolean btnState = digitalRead(_pin);
if (!btnState && !_flag && millis() - _tmr >= 100) {
_flag = true;
key = true;
_tmr = millis();
return true;}
if (!btnState && _flag && millis() - _tmr >= 400) {
_tmr = millis ();
key = true;
return true;}
if (btnState && _flag) {
_flag = false;
key = false;
_tmr = millis();}
return false;}
private:
byte _pin;
uint32_t _tmr;
boolean _flag;};
button up(PIN_UP);
button st(PIN_ST);
button dw(PIN_DW);
byte button::key;
void setup(){
LEDS.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS); // Установить тип светодиодной ленты
figure[0] = 0;
figure[1] = 7 * SEGMENT_LEDS;
figure[2] = (14 * SEGMENT_LEDS) + 2;
figure[3] = (21 * SEGMENT_LEDS) + 2;
t = millis();}
// END setup
void BrightnessCheck(){ // Установка яркости по датчику освещенности
if (setups[4] == 0) brightness = map(analogRead(LIGHT_PIN), 0, 1023, LIGHT_MIN, LIGHT_MAX);
else brightness = setups[4] * 10;
LEDS.setBrightness(brightness);}
void TempColor(byte set_sh, int temp_x, int temp_min, int temp_max){ // Функция изменения цвета от температуры
byte sum_color;
if (set_sh == 0) temp_color = led_color;
if (temp_x < temp_min * 10) temp_x = temp_min * 10;
if (temp_x > temp_max * 10) temp_x = temp_max * 10;
if (set_sh == 1) sum_color = map(temp_x, temp_min * 10, temp_max * 10, 170, 0);
if (set_sh == 2) sum_color = map(temp_x, temp_min * 10, temp_max * 10, 170, 255);
temp_color = CHSV(sum_color, 255, 255);}
void DigitOut(byte digit_1, byte digit_2, byte digit_3, byte digit_4, byte point, CRGB d_color){ // Функция вывода на диоды
byte digit[4] = {digit_1, digit_2, digit_3, digit_4};
for (byte s = 0; s <= 3; s++){
byte cursor = figure[s];
for(byte k = 0; k <= 6; k++){
for(byte i = 1; i <= SEGMENT_LEDS; i++){
if ((digits[digit[s]] & 1 << k) == 1 << k)
leds[cursor] = d_color;
else leds[cursor] = 0x000000;
cursor++;}}}
if (point == 0){leds[POINT_DW] = 0x000000; leds[POINT_UP] = 0x000000;}
else if (point == 1){leds[POINT_DW] = d_color; leds[POINT_UP] = 0x000000;}
else if (point == 2){leds[POINT_DW] = d_color; leds[POINT_UP] = d_color;}}
int PressingButton(byte variable, byte var_min, byte var_max){ // Функция работы кнопок + и -
if (up.click()) { //увеличиваем
if (variable == var_max) variable = var_min; else variable += 1;}
if (dw.click()) { //уменьшаем
if (variable == var_min) variable = var_max; else variable -= 1;}
return variable;}
void Animate(int v) { // Функция анимации
static uint8_t hue = 0;
leds[v] = CHSV(hue++, 255, 255);
FastLED.show();
for(byte m = 0; m < NUM_LEDS; m++) {
leds[m].nscale8(250);}
delay(ANIME_DELAY / SEGMENT_LEDS);}
void Animation() { // Фунция анимации при смене минуты
for(int i = 0; i < NUM_LEDS; i++) Animate(i);
for(int i = NUM_LEDS - 1; i >= 0; i--) Animate(i);}
// Работа с памятью ===============================================================================================
void ReadEEPROM() { // Чтение
for (byte i = 1; i <= 7; i++) setups[i] = EEPROM.readByte(i);
read_eeprom = false;}
void UpdateEEPROM() { // Запись
for (byte i = 1; i <= 7; i++) EEPROM.updateByte(i, setups[i]);}
void EepromTick() { // Проверка
if (eeprom_flag && mode_flag){
for (byte i = 1; i <= 7; i++){
if (setups[i] != EEPROM.readByte(i)){
UpdateEEPROM();
break;}}
eeprom_flag = false;}}
void(* resetFunc) (void) = 0; // Функция перезагрузкки МК.
void ResetClock(){ // Сброс на настройки по умолчанию
byte def_setup[8] = {0, TIME_TEMP, TIME_DATE, SET_COLOR, SET_LIGHT, SET_TEMPS, SET_SHOWS, TIME_TIME};
// setups[i] i = _ 1 2 3 4 5 6 7
for (byte i = 1; i <= 7; i++) EEPROM.updateByte(i, def_setup[i]);
delay(100);
resetFunc();}
// ==================================================================================================================
void loop(){ // Основной цикл -------------------------------------------------------------------------------
if (read_eeprom) ReadEEPROM();
BrightnessCheck(); // Проверка яркости
time_t tm = RTC.get(); // Получение времени
tmElements_t Now;
if (mode == 0){
Now.Hour = hour(tm);
Now.Minute = minute(tm);
Now.Second = second(tm);
Now.Day = day(tm);
Now.Month = month(tm);
Now.Year = year(tm) - 1970;}
if (mode < 20 && mode != 0 && (millis() - key_pressed) > (TIME_EXIT * 1000)) mode = 0; // Переход в режим часов
if ((mode == 0 && up.click()) || (setups[1] != 0 && mode == 0 && (second(tm) % (60 / setups[7]) == 0))) { // Переход в режим запроса температуры
sensor1.requestTemp();
sensor2.requestTemp();
temp_flag = true;
t = millis();
if (setups[1] == 0) time_tp = 2000;
else if (setups[5] == 2) time_tp = setups[1] * 500;
else time_tp = setups[1] * 1000;}
if (temp_flag && mode == 0 && millis() - t > 2000) { // Запрос температуры
temp_flag = false;
t = millis();
temp1 = 10 * sensor1.getTemp();
temp2 = 10 * sensor2.getTemp();
if (setups[5] == 0 || setups[5] == 2) mode = 20;
else if (setups[5] == 1) mode = 21;}
if ((mode == 0 && dw.click()) || (setups[1] == 0 && setups[2] != 0 && mode == 0 && (second(tm) % (60 / setups[7]) == 0))) { // Переход в режим даты
t = millis();
mode = 25;}
if (mode < 20 && st.click()){ // если нажата кнопка 2, то гоняем меню по кругу
if (mode == 14) mode = 0; else mode++;}
if (mode < 20 && button::key) { // Отслеживание нажатия любой кнопки
key_pressed = millis();
if (mode >= 7 && mode <= 14) {
eeprom_flag = true;
mode_flag = false;}}
if (second(tm) == 2) old_color = color;
// ------------------------------------------ switch -----------------------------------
switch (mode) {
case 0: { // Нормальный режим отображение времени
if (second(tm) == 0) {
if (setups[3] == 0) {
while (abs(color - old_color) < 25) color = random8();
led_color = CHSV(color, 255, 255);}
else led_color = CHSV(setups[3] * 10, 255, 255);
if (setups[6] == 1) Animation();}
if (second(tm) % 2 == 0) points = 2; else points = 0;
DigitOut(hour(tm) / 10, hour(tm) % 10, minute(tm) / 10, minute(tm) % 10, points, led_color);
mode_flag = true;
break;}
case 1: { // Режим установки часов
Now.Hour = PressingButton(Now.Hour, 0, 23);
if (Now.Hour != hour(tm)) RTC.write(Now);
DigitOut(12, 15, Now.Hour / 10, Now.Hour % 10, 0, led_color);
break;}
case 2: { // Режим установки минут
Now.Minute = PressingButton(Now.Minute, 0, 59);
if (Now.Minute != minute(tm)) RTC.write(Now);
DigitOut(13, 15, Now.Minute / 10, Now.Minute % 10, 0, led_color);
break;}
case 3: { // Режим установки секунд
if (up.click()) { //обнуляем секунды + 1 минута
Now.Second = 0;
Now.Minute = Now.Minute + 1;
RTC.write(Now);}
if (dw.click()) { //обнуляем секунды
Now.Second = 0;
RTC.write(Now);}
key_pressed = millis();
DigitOut(14, 15, second(tm) / 10, second(tm) % 10, 0, led_color);
break;}
case 4: { // Режим установки дня
Now.Day = PressingButton(Now.Day, 1, 31);
if (Now.Day != day(tm)) RTC.write(Now);
DigitOut(16, 15, Now.Day / 10, Now.Day % 10, 0, led_color);
break;}
case 5: { // Режим установки месяца
Now.Month = PressingButton(Now.Month, 1, 12);
if (Now.Month != month(tm)) RTC.write(Now);
DigitOut(17, 15, Now.Month / 10, Now.Month % 10, 0, led_color);
break;}
case 6: { // Режим установки года
Now.Year = PressingButton(Now.Year, 50, 99);
if (Now.Year != year(tm)) RTC.write(Now);
DigitOut(2, 0, ((Now.Year - 30) / 10) % 100, ((Now.Year - 30) % 10), 0, led_color);
break;}
case 7: { // Режим установки цвета
setups[3] = PressingButton(setups[3], 0, 25);
if (setups[3] > 0) led_color = CHSV(setups[3] * 10, 255, 255);
DigitOut(11, 15, setups[3] / 10, setups[3] % 10, 0, led_color);
break;}
case 8: { // Режим установки яркости
setups[4] = PressingButton(setups[4], 0, 25);
DigitOut(20, 15, setups[4] / 10, setups[4] % 10, 0, led_color);
break;}
case 9: { // Режим установки датчиков температуры
setups[5] = PressingButton(setups[5], 0, 2);
DigitOut(18, 19, 15, setups[5], 0, led_color);
break;}
case 10: { // Режим установки времени отображения температуры
setups[1] = PressingButton(setups[1], 0, 5);
DigitOut(19, 19, 15, setups[1], 0, led_color);
break;}
case 11: { // Режим установки времени отображения даты
setups[2] = PressingButton(setups[2], 0, 5);
DigitOut(19, 16, 15, setups[2], 0, led_color);
break;}
case 12: { // Режим установки отображения анимации при смене минут
setups[6] = PressingButton(setups[6], 0, 1);
DigitOut(14, 12, 15, setups[6], 0, led_color);
break;}
case 13: { // Режим установки количества отображения часов
setups[7] = PressingButton(setups[7], 1, 6);
DigitOut(19, 12, 15, setups[7], 0, led_color);
break;}
case 14: { // Режим сброса
if (up.click() || dw.click()) {
ResetClock();}
DigitOut(15, 15, 15, 15, 0, led_color);
break;}
case 20: { // Режим вывода температуры внутри
TempColor(TEMP_INSIDE_COLOR, temp1, TEMP_INSIDE_MIN, TEMP_INSIDE_MAX);
if (temp1 < 100) DigitOut(18, (temp1 % 100) / 10, temp1 % 10, TEMP_INSIDE, 1, temp_color);
else DigitOut(temp1 / 100, (temp1 % 100) / 10, temp1 % 10, TEMP_INSIDE, 1, temp_color);
if (millis() - t > time_tp) {
if (setups[5] == 2) {
mode = 21;
t = millis();}
else if (setups[2] != 0) mode = 25;
else mode = 0;}
break;}
case 21: { // Режим вывода температуры без десятых уличной
if (temp2 < 0) znak = 15;
else znak = 18;
TempColor(TEMP_OUTSIDE_COLOR, temp2, TEMP_OUTSIDE_MIN, TEMP_OUTSIDE_MAX);
if (abs(temp2) < 100) DigitOut(18, znak, (abs(temp2) % 100) / 10, TEMP_OUTSIDE, 0, temp_color);
else DigitOut(znak, abs(temp2) / 100, (abs(temp2) % 100) / 10, TEMP_OUTSIDE, 0, temp_color);
if (millis() - t > time_tp) {
if (setups[2] != 0) {
mode = 25;
t = millis();}
else mode = 0;}
break;}
case 25: { // Режим вывода даты
DigitOut(day(tm) / 10, day(tm) % 10, month(tm) / 10, month(tm) % 10, 1, led_color);
if (setups[2] == 0 && millis() - t > 2000) mode = 0;
else if (setups[2] != 0 && millis() - t > (setups[2] * 1000)) mode = 0;
break;}
case 55: { // Режим вывода версии проошивки
led_color = 0xFF0000;
DigitOut(15, 1, 8, 15, 1, led_color);
if (millis() - t > 3000) mode = 0;
break;}
} // Конец switch
EepromTick(); // Проверка на сохранение в пямать
FastLED.show(); // Вывод на диоды
}
// END. -----------------------------------------------------------------------------------------------------------