Прошу замечаний по таймеру

std
Offline
Зарегистрирован: 05.01.2012
if(timeNow<timeOn  && timeNow<timeOff)  onFlag=1;
if(timeNow>=timeOn && timeNow<timeOff)  onFlag=1;
if(timeNow>=timeOn && timeNow>=timeOff) onFlag=0;
if(timeNow<timeOn  && timeNow>=timeOff) onFlag=0;

Надо включать в пределах суток релюху. Есть ли в приведённом коде камни, над которыми программа может тупить? Время выключения может быть меньше (абсолютно) чем время включения, то есть наример включить в 10 вечера, выключить в час ночи.

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

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

X-Dron
Offline
Зарегистрирован: 24.01.2015

А тип переменных timeNow,timeOn,timeOff узнать можно? Как они вообще сформированы.

std
Offline
Зарегистрирован: 05.01.2012
int timeNow=rtc.hours()*60+rtc.minutes();

И в этом духе. А тип rtc.xxx() - byte.

Щас кстати лютая магия случилась. Во флеше есть массив с режимом работы на год. Так вот, если читать его в pgm_read(), портится одна из переменных в функции опроса клавиатуры. В результате меню сходит с ума, хаотично мигая экраном. Кнопок пять, и приходится на тройку, которая служит клавишей "налево". Если заменить на скажем 7, то работает как задумано. Но не всплывёт ли оно потом ещё где-то - хз. Свободной памяти 1686, из периферии только кнопки, 1602 и 1307. Как лучше, тут багу описать или отдельную тему сделать? А то клавиатурная функция большая, там прерывание чтобы отличать - когда кнопка нажимается 1 раз, а когда удерживается нажатой долго.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Давай весь код под спойлер, посмотрим

std
Offline
Зарегистрирован: 05.01.2012
#include <Wire.h>
#include <RTClib.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <MemoryFree.h>
#include <avr/pgmspace.h>

#define out_dis (3)
#define out_en  (4)

#define btn1 (5)
#define btn2 (9)
#define btn3 (8)
#define btn4 (6)
#define btn5 (7)

#define reprate    (200)    // repeat rate, ms
#define btnsingthr (500)    // hold thresholds, ms
#define btndecthr  (2000)

LiquidCrystal lcd(1,0,A0,A1,A2,A3); //RS,E,D4-D7
RTC_DS1307 rtc;

/* Keycodes:
  1: up    3: <
  2: dn    4: >
  5: set;

  1..4   - press
  11..14 - hold
  21..24 - hold more
  0      - no key          */
/* Modes:
  0: idle

  1: set day    4: hour
  2: mon        5: min
  3: year       6: sec
  
  7: set ON offset
  8: set OFF time
  9: 0
                          */
const uint8_t dom[12] PROGMEM={31,28,31,30,31,30,31,31,30,31,30,31};
const uint8_t twhr[12][31] PROGMEM={
  {17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17},
  {17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18},
  {18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19},
  {19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20},
  {20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21},
  {21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21},
  {21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,20,20},
  {20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,19,19,19},
  {19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18,18,18,18,18,18,18},
  {18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17,17,17,17,17},
  {17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17},
  {17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17}
};
const uint8_t twmn[12][31] PROGMEM={
  {17,18,19,20,21,22,23,24,25,26,28,29,30,32,33,34,36,37,38,40,41,43,44,46,47,49,50,52,54,55,57},
  {58, 0, 2, 3, 5, 6, 8,10,11,13,15,16,18,19,21,23,24,26,28,29,31,33,34,36,38,39,41,42,44,44,44},
  {44,46,47,49,51,52,54,55,57,59, 0, 2, 4, 5, 7, 8,10,12,13,15,17,18,20,22,23,25,27,28,30,31,33},
  {35,36,38,40,41,43,45,47,48,50,52,53,55,57,58, 0, 2, 4, 5, 7, 9,11,12,14,16,18,19,21,23,25,26},
  {26,28,30,32,33,35,37,38,40,42,44,45,47,49,50,52,54,55,57,58, 0, 2, 3, 5, 6, 8, 9,10,12,13,14},
  {16,17,18,19,20,21,22,23,24,25,26,27,27,28,29,29,30,30,31,31,31,31,32,32,32,32,32,31,31,31,31},
  {31,30,30,29,29,28,27,27,26,25,24,23,22,21,20,19,18,17,15,14,13,11,10, 8, 7, 5, 4, 2, 1,59,57},
  {55,54,52,50,48,46,44,42,41,39,37,35,33,30,28,26,24,22,20,18,16,14,11, 9, 7, 5, 3, 0,58,56,54},
  {51,49,47,45,42,40,38,35,33,31,29,26,24,22,20,17,15,13,10, 8, 6, 4, 1,59,57,55,52,50,48,46,44},
  {44,41,39,37,35,33,31,28,26,24,22,20,18,16,14,12,10, 8, 6, 4, 2, 0,59,57,55,53,51,50,48,46,44},
  {43,41,40,38,36,35,33,32,31,29,28,27,25,24,23,22,21,20,18,17,16,16,15,14,13,12,12,11,10,10, 9},
  { 9, 9, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 9, 9,10,10,11,12,12,13,14,15,16}
};

byte mode=100;         // mode
byte ontime=0;         // ON offset time
int  offtime=0;        // OFF absolute time
byte tmp;              // data buffer

int timeNow, timeOn, timeOff;  // time buffers

byte time[7]={0,0,0,0,0,0,99};
volatile boolean       bKeyDown=false,
                       bPrevKeyDown=false;
volatile byte          nKey,prevKey;
unsigned long tot_key,last_key;
boolean  bKeyPressing=false;

// ----------------------- Mode --------------------------------------------------------------------------------

void changeMode(byte newMode){
  DateTime now;
//  delay(200); ////////////DEBUG
  if(mode>=1 && mode<=6 && time[7]!=99) rtc.adjust(DateTime(time[0]+2000, time[1], time[2], time[3], time[4], time[5])); // store RTC time
  if(mode==7){                                       // store ON offset time
    if(EEPROM.read(0)!=ontime) EEPROM.write(0,ontime);
  }
  if(mode==8){                                       // store OFF time
    tmp=(byte)(offtime/256);
    if(EEPROM.read(1)!=tmp) EEPROM.write(1,tmp);
    tmp=offtime-(tmp*256);
    if(EEPROM.read(2)!=tmp) EEPROM.write(2,tmp);
  }
  now=rtc.now();
  mode=newMode;
  //if(mode==7 || mode==8)
//  lcd.clear();  // DEBUG
  if(mode>0){                           // cursor mode
    lcd.blink();
    lcd.cursor();
  }else{
    lcd.noBlink();
    lcd.noCursor();
  }
  lcd.setCursor(0,0);                   // initial print
  if(mode==7){
    lcd.print("ON offset:");
    showOnTime();
  }
  if(mode==8){
    lcd.print("OFF time:");
    showOffTime();
  }
  if(mode>=1 && mode<=6){
    lz(now.day());       // d
    lcd.print("/");
    lz(now.month());     // m
    lcd.print("/20");
    lz(now.year()-2000); // y
    lcd.setCursor(0,1);
    lz(now.hour());      // hh
    lcd.print(":");
    lz(now.minute());    // mm
    lcd.print(":");
    lz(now.second());    // ss
  }
  switch(mode){                         // initial cursor
    case 1: lcd.setCursor(0,0); break;
    case 2: lcd.setCursor(3,0); break;
    case 3: lcd.setCursor(8,0); break;
    case 4: lcd.setCursor(0,1); break;
    case 5: lcd.setCursor(3,1); break;
    case 6: lcd.setCursor(6,1); break;
  }
}

// ----------------------- Keyboard ----------------------------------------------------------------------------

void kbdFlag(){
  if(digitalRead(2)){          // rising
    prevKey=nKey;
    nKey=0;
    bKeyDown=false;
  }else{                       // falling
    nKey=0;
    if(!digitalRead(btn1)) nKey=1;
    if(!digitalRead(btn2)) nKey=2;
    if(!digitalRead(btn3)) nKey=7;  //DEBUG
    if(!digitalRead(btn4)) nKey=4;
    if(!digitalRead(btn5)) nKey=5;
    bKeyDown=true;
  }
}

