Доработка скетча для LED часов на адресной ленте

Доброго времени суток! Уважаемые форумчане, прошу Вашей помощи. Я решил повторить замечательный проект часов с 7-мисегментными цифрами на адресной ленте WS2812b, размещенный на Хабре. Если кому интересно, даю ссылку Крутые часы на адресных диодах / Хабр.

Часы построены на Ардуино нано, имеют модуль часов реального времени, датчик освещенности для корректировки яркости свечения светодиодов и модуль bluetooth для корректировки времени через приложение. В качестве датчика температуры используется DHT22. Функции часов самые простые: постоянное отображение текущего времени и с 29 по 31 секунду – индикация температуры.

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

Я пытался связаться с автором проекта по поводу доработки кода, но, к сожалению, пока ничего не получается. Сам в программировании «ноль», единственное на что хватило моих знаний, это изменение кода в скетче числа используемых светодиодов (у меня иное количество, в отличие от проекта) и, соответственно правильная индикация выводимой информации. Также изменил отображение градусов Цельсия, тем самым освободив последнюю цифру на табло для отображения знака минус (для отрицательной температуры). Дальше никак.

Поэтому прошу Вашей помощи!

Мои задачи:

  1. Отображение значений отрицательной температуры и индикация знака «минус» (светодиоды с 108 по 112).

  2. Отображение значений влажности воздуха сразу после индикации температуры, с 32 по 34 секунду. Индикация на последних двух цифрах табло знака % без косой черты.

Заранее всем спасибо!

// ТЕМПЕРАТУРА - ПИН 7
// ЛЕНТА - ПИН 13

// -------- БИБЛИОТЕКИ ---------
#include <BH1750.h>       // библиотека датчика освещённости
// библиотека Wire.h для I2C подключается вместе с датчиком освещённости
#include <iarduino_RTC.h> // библиотека часов
#include "DHT.h"          // подключаем библиотеку для датчика температуры
#include <EEPROM.h>       // библиотека энергонезависимой памяти

// -------- КОНСТАНТЫ ---------
#define serialTimeout 5000  //5 секунд на ввод в данных в serial порт при запросе
#define ArduinoLED 13 //горящий диод ардуины, потом выключим
#define DHT_Port 7 //порт датчика температуры
#define BH1750address = 0x23; //адрес датчика освещения

//  Лента
#define COLOR_DEBTH 3         // Разрешение ленты
#define NUM_LEDS 142          // число светодиодов в кольце/ленте
#define LED_PIN 13             // пин, куда подключен DIN ленты
#define thisdelay 20          //-FX LOOPS DELAY VAR
#define thisstep 10           //-FX LOOPS DELAY VAR
#define thishue 0             //-FX LOOPS DELAY VAR
#define thissat 255             //-FX LOOPS DELAY VAR
#define max_bright 30         // максимальная яркость (0 - 255) на старте
//----------------------//
#include "microLED.h"   // Библиотеку подключать после COLOR_DEBTH и ORDER_GRB

// -------- ОБЪЕКТЫ И КЛАССЫ ОБОРУДОВАНИЯ ---------
iarduino_RTC time(RTC_DS3231);          // подключаем RTC модуль на базе чипа DS3231, используется аппаратная шина I2C
DHT dht(DHT_Port, DHT22); // сообщаем на каком порту будет датчик и версию датчика температуры
BH1750 lightMeter;        // объект датчик освещённости


// -------- ПЕРЕМЕННЫЕ ---------
// Обратная связь через сериал порт (при подключении bluetooth на RX и TX автоматически порт работает там)
String inputString = "";         // строка, в которую будут записываться входящие данные
boolean stringComplete = false;  // заполнилась ли строка или нет

// Часы
bool Dot;  //объявление переменной состояния точки (разделение часы : минуты)
long ledColor = BLUE;          // объявление переменной цвета диодной ленты
byte z = 1;                    //Индекс? диода для конвертации 
word Now;

///-- переменные времени для управления цветом
byte startWorkHours = EEPROM.read(0);
byte startWorkMinutes = EEPROM.read(1);
byte endWorkHours = EEPROM.read(2);
byte endWorkMinutes = EEPROM.read(3);

