Автоматическая забота о курах [снова умный курятник]

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

Всем привет. Проект водоснабжения вполне себе живет в первой реализации и ждет своего продолжения. А пока нужда возникла в кормлении кур.

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

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

Отслеживание запуска задач (их видится пока 4: выдать корм, налить воды, открыть дверь выгула, закрыть дверь выгула) через прерывание

Создал класс таймера

class Auto_Feeder_timer
{
public: //я что-то подумал, что пока откажусь от головоломки, что в приват ставить, и что в паблик
    String timerName;
    unsigned long start_time; // время включения формат ЧЧММ
    void (*ptrOnFuniction)(); // указатель на запускаемую функцию,
    bool haveStartedToday = false; // ставим после запуска, 
    Auto_Feeder_timer (String tName, unsigned long tStart_Time, void f())
    {
      timerName = tName;
      start_time = tStart_Time;// назначаем время старта
      ptrOnFuniction = f; // указатель на запускаемую функцию
    }
bool getIsItTime(unsigned long currentTime){
      if (currentTime == start_time && !haveStartedToday) // если текущее время равно или больше времени старта
      {
       haveStartedToday = true;
       Serial.println ("we are starting");       
       ptrOnFuniction();  // запускаем функцию
       return true;
      }
       return false;
     }
};

Теперь прерывание

void setup() {
 OCR0A = 0xAF;
 TIMSK0 |= _BV(OCIE0A);
 setSyncProvider(RTC.get);   // получаем время с RTC
 prevMillis = millis();
}

Прототипы функций и создание экземпляра

// прототипы и укзатели функций
void feeder_ON_OFF ();
void (*ptrfeederOnOff)() = feeder_ON_OFF;
// устанавливаем таймеры
  Auto_Feeder_timer  food_timer("feed", feed_time, ptrfeederOnOff);

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

SIGNAL(TIMER0_COMPA_vect) 
{
 if (millis() - prevMillis >= my_delay) { // my_delay по умолчанию я решил ставить в 30 сек.  чтобы снизить частоту запуска проверки таймеров
  prevMillis = millis();
  time_t t = now();
  if (food_timer.getIsItTime(hour(t)*60UL + minute(t))) {// если возвращается true - значит таймер сработал
         RecordTimerResult(food_timer.timerName, t); // записываем в лог
     }
 if (hour(t) == 00 && minute(t) == 00 && !resetFlag) { // в 00:00
    resetFlags(); // сбрасываем флаги выполненных таймеров
  }
 }
}

У меня была какя-то слегка странная идея, чтобы запускать проверку таймера не часто, например раз в полминуты. Я даже не знаю зачем... И я вижу, что это создает много сложностей. Например, костыль с ResetFlags(). Поскольку условие срабатывает в 00:00, то эта функция обнуления может быть запущена дважды. Чтобы этого не происходило (хотя это и не страшно, но как-то глупо), пришлось устанавливать флаг bool ResetFlag, ставить его true и через минуту ставить false... короче, бред. Видимо, я сейчас переделаю на проверку раз в секунду, буду передавать время в секундах.

Но может быть идея делать проверку раз в пол-минуты хороша собой и стоит того?

В остальном пока вроде все понятно.

А, вот еще. Есть задача, чтобы если в деревне отключат электричество, то после его подачи не произошло "двойного кормления" или сдвига времени кормления. Так что я использую RTC и SD-карточку (сначала думал писать в EEPROM, который на RTC-модуле, но потом плюнул - циклы записи, сколько раз и т.д.).

Сейчас додумываю формат записи флагов, чтобы при включении системы пройтись по ним и проверить, не был ли пропущен запуск какого-либо таймера, пока не было леФтричествА

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

Не удержался ... После публикации темы получил сообщение:

Тема форума Автоматическая забота о курах [снова умный курятник] был создан.
Сразу вспомнил: "Зарежь мне арбуз, пожалуйста. Да, я говорю по русски, я русский школа скончался"

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

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

