Input from several PS2 Keyboards

I want to connect several (6-8) PS2 Keyboards to one arduino, that will pass on the information about which keys that are pressed to a computer via USB.

There is a library for using one keyboard here:
http://playground.arduino.cc/Main/PS2Keyboard

But this code can only handle input from one keyboard due to that when a scan code is sent from the keyboard the arduino have to wait for the whole code to arrive. This takes minimum 600µs and during this time the arduino can not read any other incomming scan codes. This library also requires that the Clock output from the keyboard must be connected to an interrupt pin.

Anyone have any ideas on how to get input from several keyboards to the arduino?

And have anyone seen any arduino code on how to send commands to the PS2 Keyboard, for example to light one of the three keyboard LEDs?
Here is how it should be done, just looking for some code example: Additional information - Reading and writing PS/2 keyboard

Hi,

Question: Does this need to handle the situation where multiple keyboards are being typed on at the same time???

terryking228:
Hi,

Question: Does this need to handle the situation where multiple keyboards are being typed on at the same time???

As he's worried about a 600uS delay, it very much looks like it. Unless you know of a typist that can type at over 1500 keys per second.

terryking228:
Hi,

Question: Does this need to handle the situation where multiple keyboards are being typed on at the same time???

Yes, if 6-8 users are typing at the same time there is a risk that information is lost due to that two scan codes was sent at the same time.

Sounds like this needs to be interrupt-driven by the keyboard clock lines??

terryking228:
Sounds like this needs to be interrupt-driven by the keyboard clock lines??

That is how they did it in the library in the link. But I would need one interrupt pin per keyboard so then the UNO would not be enough with only two. The MEGA has 6. An alternative could be Teensy 3.0, where all pins are interrupt pins (if I understood correctly).

But could this not be done without interrupts? The PS2 keyboard clock speed is 10-20 kHz. Isn't the arduino quick enough to read the clock state every loop anyhow?

It depends on how much other stuff you are trying to do, but yet it would seem that you could just pole the inputs as fast as possible and be sampling plenty fast for the slow 10-16 KHz clock. You could also use discrete logic and gates to make a master clock that will trigger on any of the keyboard clocks. The clocks default to a high state so anding them all together would make a 'clock' that would trigger on any of them. Feed this clock to your interrupt and then have the interrupt service routine check each clock individually to see which keyboard caused the interrupt.

Brainfart...
Why read 'm with a single controller ?
The examples of the library use about 4-5 kb flash and easily fit an $1 atmega8-chip (used in oldest type arduinos). If you use one for each keyboard, you could connect 'm in a simple I2C-, rs485-, or other type of network of your choice. Using an USB-connected arduino as master and all keyboards configured as slaves, the arduino could continually check all slaves for new info and send it to USB. No worries about timing and since each keyboard will have it's own ram-buffer, no worries about missed keys. Would give you the possibility to add mouse-/other HID-slaves (and devices to punish the laziest typist) as well.

jroorda:
It depends on how much other stuff you are trying to do, but yet it would seem that you could just pole the inputs as fast as possible and be sampling plenty fast for the slow 10-16 KHz clock. You could also use discrete logic and gates to make a master clock that will trigger on any of the keyboard clocks. The clocks default to a high state so anding them all together would make a 'clock' that would trigger on any of them. Feed this clock to your interrupt and then have the interrupt service routine check each clock individually to see which keyboard caused the interrupt.

If two scan codes are incoming at the same time, wouldn't the clock be offsync? And if the clocks have to be read separatly anyway, why have a master clock?

Here is a code example that is to slow:

/*
PS2 Keyboard Input
Will send string of 0 and 1 representing the scan code sent by the keyboard

Make codes will start with Keyboard ID at pos[0]
Pos[10] will be 1 if keyboard is connected, otherwise fake code from unconnected pin

Break codes will be in two parts first 00000111111, which means that the next scan 
code if from that key when it was released.
*/

#define MAX_KEYBOARDS 6

int g_scan_prog[MAX_KEYBOARDS]={0};//position in scancode, 0 = OFF
int g_last_clock_state[MAX_KEYBOARDS]={0};//0 if LOW, 1 if HIGH
char g_scan_code[MAX_KEYBOARDS][11]={'0'};
char g_num_to_char_id[MAX_KEYBOARDS]={'a','b','c','d','e','f'};

int g_data_pins[MAX_KEYBOARDS]={2,4,6,8,10,12};
int g_clock_pins[MAX_KEYBOARDS]={0,3,5,7,9,11};

void setup()
{
  //init pins
  for(int i=0;i<MAX_KEYBOARDS;i++)
  {
    pinMode(g_data_pins[i],INPUT);
    pinMode(g_clock_pins[i],INPUT);
    g_scan_code[i][0]=g_num_to_char_id[i];//Keyboard ID
  }
  
  //init data com
  Serial.begin(9600);
  while(!Serial) ;//wait for computer
  Serial.println("Startup done!");
}