int brightnessCorrection = EEPROM.read(7); // коэффициент коррекции яркости, в кабинете равен +5
                                         // именно integer, чтобы могло быть отрицательным (поправка на меньшую яркость и больше чем 127)  

// --------------- ОБЪЯВЛЕНИЕ ЛЕНТЫ ---------------- //
LEDdata leds[NUM_LEDS];
microLED strip(leds, NUM_LEDS, LED_PIN);

/////////////////////////////////////////////////////////////////////////////////////////////////////////

byte digits[10][7] = {{0, 1, 1, 1, 1, 1, 1}, // Digit 0
 {0, 1, 0, 0, 0, 0, 1}, // Digit 1
 {1, 1, 1, 0, 1, 1, 0}, // Digit 2
 {1, 1, 1, 0, 0, 1, 1}, // Digit 3
 {1, 1, 0, 1, 0, 0, 1}, // Digit 4
 {1, 0, 1, 1, 0, 1, 1}, // Digit 5
 {1, 0, 1, 1, 1, 1, 1}, // Digit 6
 {0, 1, 1, 0, 0, 0, 1}, // Digit 7
 {1, 1, 1, 1, 1, 1, 1}, // Digit 8
 {1, 1, 1, 1, 0, 1, 1}
};  // Digit 9 | 2D Array for numbers on 7 segment

void setup()
{
 Serial.begin(9600);
 pinMode(ArduinoLED, OUTPUT); //диод на плате ардуины
 digitalWrite(ArduinoLED, LOW); //выключаем диорд на плате
 delay(1000);                  // Ждем готовности всех датчиков.
 lightMeter.begin();
 dht.begin(); // запускаем датчик температуры
 strip.setBrightness(max_bright);  // ограничить максимальную яркость
 inputString.reserve(20); // «бронируем» 20 байт для inputString
 BrightnessCheck();
 startLed();     // красиво запустить ленту
 LedColor();
}

void loop()
{
 serialParse();
 if (GetSecond() >= 29 && GetSecond() <= 31) {
   displayTemp(); //вызов функции отображения температуры
 }
 if (GetSecond() % 10 == 0) { //регулировка яркости
   BrightnessCheck();
 }
 if (GetSecond() == 0 || GetSecond() == 2 || GetSecond() == 7) { //регулировка цвета
   LedColor();
 }
 else{
 displayClock();
 }
}

///////// Основные функции

void displayClock() // функция вывода времени
{
 int Now = GetTime();
 int z = 1;
 convert(Now, z);// Get leds array with required configuration
 strip.show();
}
// Определение цвета ленты
void LedColor() {
 time.gettime();
 if (time.Hours == 13 && time.weekday != 0 && time.weekday != 6) { //Обед
   ledColor = GREEN;
 }
 else if (time.Hours == 12 && time.minutes >= 40 ) { //Почти обед
   ledColor = LIME;
 }
 else if (time.Hours > endWorkHours || (time.Hours == endWorkHours && time.minutes >= endWorkMinutes) || time.weekday == 6 || time.weekday == 0) {
   ledColor = RED; //Если выходной или нерабочее время
 }
 else if (time.weekday != 5 && (time.Hours == (endWorkHours - 1) && time.minutes >= endWorkMinutes) || time.Hours == endWorkHours && time.minutes < endWorkMinutes) {
   ledColor = OLIVE; //Скоро домой (не пятница)
 }
 else if (time.Hours < startWorkHours || (time.Hours == startWorkHours && time.minutes < startWorkMinutes)) {
   ledColor = MAROON;
 }
 else if (time.weekday == 5) {
   if ((time.Hours == (endWorkHours - 2) && time.minutes >= endWorkMinutes) || (time.Hours == (endWorkHours - 1) && time.minutes < endWorkMinutes)) {
     ledColor = OLIVE;
   }
   else if (time.Hours == (endWorkHours - 1) && time.minutes >= endWorkMinutes) {
     ledColor = MAROON;  //В пятницу они будут красными не сразу
   }
   else {
     ledColor = BLUE;
   }
 }
  else {
   ledColor = BLUE;
 }
}
// Настройка яркости по датчику освещённости

void BrightnessCheck() {
 word lightValue = lightMeter.readLightLevel();
 if (lightValue <= 1) {
   strip.setBrightness(2);
 }
 else if (lightValue > 1 && lightValue <= 200 ) {
   strip.setBrightness(lightValue + brightnessCorrection);
 }
 else if (lightValue > 200) {
   strip.setBrightness(210);
 }
 else if (lightValue == 54612) {
   strip.setBrightness(max_bright);  // Если датчик отвалился, делаем яркость вручную
 }
 else {
   strip.setBrightness(max_bright);
 }
}

//  ---------------------------------------------------------------------------------------------- //
// Работа с часами и временем
//функция запроса текущей секунды, результат в переменной "second"

byte GetSecond() {
 time.gettime();
 byte second = time.seconds;
 return (second);
};

word GetTime() { //функция запроса текущего времени, результат в виде целого числа, то есть 10:25 будет 1025
 byte hour = time.Hours;
 byte minutes = time.minutes;
 byte second = time.seconds;
 if (second % 2 == 0) {
   Dot = false;
 }
 else {
   Dot = true;
 };
 return (hour * 100 + minutes);
}

void displayTemp() // функция вывода температуры
{
 float temp = dht.readTemperature();
 int Temp = temp + 0.5;
 strip.fill(BLACK);
 Dot = false;
 byte z = 3;
 convert(Temp, z);// Get leds array with required configuration
 for (byte i = 71; i < 72; i++)
 {
   strip.setColor(i, ledColor);
 }
 for (int i = 72; i < 77; i++)
 {
   strip.setColor(i, ledColor);
 }
 for (int i = 82; i < 92; i++)
 {
   strip.setColor(i, ledColor);
 }
 for (int i = 92; i <= 141; i++) {
   {
     strip.setColor(i, BLACK);
   }
 }
 strip.show();// Display leds array
 delay(3000);
}
// Понтовое включение часов - пробежка радугой
void startLed() {
 strip.fill(BLACK);
 strip.show();
 byte thisled;
 byte ihue;
 while (thisled < NUM_LEDS) {
   ihue = ihue + thisstep;
   strip.setLED(thisled, mHSV(ihue, thissat, 255));
   strip.show();
   thisled++;
   delay(thisdelay);
 }
 delay(200);
 thisled = 0;
 while (thisled < NUM_LEDS) {
   strip.setLED(thisled, mRGB(0, 0, 0));
   strip.show();
   thisled++;
   delay(thisdelay);
 }
};

//////// конвертация цифр
LEDdata convert (int DataIn, byte z) { //функция преобразования любого числа в массив для отображения на ленте, результат - массив leds
 int cursor;
 if (Dot) {
   leds[70] = leds[71] = mCOLOR(ledColor);
 }
 else {
   leds[70] = leds[71] = 0x000000;
 };

 for (byte i = z; i <= 4; i++) {
   int digit = DataIn % 10; // get last digit in time
   //    Serial.println(digit);
   if (i == 1) {
     cursor = 107;
     for (byte k = 0; k < 7; k++) {
       if (digits[digit][k] == 1) {
         for (int i = 0; i < 5; i++) {
           //            leds[cursor] = mCOLOR(ledColor);
           strip.setLED(cursor, mCOLOR(ledColor));
           cursor ++;
         }
       }
       else {
         for (byte i = 0; i < 5; i++) {
           //leds[cursor] = 0x000000;
           strip.setLED(cursor, mCOLOR(BLACK));
           cursor ++;
         }
       }
     }
   }
   else if (i == 2) {
     cursor = 72;
     for (byte k = 0; k < 7; k++) {
       if (digits[digit][k] == 1) {
         for (byte i = 0; i < 5; i++) {
           //leds[cursor] = mCOLOR(ledColor);
           strip.setLED(cursor, mCOLOR(ledColor));
           cursor ++;
         }
       }
       else {
         for (byte i = 0; i < 5; i++) {
           // leds[cursor] = 0x000000;
           strip.setLED(cursor, mCOLOR(BLACK));
           cursor ++;
         }
       }
     }
   }
   else if (i == 3) {
     cursor = 35;
     for (int k = 0; k < 7; k++) {
       if (digits[digit][k] == 1) {
         for (int i = 0; i < 5; i++) {
           //leds[cursor] = mCOLOR(ledColor);
           strip.setLED(cursor, mCOLOR(ledColor));
           cursor ++;
         }
       }
       else {
         for (byte i = 0; i < 5; i++) {
           leds[cursor] = 0x000000;
           strip.setLED(cursor, mCOLOR(BLACK));
           cursor ++;
         }
       }
     }
   }
   else if (i == 4) {
     cursor = 0;
     for (byte k = 0; k < 7; k++) {
       if (digits[digit][k] == 1) {
         for (byte i = 0; i < 5; i++) {
           //leds[cursor] = mCOLOR(ledColor);
           strip.setLED(cursor, mCOLOR(ledColor));
           cursor ++;
         }
       }
       else {
         for (byte i = 0; i < 5; i++) {
           //leds[cursor] = 0x000000;
           strip.setLED(cursor, mCOLOR(BLACK));
           cursor ++;
         }
       }
     }
   }
   DataIn /= 10;
   //Serial.println(digit);
 };
};
//////////////////////////////////////
/*
 Функция SerialEvent используется всякий раз при получении
 новых данных через последовательный порт. Она запускается
 при каждом запуске нового цикла, поэтому использование задержки
 может затормозить ответ. Позволяет передавать сразу несколько байтов.
*/

