-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsparkfun_qwiicjoystick.py
216 lines (173 loc) · 7.29 KB
/
sparkfun_qwiicjoystick.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2019-2021 Gaston Williams
#
# SPDX-License-Identifier: MIT
# The MIT License (MIT)
#
# Copyright (c) 2019 Gaston Williams
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`sparkfun_qwiicjoystick`
================================================================================
CircuitPython library for Sparkfun Qwiic Joystick
* Author(s): Gaston Williams
Implementation Notes
--------------------
**Hardware:**
* This is library is for the SparkFun Qwiic Joystick.
* SparkFun sells these at its website: www.sparkfun.com
* Do you like this library? Help support SparkFun. Buy a board!
https://www.sparkfun.com/products/15168
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
* Adafruit's Bus Device library:
https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
"""
# imports
# imports__version__ = "0.0.0-auto.0"
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/fourstix/Sparkfun_CircuitPython_QwiicJoystick.git"
from time import sleep
from micropython import const
from adafruit_bus_device.i2c_device import I2CDevice
# public constants
QWIIC_JOYSTICK_ADDR = const(0x20) # default I2C Address
# private constants
_JOYSTICK_ID = const(0x00)
_JOYSTICK_VERSION1 = const(0x01)
_JOYSTICK_VERSION2 = const(0x02)
_JOYSTICK_X_MSB = const(0x03)
_JOYSTICK_X_LSB = const(0x04)
_JOYSTICK_Y_MSB = const(0x05)
_JOYSTICK_Y_LSB = const(0x06)
_JOYSTICK_BUTTON = const(0x07)
_JOYSTICK_STATUS = const(0x08) # 1 - button clicked
_JOYSTICK_I2C_LOCK = const(0x09)
_JOYSTICK_CHANGE_ADDRESS = const(0x0A)
# class
class Sparkfun_QwiicJoystick:
"""CircuitPython class for the Sparkfun QwiicJoystick
Usage:
# import the CircuitPython board and busio libraries
import board
import busio
# Create bus object using the board's I2C port
i2c = busio.I2C(board.SCL, board.SDA)
joystick = QwiicJoystick(i2c) # default address is 0x20
# use QwiicJoystick(i2c, address) for a different address
# joystick = QwiicJoystick(i2c, 0x21)"""
def __init__(self, i2c, address=QWIIC_JOYSTICK_ADDR, debug=False):
"""Initialize Qwiic Joystick for i2c communication."""
self._device = I2CDevice(i2c, address)
# save handle to i2c bus in case address is changed
self._i2c = i2c
self._debug = debug
# public properites
@property
def connected(self):
"""True if the Joystick is connected and a valid id is successful read."""
try:
# Attempt to read the id and see if we get an error
self._read_register(_JOYSTICK_ID)
except ValueError:
return False
return True
@property
def version(self):
"""Firmware version string for joystick."""
major = self._read_register(_JOYSTICK_VERSION1)
minor = self._read_register(_JOYSTICK_VERSION2)
return "v" + str(major) + "." + str(minor)
@property
def horizontal(self):
"""X value from 0 - 1023 of the joystick postion."""
# Read MSB for horizontal joystick position
x_msb = self._read_register(_JOYSTICK_X_MSB)
# Read LSB for horizontal joystick position
x_lsb = self._read_register(_JOYSTICK_X_LSB)
# mask off bytes and combine into 10-bit integer
x = ((x_msb & 0xFF) << 8 | (x_lsb & 0xFF)) >> 6
return x
@property
def vertical(self):
"""Y value from 0 to 1023 of the joystick postion."""
# Read MSB for veritical joystick position
y_msb = self._read_register(_JOYSTICK_Y_MSB)
# Read LSB for vertical joystick position
y_lsb = self._read_register(_JOYSTICK_Y_LSB)
# mask off bytes and combine into 10-bit integer
y = ((y_msb & 0xFF) << 8 | (y_lsb & 0xFF)) >> 6
return y
@property
def button(self):
"""0 if button is down, 1 if button is up."""
button = self._read_register(_JOYSTICK_BUTTON)
return button
# Issue: register 0x08 always contains 1 for some reason, even when cleared
@property
def button_status(self):
"""1 if button pressed between reads, cleared after read."""
# read button status (since last check)
status = self._read_register(_JOYSTICK_STATUS)
# clear button status
self._write_register(_JOYSTICK_STATUS, 0x00)
return status & 0xFF
# public functions
def set_i2c_address(self, new_address):
"""Change the i2c address of Joystick snd return True if successful."""
# check range of new address
if new_address < 8 or new_address > 119:
print("ERROR: Address outside 8-119 range")
return False
# write magic number 0x13 to lock register, to unlock address for update
self._write_register(_JOYSTICK_I2C_LOCK, 0x13)
# write new address
self._write_register(_JOYSTICK_CHANGE_ADDRESS, new_address)
# wait a second for joystick to settle after change
sleep(1)
# try to re-create new i2c device at new address
try:
self._device = I2CDevice(self._i2c, new_address)
except ValueError as err:
print("Address Change Failure")
print(err)
return False
# if we made it here, everything went fine
return True
# No i2c begin function is needed since I2Cdevice class takes care of that
# private functions
def _read_register(self, addr):
# Read and return a byte from the specified 8-bit register address.
with self._device as device:
device.write(bytes([addr & 0xFF]))
result = bytearray(1)
device.readinto(result)
# For some reason, write_then_readinto returns invalid data
# device.write_then_readinto(bytes([addr & 0xFF]), result)
if self._debug:
print("$%02X => %s" % (addr, [hex(i) for i in result]))
return result[0]
def _write_register(self, addr, value):
# Write a byte to the specified 8-bit register address
with self._device as device:
device.write(bytes([addr & 0xFF, value & 0xFF]))
if self._debug:
print("$%02X <= 0x%02X" % (addr, value))