byte kbdMain(){  
  byte kbdres;
  if(bPrevKeyDown!=bKeyDown){        // interrupt noise reduction
    bPrevKeyDown=bKeyDown;
    delay(10);
  }
  if(!bKeyPressing && bKeyDown){     // press
    if(nKey!=0) tot_key=millis();
    bKeyPressing=true;
    return 0;
  }
  if(bKeyPressing && bKeyDown){      // repeat
    if(millis()-tot_key>=btndecthr) kbdres=nKey+20;
      else if(millis()-tot_key>=btnsingthr) kbdres=nKey+10;
  }
  if(bKeyPressing && !bKeyDown){     // release
    if(millis()-tot_key<btnsingthr) kbdres=prevKey;
     else kbdres=0;
    bKeyPressing=false;
  }
  if(kbdres<10) return kbdres;       // return press code immediately
   else{
    if(millis()-last_key>=reprate){  // return hold code at repeat rate
      last_key=millis();
      return kbdres;
    }
  }
}

// ------------------------------------------------- Leading zero
void lz(byte arg){
 if(arg<10) lcd.print("0");
 lcd.print(arg);
}

// ------------------------------------------------- Show clock time while change
void showTime(byte x,byte y,byte data){
  lcd.setCursor(x,y);
  lz(data);
  lcd.setCursor(x,y);
};

// ------------------------------------------------- Show ON offset time while change
void showOnTime(){
  lcd.setCursor(0,1);
  lcd.print("      ");
  lcd.setCursor(0,1);
  if(ontime<120){
    lcd.print("-");
    tmp=(byte)(ontime/60);
    if(ontime==60 || ontime==0) tmp--;
    lz(1-tmp);
  }else{
    lcd.print("+");
    tmp=(byte)((ontime-120)/60);
    lz(tmp);
  }
  lcd.print(":");
  if(ontime<120){
    tmp=60-ontime%60;
    if(tmp==60) tmp=0;
    lz(tmp);
  }else{
    lz((ontime-120)%60);
  }
  lcd.setCursor(0,7);
};

// ------------------------------------------------- Show OFF time while change
void showOffTime(){
  lcd.setCursor(0,1);
  lcd.print("     ");
  lcd.setCursor(0,1);
  lz((byte)(offtime/60));
  lcd.print(":");
  lz(offtime%60);
  lcd.setCursor(0,6);
};

// ----------------------- Setup -------------------------------------------------------------------------------

void setup(){
  Wire.begin();
  rtc.begin();
  lcd.begin(16,2);
/*
  byte i,j,k,l;
  for(i=0;i<=11;i++){
   for(j=0;j<=30;j++){
     k=pgm_read_byte(&(twhr[i][j]));
     l=pgm_read_byte(&(twmn[i][j]));
   }
  }*/
//  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));     // set system time (uncomment before uploading)
  rtc.adjust(DateTime(2010, 1, 1, 4, 0, 0)); /// DEBUG
  pinMode(2,INPUT_PULLUP);     // int0
  pinMode(btn1,INPUT_PULLUP);  // btns
  pinMode(btn2,INPUT_PULLUP);
  pinMode(btn3,INPUT_PULLUP);
  pinMode(btn4,INPUT_PULLUP);
  pinMode(btn5,INPUT_PULLUP);
  attachInterrupt(0,kbdFlag,CHANGE);
  last_key=millis();
  nKey=0;
  ontime=EEPROM.read(0);        // switch ON offset read
  if(ontime>240) ontime=120;     // validate
  if(EEPROM.read(0)!=ontime) EEPROM.write(0,ontime); // store
  offtime=EEPROM.read(1)*256;   // switch OFF time
  offtime+=EEPROM.read(2);
  if(offtime>1439 || offtime<0) offtime=60;    // validate
  tmp=(byte)(offtime/256);     //store
  if(EEPROM.read(1)!=tmp) EEPROM.write(1,tmp);
  tmp=offtime-(tmp*256);
  if(EEPROM.read(2)!=tmp) EEPROM.write(2,tmp);
  changeMode(0);
}

// ----------------------- Main --------------------------------------------------------------------------------