Angbor пишет:

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

Борьба с пишущими курсачи.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Angbor пишет:

А, вот еще. Есть задача, чтобы если в деревне отключат электричество, то после его подачи не произошло "двойного кормления" или сдвига времени кормления. Так что я использую RTC и SD-карточку (сначала думал писать в EEPROM, который на RTC-модуле, но потом плюнул - циклы записи, сколько раз и т.д.).

Сейчас додумываю формат записи флагов, чтобы при включении системы пройтись по ним и проверить, не был ли пропущен запуск какого-либо таймера, пока не было леФтричествА

Возьмите FRAM и пишите в него без забот. При определенной сноровке можете запаять его на место EEPROM, который, дополнительно приделан к RTC-чипу. Модули с DS3231 зачастую снабжаются епромкой, которая по ногам совпадает с FRAM

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

bwn пишет:
Борьба с пишущими курсачи.
не понял, но не важно, наверное :) На других форумах как-то справляются другими методами...

sadman41 пишет:
Возьмите FRAM и пишите в него без забот...
За идею спасибо, но у меня задача - сделать рабочую систему с минимум затрат времени и без рюшечек. SD уже есть в наличии, ничего перепаивать не надо. У меня еще такой вагон других тем для технического творчества, что просто не до "совершенства", говорю ж - перуанский стиль. Ты уходишь - сельва приходит, ты возвращаешься, начинай строить сначала. И смысл вставлять резные окна? Все одно терминты сгрызут :)

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

Angbor пишет:

sadman41 пишет:
Возьмите FRAM и пишите в него без забот...
За идею спасибо, но у меня задача - сделать рабочую систему с минимум затрат времени и без рюшечек. SD уже есть в наличии, ничего перепаивать не надо. У меня еще такой вагон других тем для технического творчества, что просто не до "совершенства", говорю ж - перуанский стиль. Ты уходишь - сельва приходит, ты возвращаешься, начинай строить сначала. И смысл вставлять резные окна? Все одно терминты сгрызут :)

Вы сами себе начинаете противоречить, хотите без рюшечек и излишеств и тут же прикручиваете SD карту, которая наличием механических контактов понижает надежность на порядки. При этом уже имеете на плате: родной EEPROM атмеги, 56 энергонезависимых байт в RTC и EEPROM на модуле RTC (если в виде модуля). 
Сорок первый предложил решение позволяющее не заботится о количестве перезаписей, в ответ на ваше опасение. 
А для обычной епромки давайте посчитаем: курей кормим 2-3, да хоть шесть раз в день. Ресурс внутреннего епрома 100 000 записей. Итого: 100 000/6/31/12 = 44,8 года если писать в одни и те же ячейки. У внешнего - увеличивается на порядок и получается запредельное количество годов. За сорок пять лет, по перуанской методологии, термиты три раза сожрут курей, птичник, девайс и хозяина))).
Лично я столько прожить уже не рассчитываю, а если и проживу, то вряд ли, будучи под сотню лет, смогу заниматься курями.((((

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

bwn пишет:
За сорок пять лет, по перуанской методологии, термиты три раза сожрут курей, птичник, девайс и хозяина
:)))))) мне нравится ваше чувство юмора

bwn пишет:
Вы сами себе начинаете противоречить, хотите без рюшечек и излишеств и тут же прикручиваете SD карту, которая наличием механических контактов понижает надежность на порядки.
Ну, на самом деле нет противоречия. Вопрос в том, что считать усложнением. Я не профессионал, а любитель. Для меня воткнуть SD проще и быстрее, чем вникать в то, как записывать String в EEPROM. В дальнейшем, когда я буду прекрасным образом разгружен от необходимости кормить трагладитов, то конечно, ближе к 90 годам жизни я изучу и этот вопрос :)

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

