Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EDIT via rotary encoder #414

Closed
cemik1 opened this issue Apr 5, 2023 · 2 comments
Closed

EDIT via rotary encoder #414

cemik1 opened this issue Apr 5, 2023 · 2 comments

Comments

@cemik1
Copy link
Contributor

cemik1 commented Apr 5, 2023

Based on your examples I prepared MENU controlled by rotary encoder. It works well (FIELDs timeOn and timeOff are fully controlled) except EDIT field. Trying edit buf1 string value by encoder only down change is possible. Encoder counter is displayed and both move directions are properly detected (up and down). It is also possible edit this string by serial input so menu system seems be correct. Could you advice is it my fault or some library issue?
I have also tested it with SSD1306 display and other encoder/button library with the same result.
Platform ESP32, project enclosed

#include <Arduino.h>

/********************
Generic Rotary/Button input
output: serial
input: serial, clickable rotary encoder

purpose:

  Having a generic rotary event-based implementation,
  leaving rotary and button libraries up to the user.
  Example uses QDEC and AceButton, but could be anything
  that suits your particular hardware and/or needs.

  TODO: userland rotary/button event mapping to menu actions,
  as doubleclick/longpress are now hardcoded to back.
  
***/

#include <menu.h>
#include <menuIO/chainStream.h>
#include <menuIO/rotaryEventIn.h>
#include <menuIO/serialIn.h>
#include <menuIO/serialOut.h>

// some example libraries to handle the rotation and clicky part
// of the encoder. These will generate our events.
#include <qdec.h> //https://github.com/SimpleHacks/QDEC
#include <AceButton.h> // https://github.com/bxparks/AceButton

// Encoder
#define ROTARY_PIN_A    (uint16_t)(32) // the first pin connected to the rotary encoder
#define ROTARY_PIN_B    (uint16_t)(33) // the second pin connected to the rotary encoder
const int ROTARY_PIN_BUT  = 35;

int8_t counterEnc = 0;  // encoder counter

using namespace ::ace_button;
using namespace ::SimpleHacks;
QDecoder qdec(ROTARY_PIN_A, ROTARY_PIN_B, true); // rotary part
AceButton button(ROTARY_PIN_BUT); // button part
//--//

#define LEDPIN LED_BUILTIN

// AndroidMenu 
// https://github.com/neu-rah/ArduinoMenu
#define MAX_DEPTH 1

unsigned int timeOn=10;
unsigned int timeOff=90;

using namespace Menu;

//char* constMEM hexDigit MEMMODE="0123456789ABCDEF";
const char hexDigit[] = "0123456789ABCDEF";
//char* constMEM hexNr[] MEMMODE={hexDigit,hexDigit};
const char* hexNr[] ={hexDigit,hexDigit};
char buf1[]="11"; 

MENU(mainMenu, "Blink menu", Menu::doNothing, Menu::noEvent, Menu::wrapStyle
  ,FIELD(timeOn,"On","ms",0,1000,10,1, Menu::doNothing, Menu::noEvent, Menu::noStyle)
  ,FIELD(timeOff,"Off","ms",0,10000,10,1,Menu::doNothing, Menu::noEvent, Menu::noStyle)
  ,FIELD(counterEnc,"Encoder"," step",-128,127,0,0,Menu::doNothing, Menu::noEvent, Menu::noStyle)
  ,EDIT("Hex",buf1,hexNr,Menu::doNothing,Menu::noEvent,Menu::noStyle)  
  ,EXIT("<Back")
);

RotaryEventIn reIn(
  RotaryEventIn::EventType::BUTTON_CLICKED | // select
  RotaryEventIn::EventType::BUTTON_DOUBLE_CLICKED | // back
  RotaryEventIn::EventType::BUTTON_LONG_PRESSED | // also back
  RotaryEventIn::EventType::ROTARY_CCW | // up
  RotaryEventIn::EventType::ROTARY_CW // down
); // register capabilities, see AndroidMenu MenuIO/RotaryEventIn.h file

serialIn serial(Serial);

MENU_INPUTS(in,&reIn, &serial);

MENU_OUTPUTS(out,MAX_DEPTH
  //,U8G2_OUT(u8g2,colors,fontX,fontY,offsetX,offsetY,{0,0,U8_Width/fontX,U8_Height/fontY})
  ,SERIAL_OUT(Serial)
  ,NONE 
);
NAVROOT(nav,mainMenu,MAX_DEPTH,in,out);
//--//