void loop (){
  DateTime now = rtc.now();
  byte daymax;

  switch(mode){
  //--------------------------------------------------------- idle
  case 0:
    time[0]=now.year()-2000;         // get new time
    time[1]=now.month();
    time[2]=now.day();
    time[3]=now.hour();
    time[4]=now.minute();
    time[5]=now.second();

  noInterrupts();
    tmp=pgm_read_byte(&(twhr[time[1]-1][time[2]-1]));
    timeOn=tmp*60;
    tmp=pgm_read_byte(&(twmn[time[1]-1][time[2]-1]));
  interrupts();
    timeOn+=tmp;
    timeOff=offtime;
    timeNow=(time[4]*60)+time[5];

/*    if(timeNow<timeOn && timeNow<timeOff) tmp=1;
    if(timeNow>=timeOn && timeNow<timeOff) tmp=1;
    if(timeNow>=timeOn && timeNow>=timeOff) tmp=0;
    if(timeNow<timeOn && timeNow>=timeOff) tmp=0;*/

    ////////////////                       DEBUG
    if((byte)(time[5]/10)%2==0){
       digitalWrite(out_dis,1);
       digitalWrite(out_en,0);
    }else{
       digitalWrite(out_dis,0);
       digitalWrite(out_en,1);
    }
    ////////////////
    if(time[5]!=time[6]){            // new time
      time[6]=time[5];               // store
      lcd.setCursor(0,0);
      lz(time[2]);     // d
      lcd.print("/");
      lz(time[1]);     // m
      lcd.print("/20");
      lz(time[0]);     // y
      lcd.setCursor(0,1);
      lz(time[3]);     // hh
      lcd.print(":");
      lz(time[4]);     // mm
      lcd.print(":");
      lz(time[5]);     // ss
    lcd.setCursor(12,1);
    lcd.print(freeMemory());

/*    lcd.setCursor(6,0);
    lcd.print("    ");
    lcd.setCursor(6,0);
    lcd.print(timeOn);
    lcd.setCursor(10,0);
    lcd.print(">");
    lcd.setCursor(11,0);
    lcd.print("    ");
    lcd.setCursor(11,0);
    lcd.print(timeOff);
    lcd.setCursor(11,1);
    lcd.print("    ");
    lcd.setCursor(11,1);
    lcd.print(timeNow);

    lcd.setCursor(14,1);
    lcd.print(tmp);*/
    }

    if(kbdMain()==25) changeMode(1);
  break;
  //--------------------------------------------------------- set day
  case 1:
    switch(kbdMain()){
    case 7: changeMode(8); break;               // mode
    case 4: changeMode(2); break;
    case 5: changeMode(0); break;
    case 1:                                     // change
    case 11:
      daymax=pgm_read_byte(dom+time[1]-1);
      if(time[1]==2 && time[0]%4==0) daymax++;
      if(time[2]<daymax) time[2]++;
      showTime(0,0,time[2]);
    break;
    case 21:
      daymax=pgm_read_byte(dom+time[1]-1);
      if(time[1]==2 && time[0]%4==0) daymax++;
      if(time[2]<daymax-9) time[2]+=10;
       else time[2]=daymax;
      showTime(0,0,time[2]);
    break;
    case 2:
    case 12:
      if(time[2]>1) time[2]--;
      showTime(0,0,time[2]);
    break;
    case 22:
      if(time[2]>10) time[2]-=10;
       else time[2]=1;
      showTime(0,0,time[2]);
    break;
    }
  break;
  //--------------------------------------------------------- set month
  case 2:
    switch(kbdMain()){
    case 7: changeMode(1); break;               // mode
    case 4: changeMode(3); break;
    case 5: changeMode(0); break;
    case 1:                                     // change
    case 11:
      if(time[1]<12) time[1]++;
      showTime(3,0,time[1]);
    break;
    case 2:
    case 12:
      if(time[1]>1) time[1]--;
      showTime(3,0,time[1]);
    break;
    }
  break;
  //--------------------------------------------------------- set year
  case 3:
    switch(kbdMain()){
    case 7: changeMode(2); break;               // mode
    case 4: changeMode(4); break;
    case 5: changeMode(0); break;
    case 1:                                     // change
    case 11:
      if(time[0]<99) time[0]++;
      showTime(8,0,time[0]);
    break;
    case 21:
      if(time[0]<90) time[0]+=10;
       else time[0]=99;
      showTime(8,0,time[0]);
    break;
    case 2:
    case 12:
      if(time[0]>1) time[0]--;
      showTime(8,0,time[0]);
    break;
    case 22:
      if(time[0]>9) time[0]-=10;
       else time[0]=0;
      showTime(8,0,time[0]);
    break;
    }
  break;
  //--------------------------------------------------------- set hour
  case 4:
    switch(kbdMain()){
    case 7: changeMode(3); break;               // mode
    case 4: changeMode(5); break;
    case 5: changeMode(0); break;
    case 1:                                     // change
    case 11:
      if(time[3]<23) time[3]++;
      showTime(0,1,time[3]);
    break;
    case 21:
      if(time[3]<14) time[3]+=10;
       else time[3]=23;
      showTime(0,1,time[3]);
    break;
    case 2:
    case 12:
      if(time[3]>1) time[3]--;
      showTime(0,1,time[3]);
    break;
    case 22:
      if(time[3]>9) time[3]-=10;
       else time[3]=0;
      showTime(0,1,time[3]);
    break;
    }
  break;
  //--------------------------------------------------------- set minute
  case 5:
    switch(kbdMain()){
    case 7: changeMode(4); break;               // mode
    case 4: changeMode(6); break;
    case 5: changeMode(0); break;
    case 1:                                     // change
    case 11:
      if(time[4]<59) time[4]++;
      showTime(3,1,time[4]);
    break;
    case 21:
      if(time[4]<50) time[4]+=10;
       else time[4]=59;
      showTime(3,1,time[4]);
    break;
    case 2:
    case 12:
      if(time[4]>0) time[4]--;
      showTime(3,1,time[4]);
    break;
    case 22:
      if(time[4]>9) time[4]-=10;
       else time[4]=0;
      showTime(3,1,time[4]);
    break;
    }
  break;
  //--------------------------------------------------------- set second
  case 6:
    switch(kbdMain()){
    case 7: changeMode(5); break;               // mode
    case 4: changeMode(7); break;
    case 5: changeMode(0); break;
    case 1:                                     // change
    case 11:
      if(time[5]<59) time[5]++;
      showTime(6,1,time[5]);
    break;
    case 21:
      if(time[5]<50) time[5]+=10;
       else time[5]=59;
      showTime(6,1,time[5]);
    break;
    case 2:
    case 12:
      if(time[5]>0) time[5]--;
      showTime(6,1,time[5]);
    break;
    case 22:
      if(time[5]>9) time[5]-=10;
       else time[5]=0;
      showTime(6,1,time[5]);
    break;
    }
  break;
  //--------------------------------------------------------- set ON offset
  case 7:
    switch(kbdMain()){
    case 7: changeMode(6); break;
    case 4: changeMode(8); break;
    case 5: changeMode(0); break;
    case 1:
    case 11: //+
      if(ontime<240) ontime++;
      showOnTime();
    break;
    case 21:
      if(ontime<230) ontime+=10;
       else ontime=240;
      showOnTime();
    break;
    case 2:
    case 12: //-
      if(ontime>0) ontime--;
      showOnTime();
    break;
    case 22:
      if(ontime>9) ontime-=10;
       else ontime=0;
      showOnTime();
    break;
    }
  break;
  //--------------------------------------------------------- set OFF time
  case 8:
    switch(kbdMain()){
    case 7: changeMode(7); break;
    case 4: changeMode(1); break;
    case 5: changeMode(0); break;
    case 1:
    case 11: //+
      if(offtime<1439) offtime++;
      showOffTime();
    break;
    case 21:
      if(offtime<1430) offtime+=10;
       else offtime=1439;
      showOffTime();
    break;
    case 2:
    case 12: //-
      if(offtime>0) offtime--;
      showOffTime();
    break;
    case 22:
      if(offtime>9) offtime-=10;
       else offtime=0;
      showOffTime();
    break;
    }
  break;
  }
}

про DEBUG:

закомментированная delay(200) в смене режимов - позволило выяснить, что левая кнопка "залипла".

nKey=7; - лекарство. Была цифра 3.

rtc.adjust() в setup() - пока тестовое время, чтобы держать часы в начале суток.

if((byte)(time[5]/10)%2==0) - переключает зелёный/красный светодиод каждые 10 с. Можно выкинуть.
std
Offline
Зарегистрирован: 05.01.2012

Новое условие

    if(timeNow>=timeOn && timeNow<timeOff)  onFlag=true;
    if(timeNow<timeOn  && timeNow>=timeOff) onFlag=false;

UPD. не пашет. Сейчас ещё что-нибудь придумаю или AND удалю.

UPD2. Да, всё значительно проще.

    if(timeNow>=timeOn)  onFlag=true;
     else if(timeNow>=timeOff) onFlag=false;

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Из того, что пока что увидел:

Использование time[7] некрасиво ;) Максимальный индекс равен 6, а не 7. Я тебе потом могу выслать как удобней оформить данные, там сразу многое пишется проще и наглядней, тем более скетч уже слегка немаленький ;)

mode > 0 - не очень хорошо читается, лучше mode != 0, признак того, что режим не Idle. Хотя это философский вопрос.

nKey = 7; - это та же проблема, у тебя был код 3 в комментариях, а в switch везде 7, потому и не работало.

Ну вот и главные "глюки":

  noInterrupts();
    tmp=pgm_read_byte(&(twhr[time[tiMonth]-1][time[tiDay]-1]));
    timeOn=tmp*60;
    tmp=pgm_read_byte(&(twmn[time[tiMonth]-1][time[tiDay]-1]));
  interrupts();
    timeOn+=tmp;

заменить на
  noInterrupts();
    timeOn=pgm_read_byte(&(twhr[time[tiMonth]-1][time[tiDay]-1]));
    timeOn *= 60;
    tmp=pgm_read_byte(&(twmn[time[tiMonth]-1][time[tiDay]-1]));
  interrupts();
    timeOn+=tmp;

иначе будет переполнение tmp при умножении на 60. (умножь 17 на 60 и сам поймешь, что это больше байта).

Аналогично:

    timeNow=(time[tiMin]*60)+time[tiSec];
Хотя бы заменить на:
    timeNow = time[tiMin];
    timeNow *= 60;
    timeNow += time[tiSec];
чтобы не было переполнения.

Вот еще переполнение, ищи в тексте все такие операции с переполнением:

offtime=EEPROM.read(1)*256;   // switch OFF time
EEPROM.read возвращает uint8_t и при умножении будет переполнение.
Можно тупо заменить на:
offtime=EEPROM.read(1);
offtime *= 256;

О птичках, насколько я понял, в EEPROM хранятся часы и минуты, когда нужно стартовать или остановить, так? Тогда почему     timeNow=(time[4]*60)+time[5]; Это же время в минутах и секундах.

    timeNow= time[3]; timeNow *= 60; timeNow += time[4]; Было бы больше "к лицу" :) Нет?

Щас еще гляжу сравнение.

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

Это один из вариантов, есть и другие.

std
Offline
Зарегистрирован: 05.01.2012

Спасибо откликнувшимся!

kisoft пишет:
Использование time[7] некрасиво ;) Максимальный индекс равен 6, а не 7

О, действительно в тексте было [7], теперь там [6] :) Это чтобы обнаружить, что изменилась секунда. По этому событию показывается новое время на LCD.

kisoft пишет:
mode > 0 - не очень хорошо читается

Там просто ещё два режима для "показать", 11-12, в которых надо скрыть курсор. Так что сейчас

if(mode>0 && mode<11)

kisoft пишет:
nKey = 7; - это та же проблема, у тебя был код 3 в комментариях, а в switch везде 7, потому и не работало.

Это и есть та самая PFM. Откуда-то мистическим образом kbdMain() возвращает эту тройку, хотя кнопки НЕ нажимаются. То есть гонит в return 3, хотя должен быть ноль. Странно также, что ноль возвращается, по настоящему. Его можно увидеть непосредственно перед кодом кнопки, то есть жмём кнопку, получаем вместо тройки ноль, потом код кнопки, потом через время btnsingthr - код кнопки+10 и так далее. Откуда 3 - непонятно. Если не обращаться к pgm_read(), то вместо 3 нормально - 0.

По поводу переполнения, теперь progmem массив читается в переменную типа int, что делает необязательным разбивание на куски. Переменная tmp остаётся в функциях EEPROM, там где надо отнять. Допустимо ли это:

  tmp=(byte)(offtime/256);
  tmp=offtime-(tmp*256);

tmp - byte, offtime - int. Это чтобы получить в tmp правый байт offtime. Вообще, что здесь лучше, highByte()/lowByte, сдвиг или можно оставить так?