// объявили класс
class Auto_Feeder_timer 
{
ля-ля
}
const byte TIMERS_AMT = 4; // количество таймеров

// прототипы, укзатели функций
void feeder_ON_OFF ();
void (*ptrfeederOnOff)() = feeder_ON_OFF;

// устанавливаем таймеры
  
  Auto_Feeder_timer  food_timer("feed", feed_time, ptrfeederOnOff, 0); // создаем таймер
... еще один 
... еще один и т.д.  
  Auto_Feeder_timer *TIMER_ARR[] = {&food_timer, &еще один, &еще один и т.д.}; //массив указателей на экземпляры класса

// ну и дальше использую это вот так

for (byte i=0; i <TIMERS_AMT; i++){
  if (TIMER_ARR[i]-> getIsItTime(hour(t)*60*60UL + minute(t)*60+ second(t))) {
         RecordTimerResult(TIMER_ARR[i], t); // записываем в лог
    }

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

Или есть более элегантный метод?

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

Нда, вот они грабли :/

Все было красиво - таймеры, прерывания. И добрался до функции запуска сервы, которая спокойно себе сидела внизу, пока я до нее не добрался. И тут - бац: delay...

oid feeder_ON_OFF () {// открываем задвижку на секунду, закрываем

servo.write(65);

delay(2000); 
Serial.println("after first delay");
for(angle =65; angle < 155; angle++)
 {
   servo.write(angle);
delay(15);
 }
delay(1000);
Serial.println("after second delay");
for(angle = 155; angle > 65; angle--)
 {
   servo.write(angle);
   delay(15);
 }
}

Никакой delay не происходит, серва не работает. Если загрузить простой скетч (с тем же обвесом из RTC&CD) - конечно все работает. Т.е. дело в использовании delay в функции.

Теперь вот думаю, что же с этим сделать? Как реализовать задержки внутри функции?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Функция-то из обработчика прерывания вызывается, поди.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

угу, теперь вот думаю...

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

if (!TIMER_ARR[i]-> haveStartedToday && !TIMER_ARR[i]-> haveFinishedToday){
    TIMER_ARR[i]-> ptrOnFuniction();
    }

и теперь .... ыыыы... ку..., точнее кю

какие костыли... надо будет видимо класс для функции создать, с флагами, которые будут устанавливаться через millis и потом в конце всего поставить флаг haveFinishedToday. Потом раз в сутки в 00:00 все флаги и так сбрасываются...

а вы говорите EEPROM - piece of cake... тут бы основной код бы доделать до пятницы, а мне уезжать...

sadman41
Онлайн
Зарегистрирован: 19.10.2016

В обработчике прерывания другие прерывания не выполняются. Delay(), millis(), библиотека servo - все используют прерывания. Делайте выводы.

Мы вас не заставляли связываться с классами и программить на скорость.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

либо, чтобы развязаться с обработчиком ... через обработчик устанавливать только флаги, а функции вызывать из loop задействую millis

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Вобщем-то, подъем флагов в обработчике - это рекомендованный способ работы с прерываниями.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

sadman41 пишет:
Мы вас не заставляли связываться с классами и программить на скорость.

вас там несколько? ... а ну да, 41...

хотя для правильного ответа на вопрос одного не хватает ;)

sadman41 пишет:

Вобщем-то, подъем флагов в обработчике - это рекомендованный способ работы с прерываниями.

вот, наверно 42-й подошел :)

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

"Во многая знания, многая печали." Либо мне чудится, либо вы сами напридумывали себе проблем. Пока, все что я вижу, пишется спокойно на миллисах и прочих диджиталреад, а вы себе наворотили классов, прерываний, таймеров, про ассемблерные вставки только забыли. ИМХО.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

вполне возможно так и вышло

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

как этот мир еще не самоуничтожился..

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

хо-хо-хо.... смех безумного профессора...

