Часы с термометром, не всегда считываются нажатия на кнопки, помогите разобраться

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

Пишу программу часов на дисплее TM1637, RTC DC1307 и DHT11. В общем ситуация следующая: не обрабатываются (как понял не фиксируются) на кнопки. Иногда срабатывают, иногда нет, помогите пожалуйста разобраться почему. С ардуино знаком около 2х месяцев, потому прошу не пинать сильно.

Вот скетч:

#include <Wire.h>             // подключаем библиотеку для работы с I2C
#include "stDHT.h"            // подключаем библиотеку для работы с датчиком DHT
#include "iarduino_RTC.h"     // подключаем библиотеку для работы с DS1307
#include "GyverTM1637.h"      // подключаем библиотеку для работы с дисплеем TM1637
#include "GyverButton.h"      // подключаем библиотеку для работы с кнопками

#define EEPROM_ADDR 0x50      //Адрес EEPROM на DS1307
#define start_addr 0xFF       //Адрес по которому пишем значения
#define sensorPin 4
#define DIO_Pin 5
#define CLK_Pin 6
#define alLedPin 3
#define buzPin 7
#define brButPin  8
#define secButPin 9
#define okButPin 10
#define hourButPin 11
#define minButPin 12
#define dispPrintDelay 1000
#define sensReadDelay 2000
#define alarmTime 300000

int8_t timeMins;
int8_t timeHours;
int8_t timeSeconds;
int8_t alarmMins = 0;
int8_t alarmHours = 0;

boolean potFlag;
boolean noPotFlag = false;
boolean alarmOn = false;
boolean alarmSound = true;
boolean alarmStart = false;
uint8_t oldTimeSec = 0;
uint8_t oldTimeMin = 0;
uint8_t menuLevel = 1;
uint8_t DHT_Temp;
uint8_t DHT_Hum;
uint8_t bright = 1;
volatile uint8_t backTime = 0;
unsigned long curMillisTime = 0;        //
unsigned long curMillisSensor = 0;      //
unsigned long curMillisAlarm = 0;       //

DHT sensor(DHT11);                        // объявляем  переменную для работы с датчиком DHT11
iarduino_RTC RTClock(RTC_DS1307);         // объявляем  переменную для работы с DS1307
GyverTM1637 disp(CLK_Pin, DIO_Pin);       // объявляем  переменную для работы с TM1637
GButton minButton(minButPin);             // объявляем  переменную для работы с кнопками
GButton hourButton(hourButPin);           // объявляем  переменную для работы с кнопками
GButton okButton(okButPin);               // объявляем  переменную для работы с кнопками
GButton secButton(secButPin);             // объявляем  переменную для работы с кнопками
GButton brButton(brButPin);               // объявляем  переменную для работы с кнопками

//============ блок программы SETUP  ====================

void setup(){
  
  Serial.begin(9600);             // открываем последовательный порт на скорости 9600 бод
  RTClock.begin();
  
  //okButton.setTimeout(1000);
  //secButton.setTimeout(1000);
  brButton.setTimeout(1000);      // время длительного нажатия на кнопку
  
  minButton.setTickMode(AUTO);    // включаем автоопрос кнопок
  hourButton.setTickMode(AUTO);
  okButton.setTickMode(AUTO);
  secButton.setTickMode(AUTO);
  brButton.setTickMode(AUTO);
  minButton.setDebounce(50);
  hourButton.setDebounce(50);
  okButton.setDebounce(50);
  secButton.setDebounce(50);
  brButton.setDebounce(50);
  
  disp.clear();
  disp.brightness(bright);             // яркость, 0 - 7 (минимум - максимум)
  
  byte welcome_banner[] = {_H, _E, _L, _L, _O};                     // текст приветствия
  disp.runningString(welcome_banner, sizeof(welcome_banner), 200);  // 200 это время в миллисекундах!
  delay(1000);                                                      // выполняем задержку
  
  Wire.beginTransmission(0x68);   // включаем выход SQW
  Wire.write(0x07);               //
  Wire.write(0x10);               // меандр 1 Гц
  Wire.endTransmission();         //

  readAlarmTime();                // считываем время будильника из EEPROM
  
  delay(50);                      
  attachInterrupt(0, blink, CHANGE);  //включаем 0 прерывание по 2-ой ноге (по изменению состояния)
  RTClock.gettime();
    
}// end setup

//************ основной цикл программы *************

