Skip to content

Commit

Permalink
Change to pass through ringbuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
sago35 committed Oct 31, 2023
1 parent 008f116 commit 88e2fd5
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 20 deletions.
68 changes: 48 additions & 20 deletions blekeyboard.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//go:build tinygo && nrf52840

package keyboard

import (
"machine"
k "machine/usb/hid/keyboard"

"tinygo.org/x/bluetooth"
Expand Down Expand Up @@ -81,11 +84,18 @@ func (k *BleTxKeyboard) Connect() error {
return nil
}

type bleKeyEvent struct {
Index int
IsHigh bool
}

type BleKeyboard struct {
RxBleName string
State []State
Keys [][]Keycode
callback Callback
ringbuf *RingBuffer[bleKeyEvent]
processed []int

adapter *bluetooth.Adapter
buf []byte
Expand All @@ -105,6 +115,7 @@ func (d *Device) AddBleKeyboard(size int, rxname string, keys [][]Keycode) *BleK
}
}

var rb [32]bleKeyEvent
k := &BleKeyboard{
RxBleName: rxname,
State: state,
Expand All @@ -113,6 +124,8 @@ func (d *Device) AddBleKeyboard(size int, rxname string, keys [][]Keycode) *BleK
callback: func(layer, index int, state State) {},
buf: make([]byte, 3),
changed: false,
ringbuf: NewRingBuffer(rb[:]),
processed: make([]int, 0, 8),
}

d.kb = append(d.kb, k)
Expand All @@ -134,25 +147,26 @@ func (d *BleKeyboard) Get() []State {
}
}

if !d.changed {
return d.State
}
d.processed = d.processed[:0]

d.changed = false

if len(d.buf) == 3 {
index := (int(d.buf[1]) << 8) + int(d.buf[2])
current := false
switch d.buf[0] {
case 0xAA: // press
current = true
case 0x55: // release
current = false
default:
d.buf[0], d.buf[1] = d.buf[1], d.buf[2]
d.buf = d.buf[:2]
cont := true
for cont {
b, ok := d.ringbuf.Peek()
if !ok {
return d.State
}
index := b.Index
current := b.IsHigh

for _, idx := range d.processed {
if index == idx {
return d.State
}
}
d.processed = append(d.processed, index)

d.ringbuf.Get()

switch d.State[index] {
case None:
if current {
Expand Down Expand Up @@ -212,6 +226,9 @@ func (d *BleKeyboard) GetKeyCount() int {
}

func (d *BleKeyboard) Init() error {
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})

err := d.adapter.Enable()
if err != nil {
return err
Expand All @@ -233,13 +250,24 @@ func (d *BleKeyboard) Init() error {
Value: d.buf[:],
Flags: bluetooth.CharacteristicReadPermission | bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission,
WriteEvent: func(client bluetooth.Connection, offset int, value []byte) {
if true {
led.Set(!led.Get())
}
if offset != 0 || len(value) != 3 {
return
}
d.buf[0] = value[0]
d.buf[1] = value[1]
d.buf[2] = value[2]
d.changed = true
index := (int(value[1]) << 8) + int(value[2])
isHigh := false
switch value[0] {
case 0xAA: // press
isHigh = true
case 0x55: // release
isHigh = false
}
d.ringbuf.Put(bleKeyEvent{
Index: index,
IsHigh: isHigh,
})
},
},
},
Expand Down
65 changes: 65 additions & 0 deletions buffer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//go:build tinygo

package keyboard

import (
"runtime/volatile"
)

// RingBuffer is ring buffer implementation inspired by post at
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
type RingBuffer[T any] struct {
buffer []T
head volatile.Register8
tail volatile.Register8
}

// NewRingBuffer returns a new ring buffer.
func NewRingBuffer[T any](buf []T) *RingBuffer[T] {
return &RingBuffer[T]{
buffer: buf,
}
}

// Used returns how many bytes in buffer have been used.
func (rb *RingBuffer[T]) Used() uint8 {
return uint8(rb.head.Get() - rb.tail.Get())
}

// Put stores a byte in the buffer. If the buffer is already
// full, the method will return false.
func (rb *RingBuffer[T]) Put(val T) bool {
if rb.Used() != uint8(len(rb.buffer)) {
rb.head.Set(rb.head.Get() + 1)
rb.buffer[rb.head.Get()%uint8(len(rb.buffer))] = val
return true
}
return false
}

// Get returns a byte from the buffer. If the buffer is empty,
// the method will return a false as the second value.
func (rb *RingBuffer[T]) Get() (T, bool) {
if rb.Used() != 0 {
rb.tail.Set(rb.tail.Get() + 1)
return rb.buffer[rb.tail.Get()%uint8(len(rb.buffer))], true
}
var ret T
return ret, false
}

// Peek peeks a byte from the buffer. If the buffer is empty,
// the method will return a false as the second value.
func (rb *RingBuffer[T]) Peek() (T, bool) {
if rb.Used() != 0 {
return rb.buffer[(rb.tail.Get()+1)%uint8(len(rb.buffer))], true
}
var ret T
return ret, false
}

// Clear resets the head and tail pointer to zero.
func (rb *RingBuffer[T]) Clear() {
rb.head.Set(0)
rb.tail.Set(0)
}

0 comments on commit 88e2fd5

Please sign in to comment.