Paddle controls - Rotary encoder

Hi guys,

I have seen that there is potential to use a potentiometer as a paddle controller for certain games.
I was wondering if it would be possible to use a rotary encoder instead.
The encoder would be like this:TTC Golden wheel 11mm yellow core silver wheel mouse wheel encoder RIVAL decoder 300 Wrangler 600 700

I would consider using it for volume control/ left or right movement / potentiometer like paddle control.

Thanks :slight_smile:

1 Like

Check out the page Arduboy Crank Mod, it’s based on rotary encoder and probably answers most of your questions. I have an example of how to set up a rotary encoder for arduboy on github.

2 Likes

I see what you did there :shushing_face:

This is a great idea and would be much more useful than the crank mod.

1 Like

Slide pots particularly :eyes:

Hi I had a read through the page but I am a little out of my depth. To my understanding, the crank mod allowed for the control of some specially modified games.

I am wondering if it could be possible to modify the main ArduBoy code and have the encoder act as left and right whilst also having the left and right pad buttons offering the same thing. This way I wouldn’t need to modify the code of each individual game.

I am looking to do this in a DIY ArduBoy clone build, with flash w25qxx module.

Could this be possible?

Thanks :slight_smile:

1 Like

Possible, yes, but practical? ehhh. It really is easier to modify each game as opposed to the Arduboy2 library. What if a game doesn’t use left and right as the main controls but up and down like Pong(which is very fun with encoders btw)? It’s better to have the option to change what it does. Also important to note is that turning the encoder in a direction won’t appear as a continuous press, but a series of short “clicks”.

1 Like

Not to sound like a smartarse … but pong would work perfectly well with a rotary encoder as the encode is not aligned with an axis - it just reports a turn one way or the other. This could be left/right or up/down or diagonally left/down versus up/right. But you knew this already :grinning:

3 Likes

Is it a difficult procedure to alter the individual codes of games to add encoder controls?
For instance editing the ArduBreakout code. Where would I begin?

Thanks!

1 Like

So the main change you’ll be making is to the movePaddle() function, from this:

void movePaddle(){
   if (xPaddle <WIDTH-12){
    if (arduboy.pressed(RIGHT_BUTTON))
    {
      xPaddle+=2;
    }
  }

  //Move left
  if(xPaddle > 0)
  {
    if (arduboy.pressed(LEFT_BUTTON))
    {
      xPaddle-=2;
    }
  }
}

To this:

void movePaddle(){
  //Move right
  int newPos = myEnc.read()/4;
  
  int difference = (newPos - oldPos);
   if (difference < 0) {
    //clockwise
    if (xPaddle < WIDTH -12) {
      xPaddle+=2;
    }
   } else if (difference >0) {
    //anticlockwise
    if (xPaddle >0){
      xPaddle-=2;
    }
   } else {
    //no change
   }
  oldPos = newPos;
}

I went ahead and fleshed out the code for that particular example, here it is:

 /*
  Breakout
 Copyright (C) 2011 Sebastian Goscik
 All rights reserved.

 Modifications by Scott Allen 2016, 2018, 2020
 after previous changes by person(s) unknown.

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 */

#include <Arduboy2.h>
#include <Encoder.h>
// block in EEPROM to save high scores
#define EE_FILE 2

// EEPROM space used: 35 bytes (7*(3+2)) starting at
// EEPROM_STORAGE_SPACE_START + (EE_FILE * 35)
Encoder myEnc(3, 1);
Arduboy2 arduboy;
BeepPin1 beep;

constexpr uint8_t frameRate = 40; // Frame rate in frames per second

// Tone frequencies. Converted to count values for the beep class
constexpr uint16_t tonePaddle = beep.freq(200); // Ball hits paddle
constexpr uint16_t toneBrick = beep.freq(261); // Ball hits a brick
constexpr uint16_t toneEdge = beep.freq(523); // Ball hits top or sides
constexpr uint16_t toneMiss = beep.freq(175); // Ball misses paddle, lose life
constexpr uint16_t toneInitialsChange = beep.freq(523); // Change initials
constexpr uint16_t toneInitialsMove = beep.freq(1046); // Select initials
// Tone durations
constexpr uint8_t toneTimeBeep = 250 / (1000 / frameRate); // Game (frames)
constexpr uint16_t toneTimeMiss = 500; // Miss paddle (milliseconds)
constexpr uint16_t toneTimeInitials = 80; // Initials entry (milliseconds)


