Ps2KeyboardHost  1.0.1
Allows you to read from one or more PS2-style keyboards on an Arduino.
ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics > Class Template Reference

Instances of this class can be used to interface with a PS2 keyboard. This class does not decode the keystroke protocol, but rather provides the data from the keyboard in a raw form. You either need to write code that directly understands what the PS2 is providing, or use one of the provided translator classes (e.g. AnsiTranslator). More...

#include <ps2_Keyboard.h>

Public Member Functions

 Keyboard (Diagnostics &diagnostics= *Diagnostics::defaultInstance())
 
void begin ()
 
bool awaitStartup (uint16_t timeoutInMillis=750)
 After the keyboard gets power, the PS2 keyboard sends a code that indicates successful or unsuccessful startup. This waits for that to happen. More...
 
KeyboardOutput readScanCode ()
 Returns the last code sent by the keyboard. More...
 
bool sendLedStatus (KeyboardLeds ledStatus)
 Sets the keyboard's onboard LED's. More...
 
bool reset (uint16_t timeoutInMillis=1000)
 Resets the keyboard and returns true if the keyboard appears well, false otherwise. More...
 
uint16_t readId ()
 Returns the device ID returned by the keyboard - according to the documentation, this will always be 0xab83. In the event of an error, this will return 0xffff. More...
 
ScanCodeSet getScanCodeSet ()
 Gets the current scancode set. More...
 
bool setScanCodeSet (ScanCodeSet newScanCodeSet)
 Sets the current scancode set. More...
 
bool echo ()
 Sends the "Echo" command to the keyboard, which should send an "Echo" in return. This can be used to verifies that a keyboard is connected and working properly. More...
 
