Keyboard input for Particle

It is true that USB is very different to PS/2, but a lot of (not too fancy) USB keyboards/mice do come with a legacy mode.
If they realize that after power up no USB enumeration is taking place, they assume to be connected to an old PC and switch into PS/2 mode.

So if this would be good for you, there shouldn't be to much trouble in using my PS2Communication lib.
The hardest bit would be to provide scancode translation for your keyboard layout.

On the hand, if you only want this for "one time" credentials input, it's a sledgehammer on a nut :wink:
For this CLI or serial might be just as usable and a lot easier.


Just a little sketch for you to play with, if you got a PS/2 capable keyboard.
I got a USB socket with female jumper connectors out of an old computer to connect a logitech wireless receiver to the Core and it works quite well :wink:

// This #include statement was automatically added by the Spark IDE.
#include "PS2Communication/PS2Communication.h"

#pragma SPARK_NO_PREPROCESSOR
#include "application.h"

SYSTEM_MODE(SEMI_AUTOMATIC);

/**********************************************************************************
 * Pin mapping for USB connector
 * 
 * D0  ... WHITE
 * D1  ... GREEN
 * Vin ... RED
 * GND ... BLACK
 *
 **********************************************************************************/
 
#define PARTICLE_BUILD_IDE      // this would be nice to be provided by Particle ;-)
#if defined(PARTICLE_BUILD_IDE)
#include "PS2Communication/PS2Communication.h"
#else
#define noSPARK_USB_MOUSE    // this should be defined externally in USB HID mode
#include "PS2Communication.h"
#endif

#define REPEATTIME     25

#define ps2_ACK              0xFA  // command was acknowledged
#define ps2_ERROR            0xFC  // command was not acknowledged

#define ps2kbd_RESET         0xFF  
#define ps2kbd_RESEND        0xFE  // Keyboard responds by resending the last-sent byte
#define ps2kbd_SETKEYMK      0xFD  // *Disable break codes and typematic repeat for specified keys
#define ps2kbd_SETKEYMKBK    0xFC  // *Disables typematic repeat for specified keys
#define ps2kbd_SETKEYTPMATIC 0xFB  // *Disable break codes for specified keys
#define ps2kbd_RESETALL      0xFA  // *Sets all keys to generate scan codes on make, break, and typematic repeat
#define ps2kbd_SETALLMK      0xF9  // *Disable break codes and typematic repeat for all keys
#define ps2kbd_SETALLMKBK    0xF8  // *Disable typematic repeat for all keys
#define ps2kbd_SETALLTPMC    0xF7  // *Disable break codes for all keys
#define ps2kbd_SETDFT        0xF6  // Load default typematic rate/delay (10.9cps / 500ms), key types (all keys typematic/make/break), and scan code set (2)
#define ps2kbd_DISABLE       0xF5  // Keyboard stops scanning, loads default values
#define ps2kbd_ENABLE        0xF4  // Re-enables keyboard after disable
#define ps2kbd_SETTPMCDELAY  0xF3  // Set typematic rate and delay (see table at http://www.computer-engineering.org/ps2keyboard/)
#define ps2kbd_READID        0xF2  // *Keyboard responds by sending a two-byte device ID of 0xAB, 0x83
#define ps2kbd_SETSCANSET    0xF0  // *Needs one argument byte from host 0x01, 0x02, 0x03 - if argument is 0x00, after "ack" sends current scan code set
#define ps2kbd_ECHO          0xEE  // The keyboard responds with "Echo" (0xEE).
#define ps2kbd_SETLEDS       0xED  // Needs one argument byte (binary 0 0 0 0 0 Caps Num Scroll)
                                   // *) Originally available in PS/2 keyboards only.
// Table of scan codes
// http://www.computer-engineering.org/ps2keyboard/scancodes2.html

