Particle threads tutorial

Oh, wow, thanks for the tip. We're doing some real-time sensor-level stuff and so we'll be using SPI a lot.

When you say SPI/UART/I2C require manually protecting them, does that mean that even though they're not thread-safe that deviceOS checks/respects the mutex before accessing them?

Thanks, that's a good starting workaround. Kudos to Particle for having thought through so many use cases.

I would never argue it's wrong to pre-empt at 1KHz, but in that case my experience is that you'll want to exhaustively make sure that all threads spend most of their time waiting for a condition or timer. In our autopilot project we tracked (and published) Heap, "Fast" Heap, IRQ Stack, and CPU load, and would alarm any time a thread reached closed to its assigned stack. That helped gobs in enforcing design intent. To my knowledge, we never had a single in-air reboot of released firmware.

If it already hasn't, Particle might do the same if it intends to ultimately move background tasks away from the main loop (i.e. SYSTEM_THREAD(ENABLED) by default)?

Great paper, we're digesting it now. Thanks!

1 Like

Correct.

Serial and Wire (I2C) implement the lock() and unlock() methods. As long as you wrap your access with calling those, or using WITH_LOCK() which does just that, you can safely use them from multiple threads.

Same for SPI, however you should use beginTransaction() / endTransaction() instead which not only acquires the lock, but also makes sure the bus parameters are set properly. When setting your CS pin, do it inside the beginTransaction() block.

The problem with SPI that was not fixed until 1.5.0-rc.1 was that there were two different mutexes, one in the system and one in the Wiring (user firmware). This obviously did not prevent simultaneous access from system and user code, but this only occurred when using the Ethernet FeatherWing. If your SPI access is only within user firmware, then previous versions are safe when protected by a transaction or lock.

1 Like

@kubark42
I have to say that at a first glance that was exactly what I thought, but a good mix of threads and FSMs is, IMO, the best way to go as @peekay123 already pointed.

I was planning on with threads all the way, but after playing with my Boron and given the capacity restrictions, FSMs are the way to go.

Right now what I'm doing is enabling threads, so I'm not restricted to the DeviceOS loop delay and firing a single additional thread that holds my main FMS.

The secret sauce IMO is to have the all the FSM calls non-block, as I would also have for threads, and use a clock and IRQ's to run the FSM. In this way, you accomplish the modularity of threads without the memory overhead.

Edit: Thanks for sharing this paper @peekay123

1 Like