2013-12-31 03:26:34 +01:00
|
|
|
// Package matrix4x3 allows interfacing 4x3 keypad with Raspberry pi.
|
|
|
|
package matrix4x3
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2014-03-02 20:25:20 +01:00
|
|
|
"github.com/kidoman/embd"
|
2013-12-31 03:26:34 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Key int
|
|
|
|
|
|
|
|
func (k Key) String() string {
|
|
|
|
switch k {
|
|
|
|
case KStar:
|
|
|
|
return "*"
|
|
|
|
case KHash:
|
|
|
|
return "#"
|
|
|
|
default:
|
2014-03-02 07:39:57 +01:00
|
|
|
return strconv.Itoa(int(k) - 1)
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2014-03-02 07:39:57 +01:00
|
|
|
KNone Key = iota
|
|
|
|
K0
|
2013-12-31 03:26:34 +01:00
|
|
|
K1
|
|
|
|
K2
|
|
|
|
K3
|
|
|
|
K4
|
|
|
|
K5
|
|
|
|
K6
|
|
|
|
K7
|
|
|
|
K8
|
|
|
|
K9
|
|
|
|
KStar
|
|
|
|
KHash
|
|
|
|
|
|
|
|
debounce = 20 * time.Millisecond
|
|
|
|
|
|
|
|
pollDelay = 150
|
|
|
|
|
|
|
|
rows = 4
|
|
|
|
cols = 3
|
|
|
|
)
|
|
|
|
|
|
|
|
var keyMap [][]Key
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
keyMap = make([][]Key, rows)
|
|
|
|
for i := 0; i < rows; i++ {
|
|
|
|
keyMap[i] = make([]Key, cols)
|
|
|
|
}
|
|
|
|
keyMap[0][0] = K1
|
|
|
|
keyMap[0][1] = K2
|
|
|
|
keyMap[0][2] = K3
|
|
|
|
keyMap[1][0] = K4
|
|
|
|
keyMap[1][1] = K5
|
|
|
|
keyMap[1][2] = K6
|
|
|
|
keyMap[2][0] = K7
|
|
|
|
keyMap[2][1] = K8
|
|
|
|
keyMap[2][2] = K9
|
|
|
|
keyMap[3][0] = KStar
|
|
|
|
keyMap[3][1] = K0
|
|
|
|
keyMap[3][2] = KHash
|
|
|
|
}
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
// A Matrix4x3 struct represents access to the keypad.
|
|
|
|
type Matrix4x3 struct {
|
2014-03-02 20:25:20 +01:00
|
|
|
rowPins, colPins []embd.DigitalPin
|
2013-12-31 03:26:34 +01:00
|
|
|
|
|
|
|
initialized bool
|
|
|
|
mu sync.RWMutex
|
|
|
|
|
|
|
|
poll int
|
|
|
|
|
|
|
|
keyPressed chan Key
|
|
|
|
quit chan bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new interface for matrix4x3.
|
2014-03-02 07:39:57 +01:00
|
|
|
func New(rowPins, colPins []int) (*Matrix4x3, error) {
|
|
|
|
m := &Matrix4x3{
|
2014-03-02 20:25:20 +01:00
|
|
|
rowPins: make([]embd.DigitalPin, rows),
|
|
|
|
colPins: make([]embd.DigitalPin, cols),
|
2014-03-02 07:39:57 +01:00
|
|
|
poll: pollDelay,
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
var err error
|
2013-12-31 03:26:34 +01:00
|
|
|
for i := 0; i < rows; i++ {
|
2014-03-02 20:25:20 +01:00
|
|
|
m.rowPins[i], err = embd.NewDigitalPin(rowPins[i])
|
2014-03-02 07:39:57 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
for i := 0; i < cols; i++ {
|
2014-03-02 20:25:20 +01:00
|
|
|
m.colPins[i], err = embd.NewDigitalPin(colPins[i])
|
2014-03-02 07:39:57 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
return m, nil
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetPollDelay sets the delay between run of key scan acquisition loop.
|
2014-03-02 07:39:57 +01:00
|
|
|
func (d *Matrix4x3) SetPollDelay(delay int) {
|
2013-12-31 03:26:34 +01:00
|
|
|
d.poll = delay
|
|
|
|
}
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
func (d *Matrix4x3) setup() error {
|
2013-12-31 03:26:34 +01:00
|
|
|
d.mu.RLock()
|
|
|
|
if d.initialized {
|
|
|
|
d.mu.RUnlock()
|
2014-03-02 07:39:57 +01:00
|
|
|
return nil
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
d.mu.RUnlock()
|
|
|
|
|
|
|
|
d.mu.Lock()
|
|
|
|
defer d.mu.Unlock()
|
|
|
|
|
|
|
|
for i := 0; i < rows; i++ {
|
2014-03-02 20:25:20 +01:00
|
|
|
if err := d.rowPins[i].SetDirection(embd.In); err != nil {
|
2014-03-02 07:39:57 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := d.rowPins[i].PullUp(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < cols; i++ {
|
2014-03-02 20:25:20 +01:00
|
|
|
if err := d.colPins[i].SetDirection(embd.Out); err != nil {
|
2014-03-02 07:39:57 +01:00
|
|
|
return err
|
|
|
|
}
|
2014-03-02 20:25:20 +01:00
|
|
|
if err := d.colPins[i].Write(embd.High); err != nil {
|
2014-03-02 07:39:57 +01:00
|
|
|
return err
|
|
|
|
}
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
d.initialized = true
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
return nil
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
func (d *Matrix4x3) findPressedKey() (Key, error) {
|
|
|
|
if err := d.setup(); err != nil {
|
|
|
|
return 0, err
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for col := 0; col < cols; col++ {
|
2014-03-02 20:25:20 +01:00
|
|
|
if err := d.colPins[col].Write(embd.Low); err != nil {
|
2014-03-02 07:39:57 +01:00
|
|
|
return KNone, err
|
|
|
|
}
|
2013-12-31 03:26:34 +01:00
|
|
|
for row := 0; row < rows; row++ {
|
2014-03-02 07:39:57 +01:00
|
|
|
value, err := d.rowPins[row].Read()
|
|
|
|
if err != nil {
|
|
|
|
return KNone, err
|
|
|
|
}
|
2014-03-02 20:25:20 +01:00
|
|
|
if value == embd.Low {
|
2013-12-31 03:26:34 +01:00
|
|
|
time.Sleep(debounce)
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
value, err = d.rowPins[row].Read()
|
|
|
|
if err != nil {
|
|
|
|
return KNone, err
|
|
|
|
}
|
2014-03-02 20:25:20 +01:00
|
|
|
if value == embd.Low {
|
|
|
|
if err := d.colPins[col].Write(embd.High); err != nil {
|
2014-03-02 07:39:57 +01:00
|
|
|
return KNone, err
|
|
|
|
}
|
|
|
|
return keyMap[row][col], nil
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-02 20:25:20 +01:00
|
|
|
if err := d.colPins[col].Write(embd.High); err != nil {
|
2014-03-02 07:39:57 +01:00
|
|
|
return KNone, err
|
|
|
|
}
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
2014-03-02 07:39:57 +01:00
|
|
|
return KNone, nil
|
2013-12-31 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pressed key returns the current key pressed on the keypad.
|
2014-03-02 07:39:57 +01:00
|
|
|
func (d *Matrix4x3) PressedKey() (key Key, err error) {
|
2013-12-31 03:26:34 +01:00
|
|
|
select {
|
|
|
|
case key = <-d.keyPressed:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
return d.findPressedKey()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run starts the continuous key scan loop.
|
2014-03-02 07:39:57 +01:00
|
|
|
func (d *Matrix4x3) Run() {
|
2013-12-31 03:26:34 +01:00
|
|
|
d.quit = make(chan bool)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
timer := time.Tick(time.Duration(d.poll) * time.Millisecond)
|
|
|
|
var key Key
|
|
|
|
|
|
|
|
for {
|
|
|
|
var keyUpdates chan Key
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-timer:
|
|
|
|
var err error
|
|
|
|
if key, err = d.findPressedKey(); err == nil {
|
|
|
|
keyUpdates = d.keyPressed
|
|
|
|
}
|
|
|
|
case keyUpdates <- key:
|
|
|
|
keyUpdates = nil
|
|
|
|
case <-d.quit:
|
|
|
|
d.keyPressed = nil
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close.
|
2014-03-02 07:39:57 +01:00
|
|
|
func (d *Matrix4x3) Close() {
|
2013-12-31 03:26:34 +01:00
|
|
|
if d.quit != nil {
|
|
|
|
d.quit <- true
|
|
|
|
}
|
|
|
|
}
|