Introduction: PUB! Programmable USB Button

PUB! is a Programmable USB Button on which you can program a sequence of keystrokes. When you press the button those keystrokes are "replayed" over a USB connection.

The button (which is really a rotary encoder with a built-in push button switch) is programmed using an innovative and slightly quirky interface that does not require any custom software to be installed on your computer. All you need is a text editor like Notepad (for Windows) or gedit (for Linux).

Features

  • One-button design (a rotary encoder with a built in switch).
  • Programmed by using an ordinary text editor as a display (for example, gedit on Linux, or Notepad on Windows).
  • Up to 127 keystrokes can be recorded and played back.
  • Support for conditional logic. For example, Compare to value, Jump on zero, etc.
  • Support for 256 x 8-bit "registers" to record state.
  • Support for basic arithmetic. Add, subtract, etc.
  • Can send USB System Control codes (Power off, sleep, wake) to your PC
  • Can send USB Consumer Device functions (e.g. Mute, Play, Pause, Stop, etc.)
  • Requires NO drivers (or custom software) for Windows/Linux etc

Futures

The possibilities are fairly broad including:

  • Using a Bluetooth module to detect proximity of the button owner's mobile phone so that the USB button is only enabled when its owner is near.
  • Addition of an external EEPROM for extra keystroke capacity.
  • The one-button user interface will be improved to make it easier to use.

Step 1: Parts

You will need the following parts:

Qty Value          Package       Parts         Description 
3 100nF 0805 C1, C2, C3 Ceramic capacitor SMT 1 4.7uF SMC_A C4 Tantalum capacitor SMT 1 470 1206 R1 Resistor SMT 1 ACTIVITY 1206 D1 LED (any color) SMT 1 EC12E_SW SW1 ALPS rotary Encoder EC12E series with switch 1 MIKROPROG JP1 5x1 Pin header (right angle) 1 PIC18F25K50 SOIC IC1 PIC 18F25K50 microcontroller SMT 1 PICKIT JP2 6x1 Pin header (right angle) 1 USB-MINIB-5PIN JP3 Mini-USB Type B socket SMT

Step 2: Assembly

All the source files for this project are freely available from the pub-usb project on SourceForge.

You will need to either make or own double-sided Printed Circuit Board (not recommended) or use the Gerber files distributed with the source code to get one manufactured. I used ITEAD to make 10 PCBs for about AUD20.

Once you have the PCB it's just a matter of soldering each component in place. I've selected all the parts to be at least 0805 size, so hand soldering is easy. You might think that the 28-pin microcontroller or the 5-pin Mini USB socket would be difficult, but both can be easily soldered by hand. Use plenty of flux.

The PCB has been sized to slide inside one of these 68x15x78 mm (WxHxD) split body aluminium enclosures from eBay for about AUD10. You will need to drill an access port in the case for the USB connector. This can be a little time consuming if you want to make a perfect fit because the Mini USB connector is trapezoid. A rectangular hole would be easier to make if you don't mind what it looks like.

The holes for the rotary encoder and for the indicator LED can be positioned easily by printing a (1:1 scale) monochrome image of the PCB top layers on plain paper (see image), attaching it to the case with sticky tape and drilling through it. Make sure that the printed image size exactly matches the PCB size. You may need to adjust the scale by 1 or 2 percent.

Step 3: Programming

To program the microcontroller, you will need either a Mikroelektronika MikroProg or a Microchip PicKit programmer (or compatible).

The hex file (pub.hex) is in the SourceForge project file.

Connect your programmer to either the 5-pin MikroProg pin header, or the 6-pin PicKit pin header on the PCB.

Step 4: Usage

How to Use PUB!

This is a work in progress. I think it could be improved by minimising press+turn interactions where possible.

The basic process is:

  • Connect PUB! to a USB port on your computer
  • Start Notepad (on Windows) or gedit (on Linux)
  • Ensure that Notepad/gedit has the keyboard focus. This is really important.
  • Press and hold PUB!'s button for about a second
  • PUB! will display a kind of menu in Notepad/gedit (this is why your text editor must have keyboard focus)
  • Navigating the menu is done by a combination of pushing the button, pushing and turning the button, turning the button, or pushing and holding the button for 1 second or more. The menu shows you when to use these alternatives.

