diff --git a/samples/keypad/matrix4x3.go b/samples/keypad/matrix4x3.go new file mode 100644 index 0000000..3bbb21f --- /dev/null +++ b/samples/keypad/matrix4x3.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "time" + + "github.com/kid0m4n/go-rpi/sensor/keypad/matrix4x3" +) + +func main() { + rowPins := []int{4, 17, 27, 22} + colPins := []int{23, 24, 25} + + keypad := matrix4x3.New(rowPins, colPins) + + for { + if key, err := keypad.PressedKey(); err == nil { + fmt.Printf("Key Pressed = %v\n", key) + } + + time.Sleep(500 * time.Millisecond) + } +} diff --git a/sensor/keypad/matrix4x3/matrix4x3.go b/sensor/keypad/matrix4x3/matrix4x3.go new file mode 100644 index 0000000..8cfc3ef --- /dev/null +++ b/sensor/keypad/matrix4x3/matrix4x3.go @@ -0,0 +1,221 @@ +// Package matrix4x3 allows interfacing 4x3 keypad with Raspberry pi. +package matrix4x3 + +import ( + "errors" + "strconv" + "sync" + "time" + + "github.com/stianeikeland/go-rpio" +) + +type Key int + +func (k Key) String() string { + switch k { + case KStar: + return "*" + case KHash: + return "#" + default: + return strconv.Itoa(int(k)) + } +} + +const ( + K0 Key = iota + 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 +} + +// A Matrix4x3 interface implements access to the keypad. +type Matrix4x3 interface { + // Run starts the continuous key scan loop. + Run() + + // SetPollDelay sets the delay between runs of key scan acquisition loop. + SetPollDelay(delay int) + + // Pressed key returns the current key pressed on the keypad. + PressedKey() (Key, error) + + // Close. + Close() error +} + +type matrix4x3 struct { + rpioRowPins, rpioColPins []rpio.Pin + + initialized bool + mu sync.RWMutex + + poll int + + keyPressed chan Key + quit chan bool +} + +// New creates a new interface for matrix4x3. +func New(rowPins, colPins []int) Matrix4x3 { + m := &matrix4x3{ + rpioRowPins: make([]rpio.Pin, rows), + rpioColPins: make([]rpio.Pin, cols), + poll: pollDelay, + } + + for i := 0; i < rows; i++ { + m.rpioRowPins[i] = rpio.Pin(rowPins[i]) + } + for i := 0; i < cols; i++ { + m.rpioColPins[i] = rpio.Pin(colPins[i]) + } + + return m +} + +// SetPollDelay sets the delay between run of key scan acquisition loop. +func (d *matrix4x3) SetPollDelay(delay int) { + d.poll = delay +} + +func (d *matrix4x3) setup() (err error) { + d.mu.RLock() + if d.initialized { + d.mu.RUnlock() + return + } + d.mu.RUnlock() + + d.mu.Lock() + defer d.mu.Unlock() + + if err = rpio.Open(); err != nil { + return + } + + for i := 0; i < rows; i++ { + d.rpioRowPins[i].Input() + d.rpioRowPins[i].PullUp() + } + + for i := 0; i < cols; i++ { + d.rpioColPins[i].Output() + d.rpioColPins[i].High() + } + + d.initialized = true + + return +} + +func (d *matrix4x3) findPressedKey() (key Key, err error) { + if err = d.setup(); err != nil { + return + } + + err = errors.New("no key pressed") + + for col := 0; col < cols; col++ { + d.rpioColPins[col].Low() + for row := 0; row < rows; row++ { + if d.rpioRowPins[row].Read() == rpio.Low { + time.Sleep(debounce) + + if d.rpioRowPins[row].Read() == rpio.Low { + key = keyMap[row][col] + err = nil + } + } + } + d.rpioColPins[col].High() + } + + return +} + +// Pressed key returns the current key pressed on the keypad. +func (d *matrix4x3) PressedKey() (key Key, err error) { + select { + case key = <-d.keyPressed: + return + default: + return d.findPressedKey() + } +} + +// Run starts the continuous key scan loop. +func (d *matrix4x3) Run() { + 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. +func (d *matrix4x3) Close() (err error) { + if d.quit != nil { + d.quit <- true + } + + rpio.Close() + + return +}