Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
83793fc668 | |||
47ee87fe72 | |||
374fa36548 | |||
358ef7e19f | |||
9cbf5efd7e | |||
a27a7fcd55 | |||
85263fb84d | |||
b5ad898c9e | |||
456df3c442 | |||
1552eb05b4 | |||
d60e2c4a7d | |||
f4b78c3ee7 | |||
dd5e1957d5 | |||
d61a63f70a | |||
4207bd06c0 | |||
58763e398b | |||
f77bf165eb | |||
4880d9ea5b | |||
ab4fb019f9 | |||
2c2df31ba3 | |||
0f2bda2496 | |||
44386be3c7 | |||
89ca66b8a9 | |||
64389a7689 | |||
f77778ef85 | |||
7d01823caf | |||
b3f9a4c92f | |||
7fe8580de8 | |||
c70d6fe01a | |||
ed7099cc97 | |||
a79704fecc | |||
6a1b560101 | |||
5e21f9ab6d | |||
74648bd1df | |||
e24d4d56b1 | |||
ac428a3936 |
@ -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
|
||||
|
||||
|
@ -167,4 +167,5 @@
|
||||
"description": "Activity 2",
|
||||
"url":"/coding/roaming-2",
|
||||
"cardType": "example"
|
||||
}, {
|
||||
}]
|
||||
```
|
||||
|
50
docs/examples/line-follower-pid.md
Normal file
50
docs/examples/line-follower-pid.md
Normal 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)
|
||||
}
|
||||
})
|
||||
```
|
@ -4,6 +4,8 @@
|
||||
import { deployCoreAsync, initAsync } from "./deploy";
|
||||
import { FieldPorts } from "./field_ports";
|
||||
import { FieldImages } from "./field_images";
|
||||
import {FieldSpeed} from "./field_speed";
|
||||
import { FieldBrickButtons } from "./field_brickbuttons";
|
||||
|
||||
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
||||
pxt.debug('loading pxt-ev3 target extensions...')
|
||||
@ -15,6 +17,12 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P
|
||||
}, {
|
||||
selector: "images",
|
||||
editor: FieldImages
|
||||
}, {
|
||||
selector: "speed",
|
||||
editor: FieldSpeed
|
||||
}, {
|
||||
selector: "brickbuttons",
|
||||
editor: FieldBrickButtons
|
||||
}],
|
||||
deployCoreAsync
|
||||
};
|
||||
|
159
editor/field_brickbuttons.ts
Normal file
159
editor/field_brickbuttons.ts
Normal file
@ -0,0 +1,159 @@
|
||||
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
export interface FieldPortsOptions extends Blockly.FieldCustomDropdownOptions {
|
||||
columns?: string;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.FieldCustom {
|
||||
public isFieldCustom_ = true;
|
||||
|
||||
// Width in pixels
|
||||
private width_: number;
|
||||
|
||||
// Columns in grid
|
||||
private columns_: number;
|
||||
|
||||
private savedPrimary_: string;
|
||||
|
||||
constructor(text: string, options: FieldPortsOptions, validator?: Function) {
|
||||
super(options.data);
|
||||
|
||||
this.columns_ = parseInt(options.columns) || 4;
|
||||
this.width_ = parseInt(options.width) || 150;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dropdown menu under the text.
|
||||
* @private
|
||||
*/
|
||||
public showEditor_() {
|
||||
// If there is an existing drop-down we own, this is a request to hide the drop-down.
|
||||
if (Blockly.DropDownDiv.hideIfOwner(this)) {
|
||||
return;
|
||||
}
|
||||
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
Blockly.DropDownDiv.clearContent();
|
||||
// Populate the drop-down with the icons for this field.
|
||||
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
|
||||
let contentDiv = document.createElement('div');
|
||||
// Accessibility properties
|
||||
contentDiv.setAttribute('role', 'menu');
|
||||
contentDiv.setAttribute('aria-haspopup', 'true');
|
||||
|
||||
const buttonsSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGGElement;
|
||||
pxsim.svg.hydrate(buttonsSVG, {
|
||||
viewBox: "0 0 256.68237 256.68237",
|
||||
width: this.width_,
|
||||
height: this.width_
|
||||
});
|
||||
contentDiv.appendChild(buttonsSVG);
|
||||
|
||||
const gWrapper = pxsim.svg.child(buttonsSVG, 'g', { 'transform': 'translate(-4.695057,58.29823)' });
|
||||
const gInnerWrapper = pxsim.svg.child(gWrapper, 'g', { 'transform': 'translate(3.9780427e-6,-32.677281)' });
|
||||
|
||||
const back = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#6a6a6a;stroke-width:3.91719985',
|
||||
d: 'M 106.30882,198.38022 C 84.431262,177.26258 50.453467,142.52878 50.453467,142.52878 v -7.12931 H 37.087971 a 32.381533,32.381533 0 1 1 0,-64.763062 H 50.457376 V 63.503186 L 105.71731,7.0563355 h 55.25604 c 25.02699,25.5048885 55.25994,55.2599395 55.25994,55.2599395 v 8.320133 h 12.77398 a 32.381533,32.381533 0 0 1 0,64.763062 h -12.77398 v 7.13323 c -29.43384,30.27603 -54.66454,55.85144 -54.66454,55.85144 z'
|
||||
})
|
||||
|
||||
const buttonLeft = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'm 36.492567,78.357208 h 40.69971 V 126.48393 H 36.492567 A 24.063359,24.063359 0 0 1 12.429199,102.42057 v 0 A 24.063359,24.063359 0 0 1 36.492567,78.357208 Z'
|
||||
})
|
||||
|
||||
const buttonRight = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'M 229.00727,126.48784 H 188.30756 V 78.361126 h 40.69971 a 24.063359,24.063359 0 0 1 24.06335,24.063354 v 0 a 24.063359,24.063359 0 0 1 -24.06335,24.06336 z'
|
||||
})
|
||||
|
||||
const buttonEnter = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#3c3c3c;stroke-width:3.91719985',
|
||||
d: 'm 109.27806,78.357208 h 46.9398 a 1.782326,1.782326 0 0 1 1.78233,1.782326 V 124.7016 a 1.782326,1.782326 0 0 1 -1.78233,1.78233 h -46.9398 a 1.782326,1.782326 0 0 1 -1.78233,-1.78233 V 80.139534 a 1.782326,1.782326 0 0 1 1.78233,-1.782326 z'
|
||||
})
|
||||
|
||||
const buttonTop = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'm 108.09114,15.967966 49.90905,-0.59542 37.43276,38.619675 -15.44943,15.449437 V 97.367379 H 165.7249 V 81.306861 A 11.978797,11.978797 0 0 0 153.84012,69.422075 c -11.59883,-0.184102 -43.37516,0 -43.37516,0 A 9.6676495,9.6676495 0 0 0 100.36251,79.520618 V 97.347793 H 86.103905 V 69.422075 L 70.654464,53.97264 Z'
|
||||
})
|
||||
|
||||
const buttonBottom = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'M 157.78865,189.01028 108.18908,189.38233 70.654464,150.794 86.323259,135.4895 v -28.08625 h 14.101921 v 16.11144 a 12.006218,12.006218 0 0 0 11.85346,11.9788 c 11.59882,0.1841 43.13227,0 43.13227,0 a 10.18472,10.18472 0 0 0 10.38059,-10.38058 v -17.70966 h 14.39179 v 28.08632 l 15.3045,15.3045 z'
|
||||
})
|
||||
|
||||
const buttons = [buttonEnter, buttonLeft, buttonRight, buttonTop, buttonBottom];
|
||||
const options = this.getOptions();
|
||||
for (let i = 0, option: any; option = options[i]; i++) {
|
||||
let content = (options[i] as any)[0]; // Human-readable text or image.
|
||||
const value = (options[i] as any)[1]; // Language-neutral value.
|
||||
const button = buttons[i];
|
||||
button.setAttribute('id', ':' + i); // For aria-activedescendant
|
||||
button.setAttribute('role', 'menuitem');
|
||||
button.setAttribute('cursor', 'pointer');
|
||||
const title = pxsim.svg.child(button, 'title');
|
||||
title.textContent = content;
|
||||
|
||||
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
|
||||
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
|
||||
// These are applied manually instead of using the :hover pseudoclass
|
||||
// because Android has a bad long press "helper" menu and green highlight
|
||||
// that we must prevent with ontouchstart preventDefault
|
||||
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
|
||||
this.setAttribute('stroke', '#ffffff');
|
||||
e.preventDefault();
|
||||
});
|
||||
Blockly.bindEvent_(button, 'mouseover', button, function () {
|
||||
this.setAttribute('stroke', '#ffffff');
|
||||
});
|
||||
Blockly.bindEvent_(button, 'mouseout', button, function () {
|
||||
this.setAttribute('stroke', 'transparent');
|
||||
});
|
||||
|
||||
button.setAttribute('data-value', value);
|
||||
}
|
||||
|
||||
contentDiv.style.width = this.width_ + 'px';
|
||||
dropdownDiv.appendChild(contentDiv);
|
||||
|
||||
Blockly.DropDownDiv.setColour('#ffffff', '#dddddd');
|
||||
|
||||
// Calculate positioning based on the field position.
|
||||
var scale = this.sourceBlock_.workspace.scale;
|
||||
var bBox = { width: this.size_.width, height: this.size_.height };
|
||||
bBox.width *= scale;
|
||||
bBox.height *= scale;
|
||||
var position = this.fieldGroup_.getBoundingClientRect();
|
||||
var primaryX = position.left + bBox.width / 2;
|
||||
var primaryY = position.top + bBox.height;
|
||||
var secondaryX = primaryX;
|
||||
var secondaryY = position.top;
|
||||
// Set bounds to workspace; show the drop-down.
|
||||
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
|
||||
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
|
||||
this.onHide_.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a button is clicked inside the drop-down.
|
||||
* Should be bound to the FieldIconMenu.
|
||||
* @param {Event} e DOM event for the click/touch
|
||||
* @private
|
||||
*/
|
||||
private buttonClick_ = function (e: any) {
|
||||
let value = e.target.getAttribute('data-value');
|
||||
this.setValue(value);
|
||||
Blockly.DropDownDiv.hide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for when the drop-down is hidden.
|
||||
*/
|
||||
private onHide_ = function () {
|
||||
Blockly.DropDownDiv.content_.removeAttribute('role');
|
||||
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
|
||||
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
|
||||
};
|
||||
}
|
96
editor/field_speed.ts
Normal file
96
editor/field_speed.ts
Normal file
@ -0,0 +1,96 @@
|
||||
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
export interface FieldSpeedOptions extends Blockly.FieldCustomOptions {
|
||||
min?: string;
|
||||
max?: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export class FieldSpeed extends Blockly.FieldSlider implements Blockly.FieldCustom {
|
||||
public isFieldCustom_ = true;
|
||||
|
||||
private params: any;
|
||||
|
||||
private speedSVG: SVGElement;
|
||||
private circleBar: SVGCircleElement;
|
||||
private reporter: SVGTextElement;
|
||||
|
||||
/**
|
||||
* Class for a color wheel field.
|
||||
* @param {number|string} value The initial content of the field.
|
||||
* @param {Function=} opt_validator An optional function that is called
|
||||
* to validate any constraints on what the user entered. Takes the new
|
||||
* text as an argument and returns either the accepted text, a replacement
|
||||
* text, or null to abort the change.
|
||||
* @extends {Blockly.FieldNumber}
|
||||
* @constructor
|
||||
*/
|
||||
constructor(value_: any, params: FieldSpeedOptions, opt_validator?: Function) {
|
||||
super(String(value_), '-100', '100', null, '10', 'Speed', opt_validator);
|
||||
this.params = params;
|
||||
if (this.params['min']) this.min_ = parseFloat(this.params.min);
|
||||
if (this.params['max']) this.max_ = parseFloat(this.params.max);
|
||||
if (this.params['label']) this.labelText_ = this.params.label;
|
||||
|
||||
(this as any).sliderColor_ = '#a8aaa8';
|
||||
}
|
||||
|
||||
createLabelDom_(labelText: string) {
|
||||
var labelContainer = document.createElement('div');
|
||||
this.speedSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGGElement;
|
||||
pxsim.svg.hydrate(this.speedSVG, {
|
||||
viewBox: "0 0 200 100"
|
||||
});
|
||||
|
||||
labelContainer.appendChild(this.speedSVG);
|
||||
|
||||
const outerCircle = pxsim.svg.child(this.speedSVG, "circle", {
|
||||
'stroke-dasharray': '565.48', 'stroke-dashoffset': '0',
|
||||
'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 0.1s linear;`,
|
||||
'stroke': '#a8aaa8', 'stroke-width': '1rem'
|
||||
}) as SVGCircleElement;
|
||||
this.circleBar = pxsim.svg.child(this.speedSVG, "circle", {
|
||||
'stroke-dasharray': '565.48', 'stroke-dashoffset': '0',
|
||||
'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 0.1s linear;`,
|
||||
'stroke': '#f12a21', 'stroke-width': '1rem'
|
||||
}) as SVGCircleElement;
|
||||
|
||||
this.reporter = pxsim.svg.child(this.speedSVG, "text", {
|
||||
'x': 100, 'y': 80,
|
||||
'text-anchor': 'middle', 'alignment-baseline': 'middle',
|
||||
'style': 'font-size: 50px',
|
||||
'class': 'sim-text inverted number'
|
||||
}) as SVGTextElement;
|
||||
|
||||
// labelContainer.setAttribute('class', 'blocklyFieldSliderLabel');
|
||||
var readout = document.createElement('span');
|
||||
readout.setAttribute('class', 'blocklyFieldSliderReadout');
|
||||
// var label = document.createElement('span');
|
||||
// label.setAttribute('class', 'blocklyFieldSliderLabelText');
|
||||
// label.innerHTML = labelText;
|
||||
// labelContainer.appendChild(label);
|
||||
// labelContainer.appendChild(readout);
|
||||
return [labelContainer, readout];
|
||||
};
|
||||
|
||||
setReadout_(readout: Element, value: string) {
|
||||
this.updateSpeed(parseFloat(value));
|
||||
// Update reporter
|
||||
this.reporter.textContent = `${value}%`;
|
||||
}
|
||||
|
||||
private updateSpeed(speed: number) {
|
||||
let sign = this.sign(speed);
|
||||
speed = (Math.abs(speed) / 100 * 50) + 50;
|
||||
if (sign == -1) speed = 50 - speed;
|
||||
let c = Math.PI * (90 * 2);
|
||||
let pct = ((100 - speed) / 100) * c;
|
||||
this.circleBar.setAttribute('stroke-dashoffset', `${pct}`);
|
||||
}
|
||||
|
||||
// A re-implementation of Math.sign (since IE11 doesn't support it)
|
||||
private sign(num: number) {
|
||||
return num ? num < 0 ? -1 : 1 : 0;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"behaviors.addBehavior|block": "add behavior %behavior",
|
||||
"behaviors.avoidCrash|block": "avoid crash using %ultrasonic",
|
||||
"behaviors.driveForward|block": "drive %motors|forward at %speed|%",
|
||||
"behaviors.driveForward|block": "drive %motors|forward at %speed=motorSpeedPicker|%",
|
||||
"behaviors|block": "behaviors",
|
||||
"{id:category}Behaviors": "Behaviors"
|
||||
}
|
@ -49,7 +49,7 @@ namespace behaviors {
|
||||
* @param motors
|
||||
* @param speed the desired speed, eg: 50
|
||||
*/
|
||||
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed|%"
|
||||
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed=motorSpeedPicker|%"
|
||||
export function driveForward(motors: motors.MotorBase, speed: number): behaviors.Behavior {
|
||||
return new DriveForwardBehavior(motors, speed);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
"sensors.ColorSensor.onColorDetected|block": "on %sensor|detected color %color",
|
||||
"sensors.ColorSensor.onLightChanged|block": "on %sensor|%mode|%condition",
|
||||
"sensors.ColorSensor.pauseForColor|block": "pause %sensor|for color %color",
|
||||
"sensors.ColorSensor.pauseForLight|block": "pause %sensor|for %mode|light %condition",
|
||||
"sensors.ColorSensor.pauseForLight|block": "pause %sensor|for %mode|%condition",
|
||||
"sensors.color1|block": "color 1",
|
||||
"sensors.color2|block": "color 2",
|
||||
"sensors.color3|block": "color 3",
|
||||
|
@ -182,7 +182,7 @@ namespace sensors {
|
||||
* @param color the color to detect
|
||||
*/
|
||||
//% help=sensors/color-sensor/pause-for-light
|
||||
//% block="pause %sensor|for %mode|light %condition"
|
||||
//% block="pause %sensor|for %mode|%condition"
|
||||
//% blockId=colorPauseForLight
|
||||
//% parts="colorsensor"
|
||||
//% blockNamespace=sensors
|
||||
|
@ -30,14 +30,20 @@
|
||||
"brick.clearScreen": "Clears the screen",
|
||||
"brick.lightPattern": "Pattern block.",
|
||||
"brick.lightPattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
|
||||
"brick.printLine": "Show text on the screen at a specific line.",
|
||||
"brick.printLine|param|line": "the line number to print the text at, eg: 0",
|
||||
"brick.printLine|param|text": "the text to print on the screen, eg: \"Hello world\"",
|
||||
"brick.printPorts": "Prints the port states on the screen",
|
||||
"brick.setLight": "Set lights.",
|
||||
"brick.setLight|param|pattern": "the lights pattern to use.",
|
||||
"brick.showImage": "Shows an image on screen",
|
||||
"brick.showImage|param|image": "image to draw",
|
||||
"brick.showNumber": "Shows a number on the screen",
|
||||
"brick.showNumber|param|line": "the line number to print the text at, eg: 1",
|
||||
"brick.showNumber|param|value": "the numeric value",
|
||||
"brick.showString": "Show text on the screen at a specific line.",
|
||||
"brick.showString|param|line": "the line number to print the text at, eg: 1",
|
||||
"brick.showString|param|text": "the text to print on the screen, eg: \"Hello world\"",
|
||||
"brick.showValue": "Shows a name, value pair on the screen",
|
||||
"brick.showValue|param|line": "the line number to print the text at, eg: 1",
|
||||
"brick.showValue|param|value": "the numeric value",
|
||||
"console": "Reading and writing data to the console output.\n\nReading and writing data to the console output.",
|
||||
"console.addListener": "Adds a listener for the log messages",
|
||||
"console.log": "Write a line of text to the console output.",
|
||||
@ -65,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",
|
||||
|
@ -37,10 +37,12 @@
|
||||
"brick.buttonUp|block": "up",
|
||||
"brick.clearScreen|block": "clear screen",
|
||||
"brick.lightPattern|block": "%pattern",
|
||||
"brick.printLine|block": "print %text| at line %line",
|
||||
"brick.printPorts|block": "print ports",
|
||||
"brick.setLight|block": "set light to %pattern=led_pattern",
|
||||
"brick.showImage|block": "show image %image=screen_image_picker",
|
||||
"brick.showNumber|block": "show number %name|at line %line",
|
||||
"brick.showString|block": "show string %text|at line %line",
|
||||
"brick.showValue|block": "show value %name|= %text|at line %line",
|
||||
"brick|block": "brick",
|
||||
"console.logValue|block": "console|log value %name|= %value",
|
||||
"console.log|block": "console|log %text",
|
||||
@ -55,12 +57,9 @@
|
||||
"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|%|for %value|%unit",
|
||||
"motors.MotorBase.setSpeed|block": "set %motor|speed to %speed|%",
|
||||
"motors.SynchedMotorPair.steerFor|block": "steer %chassis|turn ratio %turnRatio|speed %speed|%|for %value|%unit",
|
||||
"motors.SynchedMotorPair.steer|block": "steer %chassis|turn ratio %turnRatio|speed %speed|%",
|
||||
"motors.SynchedMotorPair.tankFor|block": "tank %motors|%speedLeft|%|%speedRight|%|for %value|%unit",
|
||||
"motors.SynchedMotorPair.tank|block": "tank %motors|%speedLeft|%|%speedRight|%",
|
||||
"motors.MotorBase.setSpeed|block": "set %motor|speed to %speed=motorSpeedPicker|%",
|
||||
"motors.SynchedMotorPair.steer|block": "steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%",
|
||||
"motors.SynchedMotorPair.tank|block": "tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%",
|
||||
"motors.largeAB|block": "large A+B",
|
||||
"motors.largeAD|block": "large A+D",
|
||||
"motors.largeA|block": "large A",
|
||||
@ -93,8 +92,7 @@
|
||||
"{id:group}Counters": "Counters",
|
||||
"{id:group}Light": "Light",
|
||||
"{id:group}More": "More",
|
||||
"{id:group}Motion": "Motion",
|
||||
"{id:group}Move": "Move",
|
||||
"{id:group}Screen": "Screen",
|
||||
"{id:group}Sensors": "Sensors",
|
||||
"{id:group}Sync Motion": "Sync Motion"
|
||||
"{id:group}Sensors": "Sensors"
|
||||
}
|
@ -92,6 +92,7 @@ namespace brick {
|
||||
//% blockNamespace=brick
|
||||
//% weight=81 blockGap=8
|
||||
//% group="Buttons"
|
||||
//% button.fieldEditor="brickbuttons"
|
||||
isPressed() {
|
||||
return this._isPressed
|
||||
}
|
||||
@ -107,6 +108,7 @@ namespace brick {
|
||||
//% blockNamespace=brick
|
||||
//% weight=80
|
||||
//% group="Buttons"
|
||||
//% button.fieldEditor="brickbuttons"
|
||||
wasPressed() {
|
||||
const r = this._wasPressed
|
||||
this._wasPressed = false
|
||||
@ -125,6 +127,7 @@ namespace brick {
|
||||
//% blockNamespace=brick
|
||||
//% weight=99 blockGap=8
|
||||
//% group="Buttons"
|
||||
//% button.fieldEditor="brickbuttons"
|
||||
onEvent(ev: ButtonEvent, body: () => void) {
|
||||
control.onEvent(this._id, ev, body)
|
||||
}
|
||||
@ -139,6 +142,7 @@ namespace brick {
|
||||
//% blockNamespace=brick
|
||||
//% weight=98 blockGap=8
|
||||
//% group="Buttons"
|
||||
//% button.fieldEditor="brickbuttons"
|
||||
pauseUntil(ev: ButtonEvent) {
|
||||
control.waitForEvent(this._id, ev);
|
||||
}
|
||||
@ -166,7 +170,6 @@ namespace brick {
|
||||
// this needs to be done in query(), which is run without the main JS execution mutex
|
||||
// otherwise, while(true){} will lock the device
|
||||
if (ret & DAL.BUTTON_ID_ESCAPE) {
|
||||
motors.stopAllMotors();
|
||||
control.reset()
|
||||
}
|
||||
return ret
|
||||
|
@ -483,7 +483,15 @@ void runLMS() {
|
||||
*/
|
||||
}
|
||||
|
||||
void stopMotors() {
|
||||
uint8_t cmd[2] = { 0xA3, 0x0F };
|
||||
int fd = open("/dev/lms_pwm", O_RDWR);
|
||||
write(fd, cmd, 2);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
extern "C" void target_reset() {
|
||||
stopMotors();
|
||||
if (lmsPid)
|
||||
runLMS();
|
||||
else
|
||||
|
@ -1 +1,14 @@
|
||||
|
||||
namespace motors {
|
||||
|
||||
/**
|
||||
* A speed picker
|
||||
* @param speed the speed, eg: 50
|
||||
*/
|
||||
//% blockId=motorSpeedPicker block="%speed" shim=TD_ID
|
||||
//% speed.fieldEditor="speed" colorSecondary="#FFFFFF"
|
||||
//% weight=0 blockHidden=1 speed.fieldOptions.decompileLiterals=1
|
||||
export function __speedPicker(speed: number): number {
|
||||
return speed;
|
||||
}
|
||||
}
|
@ -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)) {
|
||||
@ -112,7 +112,7 @@ namespace motors {
|
||||
*/
|
||||
//% blockId=motorStopAll block="stop all motors"
|
||||
//% weight=5
|
||||
//% group="Motion"
|
||||
//% group="Move"
|
||||
export function stopAllMotors() {
|
||||
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
|
||||
writePWM(b)
|
||||
@ -121,7 +121,7 @@ namespace motors {
|
||||
/**
|
||||
* Resets all motors
|
||||
*/
|
||||
//% group="Motion"
|
||||
//% group="Move"
|
||||
export function resetAllMotors() {
|
||||
reset(Output.ALL)
|
||||
}
|
||||
@ -164,7 +164,7 @@ namespace motors {
|
||||
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake"
|
||||
//% brake.fieldEditor=toggleonoff
|
||||
//% weight=60 blockGap=8
|
||||
//% group="Motion"
|
||||
//% group="Move"
|
||||
setBrake(brake: boolean) {
|
||||
this.init();
|
||||
this._brake = brake;
|
||||
@ -176,7 +176,7 @@ namespace motors {
|
||||
//% blockId=motorSetReversed block="set %motor|reversed %reversed"
|
||||
//% reversed.fieldEditor=toggleonoff
|
||||
//% weight=59
|
||||
//% group="Motion"
|
||||
//% group="Move"
|
||||
setReversed(reversed: boolean) {
|
||||
this.init();
|
||||
const b = mkCmd(this._port, DAL.opOutputPolarity, 1)
|
||||
@ -202,35 +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|%"
|
||||
//% on.fieldEditor=toggleonoff
|
||||
//% weight=99 blockGap=8
|
||||
//% speed.min=-100 speed.max=100
|
||||
//% group="Motion"
|
||||
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|%|for %value|%unit"
|
||||
//% weight=98 blockGap=8
|
||||
//% speed.min=-100 speed.max=100
|
||||
//% group="Motion"
|
||||
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) {
|
||||
@ -278,8 +259,8 @@ namespace motors {
|
||||
* @param timeOut optional maximum pausing time in milliseconds
|
||||
*/
|
||||
//% blockId=motorPauseUntilRead block="%motor|pause until ready"
|
||||
//% weight=97
|
||||
//% group="Motion"
|
||||
//% weight=90
|
||||
//% group="Move"
|
||||
pauseUntilReady(timeOut?: number) {
|
||||
pauseUntil(() => this.isReady(), timeOut);
|
||||
}
|
||||
@ -457,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,
|
||||
@ -466,32 +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|%|%speedRight|%"
|
||||
//% weight=20 blockGap=8
|
||||
//% group="Sync Motion"
|
||||
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|%|%speedRight|%|for %value|%unit"
|
||||
//% weight=19
|
||||
//% speedLeft.min=-100 speedLeft=100
|
||||
//% speedRight.min=-100 speedRight=100
|
||||
//% blockId=motorPairTank block="tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%"
|
||||
//% weight=96 blockGap=8
|
||||
//% inlineInputMode=inline
|
||||
//% group="Sync Motion"
|
||||
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);
|
||||
@ -502,39 +464,22 @@ namespace motors {
|
||||
? (100 - speedRight / speedLeft * 100)
|
||||
: (speedLeft / speedRight * 100 - 100);
|
||||
|
||||
this.steerFor(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
|
||||
*/
|
||||
//% blockId=motorPairSteer block="steer %chassis|turn ratio %turnRatio|speed %speed|%"
|
||||
//% weight=7 blockGap=8
|
||||
//% turnRatio.min=-200 turnRatio=200
|
||||
//% inlineInputMode=inline
|
||||
//% group="Sync Motion"
|
||||
steer(turnRatio: number, speed: number) {
|
||||
this.steer(turnRatio, speed);
|
||||
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=motorPairSteerFor block="steer %chassis|turn ratio %turnRatio|speed %speed|%|for %value|%unit"
|
||||
//% weight=6 blockGap=8
|
||||
//% blockId=motorPairSteer block="steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%"
|
||||
//% weight=95
|
||||
//% turnRatio.min=-200 turnRatio=200
|
||||
//% inlineInputMode=inline
|
||||
//% group="Sync Motion"
|
||||
steerFor(turnRatio: number, speed: number, value: number, unit: MoveUnit) {
|
||||
//% group="Move"
|
||||
steer(turnRatio: number, speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
|
||||
this.init();
|
||||
speed = Math.clamp(-100, 100, speed >> 0);
|
||||
if (!speed) {
|
||||
|
@ -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)
|
||||
@ -90,18 +68,43 @@ namespace brick {
|
||||
/**
|
||||
* Show text on the screen at a specific line.
|
||||
* @param text the text to print on the screen, eg: "Hello world"
|
||||
* @param line the line number to print the text at, eg: 0
|
||||
* @param line the line number to print the text at, eg: 1
|
||||
*/
|
||||
//% blockId=screen_print block="print %text| at line %line"
|
||||
//% blockId=screen_print block="show string %text|at line %line"
|
||||
//% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
|
||||
//% line.min=0 line.max=9
|
||||
export function printLine(text: string, line: number) {
|
||||
//% line.min=1 line.max=10
|
||||
export function showString(text: string, line: number) {
|
||||
const NUM_LINES = 9;
|
||||
const offset = 5;
|
||||
const y = offset + (Math.clamp(0, NUM_LINES, line) / (NUM_LINES + 2)) * DAL.LCD_HEIGHT;
|
||||
const y = offset + (Math.clamp(0, NUM_LINES, line - 1) / (NUM_LINES + 2)) * DAL.LCD_HEIGHT;
|
||||
brick.print(text, offset, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a number on the screen
|
||||
* @param value the numeric value
|
||||
* @param line the line number to print the text at, eg: 1
|
||||
*/
|
||||
//% blockId=screenShowNumber block="show number %name|at line %line"
|
||||
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
|
||||
//% line.min=1 line.max=10
|
||||
export function showNumber(value: number, line: number) {
|
||||
showString("" + value, line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a name, value pair on the screen
|
||||
* @param value the numeric value
|
||||
* @param line the line number to print the text at, eg: 1
|
||||
*/
|
||||
//% blockId=screenShowValue block="show value %name|= %text|at line %line"
|
||||
//% 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);
|
||||
}
|
||||
|
||||
export function print(text: string, x: number, y: number, mode = Draw.Normal) {
|
||||
x |= 0
|
||||
y |= 0
|
||||
@ -138,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,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;
|
||||
@ -213,30 +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)
|
||||
|
||||
console.logValue(`speed.` + "ABCD"[i], data.actualSpeed);
|
||||
console.logValue(`angle.` + "ABCD"[i], data.count);
|
||||
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)
|
||||
|
||||
console.logValue(`sensor.` + si.port(), v);
|
||||
print(`${scale(v)}`, x, 9 * brick.LINE_HEIGHT)
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace sensors {
|
||||
}
|
||||
|
||||
//% color="#A5CA18" weight=90 icon="\uf10d"
|
||||
//% groups='["Motion", "Counters", "Sync Motion"]'
|
||||
//% groups='["Move", "Counters"]'
|
||||
//% labelLineWidth=0
|
||||
namespace motors {
|
||||
}
|
||||
|
1
libs/matrix/README.md
Normal file
1
libs/matrix/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Matrix
|
15
libs/matrix/_locales/matrix-jsdoc-strings.json
Normal file
15
libs/matrix/_locales/matrix-jsdoc-strings.json
Normal 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"
|
||||
}
|
4
libs/matrix/_locales/matrix-strings.json
Normal file
4
libs/matrix/_locales/matrix-strings.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"matrix|block": "matrix",
|
||||
"{id:category}Matrix": "Matrix"
|
||||
}
|
186
libs/matrix/matrix.ts
Normal file
186
libs/matrix/matrix.ts
Normal 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
15
libs/matrix/pxt.json
Normal 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
1
libs/matrix/test.ts
Normal file
@ -0,0 +1 @@
|
||||
// add tests here
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pxt-ev3",
|
||||
"version": "0.0.55",
|
||||
"version": "0.0.57",
|
||||
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
|
||||
"private": true,
|
||||
"keywords": [
|
||||
|
@ -18,7 +18,8 @@
|
||||
"libs/chassis",
|
||||
"libs/ev3",
|
||||
"libs/tests",
|
||||
"libs/behaviors"
|
||||
"libs/behaviors",
|
||||
"libs/matrix"
|
||||
],
|
||||
"simulator": {
|
||||
"autoRun": true,
|
||||
|
@ -74,6 +74,19 @@ span.blocklyTreeLabel {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
.blocklyDropDownArrow.arrowTop {
|
||||
border-top-left-radius: 0px !important;
|
||||
}
|
||||
|
||||
.blocklyToggleRect {
|
||||
rx: 0 !important;
|
||||
ry: 0 !important;
|
||||
}
|
||||
|
||||
.blocklyDropDownButton {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media only screen and (max-width: @largestMobileScreen) {
|
||||
#blocklyTrashIcon {
|
||||
|
Reference in New Issue
Block a user