constexpr unsigned int columns = 13; //Columns of bricks
constexpr unsigned int rows = 4;     //Rows of bricks

int oldPos = -999;

int dx = -1;        //Initial movement of ball
int dy = -1;        //Initial movement of ball
int xb;           //Ball's starting position
int yb;           //Ball's starting position
boolean released;     //If the ball has been released by the player
boolean paused = false;   //If the game has been paused
byte xPaddle;       //X position of paddle
boolean isHit[rows][columns];   //Array of if bricks are hit or not
boolean bounced=false;  //Used to fix double bounce glitch
byte lives = 3;       //Amount of lives
byte level = 1;       //Current level
unsigned int score=0;   //Score for the game
unsigned int brickCount;  //Amount of bricks hit
boolean pad, pad2, pad3;  //Button press buffer used to stop pause repeating
boolean oldpad, oldpad2, oldpad3;
char text_buffer[16];      //General string buffer
boolean start=false;    //If in menu or in game
boolean initialDraw=false;//If the inital draw has happened
char initials[3];     //Initials used in high score

//Ball Bounds used in collision detection
byte leftBall;
byte rightBall;
byte topBall;
byte bottomBall;

//Brick Bounds used in collision detection
byte leftBrick;
byte rightBrick;
byte topBrick;
byte bottomBrick;

byte tick;

void setup()
{
  arduboy.begin();
  beep.begin();
  pinMode(3, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  arduboy.setFrameRate(frameRate);
  arduboy.initRandomSeed();
}

void loop()
{
  // pause render until it's time for the next frame
  if (!(arduboy.nextFrame()))
    return;

  // Handle the timing and stopping of tones
  beep.timer();

  
  
  //Title screen loop switches from title screen
  //and high scores until FIRE is pressed
  while (!start)
  {
    start = titleScreen();
    if (!start)
    {
      start = displayHighScores(EE_FILE);
    }
  }

  //Initial level draw
  if (!initialDraw)
  {
    //Clears the screen
    arduboy.clear();
    //Selects Font
    //Draws the new level
    level = 1;
    initialDraw=true;
    newLevel();
    score = 0;
  }

  if (lives>0)
  {
    drawPaddle();

    //Pause game if FIRE pressed
    pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);

    if(pad == true && oldpad == false && released)
    {
      oldpad2 = false; //Forces pad loop 2 to run once
      pause();
    }

    oldpad = pad;
    drawBall();

    if(brickCount == rows * columns)
    {
      level++;
      newLevel();
    }
  }
  else
  {
    drawGameOver();
    if (score > 0)
    {
      enterHighScore(EE_FILE);
    }

    arduboy.clear();
    initialDraw=false;
    start=false;
    lives=3;
    newLevel();
  }

  arduboy.display();
}

void movePaddle()
{
  //Move right
  int newPos = myEnc.read()/4;
  
  int difference = (newPos - oldPos);
   if (difference < 0) {
    //clockwise
    if (xPaddle < WIDTH -12) {
      xPaddle+=2;
    }
   } else if (difference >0) {
    //anticlockwise
    if (xPaddle >0){
      xPaddle-=2;
    }
   } else {
    //no change
   }
  oldPos = newPos;
}