void loop(){

  //okButton.tick();
  //minButton.tick();
  //hourButton.tick();
  //secButton.tick();
  //brButton.tick();
  
//============ считываем время и др. раз в секунду ==============  
  if ((millis() - curMillisTime) > dispPrintDelay){
    curMillisTime = millis();
    curMillisAlarm = millis();
    timeHours = RTClock.Hours;
    timeMins = RTClock.minutes;
    //RTClock.gettime();
    Serial.println(RTClock.gettime("d-m-Y, H:i:s, D")); // выводим время
    Serial.println((String) "DHT11: " + DHT_Temp + " *C - " + DHT_Hum + " %");
    printOnDisp();
    if ((alarmHours == timeHours) && (alarmMins == timeMins) && alarmOn && !alarmStart){ //включаем будильник, если пришло время
      alarmSound = true;
      alarmStart = true;
      curMillisAlarm = millis();
    }//end if

  }//end if

//============ считываем показания DHT11 раз в 2 секунды ==============    
  if ((millis() - curMillisSensor) > sensReadDelay){
    curMillisSensor = millis();
    DHT_Temp = sensor.readTemperature(sensorPin);
    DHT_Hum = sensor.readHumidity(sensorPin);
  }//end if

//============ вызываем функцию будильника, если флаг звука будильника "true" ==================    
  if (alarmSound) alarm();
    
//============ выключаем будильник, если он звонит более alarmTime ==============
  if ((millis() - curMillisAlarm) > alarmTime){     //время по умолчанию 5 минут
    alarmSound = false;
    alarmStart = false;
  }//end if

//============ выключаем звук будильника нажатием на кнопку ==============    
if (secButton.isClick() && alarmStart) alarmSound = false;
    
//============ переключение режимов отображения ==============  
  if (okButton.isClick()){
    menuLevel ++;
    if (menuLevel > 3) menuLevel = 1;
    //delay(50);
  }//end if

//============ устанавливаем флаг точек ==============
  if (menuLevel >=2) noPotFlag = true;
  else noPotFlag = false;

  //okButton.tick();
  //minButton.tick();
//============ устанавливаем минуты ==============
  if (okButton.isHold() && minButton.isClick()){
    backTime = 0;
    menuLevel = 1;
    timeMins ++;
    if (timeMins > 59) timeMins = 0;
    RTClock.settime(-1, timeMins, -1, -1, -1, -1, -1);      // устанавливаем только минуты, секунды не трогаем
    //delay(50);
  }//end if
  
  //okButton.tick();
  //hourButton.tick();
//============ устанавливаем часы ==============
  if (okButton.isHold() && hourButton.isClick()){
    backTime = 0;
    menuLevel = 1;
    timeHours ++;
    if (timeHours > 23) timeHours = 0;
    RTClock.settime(-1, -1, timeHours, -1, -1, -1, -1);   // устанавливаем только часы, секунды и минуты не трогаем
    //delay(50);
  }//end if     

  //secButton.tick();
//============ выводим на экран секунды =================
  if (secButton.isClick()&& !alarmStart){
    menuLevel = 5;      // выводим на экран секунды
    printOnDisp();
    //delay(50);
  }//end if

  //okButton.tick();
//============ сбрасываем секунды в 0 =================  
  if ((menuLevel == 4) && okButton.isClick()){
    RTClock.settime(0, -1, -1, -1, -1, -1, -1);
    //delay(50);
  }//end if

  //brButton.tick();
//============ изменяем яркость =================
  if (brButton.isClick()){
    bright ++;
    if (bright > 7) bright = 1;
    disp.brightness(bright);
    //delay(50);
  }//end if

  //brButton.tick();
//============ вкл/выкл будильника =================
  if (brButton.isHolded()){
    alarmOn != alarmOn;
    digitalWrite(alLedPin,alarmOn);
  }//end if

  //secButton.tick();
//============ выводим время будильника =================
  if (secButton.isHold() && !(menuLevel == 4)){
    backTime = 0;
    menuLevel = 4;
    printOnDisp();
  }//end if

  //hourButton.tick();
//============ устанавливаем время будильника (часы) =================  
  if ((menuLevel == 4) && hourButton.isClick()){
    alarmHours ++;
    if (alarmHours > 23) timeHours = 0;
    backTime = 0;
  }// end if

  //minButton.tick();
//============ устанавливаем время будильника (минуты) =================
  if ((menuLevel == 4) && minButton.isClick()){  
    alarmMins ++;
    if (alarmMins > 59) timeMins = 0;
    backTime = 0;
  }//end if

  //okButton.tick();
//============ сохраняем время будильника ================= 
  if ((menuLevel == 4) && okButton.isClick()){
    //saveAlarmTime();
    backTime = 0;
    alarmStart = false;
  }//end if  
    
  //delay(10);
          
}//end loop