//=========== настройка сервы
int servoPin = 10;
Servo servo;
int angleClose = 65; // угол сервы в градусах
int angleOpen = 155;
// флаги сервы
bool servoDelay = false;
bool servoIsOpen = false;
unsigned long servoMillis = 0;

в классе в вызов функции добавил ссылку на экземпляр класса и изменил соответственно прототипы и указатели

void (*ptrOnFuniction)(struct Auto_Feeder_timer *pTimer); // указатель на запускаемую функцию

// прототипы, укзатели функций
void feeder_ON_OFF (struct Auto_Feeder_timer *pTimer);
void (*ptrfeederOnOff)(struct Auto_Feeder_timer *pTimer) = feeder_ON_OFF;

в прерывании проверяем

   if (TIMER_ARR[i]-> haveStartedToday && !TIMER_ARR[i]-> haveFinishedToday){
    TIMER_ARR[i]-> ptrOnFuniction(TIMER_ARR[i]);
    }

и сама функция

void feeder_ON_OFF (struct Auto_Feeder_timer *pTimer) {// открываем задвижку на 2 с. и закрываем

if (!servoIsOpen) {
  servo.write(angleOpen);
  servoIsOpen = true;
}

if (!servoDelay) {
  if (millis() - servoMillis > 500) {
      servoDelay = true;
      servoMillis = millis();
  }
}
  
if (servoIsOpen && servoDelay) {
  if (millis() - servoMillis > 2000) {
    servo.write(angleClose);
    servoDelay = false;
    servoIsOpen = false;
    servoMillis = millis();
    pTimer -> haveFinishedToday = true;
  }
}
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Вам бы бритвой Оккама побриться...

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

Выложу-ка скетчик, пока не произошло что-то не предсказуемое :)

#include <SPI.h>
#include <SD.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <Servo.h>

//=========== настройка сервы
int servoPin = 10;
Servo servo;
int angleClose = 65; // угол сервы в градусах
int angleOpen = 155;
// флаги сервы
bool servoDelay = false;
bool servoIsOpen = false;
unsigned long servoMillis = 0;

// ========== настройки SD
const int chipSelect = 4;
File logFile;
File flagsFile;
byte IOErr = 0;

//============ установки времени включения 
//const unsigned long feed_time     = 82200;// 66900 18:35:00
unsigned long feed_time; // временно убираем константу для упрощения отладки
const unsigned long door_open     = 43200;//12:00:00
const unsigned long door_close    = 75600; // 21:00:00
const unsigned long water_pump_on = 67200; // 18:40:00

const int  my_delay      = 1000; // задержка чтения времени 1 сек
unsigned long prevMillis; // для замедлителя вызова таймеров

const byte TIMERS_AMT = 1; // количество таймеров

//=====================================================
//=================================
class Auto_Feeder_timer 
{
public: //private:
    String timerName;
    unsigned long start_time; // время включения формат ЧЧММСС
    void (*ptrOnFuniction)(struct Auto_Feeder_timer *pTimer); // указатель на запускаемую функцию
    bool haveStartedToday = false; // ставим после запуска 
    bool haveFinishedToday = false;// ставим после выполнения функции
    byte placeInArray;// где храним указатель на экземпляр 
  
    Auto_Feeder_timer (String tName, unsigned long tStart_Time, void f(), byte pInArray) 
    {
      timerName = tName;
      start_time = tStart_Time;// назначаем время старта
      ptrOnFuniction = f; // указатель на запускаемую функцию
      placeInArray = pInArray; //где будем хранить указатель 
    }

bool getIsItTime(unsigned long currentTime){
  if (currentTime >= start_time && !haveStartedToday) /* если текущее время равно времени старта
                                                             если система была выключена и пропустили время запуска 
                                                            (haveStartedToday не установлен), то все пропущенные задачи запустятся*/
    {
     haveStartedToday = true;
     return true;
    }
    else return false;
   }
};
//=============================
//=============================