void moveBall()
{
  tick++;
  if(released)
  {
    //Move ball
    if (abs(dx)==2)
    {
      xb += dx/2;
      // 2x speed is really 1.5 speed
      if (tick%2==0)
        xb += dx/2;
    }
    else
    {
      xb += dx;
    }
    yb=yb + dy;

    //Set bounds
    leftBall = xb;
    rightBall = xb + 2;
    topBall = yb;
    bottomBall = yb + 2;

    //Bounce off top edge
    if (yb <= 0)
    {
      yb = 2;
      dy = -dy;
      playTone(toneEdge, toneTimeBeep);
    }

    //Lose a life if bottom edge hit
    if (yb >= 64)
    {
      arduboy.drawRect(xPaddle, 63, 11, 1, BLACK);
      xPaddle = 54;
      yb=60;
      released = false;
      lives--;
      playToneTimed(toneMiss, toneTimeMiss);
      if (random(0, 2) == 0)
      {
        dx = 1;
      }
      else
      {
        dx = -1;
      }
    }

    //Bounce off left side
    if (xb <= 0)
    {
      xb = 2;
      dx = -dx;
      playTone(toneEdge, toneTimeBeep);
    }

    //Bounce off right side
    if (xb >= WIDTH - 2)
    {
      xb = WIDTH - 4;
      dx = -dx;
      playTone(toneEdge, toneTimeBeep);
    }

    //Bounce off paddle
    if (xb+1>=xPaddle && xb<=xPaddle+12 && yb+2>=63 && yb<=64)
    {
      dy = -dy;
      dx = ((xb-(xPaddle+6))/3); //Applies spin on the ball
      // prevent straight bounce
      if (dx == 0)
      {
        dx = (random(0, 2) == 1) ? 1 : -1;
      }
      playTone(tonePaddle, toneTimeBeep);
    }

    //Bounce off Bricks
    for (byte row = 0; row < rows; row++)
    {
      for (byte column = 0; column < columns; column++)
      {
        if (!isHit[row][column])
        {
          //Sets Brick bounds
          leftBrick = 10 * column;
          rightBrick = 10 * column + 10;
          topBrick = 6 * row + 1;
          bottomBrick = 6 * row + 7;

          //If A collison has occured
          if (topBall <= bottomBrick && bottomBall >= topBrick &&
              leftBall <= rightBrick && rightBall >= leftBrick)
          {
            Score();
            brickCount++;
            isHit[row][column] = true;
            arduboy.drawRect(10*column, 2+6*row, 8, 4, BLACK);

            //Vertical collision
            if (bottomBall > bottomBrick || topBall < topBrick)
            {
              //Only bounce once each ball move
              if(!bounced)
              {
                dy =- dy;
                yb += dy;
                bounced = true;
                playTone(toneBrick, toneTimeBeep);
              }
            }

            //Hoizontal collision
            if (leftBall < leftBrick || rightBall > rightBrick)
            {
              //Only bounce once brick each ball move
              if(!bounced)
              {
                dx =- dx;
                xb += dx;
                bounced = true;
                playTone(toneBrick, toneTimeBeep);
              }
            }
          }
        }
      }
    }
    //Reset Bounce
    bounced = false;
  }
  else
  {
    //Ball follows paddle
    xb=xPaddle + 5;

    //Release ball if FIRE pressed
    pad3 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
    if (pad3 == true && oldpad3 == false)
    {
      released = true;

      //Apply random direction to ball on release
      if (random(0, 2) == 0)
      {
        dx = 1;
      }
      else
      {
        dx = -1;
      }
      //Makes sure the ball heads upwards
      dy = -1;
    }
    oldpad3 = pad3;
  }
}

void drawBall()
{
  arduboy.drawPixel(xb,   yb,   BLACK);
  arduboy.drawPixel(xb+1, yb,   BLACK);
  arduboy.drawPixel(xb,   yb+1, BLACK);
  arduboy.drawPixel(xb+1, yb+1, BLACK);

  moveBall();

  arduboy.drawPixel(xb,   yb,   WHITE);
  arduboy.drawPixel(xb+1, yb,   WHITE);
  arduboy.drawPixel(xb,   yb+1, WHITE);
  arduboy.drawPixel(xb+1, yb+1, WHITE);
}

void drawPaddle()
{
  arduboy.drawRect(xPaddle, 63, 11, 1, BLACK);
  movePaddle();
  arduboy.drawRect(xPaddle, 63, 11, 1, WHITE);
}

void drawGameOver()
{
  arduboy.drawPixel(xb,   yb,   BLACK);
  arduboy.drawPixel(xb+1, yb,   BLACK);
  arduboy.drawPixel(xb,   yb+1, BLACK);
  arduboy.drawPixel(xb+1, yb+1, BLACK);
  arduboy.setCursor(37, 42);
  arduboy.print("Game Over");
  arduboy.setCursor(31, 56);
  arduboy.print("Score: ");
  arduboy.print(score);
  arduboy.display();
  arduboy.delayShort(4000);
}

void pause()
{
  paused = true;
  //Stop tone if playing
  beep.noTone();
  //Draw pause to the screen
  arduboy.setCursor(52, 45);
  arduboy.print("PAUSE");
  arduboy.display();
  while (paused)
  {
    arduboy.delayShort(150);
    //Unpause if FIRE is pressed
    pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
    if (pad2 == true && oldpad2 == false && released)
    {
        arduboy.fillRect(52, 45, 30, 11, BLACK);

        paused=false;
    }
    oldpad2 = pad2;
  }
}

