Microsoft MakeCode

How to go about an Interval extension

I wanted to use an interval based execution instead of relying on the forever loop with sleeps. Since I didn’t find a good solution so far from the pxt side of stuff, I looked at the actual uBit and mBed code, and it seems they have a Ticker that allows you to add an interval based execution. However, I wanted to use this from the Blocks interface and started with a pxt extension. However, I am a little bit confused about the way this would have to interact, as I woudl not attach to the message bus, and I would be using mbed directly without using the microbit-dal.

How to go about this?

namespace interval {
    /**
    * Trigger the execution
    */
    //% weight=30 blockId="interval_onInterval" block="Execute codeblock on interval"
    export function OnInterval(interval: number, cb: () => void) {
        onInterval(interval, cb)
    }


    //% shim=interval::onInterval
    function onInterval(interval: number, cb: () => void) {
        return;
    }
}
#include <stdlib.h>
#include "pxt.h"
#include "mbed.h"

using namespace std;

namespace interval {
    Ticker timer;

    //%
    void onInterval(uint8_t interval, void (*fptr)(void)) {
        timer.attach(fptr, interval)
    }
    
} // namespace interval

However this fails on void (*fptr)(void) not being a valid declaration.

Extension interval: interval/main.cpp(12): declaration not understood: void onInterval(uint8_t interval, void (*fptr)(void)) { 

Looking at https://github.com/microsoft/pxt-common-packages/blob/b21afa452bc4c841f865d89587d06b2039d6aeb3/libs/base/docs/reference/control/wait-for-event.md

Wait for Event, but this seems to run a piece of code in parallel with a pause statement, and the caller would also have to be a parallel, like a while(1) loop to accomplish having a callback being triggered after the event ‘raised’.

const myTimer = 6;
const timerTimeout = 1;

control.runInParallel(() => {
    while (true) {
        pause(2000);
        control.raiseEvent(myTimer, timerTimeout);
    }
})

control.runInParallel(() => {
    while (true) {
       control.waitForEvent(myTimer, timerTimeout);
       tick(); // or cb()
   }
}

I would hope something more low-level with a timer interrupt from hardware is possible.

Also, I know arcade has the following:

game.onUpdateInterval(1000, function () {
})

which as far as I remember uses the frame loop to determine if a certain time period has passed before invoking the call… but this is not available on a micro:bit (or maker?)

What precision are you looking for? If 6ms is enough you can just implement on interval with run in parallel, while and pause.

it is not the precision I am looking for, but the use and integration options between lower level code and extensions. For now I’ll try to go the route of using the aforementioned option. However, am I even allowed to use the control namespace within an extensions, and if so… how? Somehow the layering is not clear to me yet and would like to know what is available to me. I am looking at https://makecode.microbit.org/reference and only basic, serial, pins, etc are available to me.

Correction: control IS available, but inBackground is exposed instead of runInParallel, and onEvent.

Test version is up: https://github.com/gbraad/pxt-interval

Example with a metronome:

Have you considered using https://github.com/microsoft/pxt-common-packages/blob/f460cc8ac025fc93ac82cd9d5dcb61b47e3a2dfb/libs/base/scheduling.ts#L7 ?

Nope, not sure how to use this (the function you refer to is not exported).

Note: as mentioned in the first message, on Acrade you can use:

game.onUpdateInterval(1000, function () {
})

to achieve what I want… but this is not exposed/available on Micro:bit

It doesn’t seem available in microbit.

As I figured as it all referred to game logic (I had seen these functions before, just unsure if they were able to be used… and therefore the extension).

Just wondering: why is there a difference between the Microbit and Arcade target for the run in parallel function?

Micro:bit

control.inBackground

Arcade

control.runInParallel