// прототипы, укзатели функций
void feeder_ON_OFF (struct Auto_Feeder_timer *pTimer);
void (*ptrfeederOnOff)(struct Auto_Feeder_timer *pTimer) = feeder_ON_OFF;

// устанавливаем таймеры
  
  Auto_Feeder_timer  food_timer("feed", feed_time, ptrfeederOnOff, 0);
  Auto_Feeder_timer *TIMER_ARR[] = {&food_timer}; //массив указателей на экземпляры класса Таймер


void setup() {

 OCR0A = 0xAF;
 TIMSK0 |= _BV(OCIE0A);
 setSyncProvider(RTC.get);   // получаем время с RTC
  
 servo.attach(servoPin);
 servo.write(angleClose);
 servoIsOpen = false;
 
 prevMillis = millis();
 time_t t = now();

//!!!!!!!!!!!!!!!!!!! 
//!!!!!!!!!!!!!!!!!!! 
//!!!!!!!!!!!!!!!!!!! 
//!!!!!!!!!!!!!!!!!!! временно назначаем тут, время запуска +5 сек после загрузки
food_timer.start_time = hour(t)*60*60UL + minute(t)*60+ second(t) + 5;
//!!!!!!!!!!!!!!!!!!! 
//!!!!!!!!!!!!!!!!!!! 
//!!!!!!!!!!!!!!!!!!! 
//!!!!!!!!!!!!!!!!!!


 if (!SD.begin(chipSelect)) {
     IOErr = 1; // Card failed, or not present;
  }
}

SIGNAL(TIMER0_COMPA_vect)
{
 if (millis() - prevMillis >= my_delay) {

  prevMillis = millis();
  time_t t = now();

  for (byte i=0; i <TIMERS_AMT; i++){
    if (TIMER_ARR[i]-> getIsItTime(hour(t)*60*60UL + minute(t)*60+ second(t))) {// если возвращается true - значит таймер сработал
         RecordTimerResult(TIMER_ARR[i], t); // записываем в лог
    }
   if (TIMER_ARR[i]-> haveStartedToday && !TIMER_ARR[i]-> haveFinishedToday){// если таймер сработал, но функция еще не завершена
    TIMER_ARR[i]-> ptrOnFuniction(TIMER_ARR[i]);
    }
  }
  
  if (hour(t) == 0 && minute(t) == 0 && second(t) == 0) { // 
    resetFlags(); // сбрасываем флаги выполненных таймеров в 00:00 
  }
 }
}
  
void resetFlags() { 
 for (byte i=0; i <TIMERS_AMT; i++){
  TIMER_ARR[i]-> haveStartedToday  = false;
  TIMER_ARR[i]-> haveFinishedToday = false;
 }
}

void RecordTimerResult(struct Auto_Feeder_timer *pTimer, time_t t) {
  logFile = SD.open("log.txt", FILE_WRITE);
  if (logFile) {
  logFile.print(day(t));
  logFile.print("/");
  logFile.print(month(t));
  logFile.print("/");
  logFile.print(year(t));
  logFile.print(" ");
  logFile.print(hour(t));
  logFile.print(":");
  if (minute(t)<10) {logFile.print("0");};
  logFile.print(minute(t));
  logFile.print(":");
  if (minute(t)<10) {logFile.print("0");};
  logFile.print(second(t));
  logFile.print(" Timer '");
  logFile.print(pTimer -> timerName);
  logFile.println("' was started.");
  logFile.close();
  } else {
    IOErr = 2; // error opening test.txt
  }
}

void feeder_ON_OFF (struct Auto_Feeder_timer *pTimer) {// открываем задвижку на 2 с. b закрываем
 if (!servoIsOpen) {
  servo.write(angleOpen);
  servoIsOpen = true;
 }
 if (!servoDelay) {
  if (millis() - servoMillis > 500) {
      servoDelay = true;
      servoMillis = millis();
  }
 }
 if (servoIsOpen && servoDelay) {
  if (millis() - servoMillis > 2000) {
    servo.write(angleClose);
    servoDelay = false;
    servoIsOpen = false;
    servoMillis = millis();
    pTimer -> haveFinishedToday = true;
  }
 }
}

