hybridgroup.gobot/drivers/i2c/wiichuck_driver.go

191 lines
4.6 KiB
Go

package i2c
import (
"fmt"
"sync"
"time"
"gobot.io/x/gobot/v2"
)
const (
// Joystick event when the Wiichuck joystick is moved
Joystick = "joystick"
// C event when the Wiichuck "C" button is pressed
C = "c"
// Z event when the Wiichuck "C" button is pressed
Z = "z"
)
const wiichuckDefaultAddress = 0x52
// WiichuckDriver contains the attributes for the i2c driver
type WiichuckDriver struct {
*Driver
interval time.Duration
pauseTime time.Duration
gobot.Eventer
mtx sync.Mutex
joystick map[string]float64
data map[string]float64
}
// NewWiichuckDriver creates a WiichuckDriver with specified i2c interface.
//
// Params:
//
// c Connector - the Adaptor to use with this Driver
//
// Optional params:
//
// i2c.WithBus(int): bus to use with this driver
// i2c.WithAddress(int): address to use with this driver
func NewWiichuckDriver(c Connector, options ...func(Config)) *WiichuckDriver {
w := &WiichuckDriver{
Driver: NewDriver(c, "Wiichuck", wiichuckDefaultAddress),
interval: 10 * time.Millisecond,
pauseTime: 1 * time.Millisecond,
Eventer: gobot.NewEventer(),
joystick: map[string]float64{
"sy_origin": -1,
"sx_origin": -1,
},
data: map[string]float64{
"sx": 0,
"sy": 0,
"z": 0,
"c": 0,
},
}
w.afterStart = w.initialize
for _, option := range options {
option(w)
}
w.AddEvent(Z)
w.AddEvent(C)
w.AddEvent(Joystick)
w.AddEvent(Error)
return w
}
// Joystick returns the current value for the joystick
func (w *WiichuckDriver) Joystick() map[string]float64 {
val := make(map[string]float64)
w.mtx.Lock()
defer w.mtx.Unlock()
val["sx_origin"] = w.joystick["sx_origin"]
val["sy_origin"] = w.joystick["sy_origin"]
return val
}
// update parses value to update buttons and joystick.
// If value is encrypted, warning message is printed
func (w *WiichuckDriver) update(value []byte) error {
if w.isEncrypted(value) {
return fmt.Errorf("Encrypted bytes")
}
w.parse(value)
w.adjustOrigins()
w.updateButtons()
w.updateJoystick()
return nil
}
// setJoystickDefaultValue sets default value if value is -1
func (w *WiichuckDriver) setJoystickDefaultValue(joystickAxis string, defaultValue float64) {
w.mtx.Lock()
defer w.mtx.Unlock()
if w.joystick[joystickAxis] == -1 {
w.joystick[joystickAxis] = defaultValue
}
}
// calculateJoystickValue returns distance between axis and origin
func (w *WiichuckDriver) calculateJoystickValue(axis float64, origin float64) float64 {
return axis - origin
}
// isEncrypted returns true if value is encrypted
func (w *WiichuckDriver) isEncrypted(value []byte) bool {
if value[0] == value[1] && value[2] == value[3] && value[4] == value[5] {
return true
}
return false
}
// decode removes encoding from `x` byte
func (w *WiichuckDriver) decode(x byte) float64 {
return float64((x ^ 0x17) + 0x17)
}
// adjustOrigins sets sy_origin and sx_origin with values from data
func (w *WiichuckDriver) adjustOrigins() {
w.setJoystickDefaultValue("sy_origin", w.data["sy"])
w.setJoystickDefaultValue("sx_origin", w.data["sx"])
}
// updateButtons publishes "c" and "x" events if present in data
func (w *WiichuckDriver) updateButtons() {
if w.data["c"] == 0 {
w.Publish(w.Event(C), true)
}
if w.data["z"] == 0 {
w.Publish(w.Event(Z), true)
}
}
// updateJoystick publishes event with current x and y values for joystick
func (w *WiichuckDriver) updateJoystick() {
joy := w.Joystick()
w.Publish(w.Event(Joystick), map[string]float64{
"x": w.calculateJoystickValue(w.data["sx"], joy["sx_origin"]),
"y": w.calculateJoystickValue(w.data["sy"], joy["sy_origin"]),
})
}
// parse sets driver values based on parsed value
func (w *WiichuckDriver) parse(value []byte) {
w.data["sx"] = w.decode(value[0])
w.data["sy"] = w.decode(value[1])
w.data["z"] = float64(uint8(w.decode(value[5])) & 0x01)
w.data["c"] = float64(uint8(w.decode(value[5])) & 0x02)
}
// reads from adaptor using specified interval to update with new value
func (w *WiichuckDriver) initialize() error {
go func() {
for {
if _, err := w.connection.Write([]byte{0x40, 0x00}); err != nil {
w.Publish(w.Event(Error), err)
continue
}
time.Sleep(w.pauseTime)
if _, err := w.connection.Write([]byte{0x00}); err != nil {
w.Publish(w.Event(Error), err)
continue
}
time.Sleep(w.pauseTime)
newValue := make([]byte, 6)
bytesRead, err := w.connection.Read(newValue)
if err != nil {
w.Publish(w.Event(Error), err)
continue
}
if bytesRead == 6 {
if err = w.update(newValue); err != nil {
w.Publish(w.Event(Error), err)
continue
}
}
time.Sleep(w.interval)
}
}()
return nil
}