void Score()
{
  score += (level*10);
}

void newLevel()
{
  //Undraw paddle
  arduboy.drawRect(xPaddle, 63, 11, 1, BLACK);

  //Undraw ball
  arduboy.drawPixel(xb,   yb,   BLACK);
  arduboy.drawPixel(xb+1, yb,   BLACK);
  arduboy.drawPixel(xb,   yb+1, BLACK);
  arduboy.drawPixel(xb+1, yb+1, BLACK);

  //Alter various variables to reset the game
  xPaddle = 54;
  yb = 60;
  brickCount = 0;
  released = false;

  //Draws new bricks and resets their values
  for (byte row = 0; row < 4; row++)
  {
    for (byte column = 0; column < 13; column++)
    {
      isHit[row][column] = false;
      arduboy.drawRect(10*column, 2+6*row, 8, 4, WHITE);
    }
  }

  if (!initialDraw)
  {
    arduboy.clear();
  }
  else
  {
    arduboy.display();
  }
}

//Used to delay images while reading button input
boolean pollFireButton(int n)
{
  for(int i = 0; i < n; i++)
  {
    arduboy.delayShort(15);
    pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
    if(pad == true && oldpad == false)
    {
      oldpad3 = true; //Forces pad loop 3 to run once
      return true;
    }
    oldpad = pad;
  }
  return false;
}

//Function by nootropic design to display highscores
boolean displayHighScores(byte file)
{
  byte y = 8;
  byte x = 24;
  // Each block of EEPROM has 7 high scores, and each high score entry
  // is 5 bytes long:  3 bytes for initials and two bytes for score.
  int address = file * 7 * 5 + EEPROM_STORAGE_SPACE_START;
  byte hi, lo;
  arduboy.clear();
  arduboy.setCursor(32, 0);
  arduboy.print("HIGH SCORES");
  arduboy.display();

  for(int i = 0; i < 7; i++)
  {
    sprintf(text_buffer, "%2d", i+1);
    arduboy.setCursor(x, y+(i*8));
    arduboy.print(text_buffer);
    arduboy.display();
    hi = EEPROM.read(address + (5*i));
    lo = EEPROM.read(address + (5*i) + 1);

    if ((hi == 0xFF) && (lo == 0xFF))
    {
      score = 0;
    }
    else
    {
      score = (hi << 8) | lo;
    }

    initials[0] = (char)EEPROM.read(address + (5*i) + 2);
    initials[1] = (char)EEPROM.read(address + (5*i) + 3);
    initials[2] = (char)EEPROM.read(address + (5*i) + 4);

    if (score > 0)
    {
      sprintf(text_buffer, "%c%c%c %u", initials[0], initials[1], initials[2], score);
      arduboy.setCursor(x + 24, y + (i*8));
      arduboy.print(text_buffer);
      arduboy.display();
    }
  }
  if (pollFireButton(300))
  {
    return true;
  }
  return false;
  arduboy.display();
}

boolean titleScreen()
{
  //Clears the screen
  arduboy.clear();
  arduboy.setCursor(16, 22);
  arduboy.setTextSize(2);
  arduboy.print("BREAKOUT");
  arduboy.setTextSize(1);
  arduboy.display();
  if (pollFireButton(25))
  {
    return true;
  }

  //Flash "Press FIRE" 5 times
  for(byte i = 0; i < 5; i++)
  {
    //Draws "Press FIRE"
    arduboy.setCursor(31, 53);
    arduboy.print("PRESS FIRE!");
    arduboy.display();

    if (pollFireButton(50))
    {
      return true;
    }

    //Removes "Press FIRE"
    arduboy.setCursor(31, 53);
    arduboy.print("           ");
    arduboy.display();

    if (pollFireButton(25))
    {
      return true;
    }
  }

  return false;
}