////////////////////  Работа с принятием данных в порт

void serialParse() {
 // если прибыл символ новой строки, показываем строку:
 if (stringComplete) {
   if (inputString.equalsIgnoreCase("SetTime\r")) {  // Если прислали команду на установку времени
     Serial.println(F("Корректировка времени:"));
     Serial.println(F("Введите часы:"));
     Serial.setTimeout(serialTimeout);
     byte newHours = Serial.parseInt();  //новые часы получаем из сериал порт
     Serial.println(F("Введите минуты:"));
     byte newMinutes = Serial.parseInt(); //новые минуты получаем из сериал порт
     time.settime(0, newMinutes, newHours);     //пишем в модуль новые минуты и часы, секунды обнуляем, остальное без изменений
     Serial.print(F("Время установлено: "));
     time.gettime();
     Serial.print(time.Hours);
     Serial.print(F(":"));
     Serial.println(time.minutes);
     LedColor();
   }
   else if (inputString.equalsIgnoreCase("SetDate\r")) {
     Serial.println(F("Корректировка даты:"));
     Serial.println(F("Введите число:"));
     Serial.setTimeout(serialTimeout);
     byte newDay = Serial.parseInt();  //получаем из сериал порт новое значение даты
     Serial.println(F("Введите месяц:"));
     byte newMonth = Serial.parseInt(); //получаем из сериал порт новое значение месяца
     Serial.println(F("Введите год (0-99):"));
     byte newYear = Serial.parseInt(); //получаем из сериал порт новое значение года
     Serial.println(F("Введите день недели (0-6):"));
     byte newWeekDay = Serial.parseInt(); //получаем из сериал порт новое значение года
     time.settime(-1, -1, -1, newDay, newMonth, newYear, newWeekDay); // Установить дату 09.02.2017, а время и день недели оставить без изменений.
     time.gettime();
     Serial.print(F("Дата установлена: "));
     Serial.println(time.gettime("d-m-Y, D"));
   }
   else if (inputString.equalsIgnoreCase("info\r")) {
     Serial.println();
     Serial.println(time.gettime("d-m-Y, H:i:s, D"));
     //////////////////// Добавить ручную и автояркость ленты
     //  Датчик температуры
     float h = dht.readHumidity();
     float t = dht.readTemperature();
     Serial.print(F("Температура: "));
     Serial.println(t);
     Serial.print(F("Влажность: "));
     Serial.println(h);
     Serial.print(F("Освещённость: "));
     Serial.println(lightMeter.readLightLevel());
     Serial.println();
     Serial.print(F("Начало работы: "));
     Serial.print(startWorkHours);
     Serial.print(F(":"));
     Serial.println(startWorkMinutes);
     Serial.print(F("Окончание работы: "));
     Serial.print(endWorkHours);
     Serial.print(F(":"));
     Serial.println(endWorkMinutes);
     Serial.print(F("Корректировка яркости:"));
     Serial.println(brightnessCorrection);
   }
   else if (inputString.equalsIgnoreCase("Demo\r")) {
     startLed();
     Serial.println();
     Serial.print(F("Demo OK"));
     Serial.println();
   }
   else if (inputString.equalsIgnoreCase("Check\r")) {
     LedColor();
     delay(10);
     BrightnessCheck();
     delay(10);
     displayTemp();
     Serial.print(F("Check OK"));
   }
   else if (inputString.equalsIgnoreCase("SetWorkTimes\r")) {  // Если прислали команду на установку времени
     Serial.setTimeout(serialTimeout);
     Serial.println(F("Начало работы:"));
     Serial.println(F("Часы:"));
     byte newStartHours = Serial.parseInt();
     EEPROM.update(0, newStartHours);
     startWorkHours = newStartHours;
     Serial.println(F("OK"));
     Serial.setTimeout(serialTimeout);
     Serial.println(F("Минуты:"));
     byte newStartMinutes = Serial.parseInt();
     EEPROM.update(1, newStartMinutes);
     startWorkMinutes = newStartMinutes;
     Serial.println("OK");
     Serial.setTimeout(serialTimeout);
     Serial.println(F("Конец работы:"));
     Serial.println(F("Часы:"));
     byte newEndHours = Serial.parseInt();
     EEPROM.update(2, newEndHours);
     endWorkHours = newEndHours;
     Serial.println(F("OK"));
     Serial.setTimeout(serialTimeout);
     Serial.println(F("Минуты:"));
     byte newEndMinutes = Serial.parseInt();
     EEPROM.update(3, newEndMinutes);
     endWorkMinutes = newEndMinutes;
     Serial.println("OK");
   }
   else if (inputString.equalsIgnoreCase("SetBright\r")) {  // Если прислали команду на установку времени
     Serial.setTimeout(serialTimeout);
     Serial.println(F("Корректировка яркости:"));
     Serial.println(F("Введите коэффициент корректировки:"));
     brightnessCorrection = Serial.parseInt();
     EEPROM.update(7, brightnessCorrection);
     Serial.print(F("Коэффициент корректировки:"));
     Serial.println(brightnessCorrection);
     Serial.println(F("OK"));
   }
   else {
     //      Serial.print(F("Невернaя команда: "));
     Serial.println(inputString);
   }
   inputString = "";         //обнуляем строку
   stringComplete = false;   //ждём нового заполнения
 }
};

