Как сделать будильник из массива строк

ardoinshik
Offline
Зарегистрирован: 14.11.2016

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

Нужно чтобы каждый день срабатывало около 5 будильников в течении года.(всего 1500-2000 будильников)

Время срабатывания будильников пытаюсь записать в строковый "массив", в котором указаны даты и времена этих будильников. Вот код:

  
 void setup(){
Serial.begin(9600);
//сюда я вбил 6 времён будильников
char* myStrings[]={"14.11.2016-10:00", "14.11.2016-11:00", "14.11.2016-12:00",
"14.11.2016-13:00", "14.11.2016-15:00","14.11.2016-20:00"};

int i;
for(i=0;i<=6;i=i+1){
//здесь x - данные,полученные с модуля реального времени ds3231. Если текущее время полученное с модуля реального времени равно времени будильника, то...

if(myStrings[i]==x){
  Serial.println("сработало");
  }
 }
    }
     
    void loop(){

}

Проблема в том , что я попробовал записать 500 будильников вместо шести и получил ошибку:

"Недостаточно памяти. Скетч использует 11 454 байт (35%) памяти устройства. Всего доступно 32 256 байт.
Глобальные переменные используют 10 096 байт (492%) динамической памяти, оставляя -8 048 байт для локальных переменных. Максимум: 2 048 байт."

Использую ардуино uno в котором 32кб памяти. Я думаю что нужно каким-то другим образом записать будильники, то есть не в строковый массив а как-то по другому...

кстати 1500 будильников весят 22 кб, если поместить их в обычный блокнот, то есть надежда у меня есть:)

Подскажите как написать код, чтобы он уместился в 32кб памяти моей ардуины? и ещё я боюсь- а хватит ли мощности процессора найти в массиве из 1500 будильников нужный будильник

 

 

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

подключай флешку и читай будильники из текстового файла

ardoinshik
Offline
Зарегистрирован: 14.11.2016

так ведь памяти  хватает. просто массив помещается толи в динамическую память то ли ещё куда-то куда не стОит его помещать,  если я правильно понимаю

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Каждый день разное время?

я бы рекомендовал добавить расписание на день и составить годовой план по таким расписаниям. Так делается в школьных звонках и не только.

ardoinshik
Offline
Зарегистрирован: 14.11.2016

Каждый день разное время надо, то есть 1500 разных будильников. Кто-то разбирается, как сделать чтобы массив ушёл не в динамическую память которой дано всего 2Кб в ардуине, а во флеш, которая 32Кб?

Araris
Offline
Зарегистрирован: 09.11.2012

Если нужно оперативно (то есть без перепрошивки скетча) менять параметры будильников - совет выше был, подключите microSD-card с текстовым файлом(файлами) и читайте параметры из них.

Если не нужно - пользуйтесь PROGMEM, тогда прямо в тексте скетча будет что-то типа const char alarms_list[] PROGMEM = "14.11.2016-10:00,14.11.2016-11:00,14.11.2016-12:00,14.11.2016-13:00,14.11.2016-15:00,14.11.2016-20:00"; Возможности изменить эти данные в процессе работы скетча - не будет.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Лучше так:

class CDates {
   
public:	

    inline uint8_t Count() { return 5; }

    inline const __FlashStringHelper * operator []( uint8_t index ) {
        
        switch ( index ) {

            case 0: return F( "14.11.2016-10:00" );
            case 1: return F( "14.11.2016-11:00" );
            case 2: return F( "14.11.2016-12:00" );
            case 3: return F( "14.11.2016-15:00" );
            case 4: return F( "14.11.2016-20:00" );
            
            default: return 0;

        }        

    }

};


CDates Dates;


void setup() 
{
    Serial.begin( 9600 );
    
    Serial.println( Dates[0] );
    Serial.println( Dates[1] );
}

 

okta
Offline
Зарегистрирован: 10.01.2015

ardoinshik пишет:

Каждый день разное время надо, то есть 1500 разных будильников. Кто-то разбирается, как сделать чтобы массив ушёл не в динамическую память которой дано всего 2Кб в ардуине, а во флеш, которая 32Кб?

PROGMEM вам поможет

const char myStrings[] PROGMEM = {"14.11.2016-10:00", "14.11.2016-11:00", "14.11.2016-12:00", "14.11.2016-13:00", "14.11.2016-15:00","14.11.2016-20:00"};

 

Araris
Offline
Зарегистрирован: 09.11.2012

А, точно, есть ведь ещё вариант - F().

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

uni пишет:

Лучше так:

не лучше - скорость работы кода будет уменьшаться с течением времени

okta
Offline
Зарегистрирован: 10.01.2015