kisoft пишет:
timeNow= time[3]; timeNow *= 60; timeNow += time[4]; Было бы больше "к лицу" :)

Это дебажный zoom, чтобы увидеть весь день за 24 минуты.

kisoft пишет:
1. При переходе через 0, будут проблемы.

Уже обнаружены. Лечение пока такое:

    if(timeOn<timeOff){
      if(timeNow>=timeOn && timeNow<timeOff) onFlag=true;
       else onFlag=false;
    }else{
      if(timeNow<timeOn && timeNow>=timeOff) onFlag=false;
       else onFlag=true;
    }

Дня и месяца не будет.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

std пишет:

kisoft пишет:
nKey = 7; - это та же проблема, у тебя был код 3 в комментариях, а в switch везде 7, потому и не работало.

Это и есть та самая PFM. Откуда-то мистическим образом kbdMain() возвращает эту тройку, хотя кнопки НЕ нажимаются. То есть гонит в return 3, хотя должен быть ноль. Странно также, что ноль возвращается, по настоящему. Его можно увидеть непосредственно перед кодом кнопки, то есть жмём кнопку, получаем вместо тройки ноль, потом код кнопки, потом через время btnsingthr - код кнопки+10 и так далее. Откуда 3 - непонятно. Если не обращаться к pgm_read(), то вместо 3 нормально - 0.

Ага, теперь понятно. Значит я не совсем правильно понял. Хотя вроде у меня и с 3 работает нормально (на макетке собрал с Леонардо).

std пишет:

По поводу переполнения, теперь progmem массив читается в переменную типа int, что делает необязательным разбивание на куски. Переменная tmp остаётся в функциях EEPROM, там где надо отнять. Допустимо ли это:
  tmp=(byte)(offtime/256);
  tmp=offtime-(tmp*256);

'tmp * 256' переполнится. Можно попробовать   tmp = offtime - (int(tmp) * 256);

Впрочем highByte & lowByte будет проще.

std пишет:

kisoft пишет:
1. При переходе через 0, будут проблемы.

Уже обнаружены. Лечение пока такое:

    if(timeOn<timeOff){
      if(timeNow>=timeOn && timeNow<timeOff) onFlag=true;
       else onFlag=false;
    }else{
      if(timeNow<timeOn && timeNow>=timeOff) onFlag=false;
       else onFlag=true;
    }

Тут есть хорошая засада, после перехода через 0, timeOn станет другим. Здесь явно лучше хранить режим (включен или нет), если включен, то timeOn не проверять, а проверять только timeOff. И наоборот, если еще не включено, то timeOff не проверять.

Да, високосный год считается по другому, %4 недостаточно ;) В гугле есть примеры. А то потом удивишься :)

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

В общем решается всё, например, конечным автоматом. Вот вариант для тестирования. Выводит в монитор информацию, нужно нажимать Enter, чтобы сделать следующий шаг. Работает когда в один день оба времени и когда в разные (соседние, конечно). Всё это ограничено условиями твоей задачи, так что скорей всего подойдет:

#include <Arduino.h>
#include <Wire.h>
#include <RTClib.h>

/* Имитация включения/выключения устройства */
#define ledPin  (13)

/* Шаг изменения now */
const TimeSpan timeStep(30);
/* Начальное значение даты-времени */
DateTime now(2015, 1, 1, 23, 50, 0);

/* Время включения устройства */
unsigned int timeOn;
/* Время выключения устройства */
unsigned int timeOff;
/* Текущее время */
unsigned int timeNow;
/* Предыдущее время */
unsigned int timeLast;

/* Состояния конечного автомата */
typedef enum _State : byte
{
  sNone      = 0,      // Начальное состояние
  sWait2400  = 1,      // Ждем достижения полуночи, включено
  sWaitOff   = 2       // Ждем выключения, включено
} State;

/* Состояние конечного автомата */
byte currentState = sNone;

// DEBUG ONLY
static unsigned int timeSet(byte p_hour, byte p_minute)
{
  unsigned int l_res = p_hour;
  l_res *= 60;
  return l_res + p_minute;
}

// DEBUG ONLY
static void print2Digit(byte p_val)
{
  if(p_val<10)
  {
    Serial.print("0");
  }
  Serial.print(p_val);
}