//----------------------------------------------------------------------------------------------//
////////Получение данных из порта //////// НЕ ТРОГАТЬ
void serialEvent() {
 while (Serial.available()) {
   // получаем новый байт:
   char inChar = (char)Serial.read();
   // добавляем его к inputString:
   inputString += inChar;
   // если получили символ новой строки, оповещаем программу об этом,
   // чтобы она могла принять дальнейшие действия.
   if (inChar == '\r') {
     stringComplete = true;
   }
 }
};
//////  ----- КОНЕЦ ПОЛУЧЕНИЯ ДАННЫХ ИЗ ПОРТА ----///////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Bluetooth подключён на порт 5V
/*
   watch.settime(0,51,21,27,10,15,2);     Записываем время в модуль: 0 сек, 51 мин, 21 час, 27, октября, 2015 года, вторник
   Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]):
   Функция записывает время в модуль.
   Год указывается без учёта века, в формате 0-99.
   Часы указываются в 24-часовом формате, от 0 до 23.
   День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота.
   Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение.
   Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён.
   Пример: watch.settime(-1, 10);               // Установить 10 минут, а секунды, часы и дату, оставить без изменений.
   Пример: watch.settime(0, 5, 13);             // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений.
   Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений.
*/

// В EEPROM записаны часы и минуты начала и окончания рабочего дня
 // На питание часов на плату между VСС и GND, 
 // керамический конденсатор 0,1-1 мкФ
 // маркировка 103 или 104