//Function by nootropic design to add high scores
void enterInitials()
{
  byte index = 0;

  arduboy.clear();

  initials[0] = ' ';
  initials[1] = ' ';
  initials[2] = ' ';

  while (true)
  {
    arduboy.display();
    arduboy.clear();

    arduboy.setCursor(16, 0);
    arduboy.print("HIGH SCORE");
    sprintf(text_buffer, "%u", score);
    arduboy.setCursor(88, 0);
    arduboy.print(text_buffer);
    arduboy.setCursor(56, 20);
    arduboy.print(initials[0]);
    arduboy.setCursor(64, 20);
    arduboy.print(initials[1]);
    arduboy.setCursor(72, 20);
    arduboy.print(initials[2]);
    for(byte i = 0; i < 3; i++)
    {
      arduboy.drawLine(56 + (i*8), 27, 56 + (i*8) + 6, 27, WHITE);
    }
    arduboy.drawLine(56, 28, 88, 28, BLACK);
    arduboy.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, WHITE);
    arduboy.delayShort(70);

    if (arduboy.pressed(LEFT_BUTTON) || arduboy.pressed(B_BUTTON))
    {
      if (index > 0)
      {
        index--;
        playToneTimed(toneInitialsMove, toneTimeInitials);
      }
    }

    if (arduboy.pressed(RIGHT_BUTTON))
    {
      if (index < 2)
      {
        index++;
        playToneTimed(toneInitialsMove, toneTimeInitials);
      }
    }

    if (arduboy.pressed(UP_BUTTON))
    {
      initials[index]++;
      playToneTimed(toneInitialsChange, toneTimeInitials);
      // A-Z 0-9 :-? !-/ ' '
      if (initials[index] == '0')
      {
        initials[index] = ' ';
      }
      if (initials[index] == '!')
      {
        initials[index] = 'A';
      }
      if (initials[index] == '[')
      {
        initials[index] = '0';
      }
      if (initials[index] == '@')
      {
        initials[index] = '!';
      }
    }

    if (arduboy.pressed(DOWN_BUTTON))
    {
      initials[index]--;
      playToneTimed(toneInitialsChange, toneTimeInitials);
      if (initials[index] == ' ')
      {
        initials[index] = '?';
      }
      if (initials[index] == '/')
      {
        initials[index] = 'Z';
      }
      if (initials[index] == 31)
      {
        initials[index] = '/';
      }
      if (initials[index] == '@')
      {
        initials[index] = ' ';
      }
    }

    if (arduboy.pressed(A_BUTTON))
    {
      playToneTimed(toneInitialsMove, toneTimeInitials);
      if (index < 2)
      {
        index++;
      }
      else
      {
        return;
      }
    }
  }

}

void enterHighScore(byte file)
{
  // Each block of EEPROM has 7 high scores, and each high score entry
  // is 5 bytes long:  3 bytes for initials and two bytes for score.
  int address = file * 7 * 5 + EEPROM_STORAGE_SPACE_START;
  byte hi, lo;
  char tmpInitials[3];
  unsigned int tmpScore = 0;

  // High score processing
  for(byte i = 0; i < 7; i++)
  {
    hi = EEPROM.read(address + (5*i));
    lo = EEPROM.read(address + (5*i) + 1);
    if ((hi == 0xFF) && (lo == 0xFF))
    {
      // The values are uninitialized, so treat this entry
      // as a score of 0.
      tmpScore = 0;
    }
    else
    {
      tmpScore = (hi << 8) | lo;
    }
    if (score > tmpScore)
    {
      enterInitials();
      for(byte j = i; j < 7; j++)
      {
        hi = EEPROM.read(address + (5*j));
        lo = EEPROM.read(address + (5*j) + 1);

        if ((hi == 0xFF) && (lo == 0xFF))
        {
        tmpScore = 0;
        }
        else
        {
          tmpScore = (hi << 8) | lo;
        }

        tmpInitials[0] = (char)EEPROM.read(address + (5*j) + 2);
        tmpInitials[1] = (char)EEPROM.read(address + (5*j) + 3);
        tmpInitials[2] = (char)EEPROM.read(address + (5*j) + 4);

        // write score and initials to current slot
        EEPROM.update(address + (5*j), ((score >> 8) & 0xFF));
        EEPROM.update(address + (5*j) + 1, (score & 0xFF));
        EEPROM.update(address + (5*j) + 2, initials[0]);
        EEPROM.update(address + (5*j) + 3, initials[1]);
        EEPROM.update(address + (5*j) + 4, initials[2]);

        // tmpScore and tmpInitials now hold what we want to
        //write in the next slot.
        score = tmpScore;
        initials[0] = tmpInitials[0];
        initials[1] = tmpInitials[1];
        initials[2] = tmpInitials[2];
      }

      score = 0;
      initials[0] = ' ';
      initials[1] = ' ';
      initials[2] = ' ';

      return;
    }
  }
}

