I'm trying to write a display for cardinal directions. I've got an interface that runs at a high refresh rate, and a hardware device that only updates a few times a second.
I need to be able to get the latest reported values from the hardware to the display every time the display updates.
My current approach is to try multiprocessing. I have a process that gets the values and updates the object, via shared-state pipes, with those values, and I have a process that takes the values from the object and updates the display.
Unfortunately, it seems to be blocking somewhere, and the display is locked to the hardware update speed.
class AccMag():
# Init manually to ensure access to and for internal functions
def init(self):
# This defines the hardware access
self.i2c = busio.I2C(board.SCL, board.SDA)
self.mag = adafruit_lis2mdl.LIS2MDL(self.i2c)
self.accel = adafruit_lsm303_accel.LSM303_Accel(self.i2c)
# Output values (mag#) and pipes to the other process
self.magx = 0
self.pipex = multiprocessing.Value('d', 0.0)
self.magy = 0
self.pipey = multiprocessing.Value('d', 0.0)
self.magz = 0
self.pipez = multiprocessing.Value('d', 0.0)
# The process, named thread. Yes, my cat is named Rex.
self.thread = multiprocessing.Process(target=self.threadmagdate, args=(self.pipex, self.pipey, self.pipez))
self.thread.start()
# A counter I use to see how often the process has run.
# Comparing this counter to a similar one in the display is how I know it's blocking.
self.tc = 0
# The function that starts the process to update the values
def magdate(self):
# Check the current process is done
self.thread.join(timeout=0)
if self.thread.is_alive() == False:
# Pull out previous values
self.magx = self.pipex.value
self.magy = self.pipey.value
self.magz = self.pipez.value
self.tc += 1
# Start a new process
self.thread = multiprocessing.Process(target=self.threadmagdate, args=(self.pipex, self.pipey, self.pipez))
self.thread.start()
# Get the data from the hardware into the pipe
def threadmagdate(self, magx, magy, magz,):
magx.value = int(self.mag.magnetic[1])
magy.value = int(self.mag.magnetic[0])
magz.value = int(self.mag.magnetic[2])
The display can then call magdate() and grab the values from the mag# values.
I'm missing something obvious, I can smell it, but I've got no idea why it's locking.
Something like this should work with threads:
class AccMag:
# Init manually to ensure access to and for internal functions
def __init__(self):
# This defines the hardware access
self.i2c = busio.I2C(board.SCL, board.SDA)
self.mag = adafruit_lis2mdl.LIS2MDL(self.i2c)
self.accel = adafruit_lsm303_accel.LSM303_Accel(self.i2c)
self.mag_data = (0, 0, 0)
self.update_count = 0
self.thread = threading.Thread(target=self._mag_thread, daemon=True)
self.thread.start()
# Get the data from the hardware into the pipe
def _mag_thread(self):
while True:
mag = tuple(map(int, self.mag.magnetic))
self.mag_data = mag
self.update_count += 1
There's no need to manually call magdata
with this implementation: just grab the x, y, z values from .mag_data
directly.
In your main display application, copy a reference to the mag_data
tuple in a local variable (e.g. cur_data = accmag.mag_data; x = cur_data[0]
) rather than accessing fields directly (e.g. x = accmag.mag_data[0]
) in order to avoid racing with the update thread and seeing partial results.
However, I suspect your blocking issue lies elsewhere, given that the other answer should exhibit zero blocking as well and yet seems to block for you.