Часы DS3231 не работает нормально скетч из примера

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

Доброго времени. Прошу помощи в понимании проблемы / кода.

Делаю метеостанцию на 128 меге. Решил добавить функцию будильника, т.к. жалко использовать память чипа только на 30% (в ардуину чуть не влезает). Решил использовать аппаратный будильник DS3231. Пришлось использовать другую библиотеку, т.к. в той, что использовал не было функций управления будильником.

Появилась проблема - не работает функция перевода даты, времени в текст.

Вот сам скетч из примера:

 

/*
  DS3231: Real-Time Clock. Date Format
  Read more: www.jarzebski.pl/arduino/komponenty/zegar-czasu-rzeczywistego-rtc-ds3231...
  GIT: https://github.com/jarzebski/Arduino-DS3231
  Web: http://www.jarzebski.pl
  (c) 2014 by Korneliusz Jarzebski
*/

#include <Wire.h>
#include <DS3231.h>

DS3231 clock;
RTCDateTime dt;

void setup()
{
  Serial.begin(9600);

  // Initialize DS3231
  Serial.println("Initialize DS3231");;
  clock.begin();

  // Set sketch compiling time
  clock.setDateTime(__DATE__, __TIME__);

  // Set from UNIX timestamp
  // clock.setDateTime(1397408400);

  // Manual (YYYY, MM, DD, HH, II, SS
  // clock.setDateTime(2014, 4, 13, 19, 21, 00);
}

void loop()
{
  dt = clock.getDateTime();

  Serial.print("Long number format:          ");
  Serial.println(clock.dateFormat("d-m-Y H:i:s", dt));

  Serial.print("Long format with month name: ");
  Serial.println(clock.dateFormat("d F Y H:i:s",  dt));

  Serial.print("Short format witch 12h mode: ");
  Serial.println(clock.dateFormat("jS M y, h:ia", dt));

  Serial.print("Today is:                    ");
  Serial.print(clock.dateFormat("l, z", dt));
  Serial.println(" days of the year.");

  Serial.print("Actual month has:            ");
  Serial.print(clock.dateFormat("t", dt));
  Serial.println(" days.");

  Serial.print("Unixtime:                    ");
  Serial.println(clock.dateFormat("U", dt));

  Serial.println();

  delay(1000);
}