bool setTypematicRateAndDelay (TypematicRate rate, TypematicStartDelay startDelay)
 Sets the typematic rate (how fast presses come) and the delay (the time between key down and the start of auto-repeating. More...
 
bool resetToDefaults ()
 Restores scan code set, typematic rate, and typematic delay. More...
 
bool enable ()
 Allows the keyboard to start sending data again. More...
 
bool disable ()
 Makes it so the keyboard will no longer responsd to keystrokes. More...
 
bool enableBreakAndTypematic ()
 Re-enables typematic and break (unmake, key release) for all keys. More...
 
bool disableBreakCodes ()
 Makes the keyboard no longer send "break" or "unmake" events when keys are released. More...
 
bool disableBreakCodes (const byte *specificKeys, int numKeys)
 Instructs the keyboard to stop sending "break" codes for some keys (that is, it disables the notifications that come when a key is released.) More...
 
bool disableTypematic ()
 Makes the keyboard no longer send multiple key down events while a key is held down. More...
 
bool disableBreakAndTypematic ()
 Re-enables typematic and break for all keys. More...
 
bool disableTypematic (const byte *specificKeys, int numKeys)
 Instructs the keyboard to disable typematic (additional key down events when the user presses and holds a key) for a given set of keys. More...
 
bool disableBreakAndTypematic (const byte *specificKeys, int numKeys)
 Instructs the keyboard to disable typematic (additional key down events when the user presses and holds a key) and break sequences (events that tell you when a key has been released) for a given set of keys. More...
 

Detailed Description

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
class ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >

Instances of this class can be used to interface with a PS2 keyboard. This class does not decode the keystroke protocol, but rather provides the data from the keyboard in a raw form. You either need to write code that directly understands what the PS2 is providing, or use one of the provided translator classes (e.g. AnsiTranslator).

Template Parameters
DataPinThe pin number of the pin that's connected to the PS2 data wire.
ClockPinThe pin number of the pin that's connected to the PS2 clock wire. This pin must be one that supports interrupts on your board.
BufferSizeThe size of the internal buffer that stores the bytes coming from the keyboard between the time they're received from the ClockPin-based interrupts and the time they're consumed by calls to readScanCode. If you are calling that method very frequently (many times per millisecond) then 1 is enough. Expect each keystroke to eat about 4 bytes, so 16 can hold up to 4 keystrokes. There's nothing wrong with larger numbers, but you probably want some amount of responsiveness to user commands.
DiagnosticsA class that will record any unpleasantness that befalls your program such as the aforementioned buffer overflows. This is purely a debugging aid. If you don't need to be doing any debugging, you can stick wit the default, NullDiagnostics, which has no-op implementations for all the event handlers. The C++ compiler will optimize empty implementations completely away, so you pay no penalty for having this debug code in your project if you don't use it. The SimpleDiagnostics implementation provides an implementation that records all the events.

A great source of information about the PS2 keyboard can be found here: http://www.computer-engineering.org/ps2keyboard/. This documentation assumes you have a basic grasp of that.

Most programs will use this class like this:

static ps2::Keyboard<4,2> ps2Keyboard(diagnostics);
void setup() {
ps2Keyboard.begin();
ps2Keyboard.reset();
// more keyboard setup if needed (e.g. set the scan code set)
}
void loop() {
ps2::KeyboardOutput scanCode = ps2Keyboard.readScanCode();
if (scanCode != ps2::KeyboardOutput::none) {
respondTo(scanCode);
};
}

In this example, the keyboard is wired up with the data pin connected to pin 4 and the clock pin connected to pin 2. The setup function should start up the keyboard. In many cases, you really don't have to do anything other than call 'begin' to get going. Here, it calls 'reset' to undo whatever mode the keyboard might already be in. In the real world, this is unlikely to ever be necessary, but there you are. A more likely move would be to set the keyboard up in the PS/2 scan code set and perhaps disabling typematic or enabling unmake codes to make it easier to interpret the keyboard's protocol.

You should poll the keyboard from your loop implementation, as illustrated here.

With all things Arduino, you should understand the performance. readScanCode takes only a few machine instructions to complete. The methods that push data to the keyboard take longer (on the order of milliseconds) because of the handshake between the two devices and the throttling of sending one bit at a time at 10KHz.

All of the keyboard setup methods return a bool that reports success or not; you can test them if you feel it necessary, but few applications will really need to.

The biggest source of legitimate error is long-running interrupts which cause the PS2 clock interrupt to be skipped. The response to the clock pin must be swift and consistent, else you'll get garbled messages. The protocol is just robust enough to know that a failure has happened, but not robust enough to fully recover. If you're going to have another interrupt source in your project, see to it that its interrupt handler is as quick as possible. For its part, the PS2 handler is just a few instructions in all cases.

Constructor & Destructor Documentation

◆ Keyboard()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::Keyboard ( Diagnostics &  diagnostics = *Diagnostics::defaultInstance())
inline

Member Function Documentation

◆ awaitStartup()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::awaitStartup ( uint16_t  timeoutInMillis = 750)
inline

After the keyboard gets power, the PS2 keyboard sends a code that indicates successful or unsuccessful startup. This waits for that to happen.

Parameters
timeoutInMillisThe number of milliseconds to wait for the keyboard to send its success/fail message.

All this function does is wait for a proscribed amount of time (750ms, as suggested by the documentation), for a batSuccessful code. That's straightforward. The thing to be careful of is this - if you call this function in your begin() and then upload the program to your Arduino, all this is going to do is delay for 750ms and then throw a diagnostic code - that's because the keyboard has power throughout the event and so doesn't power-on.

But once you get your device into the field, you really can count on the proper behavior (well, unless you have, say, a reset button that resets the Arduino).

There are two reasonable ways to deal with this - first, if you need to set up the keyboard, e.g. set up a scan code set and all that, you need to call this method before you get up to your shennanigans - the keyboard just won't respond until it's done booting. If that's you, and you're keen on having Diagnostic data, you can call SimpleDiagnostic::reset right after this to clean it up. If you're really keen on diagnostics, then you'll set up a "RELEASE" preprocessor symbol and skip the reset when you compile in that mode.

If you don't actually need to do any setup, then you can just skip calling this method. Yes, the keyboard will send that batSuccessful (hopefully) signal later on, but readScanCode specifically looks for that code and drops it on the floor when it arrives.

◆ begin()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
void ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::begin ( )
inline

Starts the keyboard "service" by registering the external interrupt. setting the pin modes correctly and driving those needed to high. The best place to call this method is in the setup routine.

◆ disable()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::disable ( )
inline

Makes it so the keyboard will no longer responsd to keystrokes.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ disableBreakAndTypematic() [1/2]

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::disableBreakAndTypematic ( )
inline

Re-enables typematic and break for all keys.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ disableBreakAndTypematic() [2/2]

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::disableBreakAndTypematic ( const byte *  specificKeys,
int  numKeys 
)
inline

Instructs the keyboard to disable typematic (additional key down events when the user presses and holds a key) and break sequences (events that tell you when a key has been released) for a given set of keys.

This method has no effect if you are not in the ps2 scan code set!

After calling this method, the keyboard will be disabled. call enable to fix that.

Parameters
specificKeysAn array consisting of valid set-3 scan codes. If it contains a single invalid one, mayhem could ensue.
numKeysThe number of keys to change

◆ disableBreakCodes() [1/2]

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::disableBreakCodes ( )
inline

Makes the keyboard no longer send "break" or "unmake" events when keys are released.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ disableBreakCodes() [2/2]

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::disableBreakCodes ( const byte *  specificKeys,
int  numKeys 
)
inline

Instructs the keyboard to stop sending "break" codes for some keys (that is, it disables the notifications that come when a key is released.)

This method has no effect if you are not in the ps2 scan code set!

After calling this method, the keyboard will be disabled. call enable to fix that.

Parameters
specificKeysAn array consisting of valid set-3 scan codes. If it contains a single invalid one, mayhem could ensue.
numKeysThe number of keys to change

◆ disableTypematic() [1/2]

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::disableTypematic ( )
inline

Makes the keyboard no longer send multiple key down events while a key is held down.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ disableTypematic() [2/2]

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::disableTypematic ( const byte *  specificKeys,
int  numKeys 
)
inline

Instructs the keyboard to disable typematic (additional key down events when the user presses and holds a key) for a given set of keys.

This method has no effect if you are not in the ps2 scan code set!

After calling this method, the keyboard will be disabled. call enable to fix that.

Parameters
specificKeysAn array consisting of valid set-3 scan codes. If it contains a single invalid one, mayhem could ensue.
numKeysThe number of keys to change

◆ echo()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::echo ( )
inline

Sends the "Echo" command to the keyboard, which should send an "Echo" in return. This can be used to verifies that a keyboard is connected and working properly.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ enable()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::enable ( )
inline

Allows the keyboard to start sending data again.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ enableBreakAndTypematic()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::enableBreakAndTypematic ( )
inline

Re-enables typematic and break (unmake, key release) for all keys.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ getScanCodeSet()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
ScanCodeSet ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::getScanCodeSet ( )
inline

Gets the current scancode set.

◆ readId()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
uint16_t ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::readId ( )
inline

Returns the device ID returned by the keyboard - according to the documentation, this will always be 0xab83. In the event of an error, this will return 0xffff.

◆ readScanCode()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
KeyboardOutput ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::readScanCode ( )
inline

Returns the last code sent by the keyboard.

You should call this function frequently, in your loop implementation most likely. The more often you call it, the more responsive your device will be. If you cannot call it frequently, make sure you have an appropriately-sized buffer (BufferSize).

If there is nothing to read, this method will return 'none'.

It can also return 'garbled' if there's been a framing error. A retry will be attempted, but that's far from a sure-thing. If you get this result, it likely indicates a collision with one of your interrupt handlers. Look into reducing the amount of processing you do in your interrupt handlers.

In any case, if you see a 'garbled' result, you might be losing some keystrokes, so do what you can to make sure that it doesn't impact the device too badly.

◆ reset()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::reset ( uint16_t  timeoutInMillis = 1000)
inline

Resets the keyboard and returns true if the keyboard appears well, false otherwise.

Parameters
timeoutInMillisThe number of milliseconds to wait for the keyboard to send its success/fail message.

This can take up to a second to complete. (Because of the Protocol spec).

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ resetToDefaults()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::resetToDefaults ( )
inline

Restores scan code set, typematic rate, and typematic delay.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ sendLedStatus()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::sendLedStatus ( KeyboardLeds  ledStatus)
inline

Sets the keyboard's onboard LED's.

This method takes several milliseconds and ties up the communication line with the keyboard. You'll probably want to keep a variable somewhere that records what the current state of the LED's are, and ensure that you only call this function when they actually change.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ setScanCodeSet()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::setScanCodeSet ( ScanCodeSet  newScanCodeSet)
inline

Sets the current scancode set.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

◆ setTypematicRateAndDelay()

template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::setTypematicRateAndDelay ( TypematicRate  rate,
TypematicStartDelay  startDelay 
)
inline

Sets the typematic rate (how fast presses come) and the delay (the time between key down and the start of auto-repeating.

Returns
Returns true if the keyboard responded appropriately and false otherwise.

The documentation for this class was generated from the following file: