Энкодер изменяет переменную там, где не должен

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

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

C++:
  static uint8_t old_a = 0;
  uint8_t a = analogRead(A7) ; 
  if (old_a != a)
  {
    old_a = a;
    Serial.println(a);
  }
 
  • Лойс +1
Реакции: minash87

minash87

✩✩✩✩✩✩✩
1 Мар 2022
32
1
@Старик Похабыч,
Спасибо! Блин, сам не догадался, искал отдельную команду, а оно вон как просто )))

В общем два пути решения проблемы.
1 - В PageMenu == 0 поставить два условия
C++:
if (Enc.rightH());
if (Enc.leftH());
2 - В PageMenu == 0 поставить сброс счётчика энкодра
C++:
Enc.resetState();
И я не знаю, правильно так, или нет. Но я так понял, что счётчик энкодера переносит его значение при нажатии кнопки на первую попавшуюся переменную.
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@minash87,Попытаюсь объяснить, что происходит.
Изменение переменной EnableRAW происходит непосредственно там, где это написано в коде.
Проблема в том, что метод Enc.tick() выполняется каждый цикл, и не зависимо от того какая функция из switch вызывается, в объекте Enc может хранится состояние, которое могло произойти до вызова необходимой функции.
В вашем конкретном случае, пока обрабатывается case 0: может быть выполнен поворот с нажатием, это состояние хранится в объекте Enc. И когда произошло переключение, на первой же итерации проверка if (Enc.rightH()) { EnableRAW = 1; } может сработать и поменять значение EnableRAW.
По хорошему, необходимо сбрасывать состояние всех кнопок, включая энкодер, при смене режима, т.е. при каждом присвоении MenuPage нового значения выполнять метод resetState() для всех объектов? которые вызывали tick(). иначе может ловить подобные побочные эффекты и дальше.

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

C++:
template<
    typename T, void (*ChangeHandler)(const T& prev, const T& now)
> class valueWrapper final {
public:
    using type           = T;
    using thisType       = valueWrapper<type, ChangeHandler>;
    using reference      = thisType&;
    using constReference = const reference;
    using changeHandler  = void (*)(const T& prev, const T& now);

    constReference operator=(constReference rhs) { T prev = value; value = rhs.value; ChangeHandler(prev, value); return *this; }
    constReference operator=(type rhs)           { T prev = value; value = rhs;       ChangeHandler(prev, value); return *this; }
    
    bool operator==(constReference rhs) const { return (value == rhs.value); }
    bool operator==(type  rhs)          const { return (value == rhs);       }

    operator type() { return value; }
    
    valueWrapper(type defaultValue) : value(defaultValue) { }
private:
    type value;
};

template<typename T>
void ChangeMenuPage(const T& prev, const T& now) {
    if (prev != now) {
        Enc.resetState();
        BtnStart.resetState();
        BtnStop.resetState();
        BtnMenu.resetState();
        BtnExit.resetState();
    }
}

// Меню
//uint8_t MenuPage = 0; // Меню
valueWrapper<uint8_t, ChangeMenuPage<uint8_t> > MenuPage{0}; // Меню

данная обертка ведет себя как обычная целочисленная переменная (касаемо операций присвоения и получения значения), при вызове операции присваивания, например: MenuPage = 10; будет вызвана функция обработчик ChangeMenuPage(), в которой будет произведен сброс состояний всех объектов. Таким образом состояния будут сбрасываться всегда, при присвоении MenuPage нового значения.
 
  • Лойс +1
Реакции: minash87

minash87

✩✩✩✩✩✩✩
1 Мар 2022
32
1

@Kir,
Добрый день. Спасибо большое!!! Я вечером посижу попытаюсь понять что вы написали (в смысле код) До меня ещё не очень дошло про классы, стринги и почему выделяетс другим цветом val )))
А вот то что вы объяснили на пальцах про .tick() это вообще агонь. Я б точно не догадался на моем уровне знаний )))

@Kir,
В общем я не очень понял код (из-за того что ещё не знаю классы), но последние строчки натолкнули меня на мысль. Опять же, я не знаю правильно так, или нет. Но я пока что решил сделать вот так:
Взял ваш
и совет @Старик Похабыч, и объединил их
C++:
  static uint8_t old_PM = 0;
  uint8_t PM = PageMenu;
  if (old_PM != PM) {
    old_PM = PM;
    ResetState();
  }

void ResetState() {
  Enc.resetState();
  BtnStart.resetState();
  BtnStop.resetState();
  BtnMenu.resetState();
  BtnExit.resetState();
}
Вроде работает )))
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@minash87,
Как хотите, но помните, что вам нужно вставлять ResetState() везде, где меняется значение MenuPage в текущем коде, и последующем. И тут главное не забыть про это.
 

minash87

✩✩✩✩✩✩✩
1 Мар 2022
32
1
@Kir,
да, я это понимаю, и поэтому сделал в начале цикла loop
C++:
void loop() {
  static uint8_t old_PM = 0;
  uint8_t PM = PageMenu;
  if (old_PM != PM) {
    old_PM = PM;
    ResetState();
  }
    
void ResetState() {
  Enc.resetState();
  BtnStart.resetState();
  BtnStop.resetState();
  BtnMenu.resetState();
  BtnExit.resetState();
}
Спасибо что помогли )))