// Play a tone at a frequency coresponding to the specified precomputed count,
// for the specified number of frames.
void playTone(uint16_t count, uint8_t frames)
{
  beep.tone(count, frames);
}

// Play a tone at a frequency coresponding to the specified precomputed count,
// for the specified duration in milliseconds, using a delay.
// Used when beep.timer() isn't being called.
void playToneTimed(uint16_t count, uint16_t duration)
{
  beep.tone(count);
  arduboy.delayShort(duration);
  beep.noTone();
}

The procedure is: include the encoder library, declare the encoder on the pins you want to use, create an int for the old position, put the two pins used in your setup using pinMode(pinNumber, INPUT_PULLUP);, and then modify the movement or player controls.

2 Likes

I beg to differ. Doing it per-game will always be the most effort. Before you factor in the effort of making the changes, actually hunting down where each game keeps its movement code is going to be a nuissance. Some games don’t use the usual button reading functions, some read the bitset and use masking operations to determine which buttons are pressed.

Modifying the library itself would mean you could just read the rotary encoder in pollButtons and interpret it the same way as the left or right buttons being pressed (i.e. by writing the correct bits to the button state bitset). You may need to set the encoder up in boot, but apart from that your work would be done. That would work even for those games that are reading the bitset because you’d be modifying the bitset.

Naturally if you need the encoder to behave differently for each game then it would make more sense to have some means of changing the encoder’s behaviour (which could be a simple as introuding an enum class, a private member variable, a getter and a setter function).

I could throw something together to demonstrate, but I’d have no means of testing it and it would take me a while to dig out my command line git so I could clone Arduboy2 properly - I don’t want to just fork it because then I won’t be able to make another fork and I occasionally contribute to the Arduboy2 library.

For directions that shouldn’t matter too much because the direction buttons would only be polled once per frame anyway, and it usually doesn’t matter whether buttons are being ‘clicked’ or held. It could be an issue with something like an RPG or if you tied them to the A or B buttons, but for the direction buttons you’d probably be fine for at least 75-90% of games.

2 Likes

Man … get yourself github desktop.

Same problem - I’d hardly ever use it so it would end up buried somewhere.

Yep … but you wouldn’t have to re-educate yourself on those pesky git commands each time.

That doesn’t bother me, I’m used to having to refer back to documentation on a regular basis.

Or do you think I actually memorise every library that I use? :P

It took marginally longer than anticipated because I had to split it over two classes to match how buttons are implemented and I had to take a detour to figure out why GitHub wasn’t happy with my password on command line git (apparently they’re using ‘personal access tokens’ now), but here it is:

Completely untested, no warranties. I don’t even guarantee that it compiles.

If it works I’ll merge it into the master branch and then see what demand there is. If there’s enough interest I might add more features, otherwise I’ll probably just leave people to fork and modify it as they please.

To use it, just download a copy with Code > Download ZIP and then copy the contents of Arduboy2WithEncoder-master into your Arduino/libraries/Arduboy2 folder to overwrite the default library. (Really you should only need to overwrite the contents of the src folder.)

If anybyody needs any clarification as to how it works then feel free to ask, but the commit details should be enough for any intermediate programmers out there.

Ideally I’d like to get rid of the awful pinMode calls but I can’t be bothered to check the documentation to see what the proper pins would be (and that would slightly complicate modifying which pins are used).
(Bonus points for anyone who can get rid of the dependency on the Encoder library.)

1 Like

Amazing! I’m excited to test this out, which digital arduino pins should I connect the encoder pins to?

Thanks :slight_smile:

1 Like

Are you doing this with a normal Arduboy or a homemade one?

If you’re doing it with a homemade one, it will depend on your processor and wiring arrangement.
I would presume any set of free digital pins should suffice, but GPIO and wiring are not my forte.

It works best with interrupt capable pins. For production arduboys I use 3 and 1 or 3 and 0. For DIY I tend to use 3 and 2 because I don’t tend to add an FX mod for those (which use pin 2)

1 Like