// DEBUG ONLY
static void printTime(unsigned int p_time)
{
  byte l_hour = p_time / 60;
  byte l_minute = p_time - ((unsigned int)l_hour) * 60;
  print2Digit(l_hour);
  Serial.print(":");
  print2Digit(l_minute);
}

// DEBUG ONLY
static unsigned int parseDateTime(const DateTime &p_datetime)
{
  unsigned int l_res = p_datetime.hour();
  l_res *= 60;
  l_res += p_datetime.minute();
  return l_res;
}

// DEBUG ONLY
static void waitPressKey()
{
  /* Ждем нажатия клавиши */
  while(0 == Serial.available())
  {
  }
  delay(100);
  /* Читаем всё, что нажато */
  while(Serial.available() > 0)
  {
    char sh = Serial.read();
  }
}

// DEBUG ONLY
static void printCurrentTimes(byte p_flag = 0)
{
  if(p_flag)
  {
    Serial.print("   *** ");
  }
  Serial.print("Now: ");
  printTime(timeNow);
  Serial.print(" Last: ");
  printTime(timeLast);
  Serial.print(" Start: ");
  printTime(timeOn);
  Serial.print(" Stop: ");
  printTime(timeOff);
  Serial.println();
}

// DEBUG ONLY
static void printState(byte p_state)
{
  switch(p_state)
  {
    case sNone:    Serial.print("None"); break;
    case sWait2400:Serial.print("Wait2400"); break;
    case sWaitOff: Serial.print("WaitOff"); break;
    default:       Serial.print("Unknown"); break;
  }
  Serial.println("");
}

/* Включение/выключение устройства. Здесь нужно включить или выключить что нужно */
static void setOnState(bool p_enable)
{
  digitalWrite(ledPin, (p_enable ? HIGH : LOW));
}

/* Проверка времен и переключение состояний автомата */
static byte checkTime(unsigned int p_timeNow, unsigned int p_timeOn, unsigned int p_timeOff)
{
  /* Признак того, что время выключения меньше времени включения */
  bool l_OnGTOff = (p_timeOff < p_timeOn);
  switch(::currentState)
  {
    /* Ждем включения */
    case sNone:
      /* Ждем, когда можно будет включить */
      {
        bool l_cond = false;
        /* Два варианта сравнения для включения */
        if (l_OnGTOff)
        {
          l_cond = (p_timeNow >= p_timeOn);
        }
        else
        {
          l_cond = (p_timeNow >= p_timeOn && p_timeNow < p_timeOff);
        }
        if(l_cond)
        {
          /* Необходимо включить, условие выполнено */
          ::setOnState(true);
          /* Если время выключения меньше времени включения, нужно ждать конца суток */
          if(l_OnGTOff)
          {
            ::currentState = sWait2400;
          }
          /* Иначе ждем времени выключения */
          else
          {
            ::currentState = sWaitOff;
          }
        }
      }
      break;
    /* Ждем получночи */
    case sWait2400:
      if(::timeLast > p_timeNow)
      {
        ::currentState = sWaitOff;
        /* Здесь break не нужен, будем сразу сравнивать с временем выключения */
      }
      else
      {
        break;
      }
    /* Ждем выполнения условия выхода */
    case sWaitOff:
      if(p_timeNow >= p_timeOff)
      {
        ::setOnState(false);
        ::currentState = sNone;
      }
      break;
  }
  return ::currentState;
}

void setup()
{
  Serial.begin(57600);
  while(!Serial) {}
  digitalWrite(ledPin, currentState);
  pinMode(ledPin,OUTPUT);
  
  timeOn = timeSet(23, 51);
  // timeOff = timeSet(23, 55);
  timeOff = timeSet(0, 3);
  waitPressKey();
}

void loop()
{
  /* Сохраняем предыдущее время, чтобы определить момент перехода через 24:00 */
  timeLast = parseDateTime(now);
  now = now + timeStep;
  timeNow = parseDateTime(now);
  /* Контрольный вывод времен */
  printCurrentTimes();
  /* Проверка включения/выключения. Включается/выключается внутри, возвращает новое состояние */
  checkTime(timeNow, timeOn, timeOff);
  // printState(currentState);

  /* Ждем нажатия Enter в терминале */
  waitPressKey();
}