void loop() {
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Зачем вы вообще классы нагородили - можете объяснить? У вас что, куры из массива timeToFeed[4] не могут покормится? 

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

sadman41 пишет:
Вам бы бритвой Оккама побриться...
я не бреюсь, на юзерпике реальное положение дел

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

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

классы я нагородил по одной простой причине - я не профессионал, у меня нет берегов :)

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

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Да я просто не понимаю - то у вас времени нет, то накрутили с классами так, что не можете разобраться. 

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

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Angbor пишет:

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

У нас уже такой есть - qwone. Хватает одного.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

sadman41 пишет:
Да я просто не понимаю - то у вас времени нет, то накрутили с классами так, что не можете разобраться.
ну, с классами, как раз у меня никаких проблем и не было, равно как и с массивами указателей :) я просто попросил посмотреть, не упустил ли я чего по невнимательности, "городя" массивы классов
сложность возникла только с millis и прерываниями.
sadman41 пишет:
В RTC есть аларм - знаете?
нет, я не знал

sadman41 пишет:
У кур же, надеюсь, нет праздников и кормить их нужно в одно и то же время.
ну конечно же нет праздников. У кур бывает только пожизненный отпуск - это когда прописываешь топорин и в суп.

sadman41 пишет:
У нас уже такой есть - qwone. Хватает одного.
не согласен, хороших людей должно быть много :)

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

Это, может, смешно прозвучит, но очень прикольное ощущение, когда вот вдруг - ррраз - и массив указателей на экземпляры классов, и это работает :)

 

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

sadman41 пишет:

Angbor пишет:

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

У нас уже такой есть - qwone. Хватает одного.