Т.е. Serial.println(clock.dateFormat... - ничего не выводит.

Вот сама функция из библиотеки:

char const* DS3231::dateFormat(const char* dateFormat, RTCDateTime dt)
{
    char *buffer[255];

    buffer[0] = 0;

    char helper[11];

    while (*dateFormat != '\0')
    {
        switch (dateFormat[0])
        {
            // Day decoder
            case 'd':
                sprintf(helper, "%02d", dt.day); 
                strcat(*buffer, (const char *)helper); 
                break;
            case 'j':
                sprintf(helper, "%d", dt.day);
                strcat(*buffer, (const char *)helper);
                break;
            case 'l':
                strcat(*buffer, (const char *)strDayOfWeek(dt.dayOfWeek));
                break;
            case 'D':
                strncat(*buffer, strDayOfWeek(dt.dayOfWeek), 3);
                break;
            case 'N':
                sprintf(helper, "%d", dt.dayOfWeek);
                strcat(*buffer, (const char *)helper);
                break;
            case 'w':
                sprintf(helper, "%d", (dt.dayOfWeek + 7) % 7);
                strcat(*buffer, (const char *)helper);
                break;
            case 'z':
                sprintf(helper, "%d", dayInYear(dt.year, dt.month, dt.day));
                strcat(*buffer, (const char *)helper);
                break;
            case 'S':
                strcat(*buffer, (const char *)strDaySufix(dt.day));
                break;

            // Month decoder
            case 'm':
                sprintf(helper, "%02d", dt.month);
                strcat(*buffer, (const char *)helper);
                break;
            case 'n':
                sprintf(helper, "%d", dt.month);
                strcat(*buffer, (const char *)helper);
                break;
            case 'F':
                strcat(*buffer, (const char *)strMonth(dt.month));
                break;
            case 'M':
                strncat(*buffer, (const char *)strMonth(dt.month), 3);
                break;
            case 't':
                sprintf(helper, "%d", daysInMonth(dt.year, dt.month));
                strcat(*buffer, (const char *)helper);
                break;

            // Year decoder
            case 'Y':
                sprintf(helper, "%d", dt.year); 
                strcat(*buffer, (const char *)helper); 
                break;
            case 'y': sprintf(helper, "%02d", dt.year-2000);
                strcat(*buffer, (const char *)helper);
                break;
            case 'L':
                sprintf(helper, "%d", isLeapYear(dt.year)); 
                strcat(*buffer, (const char *)helper); 
                break;

            // Hour decoder
            case 'H':
                sprintf(helper, "%02d", dt.hour);
                strcat(*buffer, (const char *)helper);
                break;
            case 'G':
                sprintf(helper, "%d", dt.hour);
                strcat(*buffer, (const char *)helper);
                break;
            case 'h':
                sprintf(helper, "%02d", hour12(dt.hour));
                strcat(*buffer, (const char *)helper);
                break;
            case 'g':
                sprintf(helper, "%d", hour12(dt.hour));
                strcat(*buffer, (const char *)helper);
                break;
            case 'A':
                strcat(*buffer, (const char *)strAmPm(dt.hour, true));
                break;
            case 'a':
                strcat(*buffer, (const char *)strAmPm(dt.hour, false));
                break;

            // Minute decoder
            case 'i': 
                sprintf(helper, "%02d", dt.minute);
                strcat(*buffer, (const char *)helper);
                break;

            // Second decoder
            case 's':
                sprintf(helper, "%02d", dt.second); 
                strcat(*buffer, (const char *)helper); 
                break;

            // Misc decoder
            case 'U': 
                sprintf(helper, "%lu", dt.unixtime);
                strcat(*buffer, (const char *)helper);
                break;

            default: 
                strncat(*buffer, dateFormat, 1);
                break;
        }
        dateFormat++;
    }

    return *buffer;
}

По ней хотел спросить, как понимать switch (dateFormat[0])? Именно dateFormat[0].

Вот ссылка на библиотеку: https://github.com/jarzebski/Arduino-DS3231

Вот так у меня выглядит:

sadman41
Offline
Зарегистрирован: 19.10.2016

Возвращать из функции указатель на char[] - плохая идея.
Библиотеку рефакторить необходимо.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

А если 27ю строку раскоментировать?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Функция возвращает указатель на ЛОКАЛЬНЫЙ объект,  такое иногда работает, но как повезёт.                                                     

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

Сами часы работают, не работает преобразование числового формата в строку. Если выводить цифрами, то все ок.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

mir0tv0rec пишет:

не работает преобразование числового формата в строку. Если выводить цифрами, то все ок.

Так я же Вам написал в #3, что оно и должно через пень колоду работать и сказал в чём именно ошибка. Вы не поняли того поста?

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

Это, я понял, но не успел отредактировать сообщение. Хотел попросить пнуть в нужную сторону, т.к. я не программист и эти подробности для меня пока темный лес. Решение, я конечно найду со временем. Но хотелось бы сделать правильно.

rkit
Offline
Зарегистрирован: 23.11.2016

Прочитай документацию на sprintf и напиши сам. А этот кошмар забудь.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

mir0tv0rec пишет:

 темный лес ... Решение, я конечно найду со временем. 

Вам полезнее было бы разобраться с темнотой и решить проблему самостоятельно.

Но если хотите по-мартышечьи, то для начала попробуйте убедиться, что проблема о которой я написал единственная. Устраните её и попробуйте. Для устранения в функции DS3231::dateFormat замените строку №3 (по Вашей вставке) на 

static char buffer[255];

в везде (от слова ВЕЗДЕ) в этой функции замените "*buffer" на "buffer". Попробуйте, что из этого выйдет.

Если не заработает, не забудьте выложить свежий текст функции.

P.S. Я не считаю правильным выполнять такую операцию библиотечной функцией (за исключением случая, когда хочется чего-то совсем экзотического). Со стандартными преобразованиями вполне справляются штатные системные  средства без всяких сторонних библиотек.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rkit пишет:

Прочитай документацию на sprintf 

Зачем, когда вполне доступна strftime?

rkit
Offline
Зарегистрирован: 23.11.2016

Затем, что библиотека не возвращает struct tm. Затем, что начинать надо всё равно со sprintf.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rkit пишет:

Затем, что библиотека не возвращает struct tm. Затем, что начинать надо всё равно со sprintf.

С какого бодуна? Кто или что мешает struct tm создать? И зачем для этого sprintf?

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

ЕвгенийП, это я сделаю, но и попробую разобраться, как сделать правильно. Можно, еще вопрос, как понимать: switch (dateFormat[0]) - что значит dateFormat[0], это же вроде указатель на константу типа char.  В данном случае, я бы написал switch(*dateFormat), т.е. переключаемся в зависимости от значения по указателю.  Нет? 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

МЛЯ, ЭТО ТРОЛЛИНГ????

Посмотрел библиотеку ПО ВАШЕЙ ССЫЛКЕ! Там функция dateFormat тоже с ошибками, но не такая, как та, что Вы выложили!!! Вот её начало, сравните с тем, что у Вас в стартовом посте.

char* DS3231::dateFormat(const char* dateFormat, RTCDateTime dt)
{
    char buffer[255];

    buffer[0] = 0;

    char helper[11];

    while (*dateFormat != '\0')
    {
        switch (dateFormat[0])
        {
            // Day decoder
            case 'd':
                sprintf(helper, "%02d", dt.day); 
                strcat(buffer, (const char *)helper); 



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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

mir0tv0rec пишет:

dateFormat[0] - я бы написал *dateFormat

Это одно и то же. Пофиг, как писать.

Вы, сначала, ответьте на мой пост #13, а то я близок к тому, чтобы послать Вас нах.

rkit
Offline
Зарегистрирован: 23.11.2016

Ах да. Еще strftime в avr не умеет в локали.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rkit пишет:

Ах да. Еще strftime в avr не умеет в локали.

А, ну тогда понятно.

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

Заработало! Хотя в первом посту функция была из старой версии библиотеки, которую компилировал уже не было * у buffer. Добавление static помогло.  Можно еще вопрос или так сказать правильно ли я понимаю суть проблемы? Функция в данном случае возвращает указатель на локальную для ф-ции переменную/данные, которая уничтожается/перезаписывается при выходе из этой функции. Соответственно указатель может быть верным, если МК данные не перезаписал/стер или ссылаться не понятно на какие данные. Добавив static,  массив buffer сохраняет значение между вызовами функции. По-этому функция возвращает указатель правильные на данные? Т.е. можно было просто вынести переменную вне функции?

rkit
Offline
Зарегистрирован: 23.11.2016

mir0tv0rec пишет:

Т.е. можно было просто вынести переменную вне функции?

Да. Но вся функция написана кошмарным рукожопом и этого мало.

Green
Offline
Зарегистрирован: 01.10.2015

А мне вот интересно, КТО сочиняет все эти рукожопые библиотеки? И как бы их фильтровать по степени рукожопости? Ведь, иной раз, просто диву даёшься их кривости! Так может нужно создать какой то комитет, по аналогии с Мизулиной(?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Green пишет:

может нужно создать какой то комитет, по аналогии с Мизулиной(?

Для начала надо найти того, кто будет платить комитетчикам так, как платят Мизулиной. А без этого в комитете, боюсь, никого не окажется :-(

sadman41
Offline
Зарегистрирован: 19.10.2016

mir0tv0rec пишет:

Функция в данном случае возвращает указатель на локальную для ф-ции переменную/данные, которая уничтожается/перезаписывается при выходе из этой функции. Соответственно указатель может быть верным, если МК данные не перезаписал/стер или ссылаться не понятно на какие данные. Добавив static,  массив buffer сохраняет значение между вызовами функции. По-этому функция возвращает указатель правильные на данные? Т.е. можно было просто вынести переменную вне функции?

Указатель верный всегда. А вот областью памяти, на который указывает данный верный указатель, компилятор волен распоряжаться как угодно после выхода из функции - писать туда новые данные, очищать и пр. Static говорит компилятору - не трожь 255 байт. Поэтому в них ничего не изменяется. Но при этом система не может их использовать более разумно.

Классический подход - передать указатель на зарезервированную область памяти извне в функцию. Так, например, реализовано в https://www.cplusplus.com/reference/cstring/strcpy/ - функции передается указатель на destination и он же возвращается как результат действия (просто для удобства дальнейшего использования). 

Green
Offline
Зарегистрирован: 01.10.2015

ЕвгенийП пишет:

Для начала надо найти того, кто будет платить комитетчикам так, как платят Мизулиной. А без этого в комитете, боюсь, никого не окажется :-(


Это да. Об этом нужно думать глубже.( 

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

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

Убрал варнинги, которые были. Остался 1, не пойму, что не нравиться компилятору:
 

uint16_t days = t / 24;
    uint8_t leap;

    for (year = 0; ; ++year)
    {
        leap = year % 4 == 0;
        if (days < 365 + leap)
        {
            break;
        }
        days -= 365 + leap;
    }

 

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

if (days < 365 + leap)

~~~~~^~~~~~~~~~~~

Вроде все переменные unsigned. Или он считает, что days -= 365 + leap; может меньше 0 быть?

AlexTLN
Offline
Зарегистрирован: 14.05.2016

mir0tv0rec пишет:

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

if (days < 365 + leap)

~~~~~^~~~~~~~~~~~

Вроде все переменные unsigned. Или он считает, что days -= 365 + leap; может меньше 0 быть?

Возможно, что одна переменная 16бит, а вторая 8. 

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

"Выкинул" вообще эту функцию из библиотеки, все-равно точно использовать не буду.

Возник еще вопрос... Что не нравиться 

case 'M':
                strncat(buffer, (const char *)strMonth(dt.month), 3);
                break;

в 4ой строке "Short forman witch..." должно быть Ноя, а выводит это...

Из-за чего может быть? Кириллица не нравится?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

mir0tv0rec пишет:

Вроде все переменные unsigned. 

Не все.

По посту #13 пояснения будут?

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

По #13 посту пояснил в 14м. Просто пока создавал тему, обновил библиотеку... Все заработало.

Там все переменные uint8...16...32.
Если только компилятор думает, что при вычитании может быть отрицательное значение.

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

Скачал. Когда начал устанавливать библиотеку, ide написал, что она уже есть. Поправил свой скетч под эту библиотеку. В библиотеке поправил вывод на кириллицу (название месяцев, дней недели). Перестала отображаться дата. Создал тему здесь, скопировал функцию из библиотеки. Пока сочинял сообщение, решил, что может быть из-за кириллицы. Чтобы не править текст взад в файле срр, заменил сами файлы из архива. Проверил опять не работает. Создал сообщение. А там уже вот что... Написал как есть.
Про 11 строку, потому и спросил, что там тип days беззнаковое целое. Также в условии, объясните, если не трудно, почему там int, если все переменные в условии uint.
На компилятор я "бочку не качу", просто предположил, не имел желания утверждать.

sadman41
Offline
Зарегистрирован: 19.10.2016

Потому что при вычислении значения расширяются до int, если влезают в таковую размерность.

Хотя, конечно, uint16 поширше int16... Тут я затрудняюсь. ЕвгенийП, наверное эту специфику прояснит.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

365 надо заменить на 365U ?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

В общем никогда так не делайте. Если выкладываете текст библиотеки - так и пишите, а если "после своих правок", тоже так и пишите. Бесите немерянно, потому что теряешь время только потому, что тебя кто-то дезинформировал.

mir0tv0rec пишет:
почему там int, если все переменные в условии uint.

А константы? 

 if (days < 365 + leap)

365 - какого типа? Получается, что правая часть сравнения - знаковая (я уже писал Вам об этом, кстати) Сделайте его беззнаковым 365u и будет Вам счастье.

mir0tv0rec
Offline
Зарегистрирован: 19.09.2018

ЕвгенийП, честно говоря, сам тупанул в 1м посте, я даже и не заметил, что там какие-то изменения были. По поводу типов понял, запомнил. Спасибо за помощь! Буду дальше ковырять...