Клапауций 234 пишет:

не лучше - скорость работы кода будет уменьшаться с течением времени

Из-за проверки 1500 условий? Не думаю, что там нужна наносекундная точность... А так да, маленько повлияет.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

okta пишет:

ardoinshik пишет:

Каждый день разное время надо, то есть 1500 разных будильников. Кто-то разбирается, как сделать чтобы массив ушёл не в динамическую память которой дано всего 2Кб в ардуине, а во флеш, которая 32Кб?

PROGMEM вам поможет

const char myStrings[] PROGMEM = {"14.11.2016-10:00", "14.11.2016-11:00", "14.11.2016-12:00", "14.11.2016-13:00", "14.11.2016-15:00","14.11.2016-20:00"};

Совет неверный, т.к. gcc не может расположить такой массив во флеш. Указатели да, строки - нет. Попробуйте это скомпилировать.

okta
Offline
Зарегистрирован: 10.01.2015

Согласен, при таком варианте надо все одной большой строкой.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Клапауций 234 пишет:

uni пишет:

Лучше так:

не лучше - скорость работы кода будет уменьшаться с течением времени

Тоже не верно, т.к. используется ключевое слово inline и код всегда один и тот же для любого индекса:

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

если на год, то нафига эту инфу .2016- писать куда-то вообще?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ardoinshik, вы объясните что вам нужно "будить", может есть варианты математически рассчитать..

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

uni пишет:

Тоже не верно, т.к. используется ключевое слово inline и код всегда один и тот же для любого индекса:

т.е. время исполнения при index 0 и 1000 будет одинаково?
 
switch ( index ) {
case 0: return F();
.....
case 1000: return F();
default: return 0;
}
 
okta
Offline
Зарегистрирован: 10.01.2015

uni пишет:

Тоже не верно, т.к. используется ключевое слово inline и код всегда один и тот же для любого индекса:

Тоже согласен (на inline внимания не обратил). Но... Исходная дата (в первом сообщении) занимает 16 символов (байт). По условию будет 1500 дат. Т.е. под хранение дат уйдет 24000 байт. Если к каждой дате обратиться более одного раза, то либо inline будет проигнорирован компилятором, либо памяти не хватит. Но это так уже "фантазии".

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Этого кода в результирующем файле не будет (на картинке я это показал, там 4 асм команды и call Serial.println() для каждой строки, где идёт обращение к Date[n]). 0x0820 - это вызов метода println().

На этапе компиляции любое обращение к объекту Dates через оператор индексирования развернётся в готовый указатель на область flash, где содержится строка. Ключевое слово inline означает включить тело функции operator[] в место вызова, а оптимизация сократит код функции до значения указателя, которое известно на момент компиляции. Поэтому не важно к какой строке мы обращаемся.

П.С. Т.е. я не совсем правльно пояснил, сам inline не сократит код, это сделает оптимизатор. inline лишь уберёт лишний вызов.

 

okta
Offline
Зарегистрирован: 10.01.2015

Ааа. Т.е. данные не раскопируются при каждом вызове. Тогда вопрос снят.

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

uni пишет:

Этого кода в результирующем файле не будет (на картинке я это показал, там 4 асм команды и call Serial.println() для каждой строки, где идёт обращение к Date[n]). 0x0820 - это вызов метода println().

На этапе компиляции любое обращение к объекту Dates через оператор индексирования развернётся в готовый указатель на область flash, где содержится строка. Ключевое слово inline означает включить тело функции operator[] в место вызова, а оптимизация сократит код функции до значения указателя, которое известно на момент компиляции. Поэтому не важно к какой строке мы обращаемся.

П.С. Т.е. я не совсем правльно пояснил, сам inline не сократит код, это сделает оптимизатор. inline лишь уберёт лишний вызов.

это как бы хорошо, НО! это же какое-то безобразие - я пишу код и желаю, что бы исполнялся switch, а не происходило обращение к индексу.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

inline не везде уместно использовать. Я скопировал этот код из другого моего проекта. Если реализация метода operator[] очень маленькая и меньше, чем код вызова этого оператора, то можно сэкономить, используя inline. Тут нужно смотреть листинги ассемблера для случая с inline и без него, чтобы точно узнать нужен ли он, либо собрать в двух случаях и посмотреть на результаты компиляции.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Клапауций 234 пишет:

uni пишет:

Этого кода в результирующем файле не будет (на картинке я это показал, там 4 асм команды и call Serial.println() для каждой строки, где идёт обращение к Date[n]). 0x0820 - это вызов метода println().

