ardoinshik, то что вы начали изначально делать -это неправильно. Программе не нужны данные в том формате, в котором они удобны человеку. У вас гигантская избыточность массивов. Какой смысл в каждой ячейке хранить полную дату? наверняка в схемах алармов есть или повторы или закономерности, используя которые можно принципиально изменить всю логистику хранения и упаковать в гораздо меньшие объёмы.
1. Не совсем понятно, зачем для хранения времени использовать текстовые строки, когда как раз на этот случай придумали unixtime.
2. Не разбирался с особенностями реализации PROGMEM, но по смыслу это должны быть статические констранты. У Вас нескаолько нестандартное описание массива myStrings для этого случая. Что в этом случае делает компилятор, я не знаю, но, судя по тому, что у Вас происходит перерасход оперативной памяти квалификатор PROGMEM попросту игнорируется. Опишите, как пололжено: с квалификатором const и вне пределов функции.
Компилятор avr-gcc не умеет хранить массив строк во флеш напрямую. Это можно сделать способами, которые я указал ранее. При этом для сравнения строки во флеш и ОЗУ нужно использовать функцию strcmp_P, специально для этого предназначенную.
Подскажите пож-та код, в котором в переменную можно занести дату и время хотя бы одного будильника с помощью unixtime? А потом бы как то сравнить её с данными с модуля времени, который выдает в виде "01.12.2016" и "09:26:00"...
Этого кода в результирующем файле не будет (на картинке я это показал, там 4 асм команды и call Serial.println() для каждой строки, где идёт обращение к Date[n]). 0x0820 - это вызов метода println().
На этапе компиляции любое обращение к объекту Dates через оператор индексирования развернётся в готовый указатель на область flash, где содержится строка. Ключевое слово inline означает включить тело функции operator[] в место вызова, а оптимизация сократит код функции до значения указателя, которое известно на момент компиляции. Поэтому не важно к какой строке мы обращаемся.
П.С. Т.е. я не совсем правльно пояснил, сам inline не сократит код, это сделает оптимизатор. inline лишь уберёт лишний вызов.
это как бы хорошо, НО! это же какое-то безобразие - я пишу код и желаю, что бы исполнялся switch, а не происходило обращение к индексу.
"Коль праведно то, чего он вожделеет, то то чего ты вожделеешь - грешно" О.Хаям
Размер структуры равен 4 байтам, также как и для time_t, которая обычно 32-разрядная. Таким образом удобно заполнять массив вручную и просто преобразовать в srtuct tm и затем в time_t для сравнения со значениями от rtc, которые наверняка тоже можно получить не в строковом формате. Либо вообще по полям делать сравнение.
Я забыл, что методы print(...) реализованы только для переменных, расположенных в ОЗУ, кроме строк, указатели на которые специально указаны. Печатается билиберда, т.к. указатели берутся для флеш, а используются для ОЗУ. Тут всё немного сложнее получается.
Нужно копировать содержимое флеши в ОЗУ:
int n = sizeof( dt) / sizeof( MYDATETIME );
for ( int i = 0; i < n; i++ )
{
MYDATETIME tmp;
memcpy_P( & tmp, & dt[i], sizeof( MYDATETIME ) );
Serial.println( tmp.fields.hour );
}
Спасибо, так работает. Как теперь склеить значения tmp.fields.Х чтобы получитьchar* вот такого вида "01.12.201611:30:20" ? Именно в таком виде модуль реального времени выдает дату и время, после маленькой склейки:
Т.е. Serial.print(result) выдает 01.12.201615:45:20
Надо ведь ещё как-то ноль добавить к числам, которые меньше 10
Кстати, теперь стало понятно, почему PROGMEM игнорируется и память переполняется. Это происходит потому, что каждую строку массива нужно объявлять в отдельную строку, это такая фишка PROGMEM. На середине страницы объяснено и разжевано если кому-то интересно
Думается мне, что кроме строковых методов там должны быть методы, которые возвращают дату и время в других форматах. Лучше использовать их, например, getHour() или getTime(). С их помощью было бы проще делать сравнение.
посмотрел другие библиотеки, думаю что не смогу сам. uni, вы можете помочь за вознаграждение?(если в этой ветке форума не запрещено договариваться). через teamviewer например, могу написать вам на почту через часа эдак четыре
Подскажите пож-та код, в котором в переменную можно занести дату и время хотя бы одного будильника с помощью unixtime? А потом бы как то сравнить её с данными с модуля времени, который выдает в виде "01.12.2016" и "09:26:00"...
Смотрите в стандартных римерах Ардуино.
Еще, кажется, чуть ли не в любую библиотеку RTC входит функция преобразования.
Зачем использовать библиотеку, а потом преобразовывать ? Обращайтесь напрямую к регистрам RTC, это и упростит преобразования и уменьшит размер кода. Например так:
#include <Wire.h>
#define DS3231_I2C_ADDRESS 0x68
byte seconds, minutes, hours, day, date, month, year;
char weekDay[4];
byte tMSB, tLSB;
float temp3231;
void setup()
{
Wire.begin();
Serial.begin(9600);
//set control register to output square wave on pin 3 at 1Hz
Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
Wire.write(0x0E); //выставляемся в 14й байт
Wire.write(B00000000); //сбрасываем контрольные регистры
Wire.write(B10001000); //выставляем 1 на статус OSF и En32kHz
Wire.endTransmission();
}
void loop() {
watchConsole(); //читаем консоль. Если нажата "T" читаем из консоли следующий 7 байт и устанавливаем время.
get3231Date(); //получаем данные
//и потом их выводим через Serial.print()
Serial.print(weekDay); Serial.print(", "); Serial.print(month, DEC); Serial.print("/"); Serial.print(date, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" - ");
Serial.print(hours, DEC); Serial.print(":"); Serial.print(minutes, DEC); Serial.print(":"); Serial.print(seconds, DEC);
Serial.print(" Temperature: "); Serial.print(get3231Temp());Serial.print("; ");
Serial.println(get3231Register(0x0F)); //получаем и выводим статусовый регистр
delay(1000);
}
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}
void watchConsole()
{
if (Serial.available()) { // Look for char in serial queue and process if found
if (Serial.read() == 84) { //If command = "T" Set Date
set3231Date();
get3231Date();
Serial.println(" ");
}
}
}
void set3231Date()
{
//T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year)
//T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
//Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209
seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result.
minutes = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
hours = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
day = (byte) (Serial.read() - 48);
date = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
year = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48));
Wire.beginTransmission(DS3231_I2C_ADDRESS); //начали сессию
Wire.write(0x00); //поставляемся в нулевой байт для чтения данных
Wire.write(decToBcd(seconds));
Wire.write(decToBcd(minutes));
Wire.write(decToBcd(hours));
Wire.write(decToBcd(day));
Wire.write(decToBcd(date));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();//закрыли сессию
}
byte get3231Register(byte regNo) {
// send request to receive data starting at register regNo
Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
Wire.write(regNo); // start at register regNo
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 1); // request one byte
if(Wire.available()) return Wire.read();
}
void set3231Register(byte regNo, byte value) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(regNo);
Wire.write(value);
Wire.endTransmission();
}
void get3231Date()
{
// send request to receive data starting at register 0
Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
Wire.write(0x00); // start at register 0
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes
if(Wire.available()) {
seconds = Wire.read(); // get seconds
minutes = Wire.read(); // get minutes
hours = Wire.read(); // get hours
day = Wire.read();
date = Wire.read();
month = Wire.read(); //temp month
year = Wire.read();
//Конвертация происходит очень просто - битовым оператором & мы очищаем МЛАДШИЕ (0,1,2,3) четыре бита , сдвигаем все вправо на 4 позиции (очищенные биты
//вылезут при этом с левой стороны и результат умножаем на 10, т.к. в старших битах хранится информация о десятках секунд.
//После этого битовым оператором & очищаем СТАРШИЕ (4,5,6,7)четыре бита и получаем секунды. Затем суммируем десятки секунд и единиц.
seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111));
minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
hours = (((hours & B00110000)>>4)*10 + (hours & B00001111));
day = (day & B00000111); // 1-7
date = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31
month = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow
year = (((year & B11110000)>>4)*10 + (year & B00001111));
}
else {
//oh noes, no data!
}
switch (day) {
case 1:
strcpy(weekDay, "Mon");
break;
case 2:
strcpy(weekDay, "Tue");
break;
case 3:
strcpy(weekDay, "Wed");
break;
case 4:
strcpy(weekDay, "Thu");
break;
case 5:
strcpy(weekDay, "Fri");
break;
case 6:
strcpy(weekDay, "Sat");
break;
case 7:
strcpy(weekDay, "Sun");
break;
}
}
float get3231Temp()
{
//temp registers (11h-12h) get updated automatically every 64s
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
if(Wire.available()) {
tMSB = Wire.read(); //2's complement int portion
tLSB = Wire.read(); //fraction portion
temp3231 = (tMSB & B01111111); //do 2's math on Tmsb
temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8
}
else {
//oh noes, no data!
}
return temp3231;
}
В зависимости от точности будильника ,объем занимаемый им может колебаться от 1 до 3 байт. Информация о годе, месяце числе и дне недели абсолютно не нужна. Она может быть получена при просмотре массива будильником простым вычислением. Вот что остается для получения времени, если убрать все лишнее
#include <Wire.h>
byte minutes, hours;
void setup()
{
//Serial.begin(9600); // this is here so that we can print the result
}
void loop()
{
Wire.beginTransmission(0x68); // обращаемся устройству 0x68 (RTC)
Wire.write(0x01); // устанавливаем начальный адрес
Wire.endTransmission(); // конец передачи
Wire.requestFrom((0x68), 2); // запрашиваем 3 байт
minutes = Wire.read(); // get minutes
hours = Wire.read(); // get hours
minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
hours = (((hours & B00110000)>>4)*10 + (hours & B00001111));
//Serial.print(hours,DEC); Serial.print(":"); Serial.println(minutes,DEC);
delay(1000);
}
нельзя математически просчитать времена срабатывания будильников, дан их список и в нем нет закономерностей.
Я имел в виду вычисление не времени срабатывания будильника, а даты. Её можно вычислить, для этого в начале группы будильников на конкретную дату установить флаг смены даты.
(551%) динамической памяти
это не ошибка - это ИИ пытается убежать в реал
ardoinshik, то что вы начали изначально делать -это неправильно. Программе не нужны данные в том формате, в котором они удобны человеку. У вас гигантская избыточность массивов. Какой смысл в каждой ячейке хранить полную дату? наверняка в схемах алармов есть или повторы или закономерности, используя которые можно принципиально изменить всю логистику хранения и упаковать в гораздо меньшие объёмы.
1. Не совсем понятно, зачем для хранения времени использовать текстовые строки, когда как раз на этот случай придумали unixtime.
2. Не разбирался с особенностями реализации PROGMEM, но по смыслу это должны быть статические констранты. У Вас нескаолько нестандартное описание массива myStrings для этого случая. Что в этом случае делает компилятор, я не знаю, но, судя по тому, что у Вас происходит перерасход оперативной памяти квалификатор PROGMEM попросту игнорируется. Опишите, как пололжено: с квалификатором const и вне пределов функции.
Компилятор avr-gcc не умеет хранить массив строк во флеш напрямую. Это можно сделать способами, которые я указал ранее. При этом для сравнения строки во флеш и ОЗУ нужно использовать функцию strcmp_P, специально для этого предназначенную.
Спасибо всем за ответы.
Подскажите пож-та код, в котором в переменную можно занести дату и время хотя бы одного будильника с помощью unixtime? А потом бы как то сравнить её с данными с модуля времени, который выдает в виде "01.12.2016" и "09:26:00"...
Этого кода в результирующем файле не будет (на картинке я это показал, там 4 асм команды и call Serial.println() для каждой строки, где идёт обращение к Date[n]). 0x0820 - это вызов метода println().
На этапе компиляции любое обращение к объекту Dates через оператор индексирования развернётся в готовый указатель на область flash, где содержится строка. Ключевое слово inline означает включить тело функции operator[] в место вызова, а оптимизация сократит код функции до значения указателя, которое известно на момент компиляции. Поэтому не важно к какой строке мы обращаемся.
П.С. Т.е. я не совсем правльно пояснил, сам inline не сократит код, это сделает оптимизатор. inline лишь уберёт лишний вызов.
это как бы хорошо, НО! это же какое-то безобразие - я пишу код и желаю, что бы исполнялся switch, а не происходило обращение к индексу.
"Коль праведно то, чего он вожделеет, то то чего ты вожделеешь - грешно" О.Хаям
Можно использовать такую промежуточную структуру для работы со временем компактно и понятно:
Использовать так, например:
Размер структуры равен 4 байтам, также как и для time_t, которая обычно 32-разрядная. Таким образом удобно заполнять массив вручную и просто преобразовать в srtuct tm и затем в time_t для сравнения со значениями от rtc, которые наверняка тоже можно получить не в строковом формате. Либо вообще по полям делать сравнение.
Пытаюсь сделать перебор минут, используя ваш код:
На выходе получаю непонятные значения:
15
0
0
48
0
0
32
32
0
0
48
10
0
32
32
0
0
48
11
0
32
32
0
Этих значений даже нет в массиве.
Если же просто вот так сделать:
То получаю правильные минуты из массива.
(в ступоре)
а я бы делал через int - хранение минут от предшествующей даты )))
Укладываемся в 1500 int-ов
Я забыл, что методы print(...) реализованы только для переменных, расположенных в ОЗУ, кроме строк, указатели на которые специально указаны. Печатается билиберда, т.к. указатели берутся для флеш, а используются для ОЗУ. Тут всё немного сложнее получается.
Нужно копировать содержимое флеши в ОЗУ:
Спасибо, так работает. Как теперь склеить значения tmp.fields.Х чтобы получить
char* вот такого вида "01.12.201611:30:20" ? Именно в таком виде модуль реального времени выдает дату и время, после маленькой склейки:
Т.е. Serial.print(result) выдает 01.12.201615:45:20
Надо ведь ещё как-то ноль добавить к числам, которые меньше 10
Кстати, теперь стало понятно, почему PROGMEM игнорируется и память переполняется. Это происходит потому, что каждую строку массива нужно объявлять в отдельную строку, это такая фишка PROGMEM. На середине страницы объяснено и разжевано если кому-то интересно
Думается мне, что кроме строковых методов там должны быть методы, которые возвращают дату и время в других форматах. Лучше использовать их, например, getHour() или getTime(). С их помощью было бы проще делать сравнение.
В используемой библиотеке нет других методов. надо попробовать другую библиотеку
посмотрел другие библиотеки, думаю что не смогу сам. uni, вы можете помочь за вознаграждение?(если в этой ветке форума не запрещено договариваться). через teamviewer например, могу написать вам на почту через часа эдак четыре
Мне сейчас некогда этим заниматься. Попросите кого-нить другого, тут найдутся, я думаю.
Спасибо всем за ответы.
Подскажите пож-та код, в котором в переменную можно занести дату и время хотя бы одного будильника с помощью unixtime? А потом бы как то сравнить её с данными с модуля времени, который выдает в виде "01.12.2016" и "09:26:00"...
Смотрите в стандартных римерах Ардуино.
Еще, кажется, чуть ли не в любую библиотеку RTC входит функция преобразования.
Зачем использовать библиотеку, а потом преобразовывать ? Обращайтесь напрямую к регистрам RTC, это и упростит преобразования и уменьшит размер кода. Например так:
В зависимости от точности будильника ,объем занимаемый им может колебаться от 1 до 3 байт. Информация о годе, месяце числе и дне недели абсолютно не нужна. Она может быть получена при просмотре массива будильником простым вычислением. Вот что остается для получения времени, если убрать все лишнее
нельзя математически просчитать времена срабатывания будильников, дан их список и в нем нет закономерностей. Сейчас попробую ваш первый код
нельзя математически просчитать времена срабатывания будильников, дан их список и в нем нет закономерностей.
Я имел в виду вычисление не времени срабатывания будильника, а даты. Её можно вычислить, для этого в начале группы будильников на конкретную дату установить флаг смены даты.
нельзя математически просчитать времена срабатывания будильников, дан их список и в нем нет закономерностей. Сейчас попробую ваш первый код
кем дан? пристрели этого упоротого дятла.