Еще одни часы на матрицах

vk007
Offline
Зарегистрирован: 16.06.2015

yucan пишет:

Спустя 3 дня график почти заполнен. Получается это барограф)

Ну, почти. Из-за ограничения разрешения экрана по вертикали всего в 8 точек, график приходится масштабировать по высоте, поскольку разброс между максимальным и минимальным значением за период, что умещается на экране, часто больше 8 мм рт.ст. Поэтому в одной точке может быть от 1 мм и больше. Соответственно и сам график с каждым новым значением может сжиматься или снова растягиваться (по вертикали). Т.е., например, визуально точно рассчитать по точкам давление, которое было некоторое время назад не получится. Но оценить его изменение или даже немного его спрогнозировать - довольно просто.

yucan
Offline
Зарегистрирован: 20.04.2015

Спасибо за пояснение. А то я прикидывал - сколько едениц в одной точке...

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

AleksBAM
Offline
Зарегистрирован: 13.09.2014

Добрый день.

А можно заменить SHT21 на DHT22. И переписать скетч с заменой. 

vk007
Offline
Зарегистрирован: 16.06.2015

AleksBAM пишет:

А можно заменить SHT21 на DHT22. И переписать скетч с заменой. 

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

misir
Offline
Зарегистрирован: 14.05.2018

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

vk007
Offline
Зарегистрирован: 16.06.2015

Не определяется в этом проекте или вообще?

misir
Offline
Зарегистрирован: 14.05.2018

В этом проекте в других работает 

yucan
Offline
Зарегистрирован: 20.04.2015

misir пишет:

В этом проекте в других работает 

Внимательно проверь монтаж.  У меня всё работает

misir
Offline
Зарегистрирован: 14.05.2018

yucan пишет:

misir пишет:

В этом проекте в других работает 

Внимательно проверь монтаж.  У меня всё работает

Заработало пришлось поменять ардуинку оказалась китайская подделка вместо атмеги328 стоит awga328 я и не обратил внимание свиду одинаковые.

yucan
Offline
Зарегистрирован: 20.04.2015

Что  такое awga328? Яндех не в курсе.

misir
Offline
Зарегистрирован: 14.05.2018

В яндексе и я искал посмотри здесь: https://elchupanibrei.livejournal.com/36239.html

AleksBAM
Offline
Зарегистрирован: 13.09.2014

Добрый вечер.

Вопрос. Как можно изменить частоту измерения освещенности по фото датчику. А то видно как пульсируем яркость дисплея. 

AleksBAM
Offline
Зарегистрирован: 13.09.2014

И еще вопрос.

Как скорректировать показания температуры и влажности в помещении. Влажность занижается, а температура завышается.

vk007
Offline
Зарегистрирован: 16.06.2015

AleksBAM пишет:
Как можно изменить частоту измерения освещенности по фото датчику. А то видно как пульсируем яркость дисплея.

Я уже упоминал о решении подобной проблемы. Скорее всего идет засветка датчика от матрицы. Вобщем, читайте сообщение #44., там я расписал, что надо подправить.

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

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

Доюрый день!

Как отключить отображение напряжения батарейки? оно тут лишнее как мне кажется

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

еще минимальнои и максимальную температуру с временем. тоже лишнее имхо.

 

Radon
Offline
Зарегистрирован: 19.02.2018

Gavrilov_S пишет:

еще минимальнои и максимальную температуру с временем. тоже лишнее имхо.

 

Так отредактируйте скетч и все дела. Уберите ненужные Вам.

yucan
Offline
Зарегистрирован: 20.04.2015

Gavrilov_S пишет:

Доюрый день!

Как отключить отображение напряжения батарейки? оно тут лишнее как мне кажется

Проверьте состояние батарейки. Если напряжение батарейки в норме - отображение отключено.

Минимальную и максимальную температуру тоже легко отлючить. В скетче есть комментарии. 

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

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

с отключениями мин макс темперетурой попробую покопаться. я в програмирование не силен. знаю только Basic и Pascal они совсем не похожи 

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

нашел как отключить мин. макс. температуру. сам написал комментарий :)

     // Tminmax(0); //включение отображения макс. темпереатуры
     // Tminmax(1); //включение отображения мин. темпереатуры
Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

мне тоже кажется что бегущая строка слишком быстро пробегает. куда там нужна delay() напписать чтобы задержку включить? :)

bwn
Offline
Зарегистрирован: 25.08.2014

Gavrilov_S пишет:

 я в програмирование не силен. знаю только Basic и Pascal они совсем не похожи 

Та ну напуй.

vk007
Offline
Зарегистрирован: 16.06.2015

Gavrilov_S пишет:

мне тоже кажется что бегущая строка слишком быстро пробегает. куда там нужна delay() напписать чтобы задержку включить? :)

Тут еще и двух страниц не набралось, а вы ленитесь прочитать предыдущие комментарии. Смотрите #14.

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

vk007, добавил паузу. Все отлично! Теперь хочу график отключить. Он мне без надобности :) достаточно атмосферное давление показать

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

vk007, забыл сказать Большое Спасибо за суперские часы! 

Давно искал такие! На матрицах! У других функционал слабоват и шрифты не красивые!

Моим друзьям тоже понравилась! Уже двое захотели себе собрать. :)

bwn
Offline
Зарегистрирован: 25.08.2014

Gavrilov_S пишет:

Ну зачем так орать? Вроде давно на форуме, знаете, что так здесь не принято.((((

vk007
Offline
Зарегистрирован: 16.06.2015

Gavrilov_S пишет:
Теперь хочу график отключить. Он мне без надобности :) достаточно атмосферное давление показать

Найдите в скетче блок, отвечающий за вывод давления на экран - он начинается с комментария

// +++++ Давление +++++

И вместо него вставьте следующий код

01if(bmpOK && (AlarmNotActive() || !beeper))
02{
03  StringToSprite("\xAE\xA0mmHg");
04  ScrollVertical(0, 31, 0);
05  delay(500);
06  PresToString(0.51 + BMP.readPressure() * 760.0 / 101325.0);
07  StringToSprite(string_buffer);
08  ScrollVertical(9, 31, 1);
09  delay(1000);
10  for(byte i = 0; i < 32; i++) sprite_buffer[i] = 0;
11}

Также из сетапа можете удалить блок

if(bmpOK)
{
  ...
}

 

yucan
Offline
Зарегистрирован: 20.04.2015

А по моему важно знать не само значение давления (на меня оно никак не влияет), а то, как оно меняется, чтобы прикидывать изменение погоды. Поэтому график нужен. Тем более время его показа незначительно.

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

Спассбо за помощ!

Скетч подправил! Все работает как надо! Без графика! 

Yucan, Если нужен график то его можно включить обратно. строки программы только закоментировал на всякий случай

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

vk007, еще одну хотелку хочу поставить! 

Чтобы каждый час когда например 12-00. на любой свободный порт логическая 1 появилась на 30 секунд(время буду точно подбирать потом по секундно) . буду мп3 плеер подключать

 

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

будет типа кукушки :)

vk007
Offline
Зарегистрирован: 16.06.2015

Gavrilov_S пишет:

еще одну хотелку хочу поставить! 

Дык, кто ж вам запретит - делайте! Спрашивать разрешение не обязательно. Можете даже не одну сделать, а вообще перекроить скетч под свои желания. С тем функционалом, что там уже есть, пара килобайт кода туда еще влезет, а если скетч еще и прооптимизировать...

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

***

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

я только паять умею 

Добрые люди! Помогите пожалуйста с кодом. Чтобы в каждый час (пример в 12-00, 13-00 и.т.д.) на свободном порте появилась логическая "1" на 30 секунд (чтобы это время можно было настраивать) для Кукушки

Заранее Спасибо!

vk007
Offline
Зарегистрирован: 16.06.2015

Gavrilov_S пишет:
я только паять умею

За почти 4 года, что Вы на форуме можно было бы уже хоть немного научиться программировать. Ну правда.

Могу дать направление, в каком стоит попробовать двигаться. В DS3231 есть аж 2 будильника, и оба они свободные (в часах я писал свои будильники). Любой из них может быть настроен на срабатывание в том числе ежечасно. Вот и воспользуйтесь этим. При срабатывании будильника на ноге SQW модуль выставит низкий уровень (при соответствующей настройке). Этот сигнал можно использовать как прерывание для ардуинки. Ну а дальше чистый полет фантазии...

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

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

а нельзя как на бейсике. что то подобное

где h - Это часы

if h=0 then let pin5=1 

else

if h=1 then let pin5=1

else

if h=2 then let pin5=1

else 

if h=3 then let pin5=1

 

 

 

vk007
Offline
Зарегистрирован: 16.06.2015

Gavrilov_S пишет:

а нельзя как на бейсике

Можно как угодно, но алгоритм и логика ведь должны быть в любом случае правильными.

А по вашей логике "pin5=1" будет вечно, чему бы h не равнялось.

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

ну да. верно. логика тут не верна. я просто накинул. что можно простым способом переделать. и часы не нужно сканировать. надо сканировать минуты и секунды.

типа

h=часы, m=минуты, sek=секунды, kuku=включение и отключение кукушки

так вроде должа работать 30 секунд каждый час. 

if m=0 and sek<=30 then kuku=1 else kuku=0

 

а если нужно только в определенные часы то 

if h=1 and m=0 and sek<=30 then kuku=1 else kuku=0

if h=12 and m=0 and sek<=30 then kuku=1 else kuku=0

vk007
Offline
Зарегистрирован: 16.06.2015

Ну сейчас да. Почти так, как вам нужно.

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

Представьте, сейчас 11:59:55 и тут наступает время отображения дополнительной информации: дата, обе температуры, влажность, давление. И даже если вы у себя отключили вывод макс/мин температуры и график давления, то все-равно за 5 сек эта инфа не успеет пробежать по экрану. А если еще учесть, что вы уменьшили скорость скроллинга? А если это последние дни декабря и добавится "до нового года осталось..."? А если разряжена батарейка? А если день рождения любимой тещи и вы так ее любите, что накатали текст поздравлялки, который еле влез во все 16 сообщений? Тут не то что 5 сек, тут и нескольких минут не хватит.

Даже если использовать флаг (было ку-ку в этом часу или нет) и куковать уже после вывода доп.инфы - ну вернутся часы на отображение времени, скажем, в 12:03 и выдадут "ку-ку". Как будет выглядеть это "ку-ку" в 12:03 этой слегка подгулявшей кукушки?

Т.е., или в корне переделывать скетч на короткий луп, что немного хлопотно (проще написать все с нуля), или воспользоваться средствами DS3231, как я описывал немного раньше, что проще - досточно добавить нужную настройку будильника DS3231 и завести функцию обработки прерывания от будильника. Будильник DS3231 не зависит от нашего лупа и сработает в нужное время, не зависимо, что будет на экране наших часов.

vk007
Offline
Зарегистрирован: 16.06.2015

Но есть еще один вариант. Велосипедный, но вариант - сделать так, как в скетче сделаны будильники.

Будильник в часах, работающих от сети 220В, то еще удовольствие. Если не было электричества, когда должен был сработать будильник - значит проспал. Поэтому будильники делались одними из последних по принципу "пускай уже будет" и все переделывать на короткий луп уже не хотелось. Еще я не хотел привязываться к какому-то конкретному модулю RTC - DS1307 или DS3231. Т.к. в DS1307, насколько мне известно (могу и ошибаться), нет выхода прерывания. А хотелось, чтобы была возможность сделать часы на обоих модулях. Потому и такой велосипед.

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

Можете пойти и таким путем, вместе с будильником проверять еще и нули минут и включать кукушку.

Только меня интересует один вопрос - зачем устанавливать на пине нужный уровень, длительностью определенное (да еще и изменяемое) время? MP3-модули ведь работают по команде, дали команду - проигрался файл.

Gavrilov_S
Offline
Зарегистрирован: 13.11.2014

плеер у меня простой. https://ru.aliexpress.com/item/TF-card-MP3-decoder-board-with-2W-power-d.... лог 1 нужна для имеено такого плеера. у него нет i2c только кнопки можно приделать. запишу туда 1 файл ку-ку т он будет включаться при появлении 5 вольт

Xer0x_king
Offline
Зарегистрирован: 22.06.2017

Кто может помочь? Выкинул sht, пытаюсь втиснуть bme280, в результате везде нули... ЧЯДНТ? куда копать?

0001/*** Dot Matrix Clock ************************************************************\
0002* Для нормального отображения кириллицы и пиктограмм необходимо перед компиляцией *
0003*       в файле preferences.txt установить preproc.substitute_unicode=false       *
0004\************************************************************************* VK007 */
0005 
0006#include <Wire.h>
0007#include <RTClib.h>
0008#include <Adafruit_BME280.h>
0009#include <OneWire.h>
0010#include <DallasTemperature.h>
0011#include <LedControl.h>
0012#include <EEPROM.h>
0013#include "fonts.h"
0014 
0015#define DS3231          // тип RTC (DS3231 или DS1307)
0016#define RUS             // язык (RUS, UKR, BLR, ENG)
0017 
0018#define ONE_WIRE_BUS 9  // уличный ds18b20
0019#define BEEPER_PIN   8  // пищалка (с генератором)
0020#define UP_PIN       7  // кнопка ВВЕРХ/ВПРАВО
0021#define OK_PIN       6  // кнопка ОК
0022#define DOWN_PIN     5  // кнопка ВНИЗ/ВЛЕВО
0023#define DIN_PIN      10 // DIN 1-й матрицы
0024#define CLK_PIN     11  // CLK матриц
0025#define CS_PIN      13  // CS матриц
0026#define BATT_PIN    A6  // напряжение батарейки часов
0027#define SENSOR_PIN  A7  // фотодатчик
0028 
0029#define TIME_SHOW_MSEC       30000UL    // продолжительность отображения времени (30 сек)
0030#define PRESSURE_UPDATE_MSEC 7200000UL  // интервал обновления графика давления (7 200 000 мс = 2 часа)
0031#define PRESSURE_VALUE_SHIFT 560        // разница между измеренным и сохраняемым значением давления
0032                                        // чтобы уместить в byte (диапазон значений давления 560 - 814 мм)
0033#define ALARMS_ADDR   256 // будильники (256..276 = 21 байт, 7х3 (вкл/выкл, часы, минуты)
0034#define MESSAGES_ADDR 288 // сообщения (288..1023 = 736 байт, 16х46 (день, месяц, текст до 44 символов))
0035 
0036const float voltage_divider_coefficient = 3.2; // коэффициент делителя напряжения
0037const float Vref = 1.043;   // опорное напряжение
0038 
0039OneWire oneWire(ONE_WIRE_BUS);
0040DallasTemperature Dallas(&oneWire);
0041#ifdef DS1307
0042  RTC_DS1307 RTC;
0043#else
0044  RTC_DS3231 RTC;
0045#endif
0046Adafruit_BME280 bme;
0047LedControl lc = LedControl(DIN_PIN, CLK_PIN, CS_PIN, 4);
0048DateTime NOW;
0049byte HOUR_OLD;
0050byte MINUTE_OLD;
0051boolean bmeOK, dallasOK;
0052boolean beeper = 1;
0053unsigned int counter;
0054byte pressure_addr;
0055char string_buffer[52];
0056byte screen_buffer[32];
0057byte sprite_buffer[255];
0058byte sprite_length;
0059float t_min, t_max;
0060byte t_min_h, t_min_m, t_max_h, t_max_m;
0061const char daysOfTheWeek[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
0062#ifdef RUS
0063  const char sunday[]      PROGMEM = "воскресенье";
0064  const char monday[]      PROGMEM = "понедельник";
0065  const char tuesday[]     PROGMEM = "вторник";
0066  const char wednesday[]   PROGMEM = "среда";
0067  const char thursday[]    PROGMEM = "четверг";
0068  const char friday[]      PROGMEM = "пятница";
0069  const char saturday[]    PROGMEM = "суббота";
0070  const char january[]     PROGMEM = "января";
0071  const char february[]    PROGMEM = "февраля";
0072  const char march[]       PROGMEM = "марта";
0073  const char april[]       PROGMEM = "апреля";
0074  const char may[]         PROGMEM = "мая";
0075  const char june[]        PROGMEM = "июня";
0076  const char july[]        PROGMEM = "июля";
0077  const char august[]      PROGMEM = "августа";
0078  const char september[]   PROGMEM = "сентября";
0079  const char october[]     PROGMEM = "октября";
0080  const char november[]    PROGMEM = "ноября";
0081  const char december[]    PROGMEM = "декабря";
0082  const char yyyy[]        PROGMEM = " года";
0083  const char message_UNY[] PROGMEM = " до Нового года";
0084  const char message_HNY[] PROGMEM = "С Новым годом!";
0085  const char days1[]       PROGMEM = " день";
0086  const char days2_4[]     PROGMEM = " дня";
0087  const char days5_0[]     PROGMEM = " дней";
0088  const char hours1[]      PROGMEM = " час";
0089  const char hours2_4[]    PROGMEM = " часа";
0090  const char hours5_0[]    PROGMEM = " часов";
0091  const char minutes1[]    PROGMEM = " минута";
0092  const char minutes2_4[]  PROGMEM = " минуты";
0093  const char minutes5_0[]  PROGMEM = " минут";
0094  const char seconds1[]    PROGMEM = " секунда";
0095  const char seconds2_4[]  PROGMEM = " секунды";
0096  const char seconds5_0[]  PROGMEM = " секунд";
0097#endif
0098 
0099const char* const months[] PROGMEM = {january, february, march, april, may, june, july, august, september, october, november, december};
0100const char* const dow[] PROGMEM = {sunday, monday, tuesday, wednesday, thursday, friday, saturday};
0101const char* const dimension[] PROGMEM = {days1, days2_4, days5_0, hours1, hours2_4, hours5_0, minutes1, minutes2_4, minutes5_0, seconds1, seconds2_4, seconds5_0};
0102 
0103//----------------------------------------------------------------------------------------------
0104// Установка яркости дисплея по фотодатчику
0105//----------------------------------------------------------------------------------------------
0106void SetBrightness()
0107{
0108  byte brightness = map(constrain(analogRead(SENSOR_PIN), 200, 900), 200, 900, 0, 15);
0109  for(byte i = 0; i < 4; i++)
0110   lc.setIntensity(i, brightness);
0111}
0112//----------------------------------------------------------------------------------------------
0113// Выводит на экран текущее время
0114// первый параметр - тип скролла:
0115// 0 - вывод без эффектов
0116// 1 - последняя цифра (единицы минут)
0117// 2 - две последние цифры (минуты)
0118// 3 - три последние цифры (единицы часов и минуты)
0119// 4 - все цифры (часы и минуты)
0120// 5 - вертикальный скролл каждой цифры по-очереди (начиная с первой слева)
0121// второй параметр - направление: 0 - вниз, 1 - вверх
0122//----------------------------------------------------------------------------------------------
0123void DisplayTime(byte scroll_mode, boolean dir)
0124{
0125  NOW = RTC.now();
0126  for(byte i = 0; i < 32; i++) sprite_buffer[i] = 0;
0127  for(byte i = 1; i < 7; i++)
0128    if(NOW.hour() / 10) sprite_buffer[i] = pgm_read_byte_near(font_digit + (NOW.hour() / 10) * 6 + i - 1);
0129  for(byte i = 8; i < 14; i++)
0130    sprite_buffer[i] = pgm_read_byte_near(font_digit + (NOW.hour() % 10) * 6 + i - 8);
0131  for(byte i = 18; i < 24; i++)
0132    sprite_buffer[i] = pgm_read_byte_near(font_digit + (NOW.minute() / 10) * 6 + i - 18);
0133  for(byte i = 25; i < 31; i++)
0134    sprite_buffer[i] = pgm_read_byte_near(font_digit + (NOW.minute() % 10) * 6 + i - 25);
0135  switch(scroll_mode)
0136  {
0137    case 0: { Show(0); break; }
0138    case 1: { ScrollVertical( 24, 31, dir); break; }
0139    case 2: { ScrollVertical( 17, 31, dir); break; }
0140    case 3: { ScrollVertical(  8, 31, dir); break; }
0141    case 4: { ScrollVertical(  0, 31, dir); break; }
0142    case 5:
0143    {
0144      sprite_buffer[15] = B01100110;
0145      sprite_buffer[16] = B01100110;
0146      ScrollVertical( 0,  7, dir);
0147      ScrollVertical( 8, 14, dir);
0148      ScrollVertical(15, 16, dir);
0149      ScrollVertical(17, 23, dir);
0150      ScrollVertical(24, 31, dir);
0151      break;
0152    }
0153  }
0154  HOUR_OLD = NOW.hour();
0155  MINUTE_OLD = NOW.minute();
0156}
0157//----------------------------------------------------------------------------------------------
0158// Возвращает адрес символа в массиве со шрифтом
0159//----------------------------------------------------------------------------------------------
0160const int char_addr[] = {1, 513, 646, 0, 1183, 184, 1117, 1149, 86, 1165, 100, 1201, 1141, 1123, 1174, 59, 451, 1192, 287, 511, 192, 1157, 1129, 1111, 9, 1135, 51, 1223, 0, 0, 0, 66};
0161int CharAddress(byte ccc)
0162{
0163  if(ccc < 32) return 0;
0164  if(ccc < 127) return (ccc - 32) * 7;
0165  if(ccc < 160) return 0;
0166  if(ccc < 192) return char_addr[ccc - 160];
0167  else return (ccc - 97) * 7;
0168}
0169//----------------------------------------------------------------------------------------------
0170// Формирует из строки изображение (сохраняет в массиве - sprite_buffer)
0171// второй параметр - промежуток между символами (в точках), необязательно (по умолчанию = 1)
0172//----------------------------------------------------------------------------------------------
0173void StringToSprite(char *str, byte space = 1)
0174{
0175  for(byte j = 0; j < sizeof(sprite_buffer); j++) sprite_buffer[j] = 0;
0176  byte i = 0;
0177  sprite_length = 0;
0178  while(str[i] != '\0' && sprite_length < sizeof(sprite_buffer))
0179  {
0180    for(byte j = 1; j <= pgm_read_byte_near(font + CharAddress(str[i])) && sprite_length < sizeof(sprite_buffer); j++)
0181      sprite_buffer[sprite_length++] = pgm_read_byte_near(font + CharAddress(str[i]) + j);
0182    for(byte j = 0; j < space && sprite_length < sizeof(sprite_buffer); j++)
0183      sprite_length++;
0184    if(space > 0 && str[i] == '7' && (str[i + 1] == '4' || str[i + 1] == ',' || str[i + 1] == '.'))
0185      sprite_length--;
0186    i++;
0187  }
0188  sprite_length -= space;
0189}
0190//----------------------------------------------------------------------------------------------
0191// Переводит значение температуры из float в строку нужного формата (сохраняет в string_buffer)
0192// второй параметр необязательный - добавлять или нет в конце строки символ 'C' (по умолчанию = 1)
0193//----------------------------------------------------------------------------------------------
0194void TempToString(float temp, boolean mode = 1)
0195{
0196  byte i = 1;
0197  byte int_temp = 0.051 + abs(temp);
0198  byte frac_temp = int((0.051 + abs(temp)) * 10) % 10;
0199  if(int_temp == 0 && frac_temp == 0)
0200    string_buffer[0] = '0';
0201  else
0202  {
0203    if(temp < 0)
0204      string_buffer[0] = '-';
0205    else
0206      string_buffer[0] = '+';
0207    if(int_temp > 9)
0208    {
0209      string_buffer[i++] = int_temp / 10 + '0';
0210      string_buffer[i++] = int_temp % 10 + '0';
0211    }
0212    else
0213      string_buffer[i++] = int_temp + '0';
0214    string_buffer[i++] = ',';
0215    string_buffer[i++] = frac_temp + '0';
0216  }
0217  string_buffer[i++] = '°';
0218  if(mode) string_buffer[i++] = 'C';
0219  string_buffer[i] = '\0';
0220}
0221//----------------------------------------------------------------------------------------------
0222// Переводит значение влажности из float в строку нужного формата (сохраняет в string_buffer)
0223//----------------------------------------------------------------------------------------------
0224void HumToString(float hum)
0225{
0226  string_buffer[0] = '\xB7';
0227  string_buffer[1] = '\xA0';
0228  byte i = 2;
0229  byte int_hum = 0.51 + hum;
0230  if(int_hum > 9)
0231  {
0232    string_buffer[i++] = int_hum / 10 + '0';
0233    string_buffer[i++] = int_hum % 10 + '0';
0234  }
0235  else
0236    string_buffer[i++] = int_hum + '0';
0237  string_buffer[i] = '%';
0238  string_buffer[i + 1] = 'R';
0239  string_buffer[i + 2] = 'H';
0240  string_buffer[i + 3] = '\0';
0241}
0242//----------------------------------------------------------------------------------------------
0243// Переводит значение давления из int в строку нужного формата (сохраняет в string_buffer)
0244//----------------------------------------------------------------------------------------------
0245void PresToString(int int_pres)
0246{
0247  string_buffer[0] = '\xAE';
0248  string_buffer[1] = ' ';
0249  string_buffer[4] = int_pres % 10 + '0';
0250  int_pres = int_pres / 10;
0251  string_buffer[3] = int_pres % 10 + '0';
0252  string_buffer[2] = int_pres / 10 + '0';
0253  string_buffer[5] = '\0';
0254}
0255//----------------------------------------------------------------------------------------------
0256// Вспомогательная функция для Tminmax() - центрирует картинку с температурой/временем
0257//----------------------------------------------------------------------------------------------
0258void ShiftT(boolean mode)
0259{
0260  byte shift = (26 - sprite_length)/2 + 6;
0261  for(byte i = 1; i <= sprite_length; i++)
0262    sprite_buffer[sprite_length + shift - i] = sprite_buffer[sprite_length - i];
0263  for(byte i = 5; i < shift; i++)
0264    sprite_buffer[i] = 0;
0265  for(byte i = 0; i < 5; i++)
0266    sprite_buffer[i] = pgm_read_byte_near(font + CharAddress(mode ? '¶' : '№') + i + 1);
0267  sprite_length += shift;
0268}
0269//----------------------------------------------------------------------------------------------
0270// Выводит на экран минимальную/максимальную температуру
0271// параметр - 0 (min температура), 1 (max температура)
0272//----------------------------------------------------------------------------------------------
0273void Tminmax(boolean mode)
0274{
0275  TempToString(mode ? t_max : t_min, 0);
0276  StringToSprite(string_buffer);
0277  ShiftT(mode);
0278  for(byte i = 0; i < 8; i++)
0279  {
0280    ScrollVerticalOneRow(0, 5, mode);
0281    ScrollVerticalOneRow(6, 31, !mode);
0282    Show(0);
0283  }
0284  delay(1500);
0285  string_buffer[0] = (mode ? t_max_h : t_min_h) / 10 + '0';
0286  string_buffer[1] = (mode ? t_max_h : t_min_h) % 10 + '0';
0287  string_buffer[2] = ':';
0288  string_buffer[3] = (mode ? t_max_m : t_min_m) / 10 + '0';
0289  string_buffer[4] = (mode ? t_max_m : t_min_m) % 10 + '0';
0290  string_buffer[5] = 0;
0291  StringToSprite(string_buffer);
0292  ShiftT(mode);
0293  ScrollVertical(6, 31, !mode);
0294  delay(1500);
0295}
0296//----------------------------------------------------------------------------------------------
0297// Используется для вывода в бегущей строке "обратного отсчета"
0298//----------------------------------------------------------------------------------------------
0299void ShowCountdown(byte num, byte ndim)
0300{
0301  string_buffer[0] = num < 10 ? ' ' : num / 10 + '0';
0302  string_buffer[1] = num % 10 + '0';
0303  string_buffer[2] = 0;
0304  StringToSprite(string_buffer);
0305  ScrollBufferToScreen(0);
0306  #ifdef ENG
0307    if(num != 1) ndim++;
0308  #else
0309    if(string_buffer[1] > '1' && string_buffer[1] < '5' && string_buffer[0] != '1') ndim++;
0310    else if(string_buffer[1] > '4' || string_buffer[1] == '0' || string_buffer[0] == '1') ndim += 2;
0311  #endif
0312  strcpy_P(string_buffer, (char*)pgm_read_word(&(dimension[ndim])));
0313  StringToSprite(string_buffer);
0314  ScrollBufferToScreen(0);
0315}
0316//----------------------------------------------------------------------------------------------
0317// Выводит изображение из спрайт-буфера на экран со сдвигом влево всего содержимого (экран + спрайт-буфер)
0318// если указан параметр 0, то на экране останется "хвост" картинки/текста
0319// если 1 - то картинка полностью "уедет" за левый край экрана
0320//----------------------------------------------------------------------------------------------
0321void ScrollBufferToScreen(boolean mode)
0322{
0323  for(byte i = 0; i < sprite_length; i++)
0324    ScrollScreenBufferOneLeft(1);
0325  if(mode)
0326    for(byte i = 0; i < 32; i++)
0327      ScrollScreenBufferOneLeft(0);
0328}
0329//----------------------------------------------------------------------------------------------
0330// Выводит в центр экрана изображение из спрайт-буфера (со сдвигом влево)
0331//----------------------------------------------------------------------------------------------
0332void SpriteToCenterScreen()
0333{
0334  for(byte i = 0; i <= (32 - sprite_length) / 2; i++)
0335    ScrollScreenBufferOneLeft(0);
0336  for(byte i = 0; i < sprite_length / 2 + 16; i++)
0337    ScrollScreenBufferOneLeft(1);
0338}
0339//----------------------------------------------------------------------------------------------
0340// Сдвигает экран на одну точку влево
0341// если параметр 0, то сдвигается только экран (последняя колонка очищается)
0342// если 1 - то сдвигается экран + спрайт-буфер (в последнюю колонку переносятся данные из спрайт-буфера)
0343//----------------------------------------------------------------------------------------------
0344void ScrollScreenBufferOneLeft(boolean mode)
0345{
0346  for(byte row = 0; row < 32; row++)
0347    screen_buffer[row] = screen_buffer[row + 1];
0348  if(mode)
0349  {
0350    screen_buffer[31] = sprite_buffer[0];
0351    for(byte i = 0; i < sprite_length - 1; i++)
0352    {
0353      sprite_buffer[i] = sprite_buffer[i + 1];
0354      sprite_buffer[i + 1] = 0;
0355    }
0356  }
0357  else
0358    screen_buffer[31] = 0;
0359  Show(0);
0360}
0361//----------------------------------------------------------------------------------------------
0362// Вертикальный скролл на один ряд экранного буфера (без отображения на экране)
0363// данные из экранного буфера вытесняются данными из аналогичной области спрайт-буфера (ячейки 0 - 31)
0364// первые два параметра - с какой колонки скроллировать и по какую (0...31)
0365// третий - направление: 1 - вверх, 0 - вниз
0366//----------------------------------------------------------------------------------------------
0367void ScrollVerticalOneRow(byte from, byte to, boolean dir)
0368{
0369  for(byte row = from; row < to + 1; row++)
0370  {
0371    if(dir)
0372    {
0373      screen_buffer[row] >>= 1;
0374      bitWrite(screen_buffer[row], 7, bitRead(sprite_buffer[row], 0));
0375      sprite_buffer[row] >>= 1;
0376    }
0377    else
0378    {
0379      screen_buffer[row] <<= 1;
0380      bitWrite(screen_buffer[row], 0, bitRead(sprite_buffer[row], 7));
0381      sprite_buffer[row] <<= 1;
0382    }
0383  }
0384}
0385//----------------------------------------------------------------------------------------------
0386// Вертикальный скролл (на все 8 точек) части экрана
0387// данные на экране (из экранного буфера) вытесняются данными из аналогичной области спрайт-буфера (ячейки 0 - 31)
0388// первые два параметра - с какой колонки скроллировать и по какую (0...31)
0389// третий - направление: 1 - вверх, 0 - вниз
0390//----------------------------------------------------------------------------------------------
0391void ScrollVertical(byte from, byte to, boolean dir)
0392{
0393  for(byte i = 0; i < 8; i++)
0394  {
0395    ScrollVerticalOneRow(from, to, dir);
0396    Show(0);
0397  }
0398}
0399//----------------------------------------------------------------------------------------------
0400// Вывод содержимого буфера на экран
0401// параметр - тип буфера: 0 - из экранного, 1 - из спрайт-буфера
0402//----------------------------------------------------------------------------------------------
0403void Show(boolean mode)
0404{
0405  for(byte row = 0; row < 32; row++)
0406    lc.setRow(row / 8, row % 8, mode ? sprite_buffer[row] : screen_buffer[row]);
0407}
0408//----------------------------------------------------------------------------------------------
0409// Мерцание определенной матрицы (0...3)
0410//----------------------------------------------------------------------------------------------
0411void Blink(byte n)
0412{
0413  byte c_brightness = 0;
0414  char direction = 1;
0415  while(digitalRead(OK_PIN) && digitalRead(UP_PIN) && digitalRead(DOWN_PIN))
0416  {
0417    byte brightness = map(constrain(analogRead(SENSOR_PIN), 200, 900), 200, 900, 0, 15);
0418    for(byte i = 0; i < 4; i++) lc.setIntensity(i, n == i ? c_brightness : brightness);
0419    if(c_brightness == 0) direction = 1;
0420    if(c_brightness == 15) direction = -1;
0421    c_brightness += direction;
0422    delay(20);
0423  }
0424}
0425//----------------------------------------------------------------------------------------------
0426// Мигающий курсор
0427// в параметрах - начало и конец курсора (0...31)
0428//----------------------------------------------------------------------------------------------
0429void Cursor(byte from, byte to)
0430{
0431  boolean on = 1;
0432  while(digitalRead(OK_PIN) && digitalRead(UP_PIN) && digitalRead(DOWN_PIN))
0433  {
0434    counter = 0;
0435    if((millis() % 500 < 250) == on)
0436    {
0437      for(byte i = from; i <= to; i++)
0438        lc.setLed(i / 8, i % 8, 0, on);
0439      on = !on;
0440    }
0441    SetBrightness();
0442  }
0443}
0444//----------------------------------------------------------------------------------------------
0445// Выдает короткий звук (щелчок) на пищалку
0446//----------------------------------------------------------------------------------------------
0447void Beep()
0448{
0449  digitalWrite(BEEPER_PIN, 1);
0450  delay(2);
0451  digitalWrite(BEEPER_PIN, 0);
0452}
0453//----------------------------------------------------------------------------------------------
0454// Выдает короткий звук (щелчок) на пищалку и ждет отпускания кнопки
0455//----------------------------------------------------------------------------------------------
0456void ButtonPressed()
0457{
0458  Beep();
0459  while(!digitalRead(OK_PIN) || !digitalRead(UP_PIN) || !digitalRead(DOWN_PIN)) delay(50);
0460}
0461//----------------------------------------------------------------------------------------------
0462// Проверка будильника
0463//----------------------------------------------------------------------------------------------
0464boolean AlarmNotActive()
0465{
0466  NOW = RTC.now();
0467  return !(EEPROM.read(NOW.dayOfTheWeek() * 3 + ALARMS_ADDR) == 1 && NOW.hour() == EEPROM.read(NOW.dayOfTheWeek() * 3 + ALARMS_ADDR + 1) && NOW.minute() == EEPROM.read(NOW.dayOfTheWeek() * 3 + ALARMS_ADDR + 2));
0468}
0469//----------------------------------------------------------------------------------------------
0470// Настройка времени
0471//----------------------------------------------------------------------------------------------
0472void SetupTime()
0473{
0474  NOW = RTC.now();
0475  char HOUR = NOW.hour();
0476  char MINUTE = NOW.minute();
0477  byte pos = 0;
0478  while(pos < 2)
0479  {
0480    string_buffer[0] = ' ';
0481    string_buffer[1] = ' ';
0482    byte i = 2;
0483    if(HOUR / 10 == 1) string_buffer[i++] = '\xA0';
0484    string_buffer[i++] = HOUR / 10 + '0';
0485    string_buffer[i++] = HOUR % 10 + '0';
0486    if(HOUR % 10 == 1) string_buffer[i++] = '\xA0';
0487    string_buffer[i++] = ':';
0488    if(MINUTE / 10 == 1) string_buffer[i++] = '\xA0';
0489    string_buffer[i++] = MINUTE / 10 + '0';
0490    string_buffer[i++] = MINUTE % 10 + '0';
0491    string_buffer[i] = '\0';
0492    StringToSprite(string_buffer);
0493    Show(1);
0494    Cursor(pos == 0 ? 6 : 18, pos == 0 ? 14 : 26);
0495    if(!digitalRead(UP_PIN))
0496    {
0497      if(!counter) Beep();
0498      if(!counter || counter > 4)
0499      {
0500        if(pos == 0)
0501        {
0502          HOUR++; if(HOUR > 23) HOUR = 0;
0503        }
0504        else
0505        {
0506          MINUTE++; if(MINUTE > 59) MINUTE = 0;
0507        }
0508      }
0509      counter++;
0510      delay(100);
0511    }
0512    if(!digitalRead(DOWN_PIN))
0513    {
0514      if(!counter) Beep();
0515      if(!counter || counter > 4)
0516      {
0517        if(pos == 0)
0518        {
0519          HOUR--; if(HOUR < 0) HOUR = 23;
0520        }
0521        else
0522        {
0523          MINUTE--; if(MINUTE < 0) MINUTE = 59;
0524        }
0525      }
0526      counter++;
0527      delay(100);
0528    }
0529    if(!digitalRead(OK_PIN))
0530    {
0531      ButtonPressed();
0532      pos++;
0533    }
0534  }
0535  NOW = RTC.now();
0536  RTC.adjust(DateTime(NOW.year(), NOW.month(), NOW.day(), HOUR, MINUTE, 0));
0537}
0538//----------------------------------------------------------------------------------------------
0539// Настройка даты
0540//----------------------------------------------------------------------------------------------
0541void SetupDate()
0542{
0543  NOW = RTC.now();
0544  int YEAR = NOW.year();
0545  char MONTH = NOW.month();
0546  char DAY = NOW.day();
0547  byte pos = 0;
0548  while(pos < 3)
0549  {
0550    string_buffer[0] = '\xA0';
0551    byte i = 1;
0552    if(DAY / 10 == 1) string_buffer[i++] = '\xA0';
0553    string_buffer[i++] = DAY / 10 + '0';
0554    string_buffer[i++] = DAY % 10 + '0';
0555    if(DAY % 10 == 1) string_buffer[i++] = '\xA0';
0556    string_buffer[i++] = '\xA0';
0557    if(MONTH / 10 == 1) string_buffer[i++] = '\xA0';
0558    string_buffer[i++] = MONTH / 10 + '0';
0559    string_buffer[i++] = MONTH % 10 + '0';
0560    if(MONTH % 10 == 1) string_buffer[i++] = '\xA0';
0561    string_buffer[i++] = '\xA0';
0562    if((YEAR - 2000) / 10 == 1) string_buffer[i++] = '\xA0';
0563    string_buffer[i++] = (YEAR - 2000) / 10 + '0';
0564    string_buffer[i++] = (YEAR - 2000) % 10 + '0';
0565    string_buffer[i] = '\0';
0566    StringToSprite(string_buffer);
0567    Show(1);
0568    if(pos == 0) Cursor(1, 9);
0569    else
0570      if(pos == 1) Cursor(12, 20);
0571      else Cursor(23, 31);
0572    if(!digitalRead(UP_PIN))
0573    {
0574      if(!counter) Beep();
0575      if(!counter || counter > 4)
0576      {
0577        if(pos == 0)
0578        {
0579          DAY++; if(DAY > 31) DAY = 1;
0580        }
0581        else
0582        {
0583          if(pos == 1)
0584          {
0585            MONTH++; if(MONTH > 12) MONTH = 1;
0586          }
0587          else
0588          {
0589            YEAR++; if(YEAR > 2099) YEAR = 2000;
0590          }
0591        }
0592      }
0593      counter++;
0594      delay(100);
0595    }
0596    if(!digitalRead(DOWN_PIN))
0597    {
0598      if(!counter) Beep();
0599      if(!counter || counter > 4)
0600      {
0601        if(pos == 0)
0602        {
0603          DAY--; if(DAY < 1) DAY = 31;
0604        }
0605        else
0606        {
0607          if(pos == 1)
0608          {
0609            MONTH--; if(MONTH < 1) MONTH = 12;
0610          }
0611          else
0612          {
0613            YEAR--; if(YEAR < 2000 || YEAR > 2099) YEAR = 2099;
0614          }
0615        }
0616      }
0617      counter++;
0618      delay(100);
0619    }
0620    if(!digitalRead(OK_PIN))
0621    {
0622      ButtonPressed();
0623      pos++;
0624    }
0625  }
0626  if((DAY == 31 && (MONTH == 4 || MONTH == 6 || MONTH == 9 || MONTH == 11)) || (MONTH == 2 && ((DAY > 28 && (YEAR % 4)) || (DAY > 29 && !(YEAR % 4)))))
0627  {
0628    DAY = 1; MONTH++;
0629  }
0630  NOW = RTC.now();
0631  RTC.adjust(DateTime(YEAR, MONTH, DAY, NOW.hour(), NOW.minute(), NOW.second()));
0632}
0633//----------------------------------------------------------------------------------------------
0634// Настройка будильников
0635//----------------------------------------------------------------------------------------------
0636void SetupAlarms()
0637{
0638  byte ndow = 1;
0639  byte pos = 0;
0640  byte state = 0;
0641  char HOUR = 0;
0642  char MINUTE = 0;
0643  while(pos < 4)
0644  {
0645    if(pos == 0)
0646    {
0647      if(ndow == 8)
0648      {
0649        state = 0; HOUR = 0; MINUTE = 0;
0650      }
0651      else
0652      {
0653        state = EEPROM.read(ndow < 7 ? ndow * 3 + ALARMS_ADDR : ALARMS_ADDR) == 1;
0654        HOUR = EEPROM.read(ndow < 7 ? ndow * 3 + ALARMS_ADDR + 1 : ALARMS_ADDR + 1);
0655        MINUTE = EEPROM.read(ndow < 7 ? ndow * 3 + ALARMS_ADDR + 2 : ALARMS_ADDR + 2);
0656      }
0657    }
0658    if(HOUR < 0 || HOUR > 23 || MINUTE < 0 || MINUTE > 59)
0659    {
0660      HOUR = 0; MINUTE = 0;
0661    }
0662    string_buffer[0] = '!';
0663    string_buffer[1] = state ? '±' : '¤';
0664    byte i = 2;
0665    if(HOUR / 10 == 1) string_buffer[i++] = '\xA0';
0666    string_buffer[i++] = HOUR / 10 + '0';
0667    string_buffer[i++] = HOUR % 10 + '0';
0668    if(HOUR % 10 == 1) string_buffer[i++] = '\xA0';
0669    string_buffer[i++] = ':';
0670    if(MINUTE / 10 == 1) string_buffer[i++] = '\xA0';
0671    string_buffer[i++] = MINUTE / 10 + '0';
0672    string_buffer[i++] = MINUTE % 10 + '0';
0673    string_buffer[i] = '\0';
0674    StringToSprite(string_buffer);
0675    sprite_buffer[0] = ndow < 8 ? 1 << (ndow - 1) : B01111111;
0676    Show(1);
0677    switch(pos)
0678    {
0679      case 0: { Cursor(0, 1); break; }
0680      case 1: { Cursor(2, 9); break; }
0681      case 2: { Cursor(11, 19); break; }
0682      case 3: { Cursor(23, 31); break; }
0683    }
0684    if(!digitalRead(UP_PIN))
0685    {
0686      if(!counter) Beep();
0687      if(!counter || counter > 4)
0688      {
0689        switch(pos)
0690        {
0691          case 0: { ndow--; if(ndow < 1) ndow = 8; break; }
0692          case 1: { state = !state; break; }
0693          case 2: { HOUR++; if(HOUR > 23) HOUR = 0; break; }
0694          case 3: { MINUTE++; if(MINUTE > 59) MINUTE = 0; break; }
0695        }
0696      }
0697      counter++;
0698      delay(100);
0699    }
0700    if(!digitalRead(DOWN_PIN))
0701    {
0702      if(!counter) Beep();
0703      if(!counter || counter > 4)
0704      {
0705        switch(pos)
0706        {
0707          case 0: { ndow++; if(ndow > 8) ndow = 1; break; }
0708          case 1: { state = !state; break; }
0709          case 2: { HOUR--; if(HOUR < 0) HOUR = 23; break; }
0710          case 3: { MINUTE--; if(MINUTE < 0) MINUTE = 59; break; }
0711        }
0712      }
0713      counter++;
0714      delay(100);
0715    }
0716    if(!digitalRead(OK_PIN))
0717    {
0718      ButtonPressed();
0719      pos++;
0720    }
0721  }
0722  if(ndow == 7) ndow = 0;
0723  if(ndow < 7)
0724  {
0725    EEPROM.write(ndow * 3 + ALARMS_ADDR, state);
0726    EEPROM.write(ndow * 3 + ALARMS_ADDR + 1, HOUR);
0727    EEPROM.write(ndow * 3 + ALARMS_ADDR + 2, MINUTE);
0728  }
0729  else
0730    for(byte i = 0; i < 7; i++)
0731    {
0732      EEPROM.write(i * 3 + ALARMS_ADDR, state);
0733      EEPROM.write(i * 3 + ALARMS_ADDR + 1, HOUR);
0734      EEPROM.write(i * 3 + ALARMS_ADDR + 2, MINUTE);
0735    }
0736}
0737//----------------------------------------------------------------------------------------------
0738// Вспомогательная функция для SerialSetup()
0739//----------------------------------------------------------------------------------------------
0740void SetAlarmTime(byte n)
0741{
0742  EEPROM.write(n * 3 + ALARMS_ADDR + 1, string_buffer[8]);
0743  EEPROM.write(n * 3 + ALARMS_ADDR + 2, string_buffer[9]);
0744  Serial.print(F("Set time alarm "));
0745  Serial.print(n, DEC);
0746  Serial.print(F(" ("));
0747  Serial.print(daysOfTheWeek[n]);
0748  Serial.print(F("): "));
0749  Serial.print(string_buffer[3]);
0750  Serial.print(string_buffer[4]);
0751  Serial.print(":");
0752  Serial.print(string_buffer[5]);
0753  Serial.print(string_buffer[6]);
0754  if(string_buffer[7])
0755  {
0756    EEPROM.write(n * 3 + ALARMS_ADDR, string_buffer[7] == '+' ? 1 : 0);
0757    Serial.print(string_buffer[7] == '+' ? F(" on") : F(" off"));
0758  }
0759  Serial.println();
0760}
0761//----------------------------------------------------------------------------------------------
0762// Настройка через Serial
0763//----------------------------------------------------------------------------------------------
0764void SerialSetup()
0765{
0766  while(Serial.available()) Serial.read();
0767  Serial.setTimeout(50);
0768  Serial.println(F("Thhmmss - set time (e.g. T094500 = 09:45:00)"));
0769  Serial.println(F("DddMMyy - set date (e.g. D050418 = 05 Apr 2018)"));
0770  Serial.println(F("Ax=hhmmz - set alarm, x = 0(or 7) - 6 (Sun/Boc - Sat/Cy6) or * (all alarms)"));
0771  Serial.println(F("\t   z = + (on), - (off) or nothing (state does not change)"));
0772  Serial.println(F("\t   (e.g. A*=0700+ = set alarm on for 07:00 every day of the week)"));
0773  Serial.println(F("Ax+\t- alarm on"));
0774  Serial.println(F("Ax-\t- alarm off"));
0775  Serial.println(F("A\t- list of alarm clocks"));
0776  Serial.println(F("Mn=ddMMtext - save message (n = 0 - 9, A - F; text max 44 characters)"));
0777  Serial.println(F("\t  (e.g. M0=2509Happy Birthday!)"));
0778  Serial.println(F("Mn-\t- delete message"));
0779  Serial.println(F("Mn\t- show message"));
0780  Serial.println(F("M\t- list of messages"));
0781  Serial.println(F("OK\n"));
0782  StringToSprite("\xA9<\xAD>\xB5");
0783  Show(1);
0784  while(digitalRead(OK_PIN) && digitalRead(UP_PIN) && digitalRead(DOWN_PIN))
0785  {
0786    if(Serial.available())
0787    {
0788      delay(20);
0789      string_buffer[Serial.readBytes(string_buffer, sizeof(string_buffer) - 1)] = 0;
0790      delay(20);
0791      while(Serial.available()) { Serial.read(); delay(5); }
0792      Serial.println(string_buffer);
0793      boolean OK = 1;
0794      bitClear(string_buffer[0], 5);
0795      switch(string_buffer[0])
0796      {
0797        case 'T':
0798        {
0799          string_buffer[7] = (string_buffer[1] - '0') * 10 + (string_buffer[2] - '0');
0800          string_buffer[8] = (string_buffer[3] - '0') * 10 + (string_buffer[4] - '0');
0801          string_buffer[9] = (string_buffer[5] - '0') * 10 + (string_buffer[6] - '0');
0802          if(byte(string_buffer[7]) < 24 && byte(string_buffer[8]) < 60 && byte(string_buffer[9]) < 60)
0803          {
0804            NOW = RTC.now();
0805            RTC.adjust(DateTime(NOW.year(), NOW.month(), NOW.day(), string_buffer[7], string_buffer[8], string_buffer[9]));
0806            Serial.print(F("Set time: "));
0807            Serial.print(string_buffer[1]);
0808            Serial.print(string_buffer[2]);
0809            Serial.print(":");
0810            Serial.print(string_buffer[3]);
0811            Serial.print(string_buffer[4]);
0812            Serial.print(":");
0813            Serial.print(string_buffer[5]);
0814            Serial.println(string_buffer[6]);
0815          }
0816          else OK = 0;
0817          break;
0818        }
0819        case 'D':
0820        {
0821          string_buffer[7] = (string_buffer[1] - '0') * 10 + (string_buffer[2] - '0');
0822          string_buffer[8] = (string_buffer[3] - '0') * 10 + (string_buffer[4] - '0');
0823          string_buffer[9] = (string_buffer[5] - '0') * 10 + (string_buffer[6] - '0');
0824          if(string_buffer[7] > 0 && !(string_buffer[7] > 31 || (string_buffer[7] > 30 && (string_buffer[8] == 4 || string_buffer[8] == 6 || string_buffer[8] == 9 || string_buffer[8] == 11)) || (string_buffer[8] == 2 && ((string_buffer[7] > 28 && (string_buffer[9] % 4)) || (string_buffer[7] > 29 && !(string_buffer[9] % 4))))) && string_buffer[8] > 0 && string_buffer[8] < 13 && byte(string_buffer[9]) < 100)
0825          {
0826            NOW = RTC.now();
0827            RTC.adjust(DateTime(2000 + string_buffer[9], string_buffer[8], string_buffer[7], NOW.hour(), NOW.minute(), NOW.second()));
0828            Serial.print(F("Set date: "));
0829            Serial.print(string_buffer[1]);
0830            Serial.print(string_buffer[2]);
0831            Serial.print(".");
0832            Serial.print(string_buffer[3]);
0833            Serial.print(string_buffer[4]);
0834            Serial.print(F(".20"));
0835            Serial.print(string_buffer[5]);
0836            Serial.println(string_buffer[6]);
0837          }
0838          else OK = 0;
0839          break;
0840        }
0841        case 'A':
0842        {
0843          if(string_buffer[1] == 0)
0844            for(byte i = 1; i < 8; i++)
0845            {
0846              Serial.print(F("Alarm "));
0847              Serial.print(i, DEC);
0848              Serial.print(F(" ("));
0849              Serial.print(daysOfTheWeek[i % 7]);
0850              Serial.print(F("): "));
0851              string_buffer[2] = EEPROM.read(i % 7 * 3 + ALARMS_ADDR + 1);
0852              string_buffer[3] = EEPROM.read(i % 7 * 3 + ALARMS_ADDR + 2);
0853              if(byte(string_buffer[2]) < 24 && byte(string_buffer[3]) < 60)
0854              {
0855                if(string_buffer[2] < 10) Serial.print("0");
0856                Serial.print(string_buffer[2], DEC);
0857                Serial.print(":");
0858                if(string_buffer[3] < 10) Serial.print("0");
0859                Serial.print(string_buffer[3], DEC);
0860                Serial.println(EEPROM.read(i % 7 * 3 + ALARMS_ADDR) == 1 ? F(" on") : F(" off"));
0861              }
0862              else Serial.println(F("not set"));
0863            }
0864          else
0865          {
0866            string_buffer[1] = string_buffer[1] - '0';
0867            if(string_buffer[1] == 7) string_buffer[1] = 0;
0868            if(byte(string_buffer[1]) < 7 || string_buffer[1] == -6)
0869            {
0870              if(string_buffer[2] == '=')
0871              {
0872                string_buffer[8] = (string_buffer[3] - '0') * 10 + (string_buffer[4] - '0');
0873                string_buffer[9] = (string_buffer[5] - '0') * 10 + (string_buffer[6] - '0');
0874                if(byte(string_buffer[8]) < 24 && byte(string_buffer[9]) < 60 && (string_buffer[7] == '+' || string_buffer[7] == '-' || string_buffer[7] == 0))
0875                {
0876                  if(string_buffer[1] == -6) for(byte i = 0; i < 7; i++) SetAlarmTime(i);
0877                  else SetAlarmTime(string_buffer[1]);
0878                }
0879                else OK = 0;
0880              }
0881              else
0882              {
0883                if(string_buffer[2] == '+' || string_buffer[2] == '-')
0884                {
0885                  if(string_buffer[1] == -6)
0886                  {
0887                    for(byte i = 0; i < 7; i++) EEPROM.write(i * 3 + ALARMS_ADDR, string_buffer[2] == '+' ? 1 : 0);
0888                    Serial.print(F("All alarms "));
0889                  }
0890                  else
0891                  {
0892                    EEPROM.write(string_buffer[1] * 3 + ALARMS_ADDR, string_buffer[2] == '+' ? 1 : 0);
0893                    Serial.print(F("Alarm "));
0894                    Serial.print(string_buffer[1], DEC);
0895                    Serial.print(F(" ("));
0896                    Serial.print(daysOfTheWeek[string_buffer[1]]);
0897                    Serial.print(F(") "));
0898                  }
0899                  Serial.println(string_buffer[2] == '+' ? F("on") : F("off"));
0900                }
0901                else OK = 0;
0902              }
0903            }
0904            else OK = 0;
0905          }
0906          break;
0907        }
0908        case 'M':
0909        {
0910          if(string_buffer[1] == 0)
0911          {
0912            for(char i = 0; i < 16; i++)
0913            {
0914              Serial.print(F("Message "));
0915              Serial.print(i < 10 ? char(i + '0') : char(i - 10 + 'A'));
0916              Serial.print(F(": "));
0917              string_buffer[2] = EEPROM.read(i * 46 + MESSAGES_ADDR);
0918              string_buffer[3] = EEPROM.read(i * 46 + MESSAGES_ADDR + 1);
0919              if(string_buffer[2] > 0 && string_buffer[2] < 32 && string_buffer[3] > 0 && string_buffer[3] < 13)
0920              {
0921                if(string_buffer[2] < 10) Serial.print("0");
0922                Serial.print(string_buffer[2], DEC);
0923                Serial.print(".");
0924                if(string_buffer[3] < 10) Serial.print("0");
0925                Serial.print(string_buffer[3], DEC);
0926                Serial.print(" ");
0927                for(byte c = 0; c < 44; c++)
0928                {
0929                  char read_char = EEPROM.read(i * 46 + MESSAGES_ADDR + 2 + c);
0930                  if(read_char != 0) Serial.print(read_char);
0931                  else break;
0932                }
0933                Serial.println();
0934              }
0935              else Serial.println(F("not set"));
0936            }
0937          }
0938          else
0939          {
0940            if(string_buffer[1] >= 'a') bitClear(string_buffer[1], 5);
0941            if((string_buffer[1] >= '0' && string_buffer[1] <= '9') || (string_buffer[1] >= 'A' && string_buffer[1] <= 'F'))
0942            {
0943              string_buffer[1] = string_buffer[1] > '9' ? string_buffer[1] - 'A' + 10 : string_buffer[1] - '0';
0944              if(string_buffer[2] == '=')
0945              {
0946                string_buffer[0] = (string_buffer[3] - '0') * 10 + (string_buffer[4] - '0');
0947                string_buffer[2] = (string_buffer[5] - '0') * 10 + (string_buffer[6] - '0');
0948                if(string_buffer[0] > 0 && string_buffer[0] < 32 && string_buffer[2] > 0 && string_buffer[2] < 13 && string_buffer[7])
0949                {
0950                  EEPROM.write(string_buffer[1] * 46 + MESSAGES_ADDR, string_buffer[0]);
0951                  EEPROM.write(string_buffer[1] * 46 + MESSAGES_ADDR + 1, string_buffer[2]);
0952                  for(byte c = 0; c < 44; c++)
0953                  {
0954                    EEPROM.write(string_buffer[1] * 46 + MESSAGES_ADDR + 2 + c, string_buffer[c + 7]);
0955                    if(string_buffer[c + 7] == 0) break;
0956                  }
0957                  Serial.print(F("Message "));
0958                  Serial.print(string_buffer[1] < 10 ? char(string_buffer[1] + '0') : char(string_buffer[1] - 10 + 'A'));
0959                  Serial.println(F(" saved"));
0960                }
0961                else OK = 0;
0962              }
0963              else
0964              {
0965                if(string_buffer[2] == 0)
0966                {
0967                  if(EEPROM.read(string_buffer[1] * 46 + MESSAGES_ADDR) > 0 && EEPROM.read(string_buffer[1] * 46 + MESSAGES_ADDR) < 32 && EEPROM.read(string_buffer[1] * 46 + MESSAGES_ADDR + 1) > 0 && EEPROM.read(string_buffer[1] * 46 + MESSAGES_ADDR + 1) < 13)
0968                  {
0969                    string_buffer[sizeof(string_buffer) - 1] = string_buffer[1];
0970                    for(byte c = 0; c < 44; c++)
0971                    {
0972                      string_buffer[c] = EEPROM.read(string_buffer[sizeof(string_buffer) - 1] * 46 + MESSAGES_ADDR + 2 + c);
0973                      if(string_buffer[c] == 0) break;
0974                    }
0975                    StringToSprite(string_buffer);
0976                    ScrollBufferToScreen(1);
0977                    StringToSprite("\xA9<\xAD>\xB5");
0978                    Show(1);
0979                  }
0980                  else
0981                  {
0982                    Serial.print(F("Message "));
0983                    Serial.print(string_buffer[1] < 10 ? char(string_buffer[1] + '0') : char(string_buffer[1] - 10 + 'A'));
0984                    Serial.println(F(" not set"));
0985                  }
0986                }
0987                else
0988                {
0989                  if(string_buffer[2] == '-')
0990                  {
0991                    EEPROM.write(string_buffer[1] * 46 + MESSAGES_ADDR, 0);
0992                    Serial.print(F("Message "));
0993                    Serial.print(string_buffer[1] < 10 ? char(string_buffer[1] + '0') : char(string_buffer[1] - 10 + 'A'));
0994                    Serial.println(F(" deleted"));
0995                  }
0996                  else OK = 0;
0997                }
0998              }
0999            }
1000            else OK = 0;
1001          }
1002          break;
1003        }
1004        default: OK = 0;
1005      }
1006      Serial.println(OK ? F("OK\n") : F("Error\n"));
1007    }
1008  }
1009  ButtonPressed();
1010}
1011//==============================================================================================
1012void setup()
1013{
1014  Wire.begin();
1015  RTC.begin();
1016  Dallas.begin();
1017  dallasOK = Dallas.getDeviceCount();
1018  bme.begin();
1019    Serial.begin(57600);
1020  for(byte i = 0; i < 4; i++)
1021  {
1022    lc.shutdown(i, false);
1023    lc.clearDisplay(i);
1024  }
1025  SetBrightness();
1026  pinMode(DOWN_PIN, INPUT);
1027  pinMode(UP_PIN, INPUT);
1028  pinMode(OK_PIN, INPUT);
1029  pinMode(BEEPER_PIN, OUTPUT);
1030  #ifdef DS1307
1031    if(!RTC.isrunning())
1032    {
1033      SetupTime(); SetupDate();
1034    }
1035  #else
1036    if(RTC.lostPower())
1037    {
1038      SetupTime(); SetupDate();
1039    }
1040  #endif
1041  if(dallasOK)
1042  {
1043    Dallas.requestTemperatures();
1044    t_min = Dallas.getTempCByIndex(0);
1045    t_max = t_min;
1046    NOW = RTC.now();
1047    t_min_h = NOW.hour();
1048    t_min_m = NOW.minute();
1049    t_max_h = t_min_h;
1050    t_max_m = t_min_m;
1051  }
1052   
1053    int i = 0;
1054    while(i < 256)
1055    {
1056      if(EEPROM.read(i) == 255)
1057      {
1058        pressure_addr = i;
1059        break;
1060      }
1061      i++;
1062    }
1063}
1064//==============================================================================================
1065void loop()
1066{
1067  unsigned long current_millis = millis();
1068  DisplayTime(5, 0);
1069  boolean flag_blink = 1;
1070  if(!beeper) beeper = AlarmNotActive();
1071  unsigned long time_previous_millis = millis();
1072  while((millis() - time_previous_millis < TIME_SHOW_MSEC && digitalRead(UP_PIN) && digitalRead(DOWN_PIN)) || (beeper && !AlarmNotActive()))
1073  {
1074    SetBrightness();
1075    if((millis() - time_previous_millis) % 2000 < 1000)
1076    {
1077      if(!flag_blink)
1078      {
1079        lc.setRow(1, 7, B01100110);
1080        lc.setRow(2, 0, B01100110);
1081        flag_blink = 1;
1082        if(!AlarmNotActive() && beeper) digitalWrite(BEEPER_PIN, 1);
1083      }
1084    }
1085    else
1086    {
1087      if(flag_blink)
1088      {
1089        lc.setRow(1, 7, 0);
1090        lc.setRow(2, 0, 0);
1091        screen_buffer[15] = 0;
1092        screen_buffer[16] = 0;
1093        flag_blink = 0;
1094        NOW = RTC.now();
1095        if(MINUTE_OLD != NOW.minute())
1096          DisplayTime(1 + (MINUTE_OLD / 10 != NOW.minute() / 10) + (HOUR_OLD % 10 != NOW.hour() % 10) + (HOUR_OLD / 10 != NOW.hour() / 10), 1);
1097        digitalWrite(BEEPER_PIN, 0);
1098      }
1099    }
1100 
1101    if(beeper && (!digitalRead(OK_PIN) || !digitalRead(UP_PIN) || !digitalRead(DOWN_PIN)) && !AlarmNotActive())
1102    {
1103      beeper = 0;
1104      ButtonPressed();
1105    }
1106 
1107// +++++ Настройки +++++
1108    if(!digitalRead(OK_PIN))
1109    {
1110      ButtonPressed();
1111      StringToSprite("\xA9\xAC\xA7\xB5");
1112      ScrollVertical(0, 31, 1);
1113      for(byte i = 0; i < 32; i++) screen_buffer[i] = 0;
1114      byte pos_menu = 0;
1115      while(pos_menu < 4)
1116      {
1117        Blink(pos_menu);
1118        if(!digitalRead(UP_PIN))
1119        {
1120          ButtonPressed();
1121          pos_menu++;
1122        }
1123        if(!digitalRead(DOWN_PIN))
1124        {
1125          ButtonPressed();
1126          pos_menu--;
1127        }
1128        if(!digitalRead(OK_PIN))
1129        {
1130          ButtonPressed();
1131          SetBrightness();
1132          switch(pos_menu)
1133          {
1134            case 0: { SetupTime(); break; }
1135            case 1: { SetupDate(); break; }
1136            case 2: { SetupAlarms(); break; }
1137            case 3: { SerialSetup(); break; }
1138          }
1139        }
1140        StringToSprite("\xA9\xAC\xA7\xB5");
1141        Show(1);
1142      }
1143      SetBrightness();
1144      DisplayTime(5, 0);
1145      time_previous_millis = millis();
1146    }
1147  }
1148  digitalWrite(BEEPER_PIN, 0);
1149 
1150// +++++ Дата +++++
1151  if(AlarmNotActive() || !beeper)
1152  {
1153    StringToSprite("  ", 28);
1154    ScrollVertical(0, 31, 0);
1155    strcpy_P(string_buffer, (char*)pgm_read_word(&(dow[NOW.dayOfTheWeek()])));
1156    StringToSprite(string_buffer);
1157    ScrollBufferToScreen(0);
1158    StringToSprite("  ", 0);
1159    ScrollBufferToScreen(0);
1160    if(NOW.day() > 9)
1161    {
1162      string_buffer[0] = NOW.day() / 10 + '0';
1163      string_buffer[1] = NOW.day() % 10 + '0';
1164      string_buffer[2] = '\0';
1165    }
1166    else
1167    {
1168      string_buffer[0] = NOW.day() + '0';
1169      string_buffer[1] = '\0';
1170    }
1171    StringToSprite(string_buffer);
1172    ScrollBufferToScreen(0);
1173    StringToSprite(" \xA0");
1174    ScrollBufferToScreen(0);
1175    strcpy_P(string_buffer, (char*)pgm_read_word(&(months[NOW.month() - 1])));
1176    StringToSprite(string_buffer);
1177    ScrollBufferToScreen(0);
1178    string_buffer[0] = ' ';
1179    string_buffer[1] = '2';
1180    string_buffer[2] = '0';
1181    string_buffer[3] = (NOW.year() - 2000) / 10 + '0';
1182    string_buffer[4] = (NOW.year() - 2000) % 10 + '0';
1183    string_buffer[5] = '\0';
1184    StringToSprite(string_buffer);
1185    ScrollBufferToScreen(0);
1186    for (byte i = 0; i <= strlen_P(yyyy); i++)
1187      string_buffer[i] = pgm_read_byte_near(yyyy + i);
1188    StringToSprite(string_buffer);
1189    ScrollBufferToScreen(1);
1190  }
1191 
1192// +++++ Вывод сообщений +++++
1193  if(AlarmNotActive() || !beeper)
1194  {
1195    // отсчет до НГ
1196    if(NOW.month() == 12 && NOW.day() > 20)
1197    {
1198      DateTime NY (NOW.year() + 1, 1, 1, 0, 0, 0);
1199      unsigned long difference = NY.unixtime() - NOW.unixtime();
1200      byte difference_hour = difference / 3600;
1201      byte difference_min = difference / 60 % 60;
1202      byte difference_sec = difference % 60;
1203      if(NOW.day() == 31)
1204      {
1205        if(!difference_hour && !difference_min) ShowCountdown(difference_sec, 9);
1206        else
1207        {
1208          if(difference_hour)
1209          {
1210            ShowCountdown(difference_hour, 3);
1211            if(difference_min)
1212            {
1213              StringToSprite(" \xA0\xA0");
1214              ScrollBufferToScreen(0);
1215            }
1216          }
1217          if(difference_min) ShowCountdown(difference_min, 6);
1218        }
1219      }
1220      else ShowCountdown(31 - NOW.day(), 0);
1221      for (byte i = 0; i <= strlen_P(message_UNY); i++)
1222        string_buffer[i] = pgm_read_byte_near(message_UNY + i);
1223      StringToSprite(string_buffer);
1224      ScrollBufferToScreen(1);
1225    }
1226    // С Новым годом!
1227    if(NOW.day() == 1 && NOW.month() == 1)
1228    {
1229      for (byte i = 0; i <= strlen_P(message_HNY); i++)
1230        string_buffer[i] = pgm_read_byte_near(message_HNY + i);
1231      StringToSprite(string_buffer);
1232      ScrollBufferToScreen(1);
1233    }
1234    // Пользовательские сообщения из EEPROM
1235    boolean mes = 0;
1236    for(byte i = 0; i < 16 && AlarmNotActive(); i++)
1237    {
1238      if(NOW.day() == EEPROM.read(i * 46 + MESSAGES_ADDR) && NOW.month() == EEPROM.read(i * 46 + MESSAGES_ADDR + 1))
1239      {
1240        mes = 1;
1241        for(byte c = 0; c < 44; c++)
1242        {
1243          string_buffer[c] = EEPROM.read(i * 46 + MESSAGES_ADDR + 2 + c);
1244          if(string_buffer[c] == 0) break;
1245        }
1246        StringToSprite(string_buffer);
1247        ScrollBufferToScreen(0);
1248        StringToSprite(" \xA0\xA0");
1249        ScrollBufferToScreen(0);
1250      }
1251    }
1252    if(mes)
1253    {
1254      StringToSprite(" ", 0);
1255      ScrollBufferToScreen(1);
1256    }
1257  }
1258 
1259// +++++ Уличная температура +++++
1260  if(AlarmNotActive() || !beeper)
1261  {
1262    StringToSprite("\xA0\xA6\xA0\xBB");
1263    for(byte i = 0; i < 8; i++)
1264    {
1265      ScrollVerticalOneRow(0, 6, 1);
1266      ScrollVerticalOneRow(7, 31, 0);
1267      Show(0);
1268    }
1269    if(dallasOK)
1270    {
1271      Dallas.requestTemperatures();
1272      float temperature = Dallas.getTempCByIndex(0);
1273      TempToString(temperature);
1274      StringToSprite(string_buffer);
1275      SpriteToCenterScreen();
1276      delay(1000);
1277      if(NOW.hour() < t_min_h || NOW.hour() < t_max_h)
1278      {
1279        t_min = temperature;
1280        t_max = temperature;
1281        t_min_h = NOW.hour();
1282        t_min_m = NOW.minute();
1283        t_max_h = t_min_h;
1284        t_max_m = t_min_m;
1285      }
1286      else
1287      {
1288        if(t_min > temperature)
1289        {
1290          t_min = temperature;
1291          t_min_h = NOW.hour();
1292          t_min_m = NOW.minute();
1293        }
1294        if(t_max < temperature)
1295        {
1296          t_max = temperature;
1297          t_max_h = NOW.hour();
1298          t_max_m = NOW.minute();
1299        }
1300      }
1301      Tminmax(0);
1302      Tminmax(1);
1303    }
1304    else
1305    {
1306      delay(700);
1307      StringToSprite("???°C");
1308      SpriteToCenterScreen();
1309      delay(1000);
1310    }
1311  }
1312// +++++ Температура в помещении +++++
1313     
1314      StringToSprite("\xA0\xA6\xA0\xA0\xAB");
1315      for(byte i = 0; i < 8; i++)
1316      {
1317        ScrollVerticalOneRow(0, 6, 0);
1318        ScrollVerticalOneRow(7, 31, 1);
1319        Show(0);
1320      }
1321      delay(300);
1322      TempToString(bme.readTemperature());
1323      StringToSprite(string_buffer);
1324      SpriteToCenterScreen();
1325      delay(1000);
1326     
1327 
1328// +++++ Влажность в помещении +++++
1329     
1330      HumToString(bme.readHumidity());
1331      StringToSprite(string_buffer);
1332      for(byte i = 0; i < 8; i++)
1333      {
1334        ScrollVerticalOneRow(0, 6, 1);
1335        ScrollVerticalOneRow(7, 31, 0);
1336        Show(0);
1337      }
1338      delay(1500);
1339     
1340// +++++ Давление +++++
1341      static unsigned long pressure_previous_millis;
1342    if(current_millis - pressure_previous_millis >= PRESSURE_UPDATE_MSEC)
1343    {
1344      pressure_previous_millis = current_millis;
1345      EEPROM.write(pressure_addr++, (0.51 + bme.readPressure() * 760.0 / 101325.0) - PRESSURE_VALUE_SHIFT);
1346      EEPROM.write(pressure_addr, 255);
1347    }
1348    StringToSprite("\xAE\xA0mmHg");
1349    ScrollVertical(0, 31, 0);
1350    delay(500);
1351    PresToString(0.51 + bme.readPressure() * 760.0 / 101325.0);
1352    StringToSprite(string_buffer);
1353    ScrollVertical(9, 31, 1);
1354    delay(1000);
1355    float scale = 1.0;    // масштаб графика (для большого разброса)
1356    byte shift = 0;       // сдвиг к середине (для малого разброса)
1357    byte value = EEPROM.read(byte(pressure_addr - 1));
1358    byte pressure_min = value;
1359    byte pressure_max = value;
1360    for(byte i = 2; i < 24; i++)
1361    {
1362      value = EEPROM.read(byte(pressure_addr - i));
1363      if(pressure_min > value) pressure_min = value;
1364      if(pressure_max < value && value != 255) pressure_max = value;
1365    }
1366    byte pressure_range = pressure_max - pressure_min + 1;
1367    if(pressure_range > 8)
1368    {
1369      scale = 8.0 / pressure_range;
1370      shift = 0;
1371    }
1372    else
1373    {
1374      scale = 1.0;
1375      shift = 4 - (pressure_range / 2);
1376    }
1377    for(byte i = 1; i < 24; i++)
1378    {
1379      value = EEPROM.read(byte(pressure_addr - i));
1380      if(value != 255)
1381      {
1382        byte bar = scale * (value - pressure_min);
1383        sprite_buffer[32 - i] = 255 << (7 - bar - shift);
1384      }
1385      else sprite_buffer[32 - i] = i % 2 ? B00010000 : 0;
1386    }
1387    ScrollVertical(9, 31, 1);
1388    for(byte i = 0; i < 32; i++) sprite_buffer[i] = 0;
1389    delay(1200);
1390 
1391// +++++ Проверка батарейки RTC +++++
1392  if(AlarmNotActive() || !beeper)
1393  {
1394    analogReference(INTERNAL); for(byte i = 0; i < 4; i++) { analogRead(BATT_PIN); delay(20); }
1395    float Vbatt = analogRead(BATT_PIN) * Vref * voltage_divider_coefficient / 1023.0;
1396    analogReference(DEFAULT); for(byte i = 0; i < 4; i++) { analogRead(SENSOR_PIN); delay(20); }
1397    if(Vbatt <= 2.3 || !digitalRead(OK_PIN) || !digitalRead(UP_PIN) || !digitalRead(DOWN_PIN))
1398    {
1399      for(byte i = 0; i < 4; i++) string_buffer[i] = ' ';
1400      string_buffer[4] = '\xA0'; string_buffer[5] = '\xA0';
1401      byte v = Vbatt * 10;
1402      string_buffer[6] = v / 10 + '0';
1403      string_buffer[7] = ',';
1404      string_buffer[8] = v % 10 + '0';
1405      string_buffer[9] = 'V';
1406      string_buffer[10] = '\0';
1407      StringToSprite(string_buffer);
1408      sprite_buffer[1] = B00111110;
1409      for(byte i = 2; i < 10; i++) sprite_buffer[i] = B00100010;
1410      sprite_buffer[10] = B00110110;
1411      sprite_buffer[11] = B00011100;
1412      ScrollVertical(0, 31, 1);
1413      time_previous_millis = millis();
1414      while(millis() - time_previous_millis < 6000)
1415      {
1416        if(Vbatt > 2.3)
1417          for(byte i = 2; i <= map(constrain(v, 23, 36), 23, 36, 3, 10); i++)
1418            lc.setRow(i / 8, i % 8, B00111110);
1419        else
1420        {
1421          if((millis() % 2000 < 1000) == flag_blink)
1422          {
1423            lc.setRow(0, 2, flag_blink ? B00100010 : B00111110);
1424            lc.setRow(0, 3, flag_blink ? B00100010 : B00111110);
1425            flag_blink = !flag_blink;
1426          }
1427        }
1428        SetBrightness();
1429      }
1430    }
1431    ScrollVertical(0, 31, 0);
1432  }
1433}
1434//==============================================================================================

 

yucan
Offline
Зарегистрирован: 20.04.2015

Сам датчик с использованной вами библиотекой работает?

b707
Offline
Зарегистрирован: 26.05.2017

Xer0x_king пишет:

Кто может помочь? Выкинул sht, пытаюсь втиснуть bme280, в результате везде нули... ЧЯДНТ? куда копать?

Вы офигели? Думаете кто-то станет разбираться, что вы там изменили в 1400 строчках?

Кто ж сразу новое оборудование в огромную программу пихает? Надо было для начала написать проверочный скетч для датчика, короткий, строк на 15-20. Убедится, что все работает. Или, если не работает - искать ошибку.. Или спросить на форуме -со скетчем в 15 строк и форум бы выстрее помог.

 

Xer0x_king
Offline
Зарегистрирован: 22.06.2017

Блин.Я не фигел.
Одним подавай кусок кода, другим подавай весь код.Всем не угодишь. Кому то лень написать что то не унизительное?
И да, еси чо, я проверял датчик, загружал скетч. Татчик опрашивается и показывает значения в мониторе последовательного порта. Или вы думаете, что настолько руки обьектно ориентированы?

Xer0x_king
Offline
Зарегистрирован: 22.06.2017

Конечно работает. Проверял.Подозреваю, что ошибка кроется где то в внесении данных в буфер и выводе их на экран.

vk007
Offline
Зарегистрирован: 16.06.2015

Xer0x_king пишет:
Кто может помочь? Выкинул sht, пытаюсь втиснуть bme280, в результате везде нули... ЧЯДНТ? куда копать?

Дайте ссылку на используемую вами библиотеку Adafruit_BME280.

Xer0x_king
Offline
Зарегистрирован: 22.06.2017

Всем спасибо за помощь. Скетч рабочий. Единственное, что изменил: указал  адрес инициализации bme - bme.begin (0x76). Заменил ардуино и скомпилировал на другой машине.
Можно использовать в качестве замены при использовани bme280 вместо dht и sht.

AleksandrM
Offline
Зарегистрирован: 20.07.2017

Подскажите для  BMP180(280) питния 5 вольт не много? По даташиту у них максимум 4,25 вольта....

 

Xer0x_king
Offline
Зарегистрирован: 22.06.2017

Питание для БМП/бме идеально 3.3 вольта. Если юзаем нано-берем с ноги стабилизатора,если про мини-с амс117-3.3