Example of programming a password

  • Open a text editing program (for example, gedit or Notepad) and make sure it has keyboard focus
  • Plug PUB! into a host USB port
  • By default it is in RUN mode, so press and hold the rotary encoder knob for more than 1 second to switch to PROGRAM mode. It will display the following in your text editor:
  PUB! Programmable USB Button v0.92
  Main:   Turn=Select, Press=OK, Press+Turn=Set At, Press+Hold=Exit
     0    Set Keystroke at 00

  At Code Action
  • Press the rotary knob to set a keystroke. It will display:
  PUB! Programmable USB Button v0.92
  Key:    Turn=Select, Press+Turn=Modify, Press=OK, Press+Hold=Return
  00 0004 a

  At Code Action
  • Turn the rotary knob to choose the first letter of your password (say c) and then press the knob to add it to the list of actions:
  PUB! Programmable USB Button v0.92
  Key:    Turn=Select, Press+Turn=Modify, Press=OK, Press+Hold=Return
  01 0006 c

  At Code Action
  00 0006 c
  • Continue choosing and adding the remaining letters (say, a and t):
  PUB! Programmable USB Button v0.92
  Key:    Turn=Select, Press+Turn=Modify, Press=OK, Press+Hold=Return
  03 0017 t

  At Code Action
  00 0006 c
  01 0004 a
  02 0017 t
  • Press and hold the rotary knob to Return to the main menu:
  PUB! Programmable USB Button v0.92
  Main:   Turn=Select, Press=OK, Press+Turn=Set At, Press+Hold=Exit
     0    Set Keystroke at 03

  At Code Action
  00 0006 c
  01 0004 a
  02 0017 t
  • Rotate the knob until "Do Local Function" is displayed:
  PUB! Programmable USB Button v0.92
  Main:   Turn=Select, Press=OK, Press+Turn=Set At, Press+Hold=Exit
     0    Do Local Function at 03

  At Code Action
  00 0006 c
  01 0004 a
  02 0017 t
  • Press the knob to enter the "Do" menu:
  PUB! Programmable USB Button v0.92
  Do:     Turn=Modify, Press=OK, Press+Turn=Set At, Press+Hold=Exit
      0    Delete action at 00

  At Code Action
  00 0006 c
  01 0004 a
  02 0017 t
  • Press-and-rotate the knob anticlockwise until "Save to EEPROM" is displayed:
  PUB! Programmable USB Button v0.92
  Do:     Turn=Modify, Press+Turn=Select, Press=OK, Press+Hold=Return
      Save to EEPROM

  At Code Action
  00 0006 c
  01 0004 a
  02 0017 t
  • Press the knob to save "cat" to the EEPROM
  Saved in EEPROM
  • The device is now in RUN mode. Press the knob to "play" the recorded sequence:
  cat

An example of some conditional logic

  • Enter the following program into the PUB! device:
At Code Action
00 E005 Let W = 05
01 E200 Put W in R00
02 0006 c
03 0004 a
04 0017 t
05 002C Space
06 E500 Say R00
07 0028 Enter
08 EF01 Wait 1 sec
09 E801 Let W = W - 01
0A F701 Jump if Not Zero to 01
  • Now when you press the knob in run mode you should see the following displayed (1 second wait between lines):
cat 05
cat 04
cat 03
cat 02
cat 01

Note that pressing the button during playback will abort the playback process.

Menu Functions

The menus, if fully expanded, would look like this:

RUN mode
        This is the normal operation mode
        Press to begin playback
        Press and hold to enter PROGRAM mode

PROGRAM mode Main menu
	0 Set Keystroke (e.g. A, Enter)
		0mxx Keystroke xx using modifier m
		     Rotate to select xx (e.g. A)
                     Press+Rotate to select m (e.g. CTL+SHIFT)
                     (too many to list them all here)
	1 Set System Control Command (e.g. power off)
		1mxx System Control Command xx (m is ignored)
		     Rotate to select xx
	2 Set Consumer Device Command (e.g. play, pause)
		2xxx Consumer Device Command xxx
		     Rotate to select xxx
	D Do Local Function
		D0xx Delete action at xx
		D1xx Redisplay (xx is ignored)
		DExx Load from EEPROM (xx is ignored)
		DFxx Save to EEPROM (xx is ignored)
	E Execute Instruction
		E0xx Let W = xx
		E1xx Get W from Rxx
		E2xx Put W in Rxx
		E3xx Compare W to xx
		E4xx Compare W to Rxx
		E5xx Say Rxx
		E6xx Format xx (display format that Say uses)
		E7xx Let W = W + xx
		E8xx Let W = W - xx
		E9xx Set all registers to xx
		EAxx Let W = W + Rxx
		EBxx Let W = W - Rxx
		ECxx Let W = W x Rxx
		EDxx Let W = W / Rxx
		EExx Wait xx milliseconds
		EFxx Wait xx seconds
	F Jump On Condition
		F0xx Jump Relative by xx
		F1xx Jump if Carry to xx
		F2xx Jump if High to xx
		F3xx Jump if High or Carry to xx
		F4xx Jump if Low to xx
		F5xx Jump if Low or Carry to xx
		F6xx Jump if Not Zero or Carry to xx
		F7xx Jump if Not Zero to xx
		F8xx Jump if Zero to xx
		F9xx Jump if Zero or Carry to xx
		FAxx Jump if Not Low or Carry to xx
		FBxx Jump if Not Low to xx
		FCxx Jump if Zero or Low to xx
		FDxx Jump if Not High to xx
		FExx Jump if Not Carry to xx
		FFxx Jump to xx

The "Condition Code" is set after each arithmetic or compare operation, in a way not unfamiliar to older IBM mainframe programmers (completely coincidental of course) as follows:

ZHLC
100x Means: Zero
010x Means: High
001x Means: Low
xxx1 Means: Carry set (not actually implemented yet...)
xxx0 Means: No carry  (...may be discarded as a bad idea)

So, Jump if Zero is encoded as:

F8xx
F    = 0b1111 = Jump on Condition
 8   = 0b1000 = Zero condition
  xx = Address jumped to if the condition is true

And, Jump Unconditionally is encoded as:

FFxx
F    = 0b1111 = Jump on Condition
 F   = 0b1111 = Zero, Low, High or Carry condition
                (it must be one of these)
  xx = Address jumped to if the condition is true
                (which it always will be)