Close

Muribot API

A project log for Muribot Robotic Educational Platform

Muribot is a low-cost, easy to use, open-source and feature-rich learning tool for exploring programming, robotics, and STEM fields.

crypto-neoCrypto [Neo] 05/08/2015 at 16:550 Comments

So today we were asked "Can you share more details about the embedded software API, and especially how does it deal with complex stuff like concurrency, reentrancy, and non-blocking , while keeping everything simple ?"

Wow this is a nice technical question! When we were writing the API we actually thought a lot about this, the PIC is a single threaded platform by nature, but it's possible to implement a sort of pseudo-multithreading on the architecture very easily. We actually implement a very simple block multithreading model. On the PIC18F hardware we actually have 2 interrupt priority levels, so you can almost think of it as being 3 threads.

The thread 0 is the main loop being clocked from the 48MHz system clock. Thread 1 runs is the low priority interrupt and it runs at 10Hz. A human normally responds to stimuli within 200-250mS, so we wanted an update speed that was similar, as our CEO does a lot of research into AI and autonomous navigation. Thread 2 is the high priority interrupt and runs at 1000Hz. It simply updates a counter we use to track the passage of time (in mS).

Thread 0 - User Thread
Thread 1 - Sensor Thread
Thread 2 - Clocking Thread

The ReadRam and WriteRam commands are the only commands that completely block the execution of the platform , simply because it hogs the I2C bus and we don't want to be writing spurious data. When other commands that need to block execution are issued, they block execution of the user code only. e.g. Move will only block when a distance or delay is given otherwise it returns immediately, and Turn will block until it turns the given angle. When a block occurs thread control is passed to Thread 1, so that the sensor data can be continuously monitored until the set time, distance, or angle has been reached.

The clocking thread doesn't affect code execution in any way, so doesn't block anything.

So lets take the following code for run through!:

if(GetDist(0) > 200) // If we get a value > 200 (about 3") from proximity sensor 0
    Turn(15, 100, 0); // Turn 15 degrees at 100% full speed, with no wheel bias (turn in place)
else
     Move(50, 50); // Move forward at 50% full speed

First GetDist(0), which simply returns the value read from the sensor during the last sensor update

Turn(15, 100, 0) will block, but during that time the sensors thread is still running, so once the robot detects is has turned >= 15 degrees it will stop and return.

Move(50, 50) will return immediately however because it doesn't have a delay or distance specified.

The user code then loops until the robot is turned of our an EndProg() command is issued which would halt execution of the platform. The code above results in a robot that drives forward at 50% speed until something is within 3" of proximity sensor 0 and then rotate until it is out of sight before proceeding (ignoring blind spots in the sensor view).

The following code was actually written by our CEOs 8 year old daughter Sabrina, IR Line Following Demo by Sabrina S., who discovered you can follow lines using nothing but the proximity sensors.

I hope this answers your questions! Let me know if I missed anything or if you have any questions. There are plenty of little tricks to using the API to get around what might seem like a limitation, so if I can expand on any you might see, let me know!

TLDR;

Concurrency - We use a simple block multithreading model where the user thread passes control to the sensor thread.

Reentrancy - The PIC hardware stack is designed with two priority interrupts that perform context saving by default, so reentrancy was automatic.

Non-blocking - Sensor updates are never blocked, and user code is only blocked when you'd be waiting for something to finish anyway.

Discussions