Tuesday, January 31, 2017

Adventures in hardware hacking: adding foot pedal support to emacs

A few months back I came across this video showing how to control a DSLR with a foot pedal. This re-ignited a long time project I've wanted to try: adding foot pedal control to emacs. How cool would it be to control emacs with not only 10 fingers, but your feet!

Looking more into this, I learned that such an idea was both totally doable and relatively inexpensive. Here's a recipe for exactly what I'd need to realize this dream: USB Foot Switch. The parts were easy enough to order off of Adafruit's website. I'd needed only a $7.00 foot switch and a $10.00 microcontroller.

The instructions promised a build time of less than an hour, however, I was skeptical. My area of expertise is in the software side of things, so clipping wires, soldering and such seems awfully intimidating.

To get around this, I decided to follow the instructions in reverse. First I'd program the chip. Then I'd see if I could get the keyboard behavior working. Then I'd plunge into the soldering side of things. It would take longer, but I'd get to start in my comfort zone.

I figured the best place to start was to get the hello world of programs running on the Trinket Pro chip, an LED blinker. To accomplish this, I followed the tutorial here.

Being an emacs snob, I completely skipped over the IDE instructions and jumped right into using AVRDude. Heck, a simple brew command line installed avrdude. I was on a roll. And then things came to a screeching halt when my attempts to use avrdude gave me various error messages. I googled for solutions, but it was beginning to look hopeless.

So, plan B: I returned to the IDE Instructions. While I had a couple of false starts, the instructions were really quite accurate. Everything seemed to work. I loaded up the blinker program:

int led = 1; // blink 'digital' pin 1 - AKA the built in red LED

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);

}

// the loop routine runs over and over again forever:
void loop() {
    digitalWrite(led, HIGH); 
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
}

Hit the upload button, and poof, I successfully loaded the program into the chip. It was so effortless that I actually dug around and turned on verbose messages just to confirm that it was truly working.

There was only one problem: the program appeared to load, yet the LED on the board didn't blink. What the heck?!

On a whim, I brought up the pin-out of the Trinket Pro 5v:

Upon closer inspection I realized that the red LED I was after was labeled as #13. Aha! I updated my program to set 'led' to 13 and uploaded it to the device. And just like that, I had a blinking Trinket. Whoo!

OK, so I can get my code to run on the Trinket. Next up to was to do something with the keyboard library. I altered my blinking program to be like so:

#include <cmdline_defs.h>
#include <ProTrinketKeyboard.h>
#include <ProTrinketKeyboardC.h>
#include <usbconfig.h>

const int PIN_SWITCH = 0;    // Trinket pin to connect to switch 
int led = 13;

void setup()
{
  pinMode(led, OUTPUT);
  TrinketKeyboard.begin();  // initialize keyboard library
}

void loop()
{
  TrinketKeyboard.poll();
  digitalWrite(led, HIGH);
  TrinketKeyboard.print("Hello World!"); // use for string instead of char
  delay(5000);
  digitalWrite(led, LOW);
  delay(3000);
} 

Along with toggling the LED, it spits out the text Hello World every 8 seconds.

I pushed the program the Trinket, and lo-and-behold, it worked! Now, it was a particularly useful program, but it did show that I was able to get the Trinket to simulate a keyboard action.

The final task I wanted to try was to have the board generate Hello World in response to a circuit being closed. After all, the switch is just a fancy way of grounding pin #1. At this point, any hardware geek would have busted out their soldering iron and gotten to work on finishing this project. But I wasn't quite ready to go there. I grabbed a few short lengths of wire and crudely attached them to the board by winding them into place:

I then installed the following program on the Trinket:

#include <cmdline_defs.h>
#include <ProTrinketKeyboard.h>
#include <ProTrinketKeyboardC.h>
#include <usbconfig.h>

const int PIN_SWITCH = 0;    // Trinket pin to connect to switch 
const int LED        = 13;
void setup()
{
  // Set button pin as input with an internal pullup resistor
  // The button is active-low, they read LOW when they are not pressed
  pinMode(PIN_SWITCH, INPUT_PULLUP);

  TrinketKeyboard.begin();  // initialize keyboard library
}

void loop()
{
  TrinketKeyboard.poll();

  if (digitalRead(PIN_SWITCH) == LOW)  // If the foot switch grounds the pin
  {
    TrinketKeyboard.print("Hello World!"); // use for string instead of char
    digitalWrite(LED, HIGH); 
    delay(3000);
    digitalWrite(LED, LOW);
  }
}

With the Trinket plugged into my Mac via the USB cable, I ever so carefully touched the two exposed wires together. And to my amazement, Hello World blurted out on the screen. I tried again. And again. Success!

The end of the project is now in sight: I need to wire in the foot pedal switch and update the program to send a keystroke rather than a chunk of text. And then I need to figure out what bit of magic I want emacs to do when I hit the foot pedal. Hopefully that will be the hardest part.

Man, this Arduino hacking stuff is fun!

Update: While I had visions of using the pedal to power emacs, my first real use was a macOS hack.

2 comments:

  1. One really obvious use-case would be to emulate the CTRL key, for those of use who love our Emacs, but don't seem to have desirable tendon stamina.

    Getting the USB attached to the switch, and hacking the keyboard driver to accept the CTRL keycode is, sadly, beyond my skill.

    ReplyDelete
  2. > Getting the USB attached to the switch, and hacking the keyboard driver to accept the CTRL keycode is, sadly, beyond my skill.

    Don't sell yourself short. I'm happily foot-pedalling away and I needed to do was strip some wires, wind them together and wrap them with tape.

    Yes, at some point I'll bust out the soldering iron - but for now, it works.

    ReplyDelete