Контроллер освещения в квартиру

PiratFox

★★★★★✩✩
13 Фев 2020
1,706
474
@DAK, да я и не собирался давать оценку Вашей работе. Ваша схема работает - ну и хорошо. Просто я бы так не делал, но это моя позиция, и я её никому не навязываю, в том числе и Вам. Код у Вас кривой, конечно, но опять же, это Ваше дело как его писать. И да, молодой человек, бычить тут не стоит. Тут серьёзные инженеры, а не то, что Вы думали.В отличии от....
 
Изменено:

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
Вопрос по коду. Хотел знать что поменять - получи, но сначала вопрос. Вот кусок кода:
C++:
if (LightLcd){
  if ((millis()-LcdTimeStart)>15000){     //Тушим подсветку при простое более 15 сек
    lcd.noDisplay();
    digitalWrite(8,LOW);
  }
}
1-ое условие, это LightLcd, логическая переменная. Видимо обозначающая включенную подсветку на LCD, если она включена, то если прошло 15 секунд выключаем дисплей и выключаем что то на 8 пине. А как будет выполняться этот кусок кода, если пройдет 15.1 , 15.5, 16 секунд ? На мой взгляд все время после 15 сек он будет гасить потухший экран и выключать пин 8, т.к. в теле нигде не увидел LightLcd=FALSE.
Я к таким кускам очень внимательно отношусь, т.к. все лишние движения в цикле снижают общую производительноть системы. Если помог, хорошо.
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,415
977
58
Марий-Эл
C++:
void LCDBacklight(byte v) { // Управление подсветкой
  if (v == 0) { // Выключить подсветку
    BacklightOffTime = millis();
    lcd.noBacklight();
  }
  else if (v == 1) { //Включить подсветку
    BacklightOffTime = millis() + BacklightDelay;
    lcd.backlight();
  }
  else { // Выключить если время вышло
    if (BacklightOffTime < millis())
      lcd.noBacklight();
    else
      lcd.backlight();
  }
}
Вот код, который написал уважаемый всеми программистами человек, но здесь на форуме его не знают.
Так как его книги не читают.
Работа с функцией проста.
Подсветку можно включить.
Выключить
Или подождать когда сам потухнет. Вызывая периодически функцию во время опроса кнопок.
 

bort707

★★★★★★✩
21 Сен 2020
3,066
913
Вот код, который написал уважаемый всеми программистами человек, но здесь на форуме его не знают.
прошу прощения, Эдуард, но строчка 7 сразу ставит под сомнение профессионализм автора. Так делать нельзя.

Кстати. а кто это? Забил код в Гугль - нашел какого-то блоггера Тсиброва (или Циброва)
https://tsibrov.blogspot.com/2018/01/arduino-text-menu.html
Но наверно это не его код, он его тоже откуда-то скопировал
 
Изменено:

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
прошу прощения, Эдуард, но строчка 7 сразу ставит под сомнение профессионализм автора. Так делать нельзя.
Почему же ? вполне можно вычислить время выключения.. но вот если будет превышено максимальное значение, то будут глюки.
И в коде если v не будет 1 или 2 , то опять же будет постоянно включаться включенный свет и выключаться выключенные. Не люблю я это.. хотя если посчитать и сказать, что лишний раз включить или выключить будет быстрее чем проверять на состояние, то почему бы и нет..
 

bort707

★★★★★★✩
21 Сен 2020
3,066
913
если будет превышено максимальное значение, то будут глюки.
ну вот именно поэтому и нельзя так делать :)
Тем более что совсем маленькая модификация кода эти глюки полностью убирает.
C++:
void LCDBacklight(byte v) { // Управление подсветкой
  if (v == 0) { // Выключить подсветку
    lcd.noBacklight();
  }
  else if (v == 1) { //Включить подсветку
    BacklightONTime = millis();
    lcd.backlight();
  }
  else { // Выключить если время вышло
    if (millis() - BacklightONTime  >  BacklightDelay)   lcd.noBacklight();
   }
}
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,415
977
58
Марий-Эл
@bort707, @Старик Похабыч,
Я привёл только часть кода, саму идею.
И я могу повторить слова топикстартера - у меня же уже целый год работает :ROFLMAO: 🤪:LOL:

И схему, которая находится в сообщении https://community.alexgyver.ru/threads/kontroller-osveschenija-v-kvartiru.3909/post-55221
я с самого начала добивался от автора. Да и та неправильно нарисована. А предыдущая не отражала полноты картины.
Так что я удивляюсь его удивлению.
 

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
У меня обычный lcd 1602, без i2c, валялся в закромах, подсветка на нём, как я понимаю не работает от команды lcd.noBacklight(), поэтому я подключил её через первый попавшийся n-канальный мосфит и управляю подсветкой через 8 пин. Просто вчера решил поиграть с этим lcd, да и на самом деле у меня с ним пока туго.
На самом деле хочется многое, а именно сделать управление всем этим чудом, тобишь сделать возможность вводить с органа управления пины для клавиш, забивать группы реле и т.д., так как на данный момент у меня управляется массивами, и они на столько большие, что мне прям кажется, что проще написать стороннюю программу и управлять настройками через ПК или телефон.... Спасибо за замечания, сейчас сижу и правлю код.
 

bort707

★★★★★★✩
21 Сен 2020
3,066
913
мне прям кажется, что проще написать стороннюю программу и управлять настройками через ПК или телефон....
не исключено, что это правильная идея.Сделать удобный пользовательсткий интерфейс на ардуине - имхо, задачка посложнее самого контроллера освещения будет.

А вот касаетельно самих массивов я бы подумал. как-то очень уж громоздко у вас это сделано.
У вас групп реле 32? - цифра подсказывает, что состояние 32 групп проще хранить в одной переменной uint32_t, чем в огромном массиве
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
Там мосфет не нужен, на сколько я помню. Там логический пин, можно от дисплея прям в ардуину.

Я привёл только часть кода, саму идею.
Кстати! Хорошая идея для коммерческих кодов, ставишь такой код, и раз 50 дней все это глючит. А если взять знаковое число, то глючить будет раз в 25 дней. Убираешь, если полностью доволен оплатой. Нет оплаты - пардоньте :D
 

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
Кажется я понял почему у меня класс без конструктора. Год назад я так и не нашёл решения одних грабель, но может кто подскажет. (И сегодня тоже не нашёл)
C++:
#include <LiquidCrystal.h>

class Matrixbutton {
  public:
    Matrixbutton(byte setPinX[], byte setCountX, byte setPinY[], byte setCountY){
      _x=setCountX;
      _y=setCountY;
      for (byte ix=0; ix<_x;ix++){
        pin_x[ix]=setPinX[ix];
      }
      for (byte iy=0; iy<_y;iy++){
        pin_y[iy]=setPinY[iy];
      }
    }
    void read(){
      for (byte xm=0;xm<_x;xm++){                                                                     // перебираем столбики
        digitalWrite(pin_x[xm],1);                                                                   
        delay(20);                                                                                    //Физика, при длинных проводах обязательно делать небольшую задержку
        for (byte ym=0;ym<_y;ym++){                                                                   //Читаем строчки
          current_state[xm][ym]=digitalRead(pin_y[ym]);                                               
          if (last_state[xm][ym]==0 && current_state[xm][ym]==1){                                     //Если предыдущее 0, а новое 1, значит клавиша нажата
            event_down[xm][ym]=1;
            eventdown_start[xm][ym]=millis();                                                         //Стартуем таймер, дабы отследить длительность нажатия кнопки
          }
          if (eventdown_start[xm][ym]!=0){                                                            //Если клавиша долго нажата то перестаём пытаться регистрировать события быстрого и долгого клика
            if ((millis()-eventdown_start[xm][ym])>= 5000){
            eventdown_start[xm][ym]=0;           
            }
          }
          if (last_state[xm][ym]==1 && current_state[xm][ym]==0){                                     //Если предыдущая 1, а новое 0, значит клавиша отпущена
            event_up[xm][ym]=1;                                                                       //Регистрируем события отпускания клавиши, но ещё необходимо проверить события быстрого или долгого клика
            if (eventdown_start[xm][ym]!=0){
              if ((millis()-eventdown_start[xm][ym]) < 1100) {                                        // Если клик меньше 1.1 сек, то будем считать это быстрым кликом, если от 1.1 до 5 сек, то долгий
                event_fastclick[xm][ym]=1;
                eventdown_start[xm][ym]=0;
              } else {
                event_longclick[xm][ym]=1;
                eventdown_start[xm][ym]=0;
              }
            }
          }
          last_state[xm][ym]=current_state[xm][ym];
        }
        digitalWrite(pin_x[xm],0);  //реверс
      }
    }
    void init(){
      for (byte i=0; i<_x;i++){                                                                       //Инициализируем пины
        pinMode(pin_x[i],OUTPUT);
        digitalWrite(pin_x[i],0);                                                                     //На тот случай, если делать  puulup
      }
      for (byte i=0; i<_y;i++){
         pinMode(pin_y[i],INPUT);                                                                     //Не забываем подтянуть строки к земле через резисторы, иначе будет весело, можно правда сделать puulup, но придётся немного программу изменить
      }
      for (byte xm=0;xm<_x;xm++){                                                                     //Обнуляем матрицы состояний и таймеров
        for (byte ym=0;ym<_y;ym++){
          current_state[xm][ym]=0;
          last_state[xm][ym]=0;
          event_up[xm][ym]=0;
          event_down[xm][ym]=0;
          event_fastclick[xm][ym]=0;
          event_longclick[xm][ym]=0;
          eventdown_start[xm][ym]=0;
        }
      }
    }
   boolean clickUp(byte button){                                                                      //Просто события, после считывания события - сбрасываем событие
      if (event_up[button/_x][button%_y] == 1){
        event_up[button/_x][button%_y] =0;
        return true;       
      } else {
        return false;
      }
    }
    boolean clickDown(byte button){
      if (event_down[button/_x][button%_y] == 1){
        event_down[button/_x][button%_y] =0;
        return true;       
      } else {
        return false;
      }
    }
    boolean fastclick(byte button){
      if (event_fastclick[button/_x][button%_y] == 1){
        event_fastclick[button/_x][button%_y] =0;
        return true;       
      } else {
        return false;
      }
    }
    boolean longclick(byte button){
      if (event_longclick[button/_x][button%_y] == 1){
        event_longclick[button/_x][button%_y] =0;
        return true;       
      } else {
        return false;
      }
    }
  private:
    byte _x;                                                                                //Количество столбиков
    byte _y;                                                                                //Количество строк
    byte pin_x[9];                                                                             //Пины столбиков (Выходы)
    byte pin_y[9];                                                                             //Пины строк     (Входы)
    boolean current_state[9][9];                                                                      //Размеры массивов должны быть не менее  _x и _y
    boolean last_state[9][9];
    boolean event_up[9][9];
    boolean event_down[9][9];
    boolean event_fastclick[9][9];
    boolean event_longclick[9][9];
    unsigned long eventdown_start[9][9];
    
};