//********* основной цикл программы окончен ***************


//++++++++++++++++++++++++ функции ++++++++++++++++++++++++

//------------------ выводим на дисплей -------------------

void printOnDisp(){
  switch (menuLevel){
    case 1:               //если уровень меню 1 - выводим время
      //noPotFlag = false;
      //disp.displayClockScroll(RTClock.Hours, RTClock.minutes, 50);
      disp.displayClock(RTClock.Hours, RTClock.minutes);
      if (backTime > 20) {
        backTime = 0;       
        menuLevel = 2;
      }//end if
    break;

    case 2:               //если уровень меню 2 - выводим температуру
      //noPotFlag = true;
      disp.displayByte(_empty, _empty, _degree, _C);
      disp.display(0, DHT_Temp / 10);
      disp.display(1, DHT_Temp % 10);
      if (backTime > 10) {
        backTime = 0;       
        menuLevel = 3;
      }//end if
    break;

    case 3:               //если уровень меню 3 - выводим влажность
      //noPotFlag = true;
      disp.displayByte(_empty, _empty, _degree, _b);
      disp.display(0, DHT_Hum / 10);
      disp.display(1, DHT_Hum % 10);
      if (backTime > 10) {
        backTime = 0;       
        menuLevel = 1;
      }//end if  
    break;

    case 4:               //если уровень меню 4 - выводим время будильника
      //noPotFlag = true;
      disp.displayClock(alarmHours, alarmMins);
      if (backTime > 20) {
        backTime = 0;       
        menuLevel = 1;
      }//end if
    break;
    
    case 5:               //если уровень меню 5 - выводим секунды
      //noPotFlag = true;
      //disp.clear();
      disp.displayByte(_empty, _empty, RTClock.seconds / 10, RTClock.seconds % 10);
      //disp.display(3, RTClock.seconds % 10);
      if (backTime > 70) {
        backTime = 0;       
        menuLevel = 1;
      }//end if
    break;
  }//end switch  
}//end printOnDisp
  
//------------ сохраняем время будильника -------------
void saveAlarmTime(){
  Wire.beginTransmission(EEPROM_ADDR);
  Wire.write((int)(start_addr >> 8));
  Wire.write((int)(start_addr & 0xFF));
  Wire.write(alarmHours);
  delay(5);
  Wire.endTransmission();
  delay(10);
  Wire.beginTransmission(EEPROM_ADDR);
  Wire.write((int)((start_addr+1) >> 8));
  Wire.write((int)((start_addr+1) & 0xFF));
  Wire.write(alarmMins);
  delay(5);
  Wire.endTransmission();
  delay(10);                           //Datasheet описана задержка записи в 5мс
}//end saveAlarmTime()

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

void readAlarmTime(){
  Wire.beginTransmission(EEPROM_ADDR);
  Wire.write((int)(start_addr >> 8));
  Wire.write((int)(start_addr & 0xFF));
  Wire.endTransmission();
  Wire.requestFrom(EEPROM_ADDR,1); 
  alarmHours = Wire.read();
  Wire.write((int)((start_addr + 1) >> 8));
  Wire.write((int)((start_addr + 1) & 0xFF));
  Wire.endTransmission();
  Wire.requestFrom(EEPROM_ADDR,1);
  alarmMins = Wire.read();
}//end readAlarmTime()

//------------ будильник -------------

void alarm(){
  for(int i=0;i<200;i++){
    digitalWrite(buzPin,HIGH);
    delay(1);
    digitalWrite(buzPin,LOW);
    delay(1);
  }//end for
}

//------------ мигаем точками -------------
//------------ (или не мигаем) ------------

void blink() {
  digitalWrite(13, !digitalRead(13));
  backTime ++;
  if (backTime > 100) backTime = 0;
  if (!noPotFlag){
  potFlag = !potFlag;
  disp.point(potFlag);   // вкл/выкл точки
  }//end if
  else disp.point(false);   // выкл точки
}//end blink

 

b707
Offline
Зарегистрирован: 26.05.2017

Вы библиотеки у Гивера взяли и попытались на них построить серьезный проект - это почти БЕЗВЫИГРЫШНАЯ лотерея. В его библиотеках столько косяков. что проще (и правильнее) свою собственную написать. чем выловить все его баги.

На первое время что могу посоветовать - посмотрите в библиотеке кнопок, не стирает ли она статус кнопки при обращении с методу isClick() - вполне возможно, что этот метод можно использовать только один раз за цикл ЛУП

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

Чет мне подсказывает, что код нихрена не самописный. Поэтому и кнопы жматься не желают.

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

Так код гиверовский.

Обратили бы Вы лучше к автору. У него и сайт свой есть и ютуб, и вконтакте. Он там вроде отвечает.

Ковыряться в какашках этого специалиста тут мало желающих найдётся.

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

Ясно, спасибо! Короче, как я понял дело скорее всего в библиотеке. А состояние кнопок после опроса сбрасывается точно - это в библиотеке прописано. Попробую другую или вручную обработку напишу. 

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

А если знаете. что сбрасывается, так зачем по мнгоу раз опрашиваете? Спросите один раз, присвойте переменной и уже везде эту переменную проверяйте. В чём проблема-то? Или код не Ваш?

b707
Offline
Зарегистрирован: 26.05.2017

mir0tv0rec пишет:

А состояние кнопок после опроса сбрасывается точно - это в библиотеке прописано.

ну тогда у вас, к примеру, строчки 144, 157, 169 и 188 одновременно работать не будут

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

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

 Или код не Ваш?

А есть сомнения?

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

Код писал я, ну некоторые моменты подглянул. По идее, как у автора описано, при установке в авто считывания кнопок, они должны опрашиваться каждый раз когда считываешь их состояние. Я даже перед каждым сравнением ставил принудительное считывание и становилось лучше, но не намного. Все равно большинство нажатий пропускалось и снопки срабатывали с задержшкой. Я вот думаю, может из-за вызова функций их состояние сбрасывается? Например мигание точками, вызывается по прерыванию со 2 ой ноги. Сначала, когда были только часы и термометр и переключение было между режимами только одной кнопкой, все работало нормально. Потом по мере добавления функций кнопки перестали нормально реагировать.

b707
Offline
Зарегистрирован: 26.05.2017

mir0tv0rec пишет:
Я даже перед каждым сравнением ставил принудительное считывание и становилось лучше, но не намного. Все равно большинство нажатий пропускалось.

Вы не поняли. Если ставить принудительное считывание перед сравнением - должно стать еще хуже. Так делать нельзя. Кнопку можно считывать ОДИН РАЗ ЗА ЦИКЛ - понимаете?

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

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

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

b707
Offline
Зарегистрирован: 26.05.2017

mir0tv0rec пишет:

У меня так и было изначально сделано (в коде строке закомментированы после loop)

А по-моему совсем не так. Где вы сохраняете результаты проверки нажатий в переменные?

Вы. похоже, вообще не понимаете, про что я вам гворю

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

Если закомментировать эти строки (обработака нажатия при опросе состояния кнопки):

065   minButton.setTickMode(AUTO);    // включаем автоопрос кнопок
066   hourButton.setTickMode(AUTO);
067   okButton.setTickMode(AUTO);
068   secButton.setTickMode(AUTO);
069   brButton.setTickMode(AUTO);

и разкомментировать эти (здесь кнопки опрашиваются и обрабатываются):

098 void loop(){
099  
100   //okButton.tick();
101   //minButton.tick();
102   //hourButton.tick();
103   //secButton.tick();
104   //brButton.tick();

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

Судя по бибилиотеке, как раз функция tick и обрабатывает все, а остальные только возвращают ложь или правда, в зависимости от запроса.

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

Хотя могу предположить в чем может быть дело: при опросе состояния это состояние сбрасывается (у автора так в описании) и, сюдя по всему, про переменные состояния вы были правы, нужно дополнительно к встроенным в библиотеку ввести еще переменные состояния кнопок. Т.е., если я вызвал опрос одной из кнопок при проверке одного из условий, к примеру if (okButton.isClick()), то если условие не выполнится, состояние кнопки все-равно будет сброшено в 0. И, если произойдет опрос далее по коду if ((menuLevel == 4) && okButton.isClick()), то уже это условие не выполнится никогда, т.к. состояние кнопки будет всегда 0. Для этого я и попробовал включить автоопрос кнопок, чтобы tick вызывался при считывании состояния. Хотя и это не помогло.

Сам пример из библиотеки работает нормально, но там только выводит в порт нажатия.

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

Странный Вы человек. Вроде бы уже написали, что в библиотеках от Gyver не стоит копаться, но вы все равно упорно это делаете и других пытаетесь вовлечь.

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

Я ни кого не пытаюсь заставить копаться в библиотеках, хочу просто понять суть проблемы, чтобы снова не наступать на теже грабли. Информация для размышления получена буду ее пробовать на деле. Спасибо за подсказки и комментариии.

 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

mir0tv0rec пишет:

чтобы снова не наступать на теже грабли. 

Так тебе объяснили как не наступать - не пользуйся софтом от Гивера. Чего непонятного-то?