// This is the ISR (interrupt service routine) for rotary events
// We will convert/relay events to the RotaryEventIn object
// Callback config in setup()
void IsrForQDEC(void) { 
  QDECODER_EVENT event = qdec.update();
  if (event & QDECODER_EVENT_CW) { reIn.registerEvent(RotaryEventIn::EventType::ROTARY_CW); counterEnc++;}
  else if (event & QDECODER_EVENT_CCW) { reIn.registerEvent(RotaryEventIn::EventType::ROTARY_CCW); counterEnc--;}

}

// This is the handler/callback for button events
// We will convert/relay events to the RotaryEventIn object
// Callback config in setup()
void handleButtonEvent(AceButton* /* button */, uint8_t eventType, uint8_t buttonState) {
  switch (eventType) {
    case AceButton::kEventClicked:
      reIn.registerEvent(RotaryEventIn::EventType::BUTTON_CLICKED);
      break;
    case AceButton::kEventDoubleClicked:
      reIn.registerEvent(RotaryEventIn::EventType::BUTTON_DOUBLE_CLICKED);
      break;
    case AceButton::kEventLongPressed:
      reIn.registerEvent(RotaryEventIn::EventType::BUTTON_LONG_PRESSED);
      break;
  }
}

bool blink(int timeOn,int timeOff) {
  return millis()%(unsigned long)(timeOn+timeOff)<(unsigned long)timeOn;
}

void setup() {
  pinMode(LEDPIN, OUTPUT);

  Serial.begin(115200);
  while(!Serial);

  // setup rotary encoder
  qdec.begin();
  attachInterrupt(digitalPinToInterrupt(ROTARY_PIN_A), IsrForQDEC, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ROTARY_PIN_B), IsrForQDEC, CHANGE);

  // setup rotary button
  pinMode(ROTARY_PIN_BUT, INPUT);
  ButtonConfig* buttonConfig = button.getButtonConfig();
  buttonConfig->setEventHandler(handleButtonEvent);
  buttonConfig->setFeature(ButtonConfig::kFeatureClick);
  buttonConfig->setFeature(ButtonConfig::kFeatureDoubleClick);
  buttonConfig->setFeature(ButtonConfig::kFeatureLongPress);
  buttonConfig->setFeature(ButtonConfig::kFeatureSuppressClickBeforeDoubleClick);
  buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterClick);
  buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterDoubleClick);
  
  mainMenu[2].disable(); // only display counterEnc value
}


void loop() {
  // put your main code here, to run repeatedly:
  button.check(); // acebutton check, rotary is on ISR
  nav.doInput(); // menu check
  if (nav.changed(0)) {
    nav.doOutput();
  }  
  digitalWrite(LEDPIN, blink(timeOn,timeOff));
}

Menu.zip

@cemik1 cemik1 mentioned this issue Feb 13, 2024
@cemik1
Copy link
Contributor Author

cemik1 commented Jul 30, 2024

I think I found problem source. There is encoder CW move event definition in rotaryEventIn.h file:
ROTARY_CW = 1 << 4,
It means 0x08 is code for this event,
But during text field processing (items.cpp file) this value is decoded as backspace event first:

void textField::parseInput(navNode& nav,menuIn& in) {
  trace(MENU_DEBUG_OUT<<"navTarget::parseInput"<<endl);
  if (/*charEdit&&*/in.available()) {
    char c=in.peek();
    if (options->useNavChars&&(c==options->getCmdChar(upCmd)
      ||c==options->getCmdChar(downCmd)
      ||c==options->getCmdChar(enterCmd)
      ||c==options->getCmdChar(escCmd))) {
        navTarget::parseInput(nav,in);
        return;
      }
    switch(c) {//special cases
      case 0x0D://enter
        in.read();
        charEdit=false;
        dirty=true;
        // edited=false;
        cursor=0;
        nav.root->exit();
        return;
      case 0x08://backspace
        in.read();
        buffer()[cursor]=validator(cursor)[0];
        if (cursor) cursor--;
        dirty=true;
        return;

I am only amateur c++ coder so my solution is to change CW definition in rotaryEventIn.h to:
ROTARY_CW = 1 << 5
Now project works as expected and text field can be edited by rotary encoder.

Please update library in proper and elegant way.

cemik1 added a commit to cemik1/ArduinoMenu that referenced this issue Aug 1, 2024
neu-rah added a commit that referenced this issue Nov 4, 2024
Fix for issue #414 (update rotaryEventIn.h)
neu-rah added a commit that referenced this issue Nov 4, 2024
(bump version after fix) for issue #414
@neu-rah
Copy link
Owner

neu-rah commented Nov 4, 2024

thank you

@neu-rah neu-rah closed this as completed Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants