gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/wiichuck_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	"gobot.io/x/gobot/v2"
     9  )
    10  
    11  const (
    12  	// Joystick event when the Wiichuck joystick is moved
    13  	Joystick = "joystick"
    14  
    15  	// C event when the Wiichuck "C" button is pressed
    16  	C = "c"
    17  
    18  	// Z event when the Wiichuck "C" button is pressed
    19  	Z = "z"
    20  )
    21  
    22  const wiichuckDefaultAddress = 0x52
    23  
    24  // WiichuckDriver contains the attributes for the i2c driver
    25  type WiichuckDriver struct {
    26  	*Driver
    27  	interval  time.Duration
    28  	pauseTime time.Duration
    29  	gobot.Eventer
    30  	mtx      sync.Mutex
    31  	joystick map[string]float64
    32  	data     map[string]float64
    33  }
    34  
    35  // NewWiichuckDriver creates a WiichuckDriver with specified i2c interface.
    36  //
    37  // Params:
    38  //
    39  //	c Connector - the Adaptor to use with this Driver
    40  //
    41  // Optional params:
    42  //
    43  //	i2c.WithBus(int):	bus to use with this driver
    44  //	i2c.WithAddress(int):	address to use with this driver
    45  func NewWiichuckDriver(c Connector, options ...func(Config)) *WiichuckDriver {
    46  	w := &WiichuckDriver{
    47  		Driver:    NewDriver(c, "Wiichuck", wiichuckDefaultAddress),
    48  		interval:  10 * time.Millisecond,
    49  		pauseTime: 1 * time.Millisecond,
    50  		Eventer:   gobot.NewEventer(),
    51  		joystick: map[string]float64{
    52  			"sy_origin": -1,
    53  			"sx_origin": -1,
    54  		},
    55  		data: map[string]float64{
    56  			"sx": 0,
    57  			"sy": 0,
    58  			"z":  0,
    59  			"c":  0,
    60  		},
    61  	}
    62  	w.afterStart = w.initialize
    63  
    64  	for _, option := range options {
    65  		option(w)
    66  	}
    67  
    68  	w.AddEvent(Z)
    69  	w.AddEvent(C)
    70  	w.AddEvent(Joystick)
    71  	w.AddEvent(Error)
    72  
    73  	return w
    74  }
    75  
    76  // Joystick returns the current value for the joystick
    77  func (w *WiichuckDriver) Joystick() map[string]float64 {
    78  	val := make(map[string]float64)
    79  	w.mtx.Lock()
    80  	defer w.mtx.Unlock()
    81  	val["sx_origin"] = w.joystick["sx_origin"]
    82  	val["sy_origin"] = w.joystick["sy_origin"]
    83  	return val
    84  }
    85  
    86  // update parses value to update buttons and joystick.
    87  // If value is encrypted, warning message is printed
    88  func (w *WiichuckDriver) update(value []byte) error {
    89  	if w.isEncrypted(value) {
    90  		return fmt.Errorf("Encrypted bytes")
    91  	}
    92  
    93  	w.parse(value)
    94  	w.adjustOrigins()
    95  	w.updateButtons()
    96  	w.updateJoystick()
    97  	return nil
    98  }
    99  
   100  // setJoystickDefaultValue sets default value if value is -1
   101  func (w *WiichuckDriver) setJoystickDefaultValue(joystickAxis string, defaultValue float64) {
   102  	w.mtx.Lock()
   103  	defer w.mtx.Unlock()
   104  	if w.joystick[joystickAxis] == -1 {
   105  		w.joystick[joystickAxis] = defaultValue
   106  	}
   107  }
   108  
   109  // calculateJoystickValue returns distance between axis and origin
   110  func (w *WiichuckDriver) calculateJoystickValue(axis float64, origin float64) float64 {
   111  	return float64(axis - origin)
   112  }
   113  
   114  // isEncrypted returns true if value is encrypted
   115  func (w *WiichuckDriver) isEncrypted(value []byte) bool {
   116  	if value[0] == value[1] && value[2] == value[3] && value[4] == value[5] {
   117  		return true
   118  	}
   119  	return false
   120  }
   121  
   122  // decode removes encoding from `x` byte
   123  func (w *WiichuckDriver) decode(x byte) float64 {
   124  	return float64((x ^ 0x17) + 0x17)
   125  }
   126  
   127  // adjustOrigins sets sy_origin and sx_origin with values from data
   128  func (w *WiichuckDriver) adjustOrigins() {
   129  	w.setJoystickDefaultValue("sy_origin", w.data["sy"])
   130  	w.setJoystickDefaultValue("sx_origin", w.data["sx"])
   131  }
   132  
   133  // updateButtons publishes "c" and "x" events if present in data
   134  func (w *WiichuckDriver) updateButtons() {
   135  	if w.data["c"] == 0 {
   136  		w.Publish(w.Event(C), true)
   137  	}
   138  	if w.data["z"] == 0 {
   139  		w.Publish(w.Event(Z), true)
   140  	}
   141  }
   142  
   143  // updateJoystick publishes event with current x and y values for joystick
   144  func (w *WiichuckDriver) updateJoystick() {
   145  	joy := w.Joystick()
   146  	w.Publish(w.Event(Joystick), map[string]float64{
   147  		"x": w.calculateJoystickValue(w.data["sx"], joy["sx_origin"]),
   148  		"y": w.calculateJoystickValue(w.data["sy"], joy["sy_origin"]),
   149  	})
   150  }
   151  
   152  // parse sets driver values based on parsed value
   153  func (w *WiichuckDriver) parse(value []byte) {
   154  	w.data["sx"] = w.decode(value[0])
   155  	w.data["sy"] = w.decode(value[1])
   156  	w.data["z"] = float64(uint8(w.decode(value[5])) & 0x01)
   157  	w.data["c"] = float64(uint8(w.decode(value[5])) & 0x02)
   158  }
   159  
   160  // reads from adaptor using specified interval to update with new value
   161  func (w *WiichuckDriver) initialize() (err error) {
   162  	go func() {
   163  		for {
   164  			if _, err := w.connection.Write([]byte{0x40, 0x00}); err != nil {
   165  				w.Publish(w.Event(Error), err)
   166  				continue
   167  			}
   168  			time.Sleep(w.pauseTime)
   169  			if _, err := w.connection.Write([]byte{0x00}); err != nil {
   170  				w.Publish(w.Event(Error), err)
   171  				continue
   172  			}
   173  			time.Sleep(w.pauseTime)
   174  			newValue := make([]byte, 6)
   175  			bytesRead, err := w.connection.Read(newValue)
   176  			if err != nil {
   177  				w.Publish(w.Event(Error), err)
   178  				continue
   179  			}
   180  			if bytesRead == 6 {
   181  				if err = w.update(newValue); err != nil {
   182  					w.Publish(w.Event(Error), err)
   183  					continue
   184  				}
   185  			}
   186  			time.Sleep(w.interval)
   187  		}
   188  	}()
   189  	return
   190  }