byte pinx_Str[]={37,39,41,43,45,47,49,51,53};                                           
byte piny_Cool[]={36,38,40,42,44,46,48,50,52};

Matrixbutton knop(pinx_Str,9,piny_Cool,9);
LiquidCrystal lcd(12,11,5,4,3,2);

String str,str1,str2;
bool fl_cur,LightLcd;
unsigned long LcdTimeStart;

void setup() {
lcd.begin(16, 2);
fl_cur=false;
pinMode(8,OUTPUT);  //Управление подсветкой LCD
lcd.noDisplay();
pinMode (13,OUTPUT);
knop.init();
Serial.begin(9600); // отладка
}

void loop() {
  knop.read();                                         //Читаем кнопки
  byte f=0;
  while (f<=80){                           //Если была нажата любая кнопка, то условие в цикле сработает
    if (knop.clickUp(f)) {
      str=str+"Up="+String(f)+" ";
      fl_cur=true;
    }
    if (knop.longclick(f)){
      str=str+"LongClick="+String(f)+" ";
      fl_cur=true;
      }
    if (knop.fastclick(f)){
      str=str+"FastClick="+String(f)+" ";
      fl_cur=true;
    }
    if (knop.clickDown(f)){
      str=str+"DOWN="+String(f)+" ";
      fl_cur=true;
    }
    f++;
  }
if (LightLcd){
  if ((millis()-LcdTimeStart)>15000){     //Тушим подсветку при простое более 15 сек
    lcd.noDisplay();
    digitalWrite(8,LOW);
    LightLcd=false;
  }
}
if (fl_cur){                             //Если были события с кнопок, кидаем их на экран
  Serial.println(str);
  digitalWrite(8,HIGH);
  lcd.display();
  LcdTimeStart=millis();
  LightLcd=true;
  lcd.clear();
  str1=str2;
  str2=str;
  lcd.setCursor(0,0);
  lcd.print(str1);
  lcd.setCursor(0,1);
  lcd.print(str2);
  fl_cur=false;
  str="";
}

}
Запихал я в класс конструктор, идея была в том, что в момент создания в класс я бы передавал только 2 массива (пины входов и выходов), конструктор бы принимал их, считал их длину (это в принципе я могу реализовать), и на основе длинны он инициализировал двумерные массивы матриц отслеживания состояний (boolean current_state[X][Y]; boolean last_state[X][Y]; boolean event_up[X][Y]....), но компилятор мне не позволяет это сделать, ведь ему реально надо знать сколько памяти выделить под данный класс, хотя с другой стороны конструктор ведь в процессе может всё сам выделить, я запутался в ноль с этим делом.

