Dymo Stylus Tact Alpha Keyboard for Teensy 3.

Status
Not open for further replies.

t3andy

Well-known member
Dymo Stylus Tact Alpha Keyboard for Teensy 3.

Ever wanted a 48 key tact keyboard for the Teensy 3?
Sparkfun has a SX1509 breakout board which handles automatic microcontroller keyboard scanning and debounce.
Note: This I2C I/O expander can also drive PWM LEDs and do level conversion.

Operation is very simple. On any tact keypress, the interrupt output (input to T3) goes low. The T3 polls this digital and
reads the I2C keypress data.

In our application, we only used 6 rows and 8 columns.(48 tact keys) (8x8 64 keys max)
You need I2C (2 wires + ground & 3.3V power) and interrupt input GPIO and one output reset GPIO.

We made our custom mini tact keyboard PCB which can be easily labeled with a Walmart Dymo 3/8" tape label maker.
An iPad stylus was used to activate the small tact swiches or "very small fingers" can be used.
Our main design goal is to use this alpha numeric tact keyboard with an e-ink display for a low power RF terminal.

https://www.sparkfun.com/products/11502

T3 code below - enjoy



Code:
/* SX1509 Library Example 03
   Keypad interfacing
   by: Jim Lindblom
   SparkFun Electronics
   license: Beerware. Please use, reuse, share, and modify this
   code. I'd ask that you maintain attribution and open-source.
   If you find it useful, you can buy me a beer when we meet
   some day.
  
   This example shows how you can use the following SX1509 
   library methods:
     - constructor
     - init()
     - configClock()
     - keypad(numRows, numCols, sleepTime, scanTime)
     - readKeyData()
     - debounceConfig(debounceTime)
     
    Hardware: The SX1509 should be hooked up like so:
    SX1509 Pin      Arduino Pin
       3.3V ---------- 3.3V
       GND ----------- GND
       SDA ----------- A4 (or SDA on newer boards)
       SCL ----------- A5 (or SCL on newer boards)
       nRST ---------- 8 (could be any unused digital pin)
       nINT ---------- 7 (could be any unused digital pin)
       OSCIO is not used in this example.
   
   See the SX1509_ADDRESS defines to decide which address you need
   to send to the constructor. By default the SX1509 Breakout
   sets both ADDR pins to 0 (so 0x3E I2C address).

   In addition SX1509 is connected to a 4x4 matrix of 16 buttons.
   I used the SparkFun 4x4 button pad breakout:
                       https://www.sparkfun.com/products/8033
   You could tie 4 of those together and interface this chip
   with the 8x8 array of 64(!) buttons.
   
   The four rows of the button matrix should be connected to
   the SX1509's 0:3 pins. The columns of the button matrix should
   be connected to the SX1509's 8:11 pins.
   
   Works on the Teensy 3 <-----------------------------<<<<<<<<<<<<
   100 KHZ default bit rate
   10 k pullups ???
*/

#include <Wire.h>  // Wire.h library is required to use SX1509 lib 
#include <sx1509_library.h>  // Include the SX1509 library

// Uncomment one of the four lines to match your SX1509's address
//  pin selects. SX1509 breakout defaults to [0:0] (0x3E).
const byte SX1509_ADDRESS = 0x3E;  // SX1509 I2C address (00) <-----<<<<< default on board
//const byte SX1509_ADDRESS = 0x3F;  // SX1509 I2C address (01)
//const byte SX1509_ADDRESS = 0x70;  // SX1509 I2C address (10)
//const byte SX1509_ADDRESS = 0x71;  // SX1509 I2C address (11)

// Arduino pin definitions
//const byte resetPin = 8;
const byte resetPin = 16;
//const byte interruptPin = 7;
const byte interruptPin = 17;

int run_led = 13;

//////////////////////////////////
//// Global Variables  ///////////
//////////////////////////////////
// Here we'll define the number of rows and columns our keypad
//  matrix has. Each of these values can be between 1 and 8.
//  4x4 = 16 buttons
const byte numRows = 6;
//const byte numRows = 6;
const byte numCols = 8;
//const byte numCols = 8;
// This key map will be used to define what keypress is sent to
//  the computer when a key on the keypad is pressed.
char keyMap[numRows][numCols] = { 
   {'1','2','3','4','5','6','7','8'},
   {'A','B','C','D','E','F','G','H'},
   {'Q','R','S','T','U','V','W','X'},
   {'9','0','+','-','u','d','l','r'},     
   {'I','J','K','L','M','N','O','P'},   
   {'Y','Z','E','B','C','H',' ','.'}};


