Compare commits

...

15 Commits

Author SHA1 Message Date
83793fc668 strings 2018-01-09 12:39:56 -08:00
47ee87fe72 adding logging 2018-01-09 09:03:22 -08:00
374fa36548 cholesky 2018-01-08 23:15:43 -08:00
358ef7e19f started on matrix library 2018-01-08 23:05:09 -08:00
9cbf5efd7e 0.0.57 2018-01-08 22:01:11 -08:00
a27a7fcd55 removing measured overloads until blocks support (#215) 2018-01-08 15:27:37 -08:00
85263fb84d Merge branch 'master' of https://github.com/Microsoft/pxt-ev3 2018-01-08 15:13:20 -08:00
b5ad898c9e Screentweak (#214)
* adding pid

* hiding functions

* precision of value
2018-01-08 15:00:00 -08:00
456df3c442 Merge branch 'master' of https://github.com/microsoft/pxt-ev3 2018-01-08 14:43:19 -08:00
1552eb05b4 matter most info 2018-01-08 14:43:15 -08:00
d60e2c4a7d adding pid 2018-01-08 14:36:34 -08:00
f4b78c3ee7 PID example 2018-01-08 14:06:39 -08:00
dd5e1957d5 fix coding page 2018-01-08 14:06:29 -08:00
d61a63f70a avoid overlaps in port view 2018-01-08 09:08:59 -08:00
4207bd06c0 removing microbit font 2018-01-08 08:53:03 -08:00
16 changed files with 318 additions and 123 deletions

View File

@ -4,7 +4,8 @@
This repo contains the editor target hosted at https://d541eec2-1e96-4b7b-a223-da9d01d0337a.pxt.io/
Issue tracker: https://src.education.lego.com/groups/ev3-makecode
LEGO Auth: https://src.education.lego.com/groups/ev3-makecode (use Google Authenticator)
LEGO Chat: https://chat.internal.education.lego.com/make-code/channels/town-square
## Local Dev setup

View File

@ -167,4 +167,5 @@
"description": "Activity 2",
"url":"/coding/roaming-2",
"cardType": "example"
}, {
}]
```

View File

@ -0,0 +1,50 @@
# Gradien follower PID + calibration
```blocks
let lasterror = 0
let D = 0
let I = 0
let P = 0
let error = 0
let setpoint = 0
let max = 0
let min = 0
let v = 0
v = sensors.color3.light(LightIntensityMode.Reflected)
min = v
max = v
setpoint = v
while (!(brick.buttonEnter.wasPressed())) {
brick.clearScreen()
brick.showString("Move robot on terrain", 1)
brick.showString("Press ENTER when done", 2)
v = sensors.color3.light(LightIntensityMode.Reflected)
min = Math.min(min, v)
max = Math.max(max, v)
setpoint = (max + min) / 2
brick.showValue("v", v, 3)
brick.showValue("min", min, 4)
brick.showValue("max", v, 5)
brick.showValue("setpoint", setpoint, 6)
loops.pause(100)
}
loops.forever(function () {
brick.clearScreen()
v = sensors.color3.light(LightIntensityMode.Reflected)
brick.showValue("light", v, 1)
error = v - setpoint
brick.showValue("error", error, 2)
P = error * 5
brick.showValue("P", P, 3)
I = I + error * 0.01
brick.showValue("I", I, 4)
D = (error - lasterror) * 0.2
brick.showValue("D", D, 5)
motors.largeBC.steer(P + (I + D), 100)
lasterror = error
if (brick.buttonEnter.wasPressed()) {
motors.largeBC.setSpeed(0)
brick.buttonDown.pauseUntil(ButtonEvent.Click)
}
})
```

View File

@ -71,29 +71,21 @@
"motors.MotorBase.setBrake": "Sets the automatic brake on or off when the motor is off",
"motors.MotorBase.setBrake|param|brake": "a value indicating if the motor should break when off",
"motors.MotorBase.setReversed": "Reverses the motor polarity",
"motors.MotorBase.setSpeed": "Sets the speed of the motor.",
"motors.MotorBase.setSpeedFor": "Sets the motor speed for limited time or distance",
"motors.MotorBase.setSpeedFor|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.MotorBase.setSpeedFor|param|unit": "the meaning of the value",
"motors.MotorBase.setSpeedFor|param|value": "the move quantity, eg: 2",
"motors.MotorBase.setSpeed": "Sets the motor speed for limited time or distance",
"motors.MotorBase.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.MotorBase.setSpeed|param|unit": "(optional) unit of the value",
"motors.MotorBase.setSpeed|param|value": "(optional) measured distance or rotation",
"motors.MotorBase.stop": "Stops the motor(s).",
"motors.SynchedMotorPair.steer": "Turns the motor and the follower motor by a number of rotations",
"motors.SynchedMotorPair.steerFor": "Turns the motor and the follower motor by a number of rotations",
"motors.SynchedMotorPair.steerFor|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.SynchedMotorPair.steerFor|param|turnRatio": "the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0",
"motors.SynchedMotorPair.steerFor|param|unit": "the meaning of the value",
"motors.SynchedMotorPair.steerFor|param|value": "the move quantity, eg: 2",
"motors.SynchedMotorPair.steer|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.SynchedMotorPair.steer|param|turnRatio": "the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0",
"motors.SynchedMotorPair.steer|param|unit": "(optional) unit of the value",
"motors.SynchedMotorPair.steer|param|value": "(optional) move duration or rotation",
"motors.SynchedMotorPair.tank": "The Move Tank block can make a robot drive forward, backward, turn, or stop. \nUse the Move Tank block for robot vehicles that have two Large Motors, \nwith one motor driving the left side of the vehicle and the other the right side. \nYou can make the two motors go at different speeds or in different directions \nto make your robot turn.",
"motors.SynchedMotorPair.tankFor": "The Move Tank block can make a robot drive forward, backward, turn, or stop. \nUse the Move Tank block for robot vehicles that have two Large Motors, \nwith one motor driving the left side of the vehicle and the other the right side. \nYou can make the two motors go at different speeds or in different directions \nto make your robot turn.",
"motors.SynchedMotorPair.tankFor|param|speedLeft": "the speed on the left motor, eg: 50",
"motors.SynchedMotorPair.tankFor|param|speedRight": "the speed on the right motor, eg: 50",
"motors.SynchedMotorPair.tankFor|param|unit": "the unit of the value",
"motors.SynchedMotorPair.tankFor|param|value": "the amount of movement, eg: 2",
"motors.SynchedMotorPair.tank|param|speedLeft": "the speed on the left motor, eg: 50",
"motors.SynchedMotorPair.tank|param|speedRight": "the speed on the right motor, eg: 50",
"motors.SynchedMotorPair.tank|param|unit": "(optional) unit of the value",
"motors.SynchedMotorPair.tank|param|value": "(optional) move duration or rotation",
"motors.SynchedMotorPair.toString": "Returns the name(s) of the motor",
"motors.mkCmd": "Allocates a message buffer",
"motors.mkCmd|param|addSize": "required additional bytes",

View File

@ -57,11 +57,8 @@
"motors.MotorBase.pauseUntilReady|block": "%motor|pause until ready",
"motors.MotorBase.setBrake|block": "set %motor|brake %brake",
"motors.MotorBase.setReversed|block": "set %motor|reversed %reversed",
"motors.MotorBase.setSpeedFor|block": "set %motor|speed to %speed=motorSpeedPicker|%|for %value|%unit",
"motors.MotorBase.setSpeed|block": "set %motor|speed to %speed=motorSpeedPicker|%",
"motors.SynchedMotorPair.steerFor|block": "steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%|for %value|%unit",
"motors.SynchedMotorPair.steer|block": "steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%",
"motors.SynchedMotorPair.tankFor|block": "tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%|for %value|%unit",
"motors.SynchedMotorPair.tank|block": "tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%",
"motors.largeAB|block": "large A+B",
"motors.largeAD|block": "large A+D",
@ -94,7 +91,6 @@
"{id:group}Buttons": "Buttons",
"{id:group}Counters": "Counters",
"{id:group}Light": "Light",
"{id:group}Measured Move": "Measured Move",
"{id:group}More": "More",
"{id:group}Move": "Move",
"{id:group}Screen": "Screen",

View File

@ -96,7 +96,7 @@ namespace motors {
return b
}
function outputToName(out: Output): string {
export function outputToName(out: Output): string {
let r = "";
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (out & (1 << i)) {
@ -202,33 +202,16 @@ namespace motors {
reset(this._port);
}
/**
* Sets the speed of the motor.
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
*/
//% blockId=motorSetSpeed block="set %motor|speed to %speed=motorSpeedPicker|%"
//% on.fieldEditor=toggleonoff
//% weight=99 blockGap=8
//% group="Move"
setSpeed(speed: number) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) // always stop
this.stop();
else
this._setSpeed(speed);
}
/**
* Sets the motor speed for limited time or distance
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
* @param value (optional) measured distance or rotation
* @param unit (optional) unit of the value
*/
//% blockId=motorMove block="set %motor|speed to %speed=motorSpeedPicker|%|for %value|%unit"
//% weight=98 blockGap=8
//% group="Measured Move"
setSpeedFor(speed: number, value: number, unit: MoveUnit) {
//% blockId=motorSetSpeed block="set %motor|speed to %speed=motorSpeedPicker|%"
//% weight=100 blockGap=8
//% group="Move"
setSpeed(speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
@ -455,7 +438,6 @@ namespace motors {
});
}
/**
* The Move Tank block can make a robot drive forward, backward, turn, or stop.
* Use the Move Tank block for robot vehicles that have two Large Motors,
@ -464,30 +446,14 @@ namespace motors {
* to make your robot turn.
* @param speedLeft the speed on the left motor, eg: 50
* @param speedRight the speed on the right motor, eg: 50
* @param value (optional) move duration or rotation
* @param unit (optional) unit of the value
*/
//% blockId=motorPairTank block="tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%"
//% weight=96 blockGap=8
//% group="Move"
tank(speedLeft: number, speedRight: number) {
this.tankFor(speedLeft, speedRight, 0, MoveUnit.Degrees);
}
/**
* The Move Tank block can make a robot drive forward, backward, turn, or stop.
* Use the Move Tank block for robot vehicles that have two Large Motors,
* with one motor driving the left side of the vehicle and the other the right side.
* You can make the two motors go at different speeds or in different directions
* to make your robot turn.
* @param speedLeft the speed on the left motor, eg: 50
* @param speedRight the speed on the right motor, eg: 50
* @param value the amount of movement, eg: 2
* @param unit the unit of the value
*/
//% blockId=motorPairTankFor block="tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%|for %value|%unit"
//% weight=19 blockGap=8
//% inlineInputMode=inline
//% group="Measured Move"
tankFor(speedLeft: number, speedRight: number, value: number, unit: MoveUnit) {
//% group="Move"
tank(speedLeft: number, speedRight: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speedLeft = Math.clamp(-100, 100, speedLeft >> 0);
@ -498,39 +464,22 @@ namespace motors {
? (100 - speedRight / speedLeft * 100)
: (speedLeft / speedRight * 100 - 100);
this.steerFor(turnRatio, speed, value, unit);
this.steer(turnRatio, speed, value, unit);
}
/**
* Turns the motor and the follower motor by a number of rotations
* @param turnRatio the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
* @param value (optional) move duration or rotation
* @param unit (optional) unit of the value
*/
//% blockId=motorPairSteer block="steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%"
//% weight=95
//% turnRatio.min=-200 turnRatio=200
//% inlineInputMode=inline
//% group="Move"
steer(turnRatio: number, speed: number) {
this.steerFor(turnRatio, speed, 0, MoveUnit.Rotations);
}
/**
* Turns the motor and the follower motor by a number of rotations
* @param turnRatio the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
*/
//% blockId=motorPairSteerFor block="steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%|for %value|%unit"
//% weight=6
//% turnRatio.min=-200 turnRatio=200
//% inlineInputMode=inline
//% group="Measured Move"
steerFor(turnRatio: number, speed: number, value: number, unit: MoveUnit) {
steer(turnRatio: number, speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {

View File

@ -58,29 +58,7 @@ namespace brick {
}
}
export function microbitFont() {
return {
charWidth: 6,
charHeight: 5,
firstChar: 32,
// source https://github.com/lancaster-university/microbit-dal/blob/master/source/core/MicroBitFont.cpp
data: hex`
0000000000 0202020002 0a0a000000 0a1f0a1f0a 0e130e190e 1309041219 0609060916 0202000000 0402020204
0204040402 000a040a00 00040e0400 0000000402 00000e0000 0000000200 1008040201 0609090906 040604040e
070806010f 0f08040906 0c0a091f08 1f010f100f 08040e110e 1f08040201 0e110e110e 0e110e0402 0002000200
0004000402 0804020408 000e000e00 0204080402 0e110c0004 0e11151906 06090f0909 0709070907 0e0101010e
0709090907 0f0107010f 0f01070101 0e0119110e 09090f0909 0702020207 1f08080906 0905030509 010101010f
111b151111 1113151911 0609090906 0709070101 060909060c 0709070911 0e01060807 1f04040404 0909090906
1111110a04 1111151b11 0909060909 110a040404 0f0402010f 0e0202020e 0102040810 0e0808080e 040a000000
000000001f 0204000000 000e09091e 0101070907 000e01010e 08080e090e 060907010e 0c02070202 0e090e0806
0101070909 0200020202 0800080806 0105030509 020202020c 001b151111 0007090909 0006090906 0007090701
000e090e08 000e010101 000c020403 02020e021c 000909091e 0011110a04 001111151b 0009060609 00110a0403
000f04020f 0c0406040c 0202020202 0302060203 0000061800
`
}
}
export function setPixel(on: boolean, x: number, y: number) {
function setPixel(on: boolean, x: number, y: number) {
x |= 0
y |= 0
if (0 <= x && x < DAL.LCD_WIDTH && 0 <= y && y < DAL.LCD_HEIGHT)
@ -123,6 +101,7 @@ namespace brick {
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
//% line.min=1 line.max=10
export function showValue(name: string, value: number, line: number) {
value = Math.round(value * 1000) / 1000;
showString((name ? name + ": " : "") + value, line);
}
@ -162,12 +141,9 @@ namespace brick {
*/
//% blockId=screen_show_image block="show image %image=screen_image_picker"
//% weight=100 group="Screen" blockGap=8
export function showImage(image: Image, delay: number = 400) {
export function showImage(image: Image) {
if (!image) return;
image.draw(0, 0, Draw.Normal);
delay = Math.max(0, delay);
if (delay > 0)
loops.pause(delay);
}
/**
@ -192,7 +168,7 @@ namespace brick {
screen.clear();
}
export function drawRect(x: number, y: number, w: number, h: number, mode = Draw.Normal) {
function drawRect(x: number, y: number, w: number, h: number, mode = Draw.Normal) {
x |= 0;
y |= 0;
w |= 0;
@ -237,25 +213,32 @@ namespace brick {
//% blockId=brickPrintPorts block="print ports"
//% weight=1 group="Screen"
export function printPorts() {
const col = 44;
clearScreen();
function scale(x: number) {
if (Math.abs(x) > 1000) return Math.round(x / 100) / 10 + "k";
return ("" + (x >> 0));
}
// motors
const datas = motors.getAllMotorData();
for(let i = 0; i < datas.length; ++i) {
const data = datas[i];
if (!data.actualSpeed && !data.count) continue;
const x = i * 52;
print(`${data.actualSpeed}%`, x, brick.LINE_HEIGHT)
print(`${data.count}>`, x, 2 * brick.LINE_HEIGHT)
const x = i * col;
print(`${scale(data.actualSpeed)}%`, x, brick.LINE_HEIGHT)
print(`${scale(data.count)}>`, x, 2 * brick.LINE_HEIGHT)
print(`${scale(data.tachoCount)}|`, x, 3 * brick.LINE_HEIGHT)
}
// sensors
const sis = sensors.internal.getActiveSensors();
for(let i =0; i < sis.length; ++i) {
const si = sis[i];
const x = (si.port() - 1) * 52;
const x = (si.port() - 1) * col;
const v = si._query();
print(`${v}`, x, 9 * brick.LINE_HEIGHT)
print(`${scale(v)}`, x, 9 * brick.LINE_HEIGHT)
}
}
}

View File

@ -14,7 +14,7 @@ namespace sensors {
}
//% color="#A5CA18" weight=90 icon="\uf10d"
//% groups='["Move", "Counters", "Measured Move"]'
//% groups='["Move", "Counters"]'
//% labelLineWidth=0
namespace motors {
}

1
libs/matrix/README.md Normal file
View File

@ -0,0 +1 @@
# Matrix

View File

@ -0,0 +1,15 @@
{
"matrix.Matrix": "A 2D matrix",
"matrix.Matrix.add": "Returns a new matrix as the sum of both matrices",
"matrix.Matrix.cholesky": "Performs a Cholesky factorized for a symmetric and positive definite",
"matrix.Matrix.clone": "Clones the matrix",
"matrix.Matrix.get": "Gets a value from the matrix",
"matrix.Matrix.get|param|row": "@param col ",
"matrix.Matrix.identity": "Creates an identity matrix",
"matrix.Matrix.logToConsole": "Renders the matrix to the console",
"matrix.Matrix.multiply": "Multiplies the current matrix with the other matrix and returns a new matrix",
"matrix.Matrix.scale": "Returns a new matrix with scaled values",
"matrix.Matrix.set": "Sets a value in the array",
"matrix.Matrix.set|param|row": "@param col ",
"matrix.Matrix.transpose": "Returns a transposed matrix"
}

View File

@ -0,0 +1,4 @@
{
"matrix|block": "matrix",
"{id:category}Matrix": "Matrix"
}

186
libs/matrix/matrix.ts Normal file
View File

@ -0,0 +1,186 @@
namespace matrix {
function pre(check: boolean) {
if (!check)
control.reset();
}
/**
* A 2D matrix
*/
export class Matrix {
private _rows: number;
private _cols: number;
private _values: number[];
constructor(rows: number, cols: number, values: number[] = undefined) {
pre(rows > 0);
pre(cols > 0);
this._rows = rows;
this._cols = cols;
const n = this._rows * this._cols;
this._values = values || [];
// fill gaps
while (this._values.length < n)
this._values.push(0);
}
/**
* Creates an identity matrix
* @param size
*/
static identity(size: number): Matrix {
const m = new Matrix(size, size);
for (let i = 0; i < size; ++i)
m._values[i * size] = 1;
return m;
}
/**
* Sets a value in the array
* @param row
* @param col
* @param val
*/
set(row: number, col: number, val: number) {
pre(row == row >> 0 && row >= 0 && row < this._rows && col == col >> 0 && col >= 0 && col < this._cols);
this._values[row * this._cols + col] = val;
}
/**
* Gets a value from the matrix
* @param row
* @param col
*/
get(row: number, col: number): number {
pre(row == row >> 0 && row >= 0 && row < this._rows && col == col >> 0 && col >= 0 && col < this._cols);
return this._values[row * this._cols + col];
}
/**
* Gets the number of rows
*/
get rows(): number {
return this._rows;
}
/**
* Gets the number of colums
*/
get cols(): number {
return this._cols;
}
/**
* Gets the raw storage buffer
*/
get values(): number[] {
return this._values;
}
/**
* Returns a new matrix as the sum of both matrices
* @param other
*/
add(other: Matrix): Matrix {
pre(this._rows != other._rows || this._cols != other._cols)
const n = this._rows * this._cols;
const r: number[] = [];
for (let i = 0; i < n; ++i) {
r[i] = this._values[i] + other._values[i];
}
return new Matrix(this._rows, this._cols, r);
}
/**
* Returns a new matrix with scaled values
* @param factor
*/
scale(factor: number): Matrix {
const n = this._rows * this._cols;
const r: number[] = [];
for (let i = 0; i < n; ++i) {
r[i] = this._values[i] * factor;
}
return new Matrix(this._rows, this._cols, r);
}
/**
* Multiplies the current matrix with the other matrix and returns a new matrix
* @param other
*/
multiply(other: Matrix): Matrix {
pre(this._cols == other._rows);
const r: number[] = [];
for (let i = 0; i < this._rows; ++i) {
for (let j = 0; j < other._cols; ++j) {
let s = 0;
for (let k = 0; k < this._cols; ++k) {
s += this._values[i * this._cols + k] * other._values[k * other._cols + j];
}
r[i * other._cols + j];
}
}
return new Matrix(this._rows, other._cols, r);
}
/**
* Returns a transposed matrix
*/
transpose(): Matrix {
const R = new Matrix(this._cols, this._rows);
const r: number[] = R._values;
for (let i = 0; i < this._rows; ++i) {
for (let j = 0; j < this._cols; ++j) {
r[i + j * this._rows] = this._values[i * this._cols + j];
}
}
return R;
}
/**
* Clones the matrix
*/
clone(): Matrix {
const r = new Matrix(this._rows, this._cols, this._values.slice(0));
return r;
}
/**
* Performs a Cholesky factorized for a symmetric and positive definite
*
*/
cholesky(): Matrix {
pre(this._rows == this._cols);
const l = this.clone();
const n = this._rows;
const L = l._values;
for (let j = 0; j < n; j++) {
const jj = L[j * n + j] = Math.sqrt(L[j * n + j]);
for (let i = j + 1; i < n; ++i)
L[i * n + j] /= jj;
for (let k = j + 1; k < n; k++)
for (let i = k; i < n; i++)
L[i * n + j] -= L[i * n + j] * L[k * n + j];
}
return l;
}
/**
* Renders the matrix to the console
*/
logToConsole(): void {
let k = 0;
for(let i = 0; i < this._rows; ++i) {
let s = ""
for(let j = 0; j < this._cols; ++j) {
if (j > 0)
s += " "
s += Math.round((this._values[k++] * 100) / 100);
}
console.log(s)
}
}
}
}

15
libs/matrix/pxt.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "matrix",
"description": "Matrix algebra",
"files": [
"README.md",
"matrix.ts"
],
"testFiles": [
"test.ts"
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

1
libs/matrix/test.ts Normal file
View File

@ -0,0 +1 @@
// add tests here

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.56",
"version": "0.0.57",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [

View File

@ -18,7 +18,8 @@
"libs/chassis",
"libs/ev3",
"libs/tests",
"libs/behaviors"
"libs/behaviors",
"libs/matrix"
],
"simulator": {
"autoRun": true,