Source code for joystick_reader
"""
Interfaces with the mpu9250_i2c.py script. :py:class:`JoystickReader` retrieves values from that script, and stores them in :py:class:`Axis` objects.
The `Axis` objects average values as they come in based on their parameters.
When the Blender addon needs readings, `JoystickReader` collects the averages from each `Axis` and returns them as a dictionary.
"""
import random # just for testing, gen random vals
try:
import RPi.GPIO as GPIO
from . import mpu9250_i2c
error_message = False
except ImportError:
error_message = True
[docs]class Axis:
"""
Stores rolling average of recorded values for an axis.
:param name: name of the axis, e.g. 'ax','wy',etc.
:type name: str
:param keep_vals: number of recently read values to keep for averaging.
:type keep_vals: int
:param weight: factor to weigh last-read value
:type weight: float
"""
def __init__(self, name, keep_vals=10, weight=0):
self.name = name
"""
name of the axis
"""
self.vals = []
"""
last few values recorded
"""
self.average = 0.0
"""
current average
"""
self.keep_vals = keep_vals
"""
number of values to keep
"""
self.weight=weight
"""
weights the last reading
"""
[docs] def add_val(self, val):
"""
Append value to recently-read values and update average based on weighting.
:param val: value to add
:type val: number
"""
self.vals.append(val)
while len(self.vals) > self.keep_vals:
self.vals.pop(0)
total = 0
for each in self.vals:
total += each
self.average = (total + val*self.weight)/ (len(self.vals)+self.weight)
# print(f"{val} added to {self.name}, new avg: {self.average}")
[docs] def get_last_val(self):
"""
Get the last read value of this axis.
:returns: last value
:rtype: number
"""
return self.vals[len(self.vals)-1]
def zero(self):
self.vals = []
self.average = 0
[docs]class JoystickReader:
"""
Interfaces with sensor and updates axis objects with readings.
:param trigger_pin: The raspberry Pi GPIO pin that the trigger button is wired to.
:type trigger_pin: int
:param top_pin: The raspberry Pi GPIO pin that the top button is wired to.
:type top_pin: int
:param keep_vals: The number of sensor readings each axes will store for averaging.
:type keep_vals: int
:param weight: The factor by which each axis will weight the last-read sensor value.
:type weight: float
"""
def __init__(self, trigger_pin, top_pin, keepvals=10, weight=0):
"""
Interfaces with sensor and updates axis objects with readings.
:param trigger_pin: The raspberry Pi GPIO pin that the trigger button is wired to.
:type trigger_pin: int
:param top_pin: The raspberry Pi GPIO pin that the top button is wired to.
:type top_pin: int
:param keep_vals: The number of sensor readings each axes will store for averaging.
:type keep_vals: int
:param weight: The factor by which each axis will weight the last-read sensor value.
:type weight: float
"""
self.trigger_pin = trigger_pin
self.top_pin = top_pin
# GPIO.setmode(GPIO.BCM)
# GPIO.setup( [self.trigger_pin, self.top_pin], GPIO.IN, pull_up_down = GPIO.PUD_UP)
self.acceleration_x = Axis('acceleration x', keep_vals=keepvals, weight=weight)
self.acceleration_y = Axis('acceleration y', keep_vals=keepvals, weight=weight)
self.acceleration_z = Axis('acceleration z', keep_vals=keepvals, weight=weight)
self.gyro_x = Axis('gyro x', keep_vals=keepvals, weight=weight)
self.gyro_y = Axis('gyro y', keep_vals=keepvals, weight=weight)
self.gyro_z = Axis('gyro z', keep_vals=keepvals, weight=weight)
self.axes = []
"""
A list of the used axes.
Includes self.acceleration_x, self.acceleration_y, self.acceleration_z, self.gyro_x, self.gyro_y, and self.gyro_z
:ref: `joystick_reader.Axis`
:type: Axis
"""
self.axes.append(self.acceleration_x)
self.axes.append(self.acceleration_y)
self.axes.append(self.acceleration_z)
self.axes.append(self.gyro_x)
self.axes.append(self.gyro_y)
self.axes.append(self.gyro_z)
[docs] def load_pins(self, trigger_pin, top_pin):
"""
Change the RPi.GPIO pins for the top and trigger line.
Turns off input detection for previously active pins.
:param trigger_pin: The raspberry Pi GPIO pin that the trigger button is wired to.
:type trigger_pin: int
:param top_pin: The raspberry Pi GPIO pin that the top button is wired to.
:type top_pin: int
"""
GPIO.setup([self.trigger_pin, self.top_pin], GPIO.IN, pull_up_down = GPIO.PUD_OFF)
self.trigger_pin = trigger_pin
self.top_pin = top_pin
GPIO.setup( [self.trigger_pin, self.top_pin], GPIO.IN, pull_up_down = GPIO.PUD_UP)
[docs] def load_keep_vals_and_weight(self, new_keep_vals, new_weight):
"""
Changes retained values length and last value weighting for all axes.
:param keep_vals: The number of sensor readings each axes will store for averaging.
:type keep_vals: int
:param weight: The factor by which each axis will weight the last-read sensor value.
:type weight: float
"""
for ax in self.axes:
ax.weight = new_weight
ax.keep_vals = new_keep_vals
[docs] def end(self):
"""
Turns off detection for currently active RPi.GPIO pins.
"""
GPIO.setup([self.trigger_pin, self.top_pin], GPIO.IN, pull_up_down = GPIO.PUD_OFF)
[docs] def get_round_of_random_values(self): # for testing without RPi.GPIO
"""
Adds a value between -5 and 5 to each Axis.
Used for debugging Blender addon outside of Raspberry Pi environment.
"""
rand_min = -5
rand_max = 5
ax = random.randrange(rand_min,rand_max)/100
ay = random.randrange(rand_min,rand_max)/100
az = random.randrange(rand_min,rand_max)/100
wx = random.randrange(rand_min,rand_max)/100
wy = random.randrange(rand_min,rand_max)/100
wz = random.randrange(rand_min,rand_max)/100
self.acceleration_x.add_val(ax)
self.acceleration_y.add_val(ay)
self.acceleration_z.add_val(az)
self.gyro_x.add_val(wx)
self.gyro_y.add_val(wy)
self.gyro_z.add_val(wz)
[docs] def zero_gyro(self):
"""
Zeros gyroscopes.
"""
self.gyro_x.zero()
self.gyro_y.zero()
self.gyro_z.zero()
[docs] def get_vals_from_sensor(self):
"""
Gets values from :py:mod:`mpu9250_i2c.py` , which interfaces with the sensor.
Passes sensor reads to appropriate Axis object.
If sensor fails to read, sets global error flag.
"""
global error_message
try:
ax, ay, az, wx, wy, wz = mpu9250_i2c.mpu6050_conv()
# print(wx,wy,wz)
# self.acceleration_x.add_val(ax)
# self.acceleration_y.add_val(ay) + 0.98 # grav offset
# self.acceleration_z.add_val(az)
self.gyro_x.add_val(wx)
self.gyro_y.add_val(wy)
self.gyro_z.add_val(wz)
error_message = False
except ( NameError, ModuleNotFoundError ):
error_message = True
[docs] def get_averages(self):
"""
Returns average readings for each Axis object as a dictionary.
:returns: Axis averages and GPIO pin reads.
:rtype: dict
"""
return {
'ax':self.acceleration_x.average,
'ay':self.acceleration_y.average,
'az':self.acceleration_z.average,
'wx':self.gyro_x.average,
'wy':self.gyro_y.average,
'wz':self.gyro_z.average,
'top': GPIO.input(self.top_pin),
'trigger': GPIO.input(self.trigger_pin)
}
[docs] def get_last(self):
"""
Returns last readings for each Axis object as a dictionary.
:returns: Axis last-reads and GPIO pin reads.
:rtype: dict
"""
return {
'ax':self.acceleration_x.get_last_val(),
'ay':self.acceleration_y.get_last_val(),
'az':self.acceleration_z.get_last_val(),
'wx':self.gyro_x.get_last_val(),
'wy':self.gyro_y.get_last_val(),
'wz':self.gyro_z.get_last_val(),
'top': GPIO.input(self.top_pin),
'trigger': GPIO.input(self.trigger_pin)
}