//char keyMap[numRows][numCols] = {
  //{'1','2','3','4'},
  //{'q','w','e','r'},
  //{'a','s','d','f'},
  //{'z','x','c','v'}};

  
   
 // Create a new sx1509Class object
sx1509Class sx1509(SX1509_ADDRESS, resetPin, interruptPin);

void setup()
{
  // This example will use a Leonardo to send USB keypresses to
  //  the computer its connected to. The 'Keyboard.' statements
  //  can easily be replaced by Serial.print's, etc.
  //#ifdef HID_ENABLED
  //Keyboard.begin();
  //#else
  Serial.begin(9600);
  //#endif
  
    // initialize the digital pin as an output.
  pinMode(run_led, OUTPUT);
 digitalWrite(run_led, LOW); // run led off
  
  delay(10000); // wait for serial monitor
  
  // Must first initialize the sx1509:
  int x = 0;
  x = sx1509.init();
  if (x)
  {
  Serial.println("SX1509 Init OK");
  }
  else
  {
  Serial.println("SX1509 Init Failed");  
  }
  
  // In order to use the keypad, the clock must first be
  // configured. We can call configureClock() with the default
  // parameters (2MHz internal oscillator, no clock in/out).
  sx1509.configClock();
  // Next call the keypad function with the number of rows
  //  and columns.
  //sx1509.keypad(numRows, numCols);  // Basic keypad init
  // There are two optional parameters in the keypad method:
  //  sleepTime and scanTime. Each of these values can be between
  //  0 and 7. If not set, these values default to 0 (sleep off
  //  scan time set to 1ms).

  byte sleepTime = 7;
  byte scanTime = 5;  // Scan time per row <--<<<< 5 min for T3
  
  sx1509.keypad(numRows, numCols, sleepTime, scanTime);  // Advanced keypad init
  // We can also debounce the keypad inputs. The debounceConfig
  //  method takes one parameter. Similar to the scanTime it's a
  //  3-bit value (0-7). This value must be <= scanTime!
  byte debounceTime = 5;   // The debounce config value
  sx1509.debounceConfig(debounceTime);
}

//////////////////////////////////
//// Loop Variables  /////////////
//////////////////////////////////
unsigned int keyData;  // The raw data from the key press register
unsigned int previousKeyData = 0;  // previously read raw data
byte activeRow;  // The row of the button being pressed
byte activeColumn;  // The column of the button being pressed
// These variables are used to emulate a key-hold. While the key
//  is held down, there's a long delay before the second character
//  is printed. Then a shorter delay between the remaining key presses
unsigned int holdCount = 0;
// This behavior is highly dependent on scanTime and debounceConfig
//  which are set in the setup.
const byte holdCountMax = 25;
// These releaseCount variables 
//  The keypad engin on the SX1509 doesn't generate an interrupt
//  when a key is relased. So we'll use this counter to generate
//  releases.
unsigned int releaseCount = 0;  // Our counter
unsigned int releaseCountMax = 100;  // Top, in about milliseconds

