Microsoft MakeCode

Get input for a function in a custom block

Hi,
I have a function inside a custom block and I need to get two inputs for it,

  1. a buffer
  2. a number from an enum

Right now the way I am trying to get an input is
//%block=“Send buffer $Buf to click $BoardNum” …(This is what I want displayed while accepting inputs as ‘Buf’ and ‘BoardNum’

sendBuffer( Buf: Buffer,BoardNum: BoardID){

The issue that I face with this is that I get an error on the webpage while building the block as "property 'sendBuffer does not exist on type buffer.

If I change the $ to %
//%block=“Send buffer %Buf to click %BoardNum”

I get the error that only one input is given altough two were expected.

Can someone please explain how I could get inputs for functions inside a class from the user in custom blocks?

You may want to try testing it out and exploring the examples on the playground: https://makecode.com/playground

That being said, I think the below works. Note that I’ve put the enums in the namespace. I think this may cause problems with some of the “block rendering” tools for documentation. Many other MakeCode examples (including on the playground) have the enums outside the namespace. If anyone from the MakeCode team could weigh in on best-practices, it’d be appreciated!

//% color="#AA278D"
namespace basic {
    export class Buffer {

    }
    export enum BoardId {
        One,
        Two,
        Three,
        Four
    }

    //% block="Send buffer $buf to click $BoardNum"
    export function sendBuffer(buf: Buffer, choice: BoardId) {

    }

}

Can you paste your entire block definition? If this is a method on a class, you need to also include the this parameter in the block definition. You can do that like this:

//% block="$this send buffer $Buf to click $BoardNum"
//% this.shadow=variables_get
//% this.defl=someName

The shadow and defl comments in this case tell MakeCode to make the this parameter a variable with the name “someName”.

You shouldn’t use % when defining blocks, it’s the old (deprecated) way of listing parameters. $ is the new way going forward. We have a lot of blocks inside of makecode that are still defined that way but it’s just because we can’t update them (it would break our existing translations)

Can you comment on enums for parameters? Should they be in the namespace and exported or outside the namespace?

It seems like keeping them in the namespace is more in keeping with the intent of using namespaces.

Hi,
Thanks for the reply
This is the class under the namespace (there are multiple classes)

///START of SPISettings
export class SPIsetting{
// SPI Function Ids
protected SPI_WRITE_id : number
protected SPI_READ_id : number
protected SPI_CONFIG_id : number
protected SPI_WRITEBULK_id : number
protected SPI_WRITEBULK_CS_id : number
protected SPI_READBULK_CS_id : number
protected SPI_BAUD_id : number
protected SPI_CONFIG_CS_id : number

    constructor(){
    this.SPI_WRITE_id = 1
    this.SPI_READ_id = 2
    this.SPI_CONFIG_id = 3
    this.SPI_WRITEBULK_id = 4
    this.SPI_WRITEBULK_CS_id = 5
    this. SPI_READBULK_CS_id = 6
    this.SPI_BAUD_id = 7
    this.SPI_CONFIG_CS_id = 8

    }

//%blockId=send_UART_Buffer
//%block=“Send buffer %Buf to click %BoardNum”
//% blockGap=7
//% weight=90 color=#9E4894 icon=“”
//% advanced=false

  sendBuffer( Buf: Buffer,BoardNum?: BoardID){



        let buffLength = Buf.length+4;


        let UARTBuf = pins.createBuffer(buffLength);

        

                UARTBuf.setNumber(NumberFormat.UInt8LE, 0, RX_TX_Settings.BBOARD_COMMAND_WRITE_RX_BUFFER_DATA)
                UARTBuf.setNumber(NumberFormat.UInt8LE, 1, clickBoardNum)
                UARTBuf.setNumber(NumberFormat.UInt8LE, 2, UART_module_id)
                UARTBuf.setNumber(NumberFormat.UInt8LE, 3, 5)

        

                for(let i=0; i<buffLength-4;i++){
        
                    UARTBuf.setNumber(NumberFormat.UInt8LE, i+4, Buf.getNumber(NumberFormat.UInt8LE,i));
                
            
                }
            
                pins.i2cWriteBuffer(BBOARD_I2C_ADDRESS, CLEAR_BBOARD_RX_BUFFER, false)
                pins.i2cWriteBuffer(BBOARD_I2C_ADDRESS, UARTBuf, false)
                pins.i2cWriteBuffer(BBOARD_I2C_ADDRESS, EXECUTE_BBOARD_COMMAND, false)
            }

}

Right now,
Even if I keep the function outside the class and use export function sendBuffer, I get the error but it may be becuase buffer is not being recognized by makecode.
I have some other functions where a string/ number has to be paased, I put them outside the class and used export function, but for cases where I am using them or enums,
I’m now getting an error
cannot read property ‘vtable’ of undefined.

Can I pm you for some help if that is possible?, I needed to share the entire file and also to learn how to use classes in makecode.

Thanks

It’s better to keep this discussion in the thread so that others can find it.

Yep, you need to add the $this to your definition. The “cannot read vtable of undefined” likely means that you are passing a variable that hasn’t been assigned. That’s the makecode equivalent of a null pointer exception.

For enums, we typically put them outside a namespace to make the code less verbose. It’s really up to the extension author though!

Great! Thanks — I’m using some common names for enums, so I’ll keep them in my namespace to avoid collisions as long as it doesn’t matter otherwise.

export class I2CSettings{

        // I2C Function Ids
        protected I2C_WRITE_id : number
        protected I2C_READ_id : number
        protected I2C_WRITE_NO_MEM_id : number
        protected I2C_READ_NO_MEM_id : number

        constructor(){
            this.I2C_WRITE_id = 1
            this.I2C_READ_id = 2
            this.I2C_WRITE_NO_MEM_id = 3
            this.I2C_READ_NO_MEM_id = 4

        }

            //%blockId=send_UART_String
    //%block="$this Send string $UARTString to click$clickBoardNum"
    //% this.shadow=variables_get
	//% this.defl=someName
    //% blockGap=7
    //% weight=90   color=#9E4894 icon=""
    //% advanced=false

    sendString( UARTString: string,clickBoardNum: clickBoardID){
        let remainingBytes = UARTString.length
        
        while( remainingBytes )
        {
            let messageLength = Math.min(remainingBytes+ 4,128);
            let UARTBuf = pins.createBuffer(messageLength);

            UARTBuf.setNumber(NumberFormat.UInt8LE, 0, RX_TX_Settings.BBOARD_COMMAND_WRITE_RX_BUFFER_DATA)
            UARTBuf.setNumber(NumberFormat.UInt8LE, 1, clickBoardNum)
            UARTBuf.setNumber(NumberFormat.UInt8LE, 2, UART_module_id)
            UARTBuf.setNumber(NumberFormat.UInt8LE, 3,  5)

            for(let i=4; i<messageLength;i++)
            {
                    UARTBuf.setNumber(NumberFormat.UInt8LE, i, UARTString.charCodeAt(UARTString.length - remainingBytes + i - 4));
            }

            // Send a message to the UART TX Line to ask for data
            pins.i2cWriteBuffer(BBOARD_I2C_ADDRESS, CLEAR_BBOARD_RX_BUFFER, false)
            pins.i2cWriteBuffer(BBOARD_I2C_ADDRESS, UARTBuf, false)
            pins.i2cWriteBuffer(BBOARD_I2C_ADDRESS, EXECUTE_BBOARD_COMMAND, false)
            remainingBytes =remainingBytes - messageLength + 4;
        }

    }


  
   
}

I tried using it this way, but I’m getting 'failed cast on null.
I’m pretty sure I did something wrong but I can’t point it out. Also, do you know how I could use the buffer type. And is there any reference or link for classes in makecode for custom blocks? , I could look up classes in typescript but they don’t work the same.

That looks right! Usually that means that in the program where you’re using this class, you haven’t assigned the variable that’s being passed into this

Usually we have a function outside the class that’s called create____.

like this:

namespace whatever {
    //% block="create i2c settings"
    //% blockSetVariable=someName
    export function createI2cSettings(): I2CSettings {
         return new I2CSettings();
    }
}

The blockSetVariable annotation will make it so that this block is assigned to a variable in the toolbox.

Hi I still get cast errors when using functions from inside the class. I have to keep functions outside the class and create a object using
let i2cobj=new I2CSettings(); and then use these functions.
I know I’m doing something wrong, but can’t igure it out.

This is one example
//**

//* Provides access to basic micro:bit functionality.

//*/

//% color=#1E90FF weight=116 icon="\uf00a"

//% advanced=true

namespace Force_Click{

export class Force{

   A : number;

   sumA : number;

   Force_voltage : number;

   Force_val : number;

   rangefactor : number;

   Vadc_3 : number;       



   constructor(){

   this.A=0;

   this.sumA=0;

   this.Force_voltage=0;

   this.Force_val=0;

   this.rangefactor=20/3.3

   this.Vadc_3=3.3/4096; 

   }

    //%blockId=force

     //%block="For $this Get value of force on click$clickBoardNum"

     //% blockGap=7

    //% this.shadow=variables_get

    //% this.defl=forceClick

     //% weight=90   color=#9E4894 icon=""

     //% advanced=false



     forceclick(clickBoardNum: clickBoardID) : number{

        for (let i=1;i<=20;i++)

        {

            this.A=(bBoard.analogRead(clickADCPin.AN,clickBoardNum))

            this.sumA+=force.A;

        }

        this.sumA=force.sumA/20;

        this.Force_voltage=this.sumA*this.Vadc_3; //Voltage for the force click board

        this.Force_val=this.Force_voltage*this.rangefactor;

        return this.Force_val

       }

}

export function createForce(): Force {

    return new Force();

}

   let force=new Force();



  //%blockId=forceS

     //%block="Get string value of force on click$clickBoardNum"

     //% blockGap=7

     //% weight=100   color=#9E4894 icon=""

     //% advanced=false



     export function forceclickstring(clickBoardNum: clickBoardID) : string{

        let valueForce= force.forceclick(clickBoardNum);

        let stringForce= valueForce.toString();

        return stringForce;

       }

}

I have tried one function from inside the class and one from outside, if i put the function inside the class, I get cast errors

Hi,
I want to have all my functions in a class for each main block. Right now what I do is,

namespace ABC{
//% block=“create lcd settings”
//% blockSetVariable=“ABCObjSettings”
//% weight=110
export function createABCObjSettings(): ABCObjSettings{
return new ABCObjSettings();
}
export class ABC{



//% block="$this example $variablePassed
//% blockId=Example
//% blockNamespace=ABC
//% this.shadow=variables_get
//% this.defl=“ABCObjSettings”
//% weight=90
exampleExposedFunction(variablePassed : number) {

sum=this.variablePassed

}

}

}

In the blocks on the webpage, I have to first place the “set ABCObjSettings to create lcd settings”, then select my exposed function.
I have to place the “set ABCObjSettings to create lcd settings” at least once to use any exposed

Is there a way to automatically create that Object and use?

Thanks!