void loop()
{ 
  for(int i=0;i<MAX_KEYBOARDS;i++)//go through all keyboards
  {
    //update clock
    int clock_state=digitalRead( g_clock_pins[i] );

    if( clock_state==1 && g_last_clock_state[i]==0 ) 
    {
      if( g_scan_prog[i]!=0 )
      {
        int data_state=digitalRead( g_data_pins[i] );
        if( data_state==0 )
        {//LOW
          g_scan_code[i][ g_scan_prog[i] ]='0';
        }
        else
        {//HIGH
          g_scan_code[i][ g_scan_prog[i] ]='1';
        }
        g_scan_prog[i]++;//next pos in scan code
      }
      else
      {
        if( digitalRead(g_data_pins[i])==LOW ) 
        {//incomming scancode
          g_scan_prog[i]=1;
        }
      }
      
      //if scan code done, send to com
      if( g_scan_prog[i]>10 )
      {//done, send to com
        //test if scan code is not fake (unconnected keyboard)
        if(g_scan_code[i][10]=='1')
        {//code OK, send
          Serial.println(g_scan_code[i]);
        }
        //reset values
        g_scan_prog[i]=0;
      }
    }

    //update last clock
    if( clock_state==1 ) g_last_clock_state[i]=1;
    else g_last_clock_state[i]=0;
       
    //go to next keyboard
  }
  //all keyboards checked this cycle
}

If the loop is set to only read the first keyboard i=0 then a correct string representing the scan code will be sent. If the number of keyboards is higher than 2 information will be lost. Which I interpret as the code being to slow.

Simpson_Jr:
Brainfart...
Why read 'm with a single controller ?
The examples of the library use about 4-5 kb flash and easily fit an $1 atmega8-chip (used in oldest type arduinos). If you use one for each keyboard, you could connect 'm in a simple I2C-, rs485-, or other type of network of your choice. Using an USB-connected arduino as master and all keyboards configured as slaves, the arduino could continually check all slaves for new info and send it to USB. No worries about timing and since each keyboard will have it's own ram-buffer, no worries about missed keys. Would give you the possibility to add mouse-/other HID-slaves (and devices to punish the laziest typist) as well.

I thought of a similar solution where a smaller unit (one per keyboard) reads the scan code and store which keys that are pressed. Then a main unit would read from these smaller to see if there was a change in which keys that is pressed since the last cycle. And then report this change to the computer.

But isn't this solution with multiple units making it unnecessarily complicated?

Depends on what the other options are.

Reading 6-8 keyboards at the same time with a single controller is probably quite difficult/complicated already. It may be possible, but I would probably need quite some time and aspirin to create a prototype that functions as it should. Being sure every bit of each board is read as it should will be quite complicated.

With a master/slave configuration you eliminate those problems, but you indeed introduce new ones, communication between master/slaves and type of network. Fortunately the device you want isn't too complicated (from a master/slave pov). It will take some time to get used to and to find the best type of network for your project, but what you need probably isn't much harder as most example-sketches for master/slaves. Advantage is also that you can find a lot of info/examples on working with master/slaves, while very few people will have read multiple keyboards with a single controller.

Simpson_Jr:
Depends on what the other options are.

Reading 6-8 keyboards at the same time with a single controller is probably quite difficult/complicated already. It may be possible, but I would probably need quite some time and aspirin to create a prototype that functions as it should. Being sure every bit of each board is read as it should will be quite complicated.

With a master/slave configuration you eliminate those problems, but you indeed introduce new ones, communication between master/slaves and type of network. Fortunately the device you want isn't too complicated (from a master/slave pov). It will take some time to get used to and to find the best type of network for your project, but what you need probably isn't much harder as most example-sketches for master/slaves. Advantage is also that you can find a lot of info/examples on working with master/slaves, while very few people will have read multiple keyboards with a single controller.

I tried to use change interrupts using (Google Code Archive - Long-term storage for Google Code Project Hosting.) and it works for one keyboard. It's harder to simulate using 6 keyboards at the same time with this method so I just have to test.

Here is the code:

/*
The keyboards clocks will be connected to a pin 
that will report when will call a function when
the signal goes up.

This fucktion will read the data state of that keyboard
and if signal is complete sent it to the computer.
*/

#include <PinChangeInt.h>

#define MAX_KEYBOARDS 6

int g_scan_prog[MAX_KEYBOARDS]={0};
char g_scan_code[MAX_KEYBOARDS][11]={'0'};
char g_num_to_char_id[MAX_KEYBOARDS]={'a','b','c','d','e','f'};

int g_clk_pin_to_keyboard_id[12]={0,0,1,1,1,2,2,3,3,4,4,5};
int g_data_pins[MAX_KEYBOARDS]={2,4,6,8,10,12};
int g_clock_pins[MAX_KEYBOARDS]={0,3,5,7,9,11};

void clock_rise();

void setup()
{
  Serial.begin(9600);
  while(!Serial) ;
  Serial.println("STARTUP DONE");
  
  //init pins
  for(int i=0;i<MAX_KEYBOARDS;i++)
  {
    pinMode(g_data_pins[i],INPUT);
    pinMode(g_clock_pins[i],INPUT); digitalWrite(g_clock_pins[i], HIGH);
    PCintPort::attachInterrupt(g_clock_pins[i], &clock_rise, RISING);
  }
}

void loop()
{
  ;
}

void clock_rise()
{
  int latest_interrupted_pin=PCintPort::arduinoPin;//to let function know which pin that called
  int keyboard_id=g_clk_pin_to_keyboard_id[latest_interrupted_pin];
  g_scan_prog[keyboard_id]++;
  if( digitalRead( g_data_pins[keyboard_id] )==HIGH ) g_scan_code[ keyboard_id ][ g_scan_prog[keyboard_id] ]='1';
  else g_scan_code[ keyboard_id ][ g_scan_prog[keyboard_id] ]='0';
  //test if ready to send
  if( g_scan_prog[keyboard_id]>=10 )
  {
    g_scan_code[ keyboard_id ][ 0 ]=g_num_to_char_id[ keyboard_id ];//set keyboard id
    Serial.println( g_scan_code[keyboard_id] );//send
    g_scan_prog[keyboard_id]=-1;//clear
  }
  
}