MX340 CircuitPython Refinements: Async Event But No Property Setter?

I’ve been writing CircuitPython code to interface with the NEC K13988 chip on board a salvaged Canon MX340 multi-function inkjet control panel. At first I only intended to explore if it was even possible to repurpose the panel. Now that I’m confident it is possible, I decided my follow-up would be to factor my code to extract a reusable library that could potentially support multiple projects. As expected, the task of making the code neater taught some valuable lessons.

Asynchronous Event

I had a few asynchronous tasks declared and running, each handling one aspect of K13988 interface, but sometimes they stepped on each other’s work. I learned about Lock() earlier but I had additional synchronization needs that didn’t fit a Lock(). Some pieces of code needed to wait for certain other startup conditions to be met before it ran. For example, I needed to wait for the K13988 to wake up enough to send data bytes before I could successfully transmit commands to it.

In the constructor, I set a boolean:

self.in_startup = True

And in my data transmission code, I check that boolean value in a loop:

while self.in_startup:
    await asyncio.sleep(0)

That would put data transmission on hold until I receive my first byte from K13988, at which point I could flip that boolean value:

# First successful read complete, exit startup mode
self.in_startup = False

This worked, and I guess it isn’t terrible. But there was a better way. When I read through Python documentation during my study session, I discovered this pattern fit Event from the asyncio library.

Using events, I declare one in the constructor:

self.transmit_startup = asyncio.Event()

And in my data transmission code, I will wait for this event before proceeding:

await self.transmit_startup.wait()

Permission to proceed will happen once I receive my first byte from K13988:

# First successful read complete, exit startup mode
self.transmit_startup.set()

This was a simple case, and I’m glad it helped me learn to add to my toolbox so I am ready to tackle more complicated scenarios in the future.

No Async Property Setter?

This control panel has four LEDs. Two are wired directly to main logic board connector pins, two are controlled by K13988. I thought I would add Python property setters for those two LEDs, following precedence of setting a CircuitPython digitalio output pin value. But it seems like I can’t expose a property setter to turn a LED on like this:

k13988.in_use_led = True

The problem is that such LED update would require sending two command bytes to K13988, and that is an asynchronous operation which meant I have to somehow declare async on the property setter and somehow use await in the caller. If there’s a Python syntax to declare such a thing, I couldn’t find it. For now the best I can do is to implement an explicit method to set LED:

await k13988.in_use_led(True)

Not terrible, just not as straightforward. I can live with this while I learn more asynchronous Python and maybe I will learn something that’ll let me come back to fix this later. In the meantime I have more cleanup work to do.

Leave a comment