Там мосфет не нужен, на сколько я помню. Там логический пин, можно от дисплея прям в ардуину.
Я так же подумал, просто на схеме к подсветки тянется +3,3В, сейчас проверил ток потребления подсветки 3 милиампера, как я понимаю надо поставить резистор номиналом в 1,7/0,003=566 Ом, воткнул сперва напрямую, как то сильно ярко засветился, поставил через сопротивление на 510 Ом, сейчас всё в норме, просто много схем вчера в инете нарыл, зачастую все через транзистор подсветку врубают.

Кстати! Хорошая идея для коммерческих кодов, ставишь такой код, и раз 50 дней все это глючит. А если взять знаковое число, то глючить будет раз в 25 дней. Убираешь, если полностью доволен оплатой. Нет оплаты - пардоньте :D
Чёт меня это начинает напрягать (не Вы, а именно этот факт про 50 дней), 50 дней, это как я понимаю предел millis(), но на сколько помню на другом форуме мне объяснили что можно плевать на переход через 50 дней и всё будет работать как надо... И да, флаг включенной подсветки в коде подправил, спасибо! Сейчас в рабочей системе нет у меня никаких millis и lcd, может я тут сижу и начинаю творить код для будущих граблей?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
Чёт меня это начинает напрягать (не Вы, а именно этот факт про 50 дней), 50 дней, это как я понимаю предел millis(), но на сколько помню на другом форуме мне объяснили что можно плевать на переход через 50 дней и всё будет работать как надо... И да, флаг включенной подсветки в коде подправил, спасибо! Сейчас в рабочей системе нет у меня никаких millis и lcd, может я тут сижу и начинаю творить код для будущих граблей?
49 дней с хвостиком. Но все зависит от метода реализации. в 40-ом посте будет плевать, а в 37 нет.
49 дней 17 часов 2 минуты 47,2 секунды если быть точным

По поводу передачи массивов.. Не уверен а) что это хорошая идея. зачем плодить сущности ? 2 массива будут занимать в 2 раза больше места. 2) массив передается по ссылке, а не значением. Тут много можно объяснять, но повторять , то что уже описано в учебниках нет смысла. Возможно проблема именно в этом была. Сделать начальные массивы константами и в класс можно передать ссылку на массив.
И без конструктора можно обойтись в общем то
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
ведь ему реально надо знать сколько памяти выделить под данный класс, хотя с другой стороны конструктор ведь в процессе может всё сам выделить, я запутался в ноль с этим делом
Массивы передавать накладно, передавайте либо указатели на массивы, либо ссылки, либо используйте кучу (new/delete).
 

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
Не уверен а) что это хорошая идея. зачем плодить сущности ? 2 массива будут занимать в 2 раза больше места. 2) массив передается по ссылке, а не значением. Тут много можно объяснять, но повторять , то что уже описано в учебниках нет смысла. Возможно проблема именно в этом была. Сделать начальные массивы константами и в класс можно передать ссылку на массив.
И без конструктора можно обойтись в общем то
C++:
class Matrixbutton {
  public:
    Matrixbutton(byte* setPinX, byte setCountX, byte* setPinY, byte setCountY){
      _x=setCountX;
      _y=setCountY;
      pin_x=setPinX;
      pin_y=setPinY;
    }
    ......
  private:
    byte _x;                                                                                //Количество столбиков
    byte _y;                                                                                //Количество строк
    byte* pin_x;                                                                             //Пины столбиков (Выходы)
    byte* pin_y;
    ......
Если я всё правильно понял, то я в класс отдал ссылки на массивы, и теперь нет лишних сущностей... Немного до конца не догоняю эту тему, но код с этими изменениями работает...

Массивы передавать накладно, передавайте либо указатели на массивы, либо ссылки, либо используйте кучу (new/delete).
Это я уже понимаю, спасибо.
Пока писал ответ, кажется сам понял как конструктор заставить делать то, что мне надо, но это не точно.
Если я в конструкторе создам массив, а потом в private сохраню на него ссылку, смогу ли я из другой функции класса по данной ссылки добраться до содержимого массива (который ранее создал в конструкторе)?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
Надо бы почитать книгу. Ссылочные типы одна из сложных тем. И в разных языках оп разному.
массив сам по себе ссылочный тип.

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

Проще создать константный массив и передать на него ссылку, кнопки это такой предмет, который в процессе работы не будет добавлен или убран, кнопки будут всегда. Так что для них константы подойдут отлично.
 