// scancode, key, with shift, with ctrl, with altgr (=ctrl+alt)
char ScanCodes[256][4] = { 
    {'\0','\0','\0','\0'},//0x00
    ... // stripped down for post 
    {'q','Q','\0','\0'},//0x15
    {'1','\0','\0','\0'},//0x16
    {'\0','\0','\0','\0'},//0x17
    {'\0','\0','\0','\0'},//0x18
    {'\0','\0','\0','\0'},//0x19
    {'z','\0','\0','\0'},//0x1A
    {'s','\0','\0','\0'},//0x1B
    {'a','A','\0','\0'},//0x1C
    {'w','\0','\0','\0'},//0x1D
    {'2','\0','\0','\0'},//0x1E
    {'\0','\0','\0','\0'},//0x1F
    {'\0','\0','\0','\0'},//0x20
    {'c','C','\0','\0'},//0x21
    {'x','\0','\0','\0'},//0x22
    {'d','D','\0','\0'},//0x23
    {'e','\0','\0','\0'},//0x24
    {'4','\0','\0','\0'},//0x25
    {'3','\0','\0','\0'},//0x26
    {'\0','\0','\0','\0'},//0x27
    {'\0','\0','\0','\0'},//0x28
    {' ','\0','\0','\0'},//0x29
    {'v','\0','\0','\0'},//0x2A
    {'f','\0','\0','\0'},//0x2B
    {'t','\0','\0','\0'},//0x2C
    {'r','\0','\0','\0'},//0x2D
    {'5','\0','\0','\0'},//0x2E
    {'\0','\0','\0','\0'},//0x2F
    {'\0','\0','\0','\0'},//0x30
    {'n','\0','\0','\0'},//0x31
    {'b','B','\0','\0'},//0x32
    {'h','\0','\0','\0'},//0x33
    {'g','\0','\0','\0'},//0x34
    {'y','\0','\0','\0'},//0x35
    {'6','\0','\0','\0'},//0x36
    {'\0','\0','\0','\0'},//0x37
    {'\0','\0','\0','\0'},//0x38
    {'\0','\0','\0','\0'},//0x39
    {'m','\0','\0','\0'},//0x3A
    {'j','\0','\0','\0'},//0x3B
    {'u','\0','\0','\0'},//0x3C
    {'7','\0','\0','\0'},//0x3D
    {'8','\0','\0','\0'},//0x3E
    {'\0','\0','\0','\0'},//0x3F
    {'\0','\0','\0','\0'},//0x40
    {'\0','\0','\0','\0'},//0x41
    {'k','\0','\0','\0'},//0x42
    {'i','\0','\0','\0'},//0x43
    {'o','\0','\0','\0'},//0x44
    {'0','\0','\0','\0'},//0x45
    {'9','\0','\0','\0'},//0x46
    ... // stripped down for post 
    {'\0','\0','\0','\0'} //0xFF
};

// dataPin D0, clkPin D1
PS2Communication* PS2;

char report[255];

uint32_t long ms; 

void ps2Read();

inline void dumpACK()
{
  delay(WAIT4PS2REPLY);
  while (PS2->available())
  {
    delay(WAIT4PS2REPLY);
    PS2->read();
  }
}

void setup()
{
  //WiFi.off();                 // don't need it unless we call connectCloud()
  Spark.connect();
  
  pinMode(D7, OUTPUT);

  Serial.begin(115200);

  PS2 = new PS2Communication(D0, D1);
  
  PS2->reset();
  
  ms = millis();
}

int leds = 0b00000100;
int flip = 0b00000110;

void loop()
{
  char c;
  
  while (PS2->available())
  {
    switch (c = PS2->read())
    {
      case 0xF0:     // break code
        while(PS2->available() < 1);
        PS2->read(); // flush it following
        break;
      case 0xE0:     // extended code
        while(PS2->available() < 1);
        PS2->read(); // flush it following
        break;
      case 0xE1:     // pause key
        while(PS2->available() < 2);
        PS2->read(); // flush it following
        PS2->read(); // flush it following
        break;
      default:
        if (ScanCodes[c][0])
          Serial.write(ScanCodes[c][0]);
        else
        {
          sprintf(report, " %02X ", c);
          Serial.print(report);
        }
        break;
    }
  }

  // blink Caps/Num alternately
  // clears kbd buffer and interferes with typematic
/*  
  if (millis() - ms > 500)
  {
    ms = millis();
    PS2->write(ps2kbd_SETLEDS);
    delay(WAIT4PS2REPLY);
    PS2->read();  // dump ACK
    PS2->write(leds);
    delay(WAIT4PS2REPLY);
    PS2->read();  // dump ACK
    leds ^= flip;
  }
*/
}
1 Like