Skip to content
On this page

Open Bot Brain

The WARDuino VM has support for specialised hardware that can be used to control Lego Mindstorms components.

Prerequisites

To use WARDuino on this hardware, the Zephyr platform should be used. To get started you will first need to install Zephyr by following the official installation guide.

By default Zephyr will use the STM32CubeProgrammer to flash the board, which you will also need to install.

Cloning WARDuino

To get started with WARDuino for Open Bot Brain you will first need to clone it using the following command:

bash
git clone --recursive git@github.com:TOPLLab/WARDuino.git

Adding your program

Before building WARDuino you will need to specify the program which the VM will execute when starting. To achieve this, you will need to compile your WebAssembly program and place the resulting executable in the platforms/Zephyr directory with the name upload.wasm.

Example

The following program written in AssemblyScript will make one of the built-in LEDs on the board blink.

ts
@external("env", "chip_delay") 
declare function delay(value: i32): void;
@external("env", "chip_pin_mode") 
declare function pinMode(pin: i32, mode: i32): void;
@external("env", "chip_digital_write") 
declare function digitalWrite(pin: i32, value: i32): void;

enum PinVoltage {
    LOW  = 0,
    HIGH = 1,
}

enum PinMode {
  INPUT = 0,
  OUTPUT = 2
}

enum Pin {
  powerSupply = 60,
  led1 = 45,
}

function setup(): void {
    // Configure and enable power supply pin (active low).
    pinMode(Pin.powerSupply, PinMode.OUTPUT);
    delay(500); // Wait for 500ms to avoid current spike.
    digitalWrite(Pin.powerSupply, PinVoltage.LOW);

    // Configure LED.
    pinMode(Pin.led1, PinMode.OUTPUT);
}

export function main(): void {
    setup();

    while (true) {
        digitalWrite(Pin.led1, PinVoltage.HIGH);
        delay(500);
        digitalWrite(Pin.led1, PinVoltage.LOW);
        delay(500);
    }
}

This program can be compiled using the following command if you have the AssemblyScript compiler globally installed on your system:

bash
asc blink.ts -o upload.wasm

With the program compiled and placed in the platforms/Zephyr directory you should now have the following directory structure:

tree
.
├── app.overlay
├── boards
│   ├── ...
├── CMakeLists.txt
├── Kconfig
├── main.cpp
├── prj.conf
└── upload.wasm 

Building

To build WARDuino for Open Bot Brain, run the following commands starting from the root directory of the repository:

bash
cd platforms/Zephyr
source ~/zephyrproject/zephyr/zephyr-env.sh
source ~/zephyrproject/.venv/bin/activate
west build -b stm32l496g_disco

Flashing

To flash WARDuino to the board, the following command can be used. By default this will use the STM32CubeProgrammer. You can also use a different runner by using the --runner flag, more info can be found in the Zephyr documentation.

bash
west flash

Once flashed, you should start seeing one of the LEDs blink.

Board information

The Open Bot Brain has various built-in LEDs and buttons, in the following table we list some of the pin numbers for use in your programs.

DescriptionPin number
Power supply60
Motor AB Driver sleep46
Motor CD Driver sleep78
Led 145
Led 256
Led 339
Button SW121
Button SW520
Button SW210

Lego Mindstorms Primitives

Aside from the built-in LEDs and buttons, Open Bot Brain also has 4 input and 4 output ports that can be used to control Lego Mindstorms components.

Outputs

In terms of outputs we support both the NXT and EV3 (medium and large) motors. These can be controlled using the following primitives:

PrimitiveDescription
drive_motor(port, speed)Makes the motor spin at a constant speed.
drive_motor_ms(port, speed, time)Makes the motor spin for x ms.
drive_motor_degrees(port, speed, angle)Makes the motor rotate x degrees.
stop_motor(port)Actively slows down the motor.

Inputs

In terms of inputs we supports UART based sensors such as the EV3 Color and Gyro sensors. These can be controlled using the following primitives:

PrimitiveDescription
setup_uart_sensor(port, mode)Configure the UART sensor and set its mode.
read_uart_sensor(port)Read the latest value from the UART sensor.

Example usage

To illustrate how some of these primitives could be used we show a simple line follower example program. This robot has two wheels and drives left or right depending on if it sees a dark or a light color with its color sensor. By doing so the robot will follow the edge of a dark line on the floor.

Line follower robot

Since a lot of this code is similar to the blink example, we highlighted the main changes. In particular we now import three extra primitives and disable the sleep mode on the motor drivers. Using this setup a simple line follower can be written in just a few lines of code as shown in the main function.

ts
@external("env", "chip_delay") 
declare function delay(value: i32): void;
@external("env", "chip_pin_mode") 
declare function pinMode(pin: i32, mode: i32): void;
@external("env", "chip_digital_write") 
declare function digitalWrite(pin: i32, value: i32): void;
@external("env", "drive_motor") 
declare function driveMotor(port: i32, speed: i32): void;
@external("env", "setup_uart_sensor")
declare function setupUARTSensor(port: i32, mode: i32): void;
@external("env", "read_uart_sensor")
declare function readUARTSensor(port: i32): i32;

enum PinVoltage {
    LOW  = 0,
    HIGH = 1,
}

enum PinMode {
  INPUT = 0,
  OUTPUT = 2
}

enum Pin {
  powerSupply = 60,
  motorABDriverSleep = 46,
  motorCDDriverSleep = 78,
}

enum InputPort {
  portA = 0,
  portB,
  portC,
  portD
}

enum OutputPort {
  port1 = 0,
  port2,
  port3,
  port4
}

enum ColorSensorMode {
  reflectivity = 0,
  ambient,
  colour
}

function setupBoard(): void {
    // Configure and enable power supply pin (active low).
    pinMode(Pin.powerSupply, PinMode.OUTPUT);
    delay(500); // Wait for 500ms to avoid current spike.
    digitalWrite(Pin.powerSupply, PinVoltage.LOW);

    // Disable sleep on motor AB driver. 
    pinMode(Pin.motorABDriverSleep, PinMode.OUTPUT)
    digitalWrite(Pin.motorABDriverSleep, PinVoltage.HIGH)

    // Disable sleep on motor CD driver.
    pinMode(Pin.motorCDDriverSleep, PinMode.OUTPUT)
    digitalWrite(Pin.motorCDDriverSleep, PinVoltage.HIGH)
}

const threshold = 5;

export function main(): void { 
    setupBoard();

    // Setup color sensor.
    setupUARTSensor(InputPort.portA, ColorSensorMode.reflectivity);

    while (true) {
        if (readUARTSensor(InputPort.portA) < threshold) {
            // Drive right.
            driveMotor(OutputPort.port1, 5000);
            driveMotor(OutputPort.port2, 0);
        }
        else {
            // Drive left.
            driveMotor(OutputPort.port1, 0);
            driveMotor(OutputPort.port2, 5000);
        }
    }
}