Найти готовый проект, где это всё “выводится” :slightly_smiling_face: Ибо их больше, чем “много” А ежели необходимы конкретные требования - добро пожаловать в Ищу исполнителя - Arduino.ru

Проекты я другие смотрел. Похожие есть, но либо с выводом на матрицу, либо вывода значений влажности нет. А по поводу Ищу исполнителя - это крайний шаг. Хочется самому сделать, но естественно с чьей то помощью. Тему для этого здесь и создал. Подскажите пожалуйста “куда копать”, с этого начну.

Вам это реально надо использовать?

Так самому, или с помощью? И с чего вы взяли, что кому-то захочется вам помогать забесплатно?

Посмотрел код - написано криво, каждая цифра из четырех рисуется отдельно (хотя они одинаковые), число диодов забито явно цифрами…
Посмотрите тему с такими часами на старом форуме - там с десяток разных вариантов.

1 лайк

Альтруизм совсем иссяк :slightly_smiling_face:
i

То есть Вы хотите, чтобы для Вас кто-то сделал достаточно большую работу (во много раз дороже, чем купить часы в магазине), но при этом считаете, что оплата работы - это “крайний шаг”?
Мне кажется, что это уже наглость, граничащая с хамством.

1 лайк

Никто не хамит. Я ни кого не прошу переписать за меня скетч. Обычно свои проблемы стараюсь решить самостоятельно. В этой ситуации мне больше нужен дельный совет, как исправить код. Посмотрел другие темы Песочницы, здесь все советы сводятся только к одному - иди в раздел Ищу исполнителя. Нафига тогда нужна Песочница?

Ты начни сам что-то делать, скажи что не получается - тогда и помогать начнут. А «мне надо» тут как «красная тряпка».)

Оригинальный проект не мой, я его лишь повторяю. А так да, я убедился, что от этого датчика толку мало.

Самому. Здесь я прошу совет, как двигаться в нужном направлении. Спасибо за наводку, буду копаться на старом форуме.

Про “мне надо” я не говорил.

Ты пришел сюда поспорить?

Уважаемый! Спорить я сюда не пришел. Отвечаю точно также, как и мне здесь написали. Спорить, доказывать, ругаться я умею, но не хочу. Хочется конструктивного диалога. Спасибо за критику.

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

  1. Изучить С++.
  2. Изучить схемотехнику.
  3. Начать делать собственные проекты.
3 лайка

Да, неужели? А вот это:

Только Вы почему-то решили сделать эти задачи нашими :frowning:

Так, начните его! Сделайте что-нибудь сами и, если заткнётесь задайте конкретные вопросы по возникшей проблеме, а не ставьте нам “свои задачи”. Заметите, как отношение к Вам изменится. Проблема-то у Вас! Значит, Вам и начинать конструктивный диалог, разве нет?

Единственно возможный совет дан в конце поста №15. Других тут быть не может.

Дружище! Не могу не ответить на твои высказывания. Получается данный форум (даже Песочница) только для избранных и залетных товарищей здесь не любят. Тогда надо в инструкции для новичков эти требования было прописывать. Типа без наличия особых компетенций нефига вопросы задавать.
Не по теме, но все же скажу. Человек приходит к доктору на прием в поликлинику по поводу лечения своего заболевания. Если исходит из такого подхода, то он должен:

  1. Изучить теорию и методологию лечения.
  2. Изучить особенности строения и функционирования больного органа.
  3. Назначить курс лечения самому себе самостоятельно до прихода к доктору.

Ладно, думаю надо закрывать наш неконструктивный диалог. Всем откликнувшимся огромное спасибо!

Если исходит из подхода “не хочу платить”, то да.

1 лайк

и говорит:

полис ОМС или оплата через кассу - это для меня крайний случай, потому работать ты будешь бесплатно, ты обязан мне помогать, иначе зачем тогда нужна поликлиника? Я не требую меня вылечить, я лишь прошу совет как двигаться в правильном направлении (вести здоровый образ жизни и хоть что-то прочитать/узнать – не предлагать)

3 лайка