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