  • Лойс +1
Реакции: DAK

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
Если я в конструкторе создам массив, а потом в private сохраню на него ссылку, смогу ли я из другой функции класса по данной ссылки добраться до содержимого массива (который ранее создал в конструкторе)?
Класс видит все свои члены вне зависимости указанной области видимости. Она нужна лишь для доступа снаружи/наследования. Но наружу тоже можно через get-метод отдать, вернув ту же ссылку.
 

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
Проще создать константный массив и передать на него ссылку, кнопки это такой предмет, который в процессе работы не будет добавлен или убран, кнопки будут всегда. Так что для них константы подойдут отлично.
Я где то не понимаю, у меня есть
C++:
  private:
    boolean current_state[9][9];                                                                      //Размеры массивов должны соответствовать _x и _y
    boolean last_state[9][9];
    boolean event_up[9][9];
    boolean event_down[9][9];
    boolean event_fastclick[9][9];
    boolean event_longclick[9][9];
    unsigned long eventdown_start[9][9];
Члены этих массивов как раз и меняются при выполнении функций в классе, как я понимаю, константный массив нельзя править. Я хотел сделать так, чтобы данные массивы (для экономии памяти) имели так сказать нужные размеры, соответствовали XиY. сейчас получается, что класс полностью будет работать с любыми матрицами до 9 * 9, а памяти будет жрать одинаково много.
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
C++:
    boolean current_state[9][9];                                                                      //Размеры массивов должны соответствовать _x и _y
    boolean last_state[9][9];
    boolean event_up[9][9];
    boolean event_down[9][9];
    boolean event_fastclick[9][9];
    boolean event_longclick[9][9];
Вот это все меняется на 11 байт * 6 == 66 байт, это если не заморачиваться с экономией бит, против ваших 81байт * 6 = 486.
Суть действия - завести плоский массив к примеру: uint8_t last_state[11], а доступ к нужному биту через функции get/set и битовые операции
Аналогично и для других boolean -массивов.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
@DAK, Возможно я не так понял. Тогда что передается в конструктор ?
И да, битовые операции помогают экономить память. Аж до 8 раз.
Массив 9 на 9 плох тем, что он не кратен 8, был бы 10 на 8, то это 10 байт и все. а так .. пока забейте пока места хватает.
 

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
@DAK, Возможно я не так понял. Тогда что передается в конструктор ?
И да, битовые операции помогают экономить память. Аж до 8 раз.
Массив 9 на 9 плох тем, что он не кратен 8, был бы 10 на 8, то это 10 байт и все. а так .. пока забейте пока места хватает.
Думаю у меня просто нет таланта правильно объяснять.
Мои идеи провалились с треском, реально, массивы можно сделать конструктором, потом ссылки на массивы сохранить в private, вернее я это сделал, и оно не работает (Тут один умный человек писал, а я сразу и не понял о чём речь "
Про создание массивов в конструкторе.
Вопрос как создать. Тут уже надо использовать кучу, потому как если просто объявить массив, то после выхода из массива память под него будет освобождена и куда будет показывать указатель через час не известно.")

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

C++:
class Matrixbutton {
  public:
    Matrixbutton(byte* setPinX, byte setCountX, byte* setPinY, byte setCountY){
      _x=setCountX;
      _y=setCountY;
      pin_x=setPinX;
      pin_y=setPinY;
     }
    static const byte nr=81;
    void read(){
      for (byte xm=0;xm<_x;xm++){                                                                     // перебираем столбики
        digitalWrite(pin_x[xm],1);                                                                  
        delay(20);                                                                                    //Физика, при длинных проводах обязательно делать небольшую задержку
        for (byte ym=0;ym<_y;ym++){                                                                   //Читаем строчки
          current_state[xm*_x+ym]=digitalRead(pin_y[ym]);                                              
          if (last_state[xm*_x+ym]==0 && current_state[xm*_x+ym]==1){                                     //Если предыдущее 0, а новое 1, значит клавиша нажата
            event_down[xm*_x+ym]=1;
            eventdown_start[xm*_x+ym]=millis();                                                         //Стартуем таймер, дабы отследить длительность нажатия кнопки
          }
          if (eventdown_start[xm*_x+ym]!=0){                                                            //Если клавиша долго нажата то перестаём пытаться регистрировать события быстрого и долгого клика
            if ((millis()-eventdown_start[xm*_x+ym])>= 5000){
            eventdown_start[xm*_x+ym]=0;          
            }
          }
          if (last_state[xm*_x+ym]==1 && current_state[xm*_x+ym]==0){                                     //Если предыдущая 1, а новое 0, значит клавиша отпущена
            event_up[xm*_x+ym]=1;                                                                       //Регистрируем события отпускания клавиши, но ещё необходимо проверить события быстрого или долгого клика
            if (eventdown_start[xm*_x+ym]!=0){
              if ((millis()-eventdown_start[xm*_x+ym]) < 1100) {                                        // Если клик меньше 1.1 сек, то будем считать это быстрым кликом, если от 1.1 до 5 сек, то долгий
                event_fastclick[xm*_x+ym]=1;
                eventdown_start[xm*_x+ym]=0;
              } else {
                event_longclick[xm*_x+ym]=1;
                eventdown_start[xm*_x+ym]=0;
              }
            }
          }
          last_state[xm*_x+ym]=current_state[xm*_x+ym];
        }
        digitalWrite(pin_x[xm],0);  //реверс
      }
    }
    void init(){
      for (byte i=0; i<_x;i++){                                                                       //Инициализируем пины
        pinMode(pin_x[i],OUTPUT);
        digitalWrite(pin_x[i],0);                                                                     //На тот случай, если делать  puulup
      }
      for (byte i=0; i<_y;i++){
         pinMode(pin_y[i],INPUT);                                                                     //Не забываем подтянуть строки к земле через резисторы, иначе будет весело, можно правда сделать puulup, но придётся немного программу изменить
      }
      for (byte xm=0;xm<(_x*_y);xm++){                                                                     //Обнуляем матрицы состояний и таймеров
          current_state[xm]=0;
          last_state[xm]=0;
          event_up[xm]=0;
          event_down[xm]=0;
          event_fastclick[xm]=0;
          event_longclick[xm]=0;
          eventdown_start[xm]=0;
       
      }
    }
   boolean clickUp(byte button){                                                                      //Просто события, после считывания события - сбрасываем событие
      if (event_up[button] == 1){
        event_up[button] =0;
        return true;      
      } else {
        return false;
      }
    }
    boolean clickDown(byte button){
      if (event_down[button] == 1){
        event_down[button] =0;
        return true;      
      } else {
        return false;
      }
    }
    boolean fastclick(byte button){
      if (event_fastclick[button] == 1){
        event_fastclick[button] =0;
        return true;      
      } else {
        return false;
      }
    }
    boolean longclick(byte button){
      if (event_longclick[button] == 1){
        event_longclick[button] =0;
        return true;      
      } else {
        return false;
      }
    }
  private:
    byte _x;                                                                                //Количество столбиков
    byte _y;                                                                                //Количество строк
    byte* pin_x;                                                                             //Пины столбиков (Выходы)
    byte* pin_y;                                                                             //Пины строк     (Входы)
    boolean current_state[nr];                                                                      //Размеры массивов должны быть не менее  _x*_y
    boolean last_state[nr];
    boolean event_up[nr];
    boolean event_down[nr];
    boolean event_fastclick[nr];
    boolean event_longclick[nr];
    unsigned long eventdown_start[nr];
};
Как бы объяснить то, что мне надо...... Надо сделать так, чтобы "nr" (количество кнопок = x*y) задавалось конструктором.
Я пока не понимаю про Битовые операции, чес слова, я в этом дурак и прекрасно это осознаю.

ПС: прошло ещё сколько-то времени, сделал так, работает, правда сам опасаюсь, нужна критика в плане реализации
C++:
#include <LiquidCrystal.h>

class Matrixbutton {
  public:
    Matrixbutton(byte* setPinX, byte setCountX, byte* setPinY, byte setCountY){
      _x=setCountX;
      _y=setCountY;
      pin_x=setPinX;
      pin_y=setPinY;
      byte nnr=_x*_y;
      boolean *current_state0 = new boolean [nnr];                                                  //высвобождаем место в памяти под массивы нужной длины
      current_state=current_state0;                                                                 //ссылку на освобождённое место передаём в private, для
      boolean *last_state0 = new boolean [nnr];                                                     //организации доступа к данной области памяти из других функций класса
      last_state=last_state0;
      boolean *event_up0 = new boolean [nnr];
      event_up=event_up0;
      boolean *event_down0 = new boolean [nnr];
      event_down=event_down0;
      boolean *event_fastclick0 = new boolean [nnr];
      event_fastclick=event_fastclick0;
      boolean *event_longclick0 = new boolean [nnr];
      event_longclick=event_longclick0;
      unsigned long *eventdown_start0 = new unsigned long [nnr];
      eventdown_start=eventdown_start0;
     }
    void read(){
      for (byte xm=0;xm<_x;xm++){                                                                     // перебираем столбики
        digitalWrite(pin_x[xm],1);                                                                   
        delay(20);                                                                                    //Физика, при длинных проводах обязательно делать небольшую задержку
        for (byte ym=0;ym<_y;ym++){                                                                   //Читаем строчки
          current_state[xm*_x+ym]=digitalRead(pin_y[ym]);                                               
          if (last_state[xm*_x+ym]==0 && current_state[xm*_x+ym]==1){                                     //Если предыдущее 0, а новое 1, значит клавиша нажата
            event_down[xm*_x+ym]=1;
            eventdown_start[xm*_x+ym]=millis();                                                         //Стартуем таймер, дабы отследить длительность нажатия кнопки
          }
          if (eventdown_start[xm*_x+ym]!=0){                                                            //Если клавиша долго нажата то перестаём пытаться регистрировать события быстрого и долгого клика
            if ((millis()-eventdown_start[xm*_x+ym])>= 5000){
            eventdown_start[xm*_x+ym]=0;           
            }
          }
          if (last_state[xm*_x+ym]==1 && current_state[xm*_x+ym]==0){                                     //Если предыдущая 1, а новое 0, значит клавиша отпущена
            event_up[xm*_x+ym]=1;                                                                       //Регистрируем события отпускания клавиши, но ещё необходимо проверить события быстрого или долгого клика
            if (eventdown_start[xm*_x+ym]!=0){
              if ((millis()-eventdown_start[xm*_x+ym]) < 1100) {                                        // Если клик меньше 1.1 сек, то будем считать это быстрым кликом, если от 1.1 до 5 сек, то долгий
                event_fastclick[xm*_x+ym]=1;
                eventdown_start[xm*_x+ym]=0;
              } else {
                event_longclick[xm*_x+ym]=1;
                eventdown_start[xm*_x+ym]=0;
              }
            }
          }
          last_state[xm*_x+ym]=current_state[xm*_x+ym];
        }
        digitalWrite(pin_x[xm],0);  //реверс
      }
    }
    void init(){
      for (byte i=0; i<_x;i++){                                                                       //Инициализируем пины
        pinMode(pin_x[i],OUTPUT);
        digitalWrite(pin_x[i],0);                                                                     //На тот случай, если делать  puulup
      }
      for (byte i=0; i<_y;i++){
         pinMode(pin_y[i],INPUT);                                                                     //Не забываем подтянуть строки к земле через резисторы, иначе будет весело, можно правда сделать puulup, но придётся немного программу изменить
      }
      for (byte xm=0;xm<(_x*_y);xm++){                                                                     //Обнуляем матрицы состояний и таймеров
          current_state[xm]=0;
          last_state[xm]=0;
          event_up[xm]=0;
          event_down[xm]=0;
          event_fastclick[xm]=0;
          event_longclick[xm]=0;
          eventdown_start[xm]=0;
        
      }
    }
   boolean clickUp(byte button){                                                                      //Просто события, после считывания события - сбрасываем событие
      if (event_up[button] == 1){
        event_up[button] =0;
        return true;       
      } else {
        return false;
      }
    }
    boolean clickDown(byte button){
      if (event_down[button] == 1){
        event_down[button] =0;
        return true;       
      } else {
        return false;
      }
    }
    boolean fastclick(byte button){
      if (event_fastclick[button] == 1){
        event_fastclick[button] =0;
        return true;       
      } else {
        return false;
      }
    }
    boolean longclick(byte button){
      if (event_longclick[button] == 1){
        event_longclick[button] =0;
        return true;       
      } else {
        return false;
      }
    }
  private:
    byte _x;                                                                                //Количество столбиков
    byte _y;                                                                                //Количество строк
    byte* pin_x;                                                                             //Пины столбиков (Выходы)
    byte* pin_y;                                                                             //Пины строк     (Входы)
    boolean* current_state;                                                                     
    boolean* last_state;
    boolean* event_up;
    boolean* event_down;
    boolean* event_fastclick;
    boolean* event_longclick;
    unsigned long* eventdown_start;
};

byte pinx_Str[]={37,39,41,43,45,47,49,51,53};                                           
byte piny_Cool[]={36,38,40,42,44,46,48,50,52};

Matrixbutton knop(pinx_Str,9,piny_Cool,9);
LiquidCrystal lcd(12,11,5,4,3,2);

String str,str1,str2;
bool fl_cur,LightLcd;
unsigned long LcdTimeStart;

void setup() {
lcd.begin(16, 2);
fl_cur=false;
pinMode(8,OUTPUT);  //Управление подсветкой LCD
lcd.noDisplay();
pinMode (13,OUTPUT);
knop.init();
Serial.begin(9600); // отладка
}

void loop() {
  knop.read();                                         //Читаем кнопки
  byte f=0;
  while (f<=80){                           //Если была нажата любая кнопка, то условие в цикле сработает
    if (knop.clickUp(f)) {
      str=str+"Up="+String(f)+" ";
      fl_cur=true;
    }
    if (knop.longclick(f)){
      str=str+"LongClick="+String(f)+" ";
      fl_cur=true;
      }
    if (knop.fastclick(f)){
      str=str+"FastClick="+String(f)+" ";
      fl_cur=true;
    }
    if (knop.clickDown(f)){
      str=str+"DOWN="+String(f)+" ";
      fl_cur=true;
    }
    f++;
  }
if (LightLcd){
  if ((millis()-LcdTimeStart)>15000){     //Тушим подсветку при простое более 15 сек
    lcd.noDisplay();
    digitalWrite(8,LOW);
    LightLcd=false;
  }
}
if (fl_cur){                             //Если были события с кнопок, кидаем их на экран
  Serial.println(str);
  digitalWrite(8,HIGH);
  lcd.display();
  LcdTimeStart=millis();
  LightLcd=true;
  lcd.clear();
  str1=str2;
  str2=str;
  lcd.setCursor(0,0);
  lcd.print(str1);
  lcd.setCursor(0,1);
  lcd.print(str2);
  fl_cur=false;
  str="";
}

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

bort707

★★★★★★✩
21 Сен 2020
3,066
913
@DAK, подумайте еще на тему, нужен ли вам класс. У вас один единственный экземпляр, который создается под конкретные пины кнопок и с заданными массивами... то есть никакого ООП и никакой абстракции.
В вашем случае класс ничего не дает, напишите код в традиционном функциональном стиле - он будет проще и понятнее и никаких заморочек с массивами.
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
Вообще если хочется с объектами поиграться, то как раз можно реализовать класс доступа к битовым массивам, чтобы сам память распределял, чтобы за собой чистил (в коде выше не увидел освобождения памяти в деструкторе и самого деструктора тоже). Туда же можно еще шаблоны приткнуть, чтобы задавать размеры битовой матрицы. Но это в общем-то все требует знаний ООП хотя бы на базовом уровне. Значит учиться/учиться+учиться долго и нудно.
 
  • Лойс +1
Реакции: bort707

Старик Похабыч

★★★★★★★
14 Авг 2019
4,267
1,303
Москва
(в коде выше не увидел освобождения памяти в деструкторе и самого деструктора тоже).
Вот тут самое интересное. Когда вызывается деструктор ? сразу как цикл loop закончится ? Нет. а если он закончился, то значит что то пошло не так и надо жать ресет. А там все чистится. Или я тут где то заблуждаюсь ?
 

bort707

★★★★★★✩
21 Сен 2020
3,066
913
если он закончился, то значит что то пошло не так и надо жать ресет. А там все чистится.
Или я тут где то заблуждаюсь ?
В коде ТС все так и есть - у него один экземпляр класса " навсегда" :), и работает он до выключения ардуины. По сути ему деструктор не нужен.
Но вообще лучше взять за правило и всегда создавать деструкторы для классов, в которых используется динамическое выделение памяти, чтобы быть уверенным, что после класса все правильно чистится. Ведь вообще-то экземпляры могут создаваться и удаляться по ходу программы. Или, например, экземпляр класса может быть не глобальной переменной, а локальной - и при каждом входе в процедуру будет создаваться заново, а при выходе чиститься. Тогда подобный класс без деструкторы вызовет утечку памяти.
 
Изменено:
  • Лойс +1
Реакции: kDn

Wan-Derer

★★★★★✩✩
Команда форума
31 Июл 2018
2,136
412
Москва
wan-derer.ru
.... До каждого выключателя от электрощита кинул витую пару (4х жилку)...
Фатальная ошибка. Хабр не только полезен, но и вреден.

5. Самопал дешевле чего либо, но это не точно, ткните меня носом, если найдётся подобный контролер с подобным функционалом пусть даже за 500 баксов (и да, пока я потратил всего 150). Я честно хотел купить что нить подобное, но не нашёл...
У тебя же лежит в щитке резервный контроллер? И меняется он лёгким движением руки? Или прям горячий резерв?

3. Одновременное нажатие нескольких кнопок. Благодаря своей лени (времени не хватало) кнопки я подрубал без использования диодов.
Не очень понятна проблема с одновременным нажиманием кнопок. Семья бегает по дому и постоянно нажимает кнопки? Какова вероятность одновременного нажатия? И что за беда случится если их таки нажмут? Но в любом случае по диоду на каждую кнопку не надо, достаточно одного на линию: http://ns2.arduino.ru/sites/default/files/u1788/with_diode.png

4. САМЫЙ ЖИРНЫЙ МИНУС, ЗАЛИПАНИЕ РЕЛЕ, есть 2 точки освещения, и они залипают.... Светодиодные светильники, в сумме по 40-50 ВАТТ могут слепить реле на 10 ампер (пробовал разные реле).....