Rotary encoder, a treadmill and oculus rift

Hello everyone,

I'm developing an art project that would involve walking in virtual reality on a treadmill.
The forward-backwards movement in the program i'm using (Outerra) is the standard W-S key.

To translate this treadmill movement into a keyboard button, i thought of hooking up a rotary encoder+arduino UNO to the treadmill itself and read the signal as a keyboard button
In this case: walking forward = W and walking backwards = S

Now: i am a complete zero in programming.
Reading up on various websites i found this very simple sketch to connect the Rotary Encoder to my Arduino UNO

Here is the code that came along with it:

// Setting up the counter
int reading = 0;
int lowest = -12;
int highest = 12;
int changeamnt = 1;

// Timing for polling the encoder
unsigned long currentTime;
unsigned long lastTime;


// Pin definitions
const int pinA = 12;
const int pinB = 11;

// Storing the readings

boolean encA;
boolean encB;
boolean lastA = false;

void setup() {
  // set the two pins as inputs with internal pullups
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  // Set up the timing of the polling
  currentTime = millis();
  lastTime = currentTime; 
  // Start the serial monitor for debugging
  Serial.begin(9600);
} 


void loop()
{
  // Read elapsed time
  currentTime = millis(); 
  // Check if it's time to read
  if(currentTime >= (lastTime + 5))
  {
    // read the two pins
    encA = digitalRead(pinA);
    encB = digitalRead(pinB);
    // check if A has gone from high to low
    if ((!encA) && (lastA))
    {
      // check if B is high 
      if (encB)
      {
        // clockwise
        if (reading + changeamnt <= highest)
        {
          reading = reading + changeamnt; 
        }
      }
      else
      {
        // anti-clockwise
        if (reading - changeamnt >= lowest)
        {
          reading = reading - changeamnt; 
        }
      }
      // Output reading for debugging
      Serial.println(reading);
    }
    // store reading of A and millis for next loop
    lastA = encA;
    lastTime = currentTime;

  }

}

So far so good, I uploaded it without problems and in the serial monitor i could see values running from -12 to +12 as i rotate the encoder.

Now the tricky part comes: how do i translate this into a keyboard pressing so that forward=w key, backwards=s key and no rotation=no key pressed?

Again: my programming skills are zero. Any kind of help is pure gold :smiley:

Thank you very much in advance!
Federico

An Arduino UNO has no native USB support in the main microcontroller, so it can't (easily) act as a keyboard. Other boards like the Leonardo, Due, Teensy ... can do this right out of the box.

You could load custom keyboard-emulating firmware onto the USB chip (ATmega16U2) of the UNO, or use the V-USB library, but these are hacks, and it's not gonna be easy.

You could also send the movement data over Serial to a script that runs on the computer and converts it to keystrokes.

Pieter

Thank you PieterP,

which option do you think is the less painful for a complete beginner?

Using a Leonardo or a Micro is definitely going to save you a lot of hassle.

You could also check out the AdaFruit Pro Trinket: it uses the same processor as the Uno, but has USB and keyboard support, maybe you can find some inspiration in their code to get it working on an Uno, but you'll need some supporting components like zeners and resistors.

I've never tried any of the approaches myself, but I've used custom ATmega16U2 firmware before, and that's probably the easiest way. The only downside is that you can't upload new sketches while the custom firmware is loaded ...

That being said, I did a quick Google search for ATmega16U2 keyboard firmware, and only found dead links ...

V-USB will definitely require a lot of programming skills.

Pieter

I had a couple of hours of reading and tips to flash the Uno but as you said: getting a Leonardo or a Due would definitely cut off plenty of time (let's say that I'm quite on a rush)

Thank you very much again. I'll probably bother you more once the Due will be in my hands! :smiley:

A Due is probably overkill for just one encoder, but it will work.

True: I'm onto getting the Leonardo right away. Is it what you best recommend? Leonardo or Leonardo ETH?

Leonardo ETH is just a Leonardo with Ethernet. I'm assuming you don't need Ethernet for your project?
The Micro has the same chip as the Leonardo, but is smaller.

Ah ok fair enough. No i won't need ethernet for it.
At the end just a Leonardo+rotary encoder mounted under a treadmill, connected to a pc.
It's always the first step in translating an idea onto something more practical that becomes an apocalyptic odyssey.
Thank you for your help!

Sorry PieterP, one last load of questions.
I'm getting the Leonardo tomorrow: could you please point me to the right steps to look for once i'll set up the schematic and be ready to program?
Once again (it's never enough), thanks!

https://www.pjrc.com/teensy/td_libs_Encoder.html
https://www.arduino.cc/en/Reference/MouseKeyboard

Thank you very much!
After some research and learning few things i came across this code that i edited a bit:

#include <KeyboardButton.h>

// Setting up the counter
int reading = 0;
int lowest = -24;
int highest = 24;
int changeamnt = 1;

// Timing for polling the encoder
unsigned long currentTime;
unsigned long lastTime;

// Pin definitions
const int pinA = 12;
const int pinB = 11;

// Storing the readings
boolean encA;
boolean encB;
boolean lastA = false;

void setup() {
  // set the two pins as inputs with internal pullups
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  // Set up the timing of the polling
  currentTime = millis();
  lastTime = currentTime; 
  // Start the serial monitor for debugging
  Serial.begin(9600);
} 
void loop()
{
  // Read elapsed time
  currentTime = millis(); 
  // Check if it's time to read
  if(currentTime >= (lastTime + 5))
  {
    // read the two pins
    encA = digitalRead(pinA);
    encB = digitalRead(pinB);
    // check if A has gone from high to low
    if ((!encA) && (lastA))
    {
      // check if B is high 
      if (encB)
      {
        // clockwise
        if (reading + changeamnt <= highest)
        {
          reading = reading + changeamnt; 
        }
        Keyboard.press(119);
        Keyboard.release(115);
      }
      else
      {
        // anti-clockwise
        if (reading - changeamnt >= lowest)
        {
          reading = reading - changeamnt; 
        }
        Keyboard.release(119);
        Keyboard.press(115);

      }
      // Output reading for debugging
      Serial.println(reading);
    }
    // store reading of A and millis for next loop
    lastA = encA;
    lastTime = currentTime;

  }

}

the only last difficulty i'm dealing with is how do i tell the encoder not to write anything (or ReleaseAll) when no rotation is detected? So basically when the person stops walking on the treadmill, no in-game movement happens?