Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
148067a143 | |||
6f34887c94 | |||
64a9930c2e | |||
5815e16647 | |||
c4f5e425c2 | |||
361ae7a2d2 | |||
3769402ade | |||
5c3c83eb52 | |||
0f07a89981 | |||
11d887a213 | |||
8150b2dbb0 |
@ -19,6 +19,7 @@ If you found a bug, please try if it hasn't been fixed yet! Go to https://makeco
|
||||
* You will need to install the latest EV3 firmware on your brick. Instructions on how to do that are located here: https://makecode.mindstorms.com/troubleshoot.
|
||||
* You will need a computer with a USB port to connect to the EV3 in order to download your programs.
|
||||
* You will need internet access and a browser on your computer to get to https://makecode.mindstorms.com.
|
||||
* You can [install the app](/offline-app) to use the editor offline.
|
||||
|
||||
### I know LabView, how is MakeCode different?
|
||||
|
||||
@ -34,6 +35,10 @@ On the home page, scroll down to the **FLL / City Shaper / Crane Mission** secti
|
||||
|
||||
Yes.
|
||||
|
||||
### Does it work without internet?
|
||||
|
||||
To make sure the editor works without internet, install the [offline app](/offline-app)!
|
||||
|
||||
### How do I figure out what a block does?
|
||||
|
||||
You can right-click on any block and select “Help” in the context menu to open the documentation page describing what that block does.
|
||||
|
@ -388,12 +388,12 @@
|
||||
}
|
||||
function downloadWin64() {
|
||||
// TODO: Keep this link up-to-date with the desired release version
|
||||
window.open("https://makecode.com/api/release/ev3/v1.0.11/win64");
|
||||
window.open("https://makecode.com/api/release/ev3/v1.1.20/win64");
|
||||
tickEvent("offlineapp.download", { "target": "ev3", "platform": "win64" });
|
||||
}
|
||||
function downloadMac64() {
|
||||
// TODO: Keep this link up-to-date with the desired release version
|
||||
window.open("https://makecode.com/api/release/ev3/v1.0.11/mac64");
|
||||
window.open("https://makecode.com/api/release/ev3/v1.1.20/mac64");
|
||||
tickEvent("offlineapp.download", { "target": "ev3", "platform": "mac64" });
|
||||
}
|
||||
</script>
|
||||
|
@ -3,9 +3,3 @@
|
||||
## Offline app #target-app
|
||||
|
||||
The MakeCode editor is available as app which you can install on a computer with Windows or Mac OS. Once installed, the **[MakeCode Offline App](/offline-app)** lets you create, run, and download your projects to the @boardname@. It works the same as the Web application does in your browser but it's a stand-alone application that will work when a connection to the internet is restricted or not available.
|
||||
|
||||
### ~ hint
|
||||
|
||||
The [MakeCode Offline App](/offline-app) is currently in development and is made available as a **pre-release** version.
|
||||
|
||||
### ~
|
||||
|
@ -42,7 +42,7 @@
|
||||
}, {
|
||||
"name": "Turtle",
|
||||
"description": "Encode moves and run them on a driving base",
|
||||
"url":"/examples/turtle",
|
||||
"url":"/tutorials/turtle",
|
||||
"cardType": "example"
|
||||
}]
|
||||
```
|
||||
|
@ -179,6 +179,7 @@ namespace sensors {
|
||||
//% group="Color Sensor"
|
||||
//% blockGap=8
|
||||
color(): ColorSensorColor {
|
||||
this.poke();
|
||||
this.setMode(ColorSensorMode.Color)
|
||||
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||
}
|
||||
@ -196,6 +197,7 @@ namespace sensors {
|
||||
//% group="Color Sensor"
|
||||
//% blockGap=8
|
||||
rgbRaw(): number[] {
|
||||
this.poke();
|
||||
this.setMode(ColorSensorMode.RgbRaw);
|
||||
return [this.getNumber(NumberFormat.UInt16LE, 0), this.getNumber(NumberFormat.UInt16LE, 2), this.getNumber(NumberFormat.UInt16LE, 4)];
|
||||
}
|
||||
@ -249,6 +251,7 @@ namespace sensors {
|
||||
//% weight=87 blockGap=8
|
||||
//% group="Color Sensor"
|
||||
light(mode: LightIntensityMode) {
|
||||
this.poke();
|
||||
this.setMode(<ColorSensorMode><number>mode)
|
||||
switch(mode) {
|
||||
case LightIntensityMode.ReflectedRaw:
|
||||
@ -279,6 +282,7 @@ namespace sensors {
|
||||
*/
|
||||
//%
|
||||
reflectedLightRaw(): number {
|
||||
this.poke();
|
||||
this.setMode(ColorSensorMode.RefRaw);
|
||||
return this.getNumber(NumberFormat.UInt16LE, 0);
|
||||
}
|
||||
|
@ -55,6 +55,10 @@ namespace brick {
|
||||
this._wasPressed = false
|
||||
}
|
||||
|
||||
protected poke() {
|
||||
|
||||
}
|
||||
|
||||
//% hidden
|
||||
_update(curr: boolean) {
|
||||
if (this == null) return
|
||||
@ -85,6 +89,7 @@ namespace brick {
|
||||
//% group="Buttons"
|
||||
//% button.fieldEditor="brickbuttons"
|
||||
isPressed() {
|
||||
this.poke();
|
||||
return this._isPressed
|
||||
}
|
||||
|
||||
@ -102,6 +107,7 @@ namespace brick {
|
||||
//% group="Buttons"
|
||||
//% button.fieldEditor="brickbuttons"
|
||||
wasPressed() {
|
||||
this.poke();
|
||||
const r = this._wasPressed
|
||||
this._wasPressed = false
|
||||
return r
|
||||
@ -144,6 +150,7 @@ namespace brick {
|
||||
namespace brick {
|
||||
let btnsMM: MMap
|
||||
let buttons: DevButton[]
|
||||
let buttonPoller: sensors.internal.Poller;
|
||||
|
||||
export namespace internal {
|
||||
export function getBtnsMM() {
|
||||
@ -167,7 +174,7 @@ namespace brick {
|
||||
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
|
||||
if (!btnsMM) control.fail("no buttons?")
|
||||
buttons = []
|
||||
sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
|
||||
buttonPoller = new sensors.internal.Poller(50, readButtons, (prev, curr) => {
|
||||
for (let b of buttons)
|
||||
b._update(!!(curr & b.mask))
|
||||
})
|
||||
@ -182,6 +189,10 @@ namespace brick {
|
||||
initBtns()
|
||||
buttons.push(this)
|
||||
}
|
||||
|
||||
protected poke() {
|
||||
buttonPoller.poke();
|
||||
}
|
||||
}
|
||||
|
||||
initBtns() // always ON as it handles ESCAPE button
|
||||
|
@ -1,28 +1,51 @@
|
||||
namespace sensors.internal {
|
||||
//% shim=pxt::unsafePollForChanges
|
||||
export function unsafePollForChanges(
|
||||
periodMs: number,
|
||||
query: () => number,
|
||||
changeHandler: (prev: number, curr: number) => void
|
||||
) {
|
||||
// This is implemented in C++ without blocking the regular JS when query() is runnning
|
||||
// which is generally unsafe. Query should not update globally visible state, and cannot
|
||||
// call any yielding functions, like sleep().
|
||||
export class Poller {
|
||||
private query: () => number;
|
||||
private update: (previous: number, current: number) => void;
|
||||
public interval: number;
|
||||
|
||||
// This is implementation for the simulator.
|
||||
private previousValue: number;
|
||||
private currentValue: number;
|
||||
private lastQuery: number; // track down the last time we did a query/update cycle
|
||||
private lastPause: number; // track down the last time we pause in the sensor polling loop
|
||||
|
||||
control.runInParallel(() => {
|
||||
let prev = query()
|
||||
changeHandler(prev, prev)
|
||||
while (true) {
|
||||
pause(periodMs)
|
||||
let curr = query()
|
||||
if (prev !== curr) {
|
||||
changeHandler(prev, curr)
|
||||
prev = curr
|
||||
}
|
||||
constructor(interval: number, query: () => number, update: (previous: number, current: number) => void) {
|
||||
this.interval = interval | 0;
|
||||
this.query = query;
|
||||
this.update = update;
|
||||
|
||||
this.poll();
|
||||
}
|
||||
|
||||
poke(): void {
|
||||
const now = control.millis();
|
||||
if (now - this.lastQuery >= this.interval * 2)
|
||||
this.queryAndUpdate(); // sensor poller is not allowed to run
|
||||
if (now - this.lastPause >= this.interval * 5)
|
||||
pause(1); // allow events to trigger
|
||||
}
|
||||
|
||||
private queryAndUpdate() {
|
||||
this.lastQuery = control.millis();
|
||||
this.currentValue = this.query();
|
||||
if (this.previousValue != this.currentValue) {
|
||||
this.update(this.previousValue, this.currentValue);
|
||||
this.previousValue = this.currentValue;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private poll() {
|
||||
control.runInBackground(() => {
|
||||
this.lastQuery = this.lastPause = control.millis();
|
||||
this.previousValue = this.currentValue = this.query();
|
||||
this.update(this.previousValue, this.currentValue);
|
||||
while (true) {
|
||||
this.lastPause = control.millis();
|
||||
pause(this.interval);
|
||||
this.queryAndUpdate();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function bufferToString(buf: Buffer): string {
|
||||
@ -38,6 +61,7 @@ namespace sensors.internal {
|
||||
let IICMM: MMap
|
||||
let powerMM: MMap
|
||||
let devcon: Buffer
|
||||
let devPoller: Poller
|
||||
let sensorInfos: SensorInfo[];
|
||||
|
||||
let batteryInfo: {
|
||||
@ -55,6 +79,7 @@ namespace sensors.internal {
|
||||
connType: number
|
||||
devType: number
|
||||
iicid: string
|
||||
poller: Poller;
|
||||
|
||||
constructor(p: number) {
|
||||
this.port = p
|
||||
@ -62,6 +87,20 @@ namespace sensors.internal {
|
||||
this.devType = DAL.DEVICE_TYPE_NONE
|
||||
this.iicid = ''
|
||||
this.sensors = []
|
||||
this.poller = new Poller(50, () => this.query(), (prev, curr) => this.update(prev, curr));
|
||||
}
|
||||
|
||||
poke() {
|
||||
this.poller.poke();
|
||||
}
|
||||
|
||||
private query() {
|
||||
if (this.sensor) return this.sensor._query();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private update(prev: number, curr: number) {
|
||||
if (this.sensor) this.sensor._update(prev, curr)
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,20 +121,11 @@ namespace sensors.internal {
|
||||
|
||||
powerMM = control.mmap("/dev/lms_power", 2, 0)
|
||||
|
||||
unsafePollForChanges(500,
|
||||
() => { return hashDevices(); },
|
||||
devPoller = new Poller(500, () => { return hashDevices(); },
|
||||
(prev, curr) => {
|
||||
detectDevices();
|
||||
});
|
||||
sensorInfos.forEach(info => {
|
||||
unsafePollForChanges(50, () => {
|
||||
if (info.sensor) return info.sensor._query()
|
||||
return 0
|
||||
}, (prev, curr) => {
|
||||
if (info.sensor) info.sensor._update(prev, curr)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getActiveSensors(): Sensor[] {
|
||||
init();
|
||||
@ -164,13 +194,13 @@ namespace sensors.internal {
|
||||
batteryVMax = ACCU_INDICATOR_HIGH;
|
||||
}
|
||||
}
|
||||
batteryInfo = {
|
||||
CinCnt: CinCnt,
|
||||
batteryInfo = {
|
||||
CinCnt: CinCnt,
|
||||
CoutCnt: CoutCnt,
|
||||
VinCnt: VinCnt
|
||||
};
|
||||
// update in background
|
||||
control.runInParallel(() => forever(updateBatteryInfo));
|
||||
control.runInParallel(() => forever(updateBatteryInfo));
|
||||
} else {
|
||||
CinCnt = batteryInfo.CinCnt = ((batteryInfo.CinCnt * (AVR_CIN - 1)) + CinCnt) / AVR_CIN;
|
||||
CoutCnt = batteryInfo.CoutCnt = ((batteryInfo.CoutCnt * (AVR_COUT - 1)) + CoutCnt) / AVR_COUT;
|
||||
@ -178,11 +208,11 @@ namespace sensors.internal {
|
||||
}
|
||||
}
|
||||
|
||||
export function getBatteryInfo(): {
|
||||
level: number;
|
||||
Ibatt: number,
|
||||
Vbatt: number,
|
||||
Imotor: number
|
||||
export function getBatteryInfo(): {
|
||||
level: number;
|
||||
Ibatt: number,
|
||||
Vbatt: number,
|
||||
Imotor: number
|
||||
} {
|
||||
init();
|
||||
if (!batteryInfo) updateBatteryInfo();
|
||||
@ -225,13 +255,13 @@ void cUiUpdatePower(void)
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
const CinV = CNT_V(CinCnt) / AMP_CIN;
|
||||
const Vbatt = CNT_V(VinCnt) / AMP_VIN + CinV + VCE;
|
||||
const Ibatt = CinV / SHUNT_IN;
|
||||
const CoutV = CNT_V(CoutCnt) / AMP_COUT;
|
||||
const Imotor = CoutV / SHUNT_OUT;
|
||||
const level = Math.max(0, Math.min(100, Math.floor((Vbatt * 1000.0 - batteryVMin)
|
||||
/ (batteryVMax - batteryVMin) * 100)));
|
||||
const CinV = CNT_V(CinCnt) / AMP_CIN;
|
||||
const Vbatt = CNT_V(VinCnt) / AMP_VIN + CinV + VCE;
|
||||
const Ibatt = CinV / SHUNT_IN;
|
||||
const CoutV = CNT_V(CoutCnt) / AMP_COUT;
|
||||
const Imotor = CoutV / SHUNT_OUT;
|
||||
const level = Math.max(0, Math.min(100, Math.floor((Vbatt * 1000.0 - batteryVMin)
|
||||
/ (batteryVMax - batteryVMin) * 100)));
|
||||
|
||||
return {
|
||||
level: level,
|
||||
@ -337,6 +367,11 @@ void cUiUpdatePower(void)
|
||||
this.markUsed();
|
||||
}
|
||||
|
||||
poke() {
|
||||
if (this.isActive())
|
||||
sensorInfos[this._port].poke();
|
||||
}
|
||||
|
||||
markUsed() {
|
||||
sensors.__sensorUsed(this._port, this._deviceType());
|
||||
}
|
||||
|
@ -446,11 +446,6 @@ static void runPoller(Thread *thr) {
|
||||
// disposeThread(thr);
|
||||
}
|
||||
|
||||
//%
|
||||
void unsafePollForChanges(int ms, Action query, Action handler) {
|
||||
setupThread(handler, 0, runPoller, query, fromInt(ms));
|
||||
}
|
||||
|
||||
uint32_t afterProgramPage() {
|
||||
return 0;
|
||||
}
|
||||
|
@ -123,7 +123,8 @@ namespace motors {
|
||||
//% help=motors/stop-all
|
||||
export function stopAll() {
|
||||
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
|
||||
writePWM(b)
|
||||
writePWM(b);
|
||||
pause(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,6 +133,7 @@ namespace motors {
|
||||
//% group="Move"
|
||||
export function resetAllMotors() {
|
||||
reset(Output.ALL)
|
||||
pause(1);
|
||||
}
|
||||
|
||||
interface MoveSchedule {
|
||||
@ -259,6 +261,8 @@ namespace motors {
|
||||
// allow 500ms for robot to settle
|
||||
if (this._brake && this._brakeSettleTime > 0)
|
||||
pause(this._brakeSettleTime);
|
||||
else
|
||||
pause(1); // give a tiny breather
|
||||
}
|
||||
|
||||
protected pauseOnRun(stepsOrTime: number) {
|
||||
@ -267,6 +271,9 @@ namespace motors {
|
||||
this.pauseUntilReady();
|
||||
// allow robot to settle
|
||||
this.settle();
|
||||
} else {
|
||||
// give a breather to the event system in tight loops
|
||||
pause(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,6 +341,7 @@ namespace motors {
|
||||
// special: 0 is infinity
|
||||
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
|
||||
this._run(schedule.speed);
|
||||
pause(1);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -697,7 +705,7 @@ namespace motors {
|
||||
this.init();
|
||||
speed = Math.clamp(-100, 100, speed >> 0);
|
||||
if (!speed) {
|
||||
stop(this._port, this._brake);
|
||||
this.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ namespace sensors {
|
||||
//% weight=64 blockGap=8
|
||||
//% group="Gyro Sensor"
|
||||
angle(): number {
|
||||
this.poke();
|
||||
if (this.calibrating)
|
||||
pauseUntil(() => !this.calibrating, 2000);
|
||||
|
||||
@ -65,6 +66,7 @@ namespace sensors {
|
||||
//% weight=65 blockGap=8
|
||||
//% group="Gyro Sensor"
|
||||
rate(): number {
|
||||
this.poke();
|
||||
if (this.calibrating)
|
||||
pauseUntil(() => !this.calibrating, 2000);
|
||||
|
||||
|
@ -235,6 +235,7 @@ namespace sensors {
|
||||
//% group="Infrared Sensor"
|
||||
//% this.fieldEditor="ports"
|
||||
proximity(): number {
|
||||
this.poke();
|
||||
this._setMode(InfraredSensorMode.Proximity)
|
||||
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||
}
|
||||
@ -284,6 +285,7 @@ namespace sensors {
|
||||
|
||||
// TODO
|
||||
private getDirectionAndDistance() {
|
||||
this.poke();
|
||||
this._setMode(InfraredSensorMode.Seek)
|
||||
return this.getNumber(NumberFormat.UInt16LE, this._channel * 2)
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ namespace sensors {
|
||||
//% weight=81 blockGap=8
|
||||
//% group="Touch Sensor"
|
||||
isPressed() {
|
||||
this.poke();
|
||||
return this.button.isPressed();
|
||||
}
|
||||
|
||||
@ -90,6 +91,7 @@ namespace sensors {
|
||||
//% weight=81
|
||||
//% group="Touch Sensor"
|
||||
wasPressed() {
|
||||
this.poke();
|
||||
return this.button.wasPressed();
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ namespace sensors {
|
||||
//% weight=65
|
||||
//% group="Ultrasonic Sensor"
|
||||
distance(): number {
|
||||
this.poke();
|
||||
// it supposedly also has an inch mode, but we stick to cm
|
||||
this._setMode(0)
|
||||
return this._query();
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pxt-ev3",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.5",
|
||||
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
|
||||
"private": false,
|
||||
"keywords": [
|
||||
@ -40,7 +40,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"pxt-common-packages": "0.23.61",
|
||||
"pxt-core": "4.0.9"
|
||||
"pxt-core": "4.0.10"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
||||
|
@ -22,6 +22,6 @@
|
||||
"Videos": "videos"
|
||||
},
|
||||
"electronManifest": {
|
||||
"latest": "v1.0.11"
|
||||
"latest": "v1.1.20"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user