// The loop will poll the interrupt pin. If the pin
//  is pulled low by the SX1509, we'll read the keypad data and
//  sort it into row and column, and send the corresponding key
//  press out to the computer.
void loop()
{
  //delay(500);
  digitalWrite(run_led, HIGH);   // turn the LED on (HIGH is the voltage level)
  //delay(500);
  
  // The interrupt is active low, and pulled-up otherwise.
  //  The interrupt will be activated whenever a key press is read
  
  
  //int y = 0;
  //y = digitalRead(interruptPin);
  //Serial.println(y,DEC); 
  
  if (!digitalRead(interruptPin))
  {
        
    // readKeyData() returns a 16-bit word of data. The lower 8-bits 
    //  represent each of the up-to 8 rows. The upper 8-bits
    //  correspond to the columns. A 1 in a bit position means
    //  that a button in that row or column is being pressed.
    keyData = sx1509.readKeyData();
    
    // Next, we'll sort out which row and column are being pressed.
    //  And we'll send out a keypress over USB HID  
    activeRow = keyData & 0xFF;  // The row is the lower 8-bits
    activeColumn = keyData >> 8;  // column is the upper 8-bits
    // The getBitPosition functio will return which bit is our 1
    activeRow = getBitPosition(activeRow);
    activeColumn = getBitPosition(activeColumn);
    
    // If it's a new button press spit it out, reset hold delay
    if (keyData != previousKeyData)
    {
      holdCount = 0;
      // Keyboard.write is a Leonardo-specific Arduino function.
      //  It'll perform a key press and release just like any
      //  keyboard connected to your computer. For testing, this
      //  could easily be replaced by
      //#ifdef HID_ENABLED      
      //Keyboard.write(keyMap[activeRow][activeColumn]);
      //#else
      Serial.println(keyMap[activeRow][activeColumn]);
      //#endif
    }
    else
    {
      holdCount++;  // Increment holdCount
      // This works as something of a key-press delay. Hold
      //  down a key on your computer to see what I'm talking
      //  about. After the initial delay, all characters following
      //  will stream out quickly.
      if (holdCount > holdCountMax)
      {
       // #ifdef HID_ENABLED      
        //Keyboard.write(keyMap[activeRow][activeColumn]);
        //#else
        Serial.println(keyMap[activeRow][activeColumn]);
        //#endif
      }
    }
    // Reset release count since there's been a key-press
    releaseCount = 0;
    // Set keyData as previousKeyData
    previousKeyData = keyData;
  }
  
  // If no keys have been pressed we'll continuously increment
  //  releaseCount. Eventually creating a release, once the count
  //  hits the max.
  releaseCount++;
  if (releaseCount == releaseCountMax)
  {
    releaseCount = 0;
    previousKeyData = 0;
  }
  delay(1);  // This gives releaseCountMax a more intuitive unit
  digitalWrite(run_led, LOW);    // turn the LED off by making the voltage LOW
} // EOL

// This function scours a byte and returns the position of the 
//  first byte it sees a 1. Great if our data bytes only have
//  a single 1 in them! Should return 0-7 if it sees a 1, 255 otherwise
byte getBitPosition(byte dataByte)
{
  for (int i=0; i<8; i++)
  {
    if (dataByte & (1<<i))
    {
      return i;
    }
  }
  return 255;  // Otherwise return an error
}
 
Last edited:
Attached pics
 

Attachments

  • tact_keyboard_R1.pdf
    430.4 KB · Views: 882
  • tact_keyboard_R3.pdf
    657.4 KB · Views: 771
Last edited:
BTW ... In our testing of the tact keyboard, the SX1509 "on chip" adjustable de-bounce settings are awesome. This relieves the Teensy 3 of this slow "cycle stealing", mundane task. This also includes the auto keyboard scanning.:cool:
 
Last edited:
No - how would SX1509 detect two keypresses with only one interrupt?
I see where you are coming from... like holding down the shift or control key with another key to expand the detected keypresses.
The SX1509 detects if you hold down one key just like on the PC. See software above.
 
Last edited:
Yes of course you can, you'll get successive interrupts for each key held during a full matrix scan, and it will continue scanning after you read out the key row/col.

I don't think there is a maximum, as you can handle buffering them yourself, but if you want full NKRO you'll need a diode on each key in the matrix, of course.

- Peter
 
but if you want full NKRO you'll need a diode on each key in the matrix, of course.
64 diodes added to this small breakout board? Sorta defeats its small form factor.

Yes of course you can, you'll get successive interrupts for each key held during a full matrix scan

Before you wire up 64 diodes, and to make 100% sure, contact the Sparkfun demo software/hardware author - Jim Lindblom.



--------------------------
KIS --> keep it simple :cool:
 
Last edited:
This is definitely going on my "shopping list". My project, controls for a flight simulator, will eventually have a lot of tactile buttons, plus a small keyboard (a bit bigger than this one.)
 
Forgive me for bringing up such an old post, but I am just learning about these things.

Could I modify this using simple mechanical switches like cherry reds and achieve the same results?
 
Status
Not open for further replies.
Back
Top