Заразный однако.((((

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Кстати, про аларм в RTC. Я тут между дел поразмышлял: с ним и EEPROM не нужен. Достаточно найти в массиве с расписанием следующую временнУю точку, значение которой превышает ту, когда RTC дернул за прерывание МК. А если не нашли - установить алармом значение из timeToFeed[0].

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Angbor пишет:

sadman41 пишет:
В RTC есть аларм - знаете?
нет, я не знал

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

Angbor пишет:
sadman41 пишет:
У кур же, надеюсь, нет праздников и кормить их нужно в одно и то же время.
ну конечно же нет праздников. У кур бывает только пожизненный отпуск - это когда прописываешь топорин и в суп.

А вот и неправда ваша, праздники у них бывают, когда линяют не несуться заразы сколько ни корми. Это постов у  них нет.

Angbor пишет:
а если серьезно, то да, конечно, можно было, видимо и проще все решить. Но так уж вышло, что я начал с классами, сейчас все переиначивать нет времени (уже ведь работает).

Это, может, смешно прозвучит, но очень прикольное ощущение, когда вот вдруг - ррраз - и массив указателей на экземпляры классов, и это работает :)

Я сделал проще, в меню можно задавать 3 времени кормления. Этого более чем достаточно для кур. Часы есть, время сравнивается и если куры не кормлены включает кормушку. После выключения кормушки пишем в епром что 1 (2,3) кормление уже было и опционально можно поставить отправку смс. Так что если свет отключат то видно кормили кур или нет.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

RTC Library  This library allows an enables an Arduino based on SAMD architectures (es. Zero, MKRZero or MKR1000 Board) to control and use the internal RTC (Real Time Clock).

при использовании с Leonardo я получаю

'class RTC_DS1307' has no member named 'setAlarmTime'

https://www.arduino.cc/en/Tutorial/SimpleRTCAlarm

Этот пример расчитан только на Arduino Zero or MKRZero or MKR1000. Так что, увы, никакие alarm-ы в rtc с моими Leonardo и ds1307 мне не доступны.

Или речь о какой-то другой библиотеке?

...

Собственно, вот ответ:
By looking at DS1307 datasheet it seems that it doesn't support an alarm. Some other RTCs have alarm interrupt output that alerts the controller.  I believe that you would have to deal with alarm in the software. You would have to check for alarm condition by comparing the current time with the alarm variable in software.

Так что возвращаемся к нашим граб... клас... бритвам Ока... короче, посмотрим :)

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

Проблема :)

Мой "наворот с классами" прерасно отрабатывает при включении и ручном изменении времени старта через монитор порта. Но если его оставить на несколько  минут, плата наглухо зависает... Риторический вопрос - почему?

Смотрю в сторону

https://platformio.org/lib/show/953/DS1307newAlarms/examples

sadman41
Онлайн
Зарегистрирован: 19.10.2016

На DS3231, например, есть алармы: https://github.com/Makuna/Rtc/wiki/RtcDS3231-AlarmOne

На DS1307... сходу только приходит идея читать RTC кажные (60*1000) ms и сравнивать с массивом. Тоже не особо накладно. Костыльно, конечно, выглядит, но это ж 1307. Он еще и идти будет неточно.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

то, что не точно, это я уже заметил, но куры простят отклонение в 5-10 минут... да даже в 15.

А про костыли... ну после моих ходуль, да еще и учитывая, что я отключаю питание сервы после выполнения цикла с помощью реле... Костылем больше-костылем мньше.

Попробую запитаться не от USB, а то действительно виснет наглухо. Если не поможет, попробую переписать по-простому... [овации в зале и крики: "наконец-то..." "да, сдрейфил просто..." "эх, мы так и не полетим к звездам..."

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

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

Я отключил всю перефирию, но все равно через 5 минут плата висла наглухо. Отключил RTC библиотеку, все работает...

И в чем может быть проблема?

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Если есть возможность уйти на DS3231 то уходи. Цена вопроса 100р, у меня курятник на ней 8 мес уже работает, ничего не виснет.
А так мало ли, если на макетке все соединения проверь, може где плохой коннект. Да и 5 минут это не плавающая ошибка которая то есть то нет, сделай вывод в сериал времени каждую секунду, будет зависать через 5 минут? Ну тады надо смотреть или другую библиотеку или другой модуль. 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Angbor пишет:
И в чем может быть проблема?

опять где-то провтыкали int и unsigned long. вот и где-то идет переполнение int $))

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

qwone пишет:
опять где-то провтыкали int и unsigned long. вот и где-то идет переполнение int $))

Не, врядли. Я отключил весь обвес и просто отключил библиотеку DS1307, весь остальной код остался прежним - и ничего не виснет.

Andrey12 пишет:
Если есть возможность уйти на DS3231 то уходи. Цена вопроса 100р, у меня курятник на ней 8 мес уже работает, ничего не виснет.
Но она виснет даже если DS1307 модуль физически отключен...

так что я пока пробую другие библиотеки.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

даааа.... докатился

void loop () {
 
 if (millis() - mainMillis >= 1000) {
    DateTime now = rtc.now();
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();

   if ( now.hour()*60*60UL + now.minute()*60+ now.second() >= feed_time && !feedFlag) {
    Serial.println("oy way ");
    servostart();
    feedFlag = true;
  }
  mainMillis = millis();
 }
}