На этапе компиляции любое обращение к объекту Dates через оператор индексирования развернётся в готовый указатель на область flash, где содержится строка. Ключевое слово inline означает включить тело функции operator[] в место вызова, а оптимизация сократит код функции до значения указателя, которое известно на момент компиляции. Поэтому не важно к какой строке мы обращаемся.

П.С. Т.е. я не совсем правльно пояснил, сам inline не сократит код, это сделает оптимизатор. inline лишь уберёт лишний вызов.

это как бы хорошо, НО! это же какое-то безобразие - я пишу код и желаю, что бы исполнялся switch, а не происходило обращение к индексу.

Никто не запрещает убрать inline и понизить уровень оптимизации. По крайней мере у себя я могу это сделать, но в стандартной среде Arduino вроде бы нельзя управлять уровнем оптимизации (точно не знаю). Я не помню какой ключ испльзуется при -O{n} при сборке.

Вы можете управлять содержимым прошивки, нужно только знать где и что нужно "подкрутить".

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Отключить оптимизацию для функции можно, используя __attribute__((optimize("O0"))) в месте её объявления. Попробовал, switch появился.

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

uni пишет:

Отключить оптимизацию для функции можно, используя __attribute__((optimize("O0"))) в месте её объявления. Попробовал, switch появился.

спасибо - теперь я спокоен.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Ещё один вариант изменения уровня оптимизации участка кода.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Не забываем ещё заменить uint8_t у index на uint16_t. То же для Count.

ardoinshik
Offline
Зарегистрирован: 14.11.2016

Товарищ Araris посоветовал такой код

#include <avr/pgmspace.h> 
     
void setup(){

Serial.begin(9600);

const char alarms_list[] PROGMEM = "14.11.2016-10:00,14.11.2016-11:00,14.11.2016-12:00,14.11.2016-13:00,14.11.2016-15:00,14.11.2016-20:00";

Serial.println(alarms_list[1]);  

  }
     
    void loop(){

} 

но комманда Serial.println(alarms_list[0]); выдает мне "1",  или например комманда Serial.println(alarms_list[1]); выдает "4",

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

(с++ - только начал осваивать)

 

ardoinshik
Offline
Зарегистрирован: 14.11.2016

а товарищ uni посоветовал код

const char myStrings[] PROGMEM = {"14.11.2016-10:00", "14.11.2016-11:00", "14.11.2016-12:00", "14.11.2016-13:00", "14.11.2016-15:00","14.11.2016-20:00"};

он вообще не компилируется, пишет ошибку

too many initializers for 'const char []'

Что ж делать то?

okta
Offline
Зарегистрирован: 10.01.2015

Товарищ uni посоветовал совсем другой код, более удобный и правильный. А по поводу этого кода пару постами ниже есть поправка, что массив строк нельзя так делать. Либо все одной строкой, либо... лучше скопируйте, что посоветовл uni

__Alexander
Offline
Зарегистрирован: 24.10.2012

товарищ uni советовал это объявить как указатели.  после char звездочку внидюрь.

ardoinshik
Offline
Зарегистрирован: 14.11.2016

uni пишет:

Лучше так:

class CDates {
   
public:	

    inline uint8_t Count() { return 5; }

    inline const __FlashStringHelper * operator []( uint8_t index ) {
        
        switch ( index ) {

            case 0: return F( "14.11.2016-10:00" );
            case 1: return F( "14.11.2016-11:00" );
            case 2: return F( "14.11.2016-12:00" );
            case 3: return F( "14.11.2016-15:00" );
            case 4: return F( "14.11.2016-20:00" );
            
            default: return 0;

        }        

    }

};


CDates Dates;


void setup() 
{
    Serial.begin( 9600 );
    
    Serial.println( Dates[0] );
    Serial.println( Dates[1] );
}

 

Если вы про этот код, то он тоже не работает, т.к. пишет ошибку

warning: case label value exceeds maximum value for type

Если я правильно понял, то case - случаев не может быть бесконечно, примерно на 256 случае валится эта ошибка

 

okta
Offline
Зарегистрирован: 10.01.2015

ну так тип переменной поменяйте на uint16_t

Araris
Offline
Зарегистрирован: 09.11.2012

ardoinshik пишет:

Товарищ Araris посоветовал такой код

#include <avr/pgmspace.h> 
     
void setup(){

Serial.begin(9600);

const char alarms_list[] PROGMEM = "14.11.2016-10:00,14.11.2016-11:00,14.11.2016-12:00,14.11.2016-13:00,14.11.2016-15:00,14.11.2016-20:00";

Serial.println(alarms_list[1]);  

  }
     
    void loop(){

} 

но комманда Serial.println(alarms_list[0]); выдает мне "1",  или например комманда Serial.println(alarms_list[1]); выдает "4",

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

(с++ - только начал осваивать)

Ну тогда имеете отличный повод освоить парсинг строк в С++ )))

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

тема, нафига писать во флеш .2016- не раскрыта

Araris
Offline
Зарегистрирован: 09.11.2012

А меня чертовски интригует фраза "Каждый день разное время надо, то есть 1500 разных будильников.ardoinshik, откройтесь же, каково предназначение устройства ?

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

Araris пишет:

А меня чертовски интригует фраза "Каждый день разное время надо, то есть 1500 разных будильников.ardoinshik, откройтесь же, каково предназначение устройства ?

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

ну, вот - 100500 будильников

Araris
Offline
Зарегистрирован: 09.11.2012

Фаджр, Зухр, Аср, Магриб, Иша ? Угадал ?

nik182
Offline
Зарегистрирован: 04.05.2015

Время програмируется четырьмя байтами. До 2038 года никаких проблем. 1500 точек * 4 байта = 6000 байт. Сравнивать не очень долго. Несколько милисекунд. Массив спокойно пишется в память программ. Программа преобразования 4 байт в строку и обратно - несколько строк кода. Какие проблемы? Курим unix timestamp.

https://ru.wikipedia.org/wiki/UNIX-%D0%B2%D1%80%D0%B5%D0%BC%D1%8F

__Alexander
Offline
Зарегистрирован: 24.10.2012

Чувак, тут поместить строки во флеш не могут, а ты хочешь чтобы их еще упаковали? 

svm
Offline
Зарегистрирован: 06.11.2016

nik182 пишет:

Время програмируется четырьмя байтами.

В данном случае это тоже избыточно.

Достаточно двух байт. Минуты 1-60 это 6 байт  Часы 0-24 это 5 байт остается еще 5 байт, которые можно использовать для служебной информации (смена дня, месяца, перевод времени,високосный год и еще что-то это в лоб, хотя 5 байтами можно закодировать не 5,а 32 комбинации). Каждый будильник будет занимать два байта, если не применять какого-то более извращенного кодирования например писать дельту времени между будильниками.

vdk
Offline
Зарегистрирован: 14.04.2013
Клапауций 234
Offline
Зарегистрирован: 24.10.2016

я запретил.

okta
Offline
Зарегистрирован: 10.01.2015

Требую закрыть тему за пропаганду курения!!!

По поводу чувств верующих надо прокосультироваться, но как минимум "Фаджр, Зухр, Аср, Магриб, Иша" уже повод.

Админы вы куда смотрите?

Araris
Offline
Зарегистрирован: 09.11.2012

okta пишет:

Админы вы куда смотрите?

Дык, собссстнно...

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Ещё один вариант:

#include "Arduino.h"

#define FLASHSTRING(name,init) \
    const char _##name[] PROGMEM = init; \
    const __FlashStringHelper * name = ( const __FlashStringHelper * ) _##name;
    
#define DATE_STR_SIZE   17

FLASHSTRING( data,  
    "14.11.2016-00:00\0" 
    "14.11.2016-01:00\0" 
    "14.11.2016-02:00\0" 
    "14.11.2016-03:00\0" 
    "14.11.2016-04:00\0"
);

class CDates {
   
public:	

    uint16_t Count() { return sizeof( _data ) / DATE_STR_SIZE; }

    const __FlashStringHelper * operator []( uint16_t index ) 
    {
        return ( const __FlashStringHelper * )( ( PGM_P ) data + index * DATE_STR_SIZE );
    }
};

CDates Dates;


void setup() 
{
	Serial.begin( 9600 );
    
    Serial.println( Dates.Count() );
    
    Serial.println( Dates[0] );
    Serial.println( Dates[1] );
    Serial.println( Dates[4] );
}

 

ardoinshik
Offline
Зарегистрирован: 14.11.2016

Всем спасибо. это была задача для студентов. модуля 3231 вообще не оказалось, передавали время по кабелю

ardoinshik
Offline
Зарегистрирован: 14.11.2016

Добрый день. Пришёл модуль реального времени ds3231 и проект нужно доделать. Два дня ломал голову, и безрезультатно.На третий день решил написать на форум. Я смог получить дату и время с модуля, но никак не могу сравнить дату, полученную с модуля с датой, взятой из массива, для того чтобы наконец-то сработало условие

Вот код:

1вариант)

//Библиотека ds3231 взята отсюда http://www.rinkydinkelectronics.com/download.php?f=DS3231.zip
//Библиотека pgmspace уже встроена в ide
#include <DS3231.h>
#include <avr/pgmspace.h> 

// Init the DS3231 using the hardware interface
DS3231  rtc(SDA, SCL);

// Init a Time-data structure
Time  t;

void setup()
{
  Serial.begin(9600);
  // Initialize the rtc object
  rtc.begin();
}

void loop()
{
//в массиве около 2,5тыс.строк. я их убрал, чтобы на форуме легче читалось
char* myStrings[] PROGMEM = {"30.11.2016", "11.11.2016", "12.11.2016", "13.11.2016", "14.11.2016", "15.11.2016", "16.11.2016", "17.11.2016", "18.11.2016", "19.11.2016", "20.11.2016", "21.11.2016", "22.11.2016", "23.11.2016"};
// Get data from the DS3231
t = rtc.getTime();
//пытаюсь перевести полученную дату с часов реального времени в char*, чтобы сравнить с первым элементом массива будильников
char* z1;
z1 = rtc.getDateStr();
//myStrings[0]-первый элемент массива
if(myStrings[0]==z1){
  Serial.print("ok");
}

delay (1000);

}

Итог -не сравниваются дата полученная с модуля ардуино и дата взятая с массива. Но если проверить, то вот эти обе команды

Serial.print(myStrings[0]);
Serial.print(z1);

выдают одинаковые данные - "30.11.2016"

2вариант)Далее попробовал не переводить rtc.getDateStr() в char*:

if(myStrings[0]==rtc.getDateStr()){
  Serial.print("ok");
}

Результат тот же-не сравнивается, но компилируется без ошибок

3вариант)Далее попробовал перевести дату и элемент массива в строки, и сравнить:

String z1(rtc.getDateStr());
int i;
for(i=0;i<=14;i=i+1){
String z2(myStrings[i]);
if(z2==z1){
  Serial.print("ok");
  delay(1000);
  break;
  }

В итоге сравниваются, и после компиляции показывает "ок". Но оказалось что "ок"  показывает всегда, даже если элементы не равны друг другу,т.е. я   в массиве изменил дату на вчерашнюю и получил "ок", хотя по логике не должно быть так
 

 

 

ardoinshik
Offline
Зарегистрирован: 14.11.2016

Может нужна какая-то библиотека для сравнивания?

ardoinshik
Offline
Зарегистрирован: 14.11.2016

Вот такой код работает

#include <DS3231.h>
#include <avr/pgmspace.h> 
#include <string.h> 

// Init the DS3231 using the hardware interface
DS3231  rtc(SDA, SCL);

// Init a Time-data structure
Time  t;

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

  // Initialize the rtc object
  rtc.begin();

}

void loop()
{
char* myStrings[] PROGMEM = {"30.11.2016", "20.11.2016", "21.11.2016", "3.3.2016-3:3", "4.4.2016-4:4", "5.5.2016-5:5", "6.6.2016-6:6", "7.7.2016-7:7", "8.8.2016-8:8", "9.9.2016-9:9", "10.10.2016-10:10", "11.11.2016-11:11", "12.12.2016-12:12", "13.13.2016-13:13"};

// Get data from the DS3231
t = rtc.getTime();

int i;
for(i=0;i<=14;i=i+1){
char* z1;
z1 = rtc.getDateStr();
char* z2;
z2 = myStrings[i];
//Serial.print(z2);

   if (strcmp (z1, z2)==0)
   {
      Serial.print("sovpadenie");
      
   }
else
{
  Serial.print("nesovpadenie");
 
  }  
  delay (500);
}

}






 

      
ardoinshik
Offline
Зарегистрирован: 14.11.2016

Но если в массиве много будильников(например 500), то выходит ошибка:

Скетч использует 14 786 байт (45%) памяти устройства. Всего доступно 32 256 байт.
Глобальные переменные используют 11 300 байт (551%) динамической памяти, оставляя -9 252 байт для локальных переменных. Максимум: 2 048 байт.
processing.app.debug.RunnerException: Недостаточно памяти; прочитайте http://www.arduino.cc/en/Guide/Troubleshooting#size
    at cc.arduino.Compiler.size(Compiler.java:338)
    at cc.arduino.Compiler.build(Compiler.java:158)
    at processing.app.Sketch.build(Sketch.java:1111)
    at processing.app.Sketch.exportApplet(Sketch.java:1146)
    at processing.app.Sketch.exportApplet(Sketch.java:1132)
    at processing.app.Editor$DefaultExportHandler.run(Editor.java:2409)
    at java.lang.Thread.run(Thread.java:745)
Недостаточно памяти; прочитайте http://www.arduino.cc/en/Guide/Troubleshooting#size

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