Required Libraries

We need four libraries for the Rotary Trinkey: one each for the rotary encoder, the NeoPixel, capacitive touch, and HID.

In the Arduino library manager, search for RotaryEncoder and install RotaryEncoder.

Then, search for NeoPixel and install Adafruit NeoPixel (double check the name!)

Next, install Adafruit FreeTouch.

Finally, install HID-Project.

NeoPixel Example

Compile and upload the following example.

#include <Adafruit_NeoPixel.h>
#include "Adafruit_FreeTouch.h"
#include <RotaryEncoder.h>

// Create the neopixel strip with the built in definitions NUM_NEOPIXEL and PIN_NEOPIXEL
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_NEOPIXEL, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
int16_t neo_brightness = 20; // initialize with 20 brightness (out of 255)

RotaryEncoder encoder(PIN_ENCODER_A, PIN_ENCODER_B, RotaryEncoder::LatchMode::FOUR3);
// This interrupt will do our encoder reading/checking!
void checkPosition() {
  encoder.tick(); // just call tick() to check the state.
}

uint8_t wheel_offset = 99;
int last_rotary = 0;

void setup() {
  Serial.begin(115200);
  //while (!Serial);

  // start neopixels
  strip.begin();
  strip.setBrightness(neo_brightness);
  strip.show(); // Initialize all pixels to 'off'

  attachInterrupt(PIN_ENCODER_A, checkPosition, CHANGE);
  attachInterrupt(PIN_ENCODER_B, checkPosition, CHANGE);
  
  // set up the encoder switch, which is separate from the encoder
  pinMode(PIN_ENCODER_SWITCH, INPUT_PULLDOWN);
}


void loop() {
  // read encoder
  int curr_rotary = encoder.getPosition();
  RotaryEncoder::Direction direction = encoder.getDirection();
  
  if (curr_rotary != last_rotary) {
    Serial.print("Encoder value: ");
    Serial.print(curr_rotary);
    Serial.print(" direction: ");
    Serial.print((int)direction);

    // behavior differs if switch is pressed
    if (!digitalRead(PIN_ENCODER_SWITCH)) {
      // update color
      if (direction == RotaryEncoder::Direction::CLOCKWISE) {
        wheel_offset++;
      }
      if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
        wheel_offset--;
      }
    } else {
      // update brightness
      if (direction == RotaryEncoder::Direction::CLOCKWISE) {
        neo_brightness += 10;
      }
      if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
        neo_brightness -= 10;
      }
      // ranges between 0 and 255
      if (neo_brightness > 255) neo_brightness = 255;
      if (neo_brightness < 0) neo_brightness = 0;
    }
    Serial.print(" wheel color: ");
    Serial.print(wheel_offset);
    Serial.print(" brightness: ");
    Serial.println(neo_brightness);

    last_rotary = curr_rotary;

    // update pixels!
    strip.setBrightness(neo_brightness);
    strip.setPixelColor(0, Wheel(wheel_offset));
    strip.show();
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

Rotate the rotary encoder to change the color of the NeoPixel. Press and rotate the rotary encoder to change the brightness of the NeoPixel.

Note that you can use the onboard NeoPixel like any strip. We have A full guide on NeoPixels and how to use and code them here.

We pre-define two constants, PIN_NEOPIXEL and NUM_NEOPIXEL so you don't have to remember the NeoPixel pin or quantity.

SurfaceDial Example

Compile and upload the following example.

#include <Adafruit_NeoPixel.h>
#include <RotaryEncoder.h>
#include "HID-Project.h"  // https://github.com/NicoHood/HID

// Create the neopixel strip with the built in definitions NUM_NEOPIXEL and PIN_NEOPIXEL
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_NEOPIXEL, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
int16_t neo_brightness = 20; // initialize with 20 brightness (out of 255)

RotaryEncoder encoder(PIN_ENCODER_A, PIN_ENCODER_B, RotaryEncoder::LatchMode::FOUR3);
// This interrupt will do our encoder reading/checking!
void checkPosition() {
  encoder.tick(); // just call tick() to check the state.
}

int last_rotary = 0;
bool last_button = false;

void setup() {
  Serial.begin(115200);
  //while (!Serial);
  delay(100);
  
  Serial.println("Rotary Trinkey Surface Dial");
  
  // start neopixels
  strip.begin();
  strip.setBrightness(neo_brightness);
  strip.show(); // Initialize all pixels to 'off'

  attachInterrupt(PIN_ENCODER_A, checkPosition, CHANGE);
  attachInterrupt(PIN_ENCODER_B, checkPosition, CHANGE);
  
  // set up the encoder switch, which is separate from the encoder
  pinMode(PIN_ENCODER_SWITCH, INPUT_PULLDOWN);

  // Sends a clean report to the host. This is important on any Arduino type.
  SurfaceDial.begin();
}


void loop() {
  // read encoder
  int curr_rotary = encoder.getPosition();
  RotaryEncoder::Direction direction = encoder.getDirection();
  // read switch
  bool curr_button = !digitalRead(PIN_ENCODER_SWITCH);
  
  if (direction != RotaryEncoder::Direction::NOROTATION) {
    Serial.print("Encoder value: ");
    Serial.print(curr_rotary);
    Serial.print(" direction: ");
    Serial.print((int)direction);

    if (direction == RotaryEncoder::Direction::CLOCKWISE) {
      Serial.println(" Rotate+");
      SurfaceDial.rotate(40);
    }
    if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
      Serial.println(" Rotate-");
      SurfaceDial.rotate(-40);
    }

    last_rotary = curr_rotary;
  }

  if (curr_button && !last_button) { // switch pressed!
    Serial.println("Press");
    SurfaceDial.press();
  }
  if (!curr_button && last_button) { // switch released!
    Serial.println("Release");
    SurfaceDial.release();
  }
  last_button = curr_button;

  delay(10); // debounce
}

Rotate the rotary encoder to engage the surface dial. Press the button to engage the surface dial options. Rotate to choose the mode.

Volume Play/Pause Example

Perhaps you'd like to simply use the rotary encoder as a volume knob and the button as a play/pause button. Compile and upload the following example.

#include <Adafruit_NeoPixel.h>
#include <RotaryEncoder.h>
#include "HID-Project.h"  // https://github.com/NicoHood/HID

// Create the neopixel strip with the built in definitions NUM_NEOPIXEL and PIN_NEOPIXEL
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_NEOPIXEL, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
int16_t neo_brightness = 20; // initialize with 20 brightness (out of 255)

RotaryEncoder encoder(PIN_ENCODER_A, PIN_ENCODER_B, RotaryEncoder::LatchMode::FOUR3);
// This interrupt will do our encoder reading/checking!
void checkPosition() {
  encoder.tick(); // just call tick() to check the state.
}

int last_rotary = 0;
bool last_button = false;

void setup() {
  Serial.begin(115200);
  //while (!Serial);
  delay(100);
  
  Serial.println("Rotary Trinkey Volume Knob");
  
  // start neopixels
  strip.begin();
  strip.setBrightness(neo_brightness);
  strip.show(); // Initialize all pixels to 'off'

  attachInterrupt(PIN_ENCODER_A, checkPosition, CHANGE);
  attachInterrupt(PIN_ENCODER_B, checkPosition, CHANGE);
  
  // set up the encoder switch, which is separate from the encoder
  pinMode(PIN_ENCODER_SWITCH, INPUT_PULLDOWN);

  // Sends a clean report to the host. This is important on any Arduino type.
  Consumer.begin();
}


void loop() {
  // read encoder
  int curr_rotary = encoder.getPosition();
  RotaryEncoder::Direction direction = encoder.getDirection();
  // read switch
  bool curr_button = !digitalRead(PIN_ENCODER_SWITCH);
  
  if (direction != RotaryEncoder::Direction::NOROTATION) {
    Serial.print("Encoder value: ");
    Serial.print(curr_rotary);
    Serial.print(" direction: ");
    Serial.print((int)direction);

    if (direction == RotaryEncoder::Direction::CLOCKWISE) {
      Serial.println(" Vol +");
      Consumer.write(MEDIA_VOLUME_UP);
    }
    if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
      Serial.println(" Vol -");
      Consumer.write(MEDIA_VOLUME_DOWN);
    }

    last_rotary = curr_rotary;
  }

  if (curr_button && !last_button) { // switch pressed!
    Serial.println("Play/Pause");
    Consumer.write(MEDIA_PLAY_PAUSE);
  }
  last_button = curr_button;

  delay(10); // debounce
}

Rotate the rotary encoder to increase or decrease your volume. Press the button to play or pause.

This guide was first published on Jun 02, 2021. It was last updated on May 26, 2021.

This page (Arduino Examples) was last updated on May 28, 2021.

Text editor powered by tinymce.