bool servostart() {
  digitalWrite(feedRelay, LOW);
  delay(500);
  servo.write(angleOpen);
  delay(2000);
  servo.write(angleClose);
  delay(500);
  digitalWrite(feedRelay, HIGH);
  return true;
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Другое дело! Уже по-нашенски, по-ардуински.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

sadman41 пишет:

Другое дело! Уже по-нашенски, по-ардуински.

Точно именно это

  digitalWrite(feedRelay, LOW);
  delay(500);
  servo.write(angleOpen);
  delay(2000);
  servo.write(angleClose);
  delay(500);
  digitalWrite(feedRelay, HIGH);

Кашу г**м(маслом) не испортишь.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

другая библиотека не глючила, но отказывалась работать с моим hi style ...

Пришлось опроститься...

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <Servo.h>

RTC_DS1307 rtc;
unsigned long mainMillis;

//=========== настройка сервы
int servoPin = 10;
Servo servo;
int angleClose = 65; 
int angleOpen = 155;

// настройка реле
int feedRelay = 7;

// ========== настройки SD
const int chipSelect = 4;
File logFile;
byte IOErr = 0;

//============ установки времени включения 
//const unsigned long feed_time     = 82200;// 66900 18:35:00
unsigned long feed_time; // временно убираем константу

bool feedFlag = false;

void setup () {

  Serial.begin(57600);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
  }
  
  mainMillis = millis(); 
  DateTime now = rtc.now();
  feed_time = now.hour()*60*60UL + now.minute()*60+ now.second() + 5;
  
  pinMode(feedRelay, OUTPUT);
  
  servo.attach(servoPin);

  if (!SD.begin(chipSelect)) {
     IOErr = 1; // Card failed, or not present;
  }
}

void loop () {
 
 if (millis() - mainMillis >= 1000) {
    DateTime t = rtc.now();

   if ( t.hour()*60*60UL + t.minute()*60+ t.second() >= feed_time && !feedFlag) {
    Serial.println("oy way ");
    servostart();
    RecordTimerResult(t);
    delay(500);
    readFromSD ();
    feedFlag = true;
  }
  mainMillis = millis();
 }

if (Serial.available()) { //поступила команда с временем
     String tmp = Serial.readStringUntil('\n');
     feed_time = tmp.toInt();
     feedFlag = false;
     Serial.print("Timer starts at: ");
     Serial.println(tmp);
  }
 
}

bool servostart() {
  digitalWrite(feedRelay, LOW);
  delay(500);
  servo.write(angleOpen);
  delay(2000);
  servo.write(angleClose);
  delay(500);
  digitalWrite(feedRelay, HIGH);
  return true;
}

void RecordTimerResult(DateTime t) {
  logFile = SD.open("log.txt", FILE_WRITE);
  if (logFile) {
  logFile.print(t.day());
  logFile.print("/");
  logFile.print(t.month());
  logFile.print("/");
  logFile.print(t.year());
  logFile.print(" ");
  logFile.print(t.hour());
  logFile.print(":");
  if (t.minute()<10) {logFile.print("0");};
  logFile.print(t.minute());
  logFile.print(":");
  if (t.second()<10) {logFile.print("0");};
  logFile.print(t.second());
  logFile.println(" Timer 'feed' was started.");
  logFile.close();
  } else {
    IOErr = 2; // error opening test.txt
  }
}

void readFromSD () {
  logFile = SD.open("log.txt");

  if (logFile) {
    Serial.println("log.txt:");
    // читаем из файла, пока не достигнем конца файла:
    while (logFile.available()) {
      Serial.write(logFile.read());
    }
    // закрываем файл:
    logFile.close();
  } else {
    // а если не открылся, то пишем об ошибке:
    Serial.println("error opening log.txt");
  }
 /* if(SD.exists("log.txt")){ // сбрасываем содержимое файла  
    SD.remove("log.txt");}*/
}

Тут немного отладочных кусков осталось, потом выброшу.

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Ну да Конечный автомат используют дураки, настоящие ардуинщики пользуются delay.

ПС:#24 ну это как вариант.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

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

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

призрак ходит по европе, классами зовется.

phoenixoid
Offline
Зарегистрирован: 14.07.2015

https://habr.com/post/217985/

4 года уже работает. Поток отсекает на ура.

Angbor
Angbor аватар
Offline
Зарегистрирован: 26.10.2015

...