Compare commits

...

62 Commits

Author SHA1 Message Date
668d814559 merge master 2019-10-31 10:26:50 -07:00
c3f539f396 Bump pxt 5.28.7 (#954)
* bump

* bump pxt
2019-10-31 10:22:37 -07:00
991813fff7 update elecron version to 1.2.26 (#953) 2019-10-23 11:22:30 -07:00
85a8f1c2c5 bump to v1.2.26 2019-10-20 16:37:57 -07:00
ea33b81b2a is color detectedd 2019-10-20 09:31:09 -07:00
c8381d7626 1.2.26 2019-10-20 09:02:44 -07:00
62b5941143 is color detected (#952)
* added query function

* reshuffle
2019-10-20 09:02:17 -07:00
1d5d18dc88 1.2.25 2019-10-18 22:53:46 -07:00
bc4b71e0d6 Pulling updates from master (#951)
* show ports on start

* don't run show ports automatically

* polarity in synched motor (#945)

* account for polarity

* more comments

* handle dual motor in runtime

* invert steer

* don't use firmware polarity

* add block to stop program (#943)

* add block to stop program

* renaming

* fix translation
2019-10-18 22:53:30 -07:00
5c47ce2f91 add stable path 2019-10-18 22:53:17 -07:00
6a577287c1 1.4.8 2019-10-16 21:24:06 -07:00
8bd884e155 bump blockly 2019-10-16 21:23:14 -07:00
20be398c2c refactor constants 2019-10-13 09:32:43 -07:00
ccf6164d87 logging 2019-10-13 09:22:07 -07:00
58294883d7 blink fix (#949) 2019-10-13 00:36:44 -07:00
00194d4aa6 1.2.24 2019-10-13 00:36:00 -07:00
ec900f805e 1.2.23 2019-10-13 00:35:47 -07:00
5f5bdd7294 blink fix (#949) 2019-10-13 00:35:20 -07:00
953bf27bc5 logging 2019-10-13 00:05:26 -07:00
d4ee9d2d62 various fixes 2019-10-13 00:01:34 -07:00
38b2fc7b95 Add run it again to try tutorial to show where to find programs on brick (#947) 2019-10-12 15:36:13 -07:00
8cc2f1219f 1.4.7 2019-10-11 21:56:27 -07:00
39a629cc58 bump pxt 5.25.13 2019-10-11 21:50:00 -07:00
df65c34f15 1.4.6 2019-10-10 09:16:19 -07:00
00fefe10d6 Boot sequence (#946)
* refactor bootsequence

* inf can be null

* align elements
2019-10-10 09:15:57 -07:00
d442f5aa41 don't ban image 2019-10-10 08:00:21 -07:00
3311865817 1.4.5 2019-10-10 07:45:53 -07:00
2cd2950496 polarity in synched motor (#945)
* account for polarity

* more comments

* handle dual motor in runtime

* invert steer

* don't use firmware polarity
2019-10-10 07:44:53 -07:00
b0de3d8c1b 1.4.4 2019-10-09 15:40:54 -07:00
acefe3ae11 bump pxt 2019-10-09 15:40:45 -07:00
cb6c83eec7 Add sources for the file manager program (#944) 2019-10-09 13:57:06 -07:00
e0c8f65a65 1.4.3 2019-10-09 09:57:53 -07:00
a4e02dcd03 add block to stop program (#943)
* add block to stop program

* renaming
2019-10-09 09:30:42 -07:00
fb5776ec41 1.4.2 2019-10-08 22:19:50 -07:00
d852fd961b fix python build 2019-10-08 22:19:34 -07:00
1ed8122804 1.4.1 2019-10-08 21:58:33 -07:00
c5cec3a6ba update to pxt 5 and pxt-common-packages 6 (#934)
* bump pxt

* fix build issues

* Auto-gen of projects/summary

* removing feild editors moved to pxt

* various typing fixes

* more typing fixes

* fixing various typing issues

* Start on integration of new pxt

* serial number fixes

* gc-ify MMap object

* Re-build generated files

* fix console listeners

* clear lf() warnings

* More generated files

* also auto-generated

* Compilation fixes

* fix merge

* mostly fixing blocks

* fix sim

* fix field motors

* enable a few features

* moving to tsx

* try to fix edtiro compilation

* more defs

* removing commands

* removing extra $

* fix blockly warning

* hiding images

* enabling more pxt features

* hide images

* setup autorun

* add lock on target_reset

* update deps

* return trylock result

* updated pxt

* rename video section

* add alpha channel

* upgraded pxt

* bump pxt/version

* removed alpha ref

* var ceanup

* don't do major bump
2019-10-08 21:57:55 -07:00
ba94322d4c 1.2.23 2019-10-08 21:26:38 -07:00
225f1da6d5 fix translation 2019-10-08 21:26:17 -07:00
a06331eef1 added channel 2019-10-08 14:23:57 -07:00
aa741ce8de rename again 2019-10-08 14:21:55 -07:00
61fe5091fe rename 2019-10-08 14:21:45 -07:00
3d90e13797 shrinkg image 2019-10-08 14:21:12 -07:00
469767a40a Add information to Try example to show where to find downloaded programs on the control brick. (#936) 2019-10-08 14:12:43 -07:00
604fa764e6 Create calibreapp-image-actions.yml 2019-10-08 14:03:58 -07:00
25cf2a9cdb move electron to 1.2.22 (#937) 2019-10-07 14:43:34 -07:00
7549f865d6 move master to 1.2.22 2019-10-02 13:59:58 -07:00
a5de9d88bb adding latest video 2019-10-02 13:53:22 -07:00
0437df10de 1.2.22 2019-10-02 13:27:53 -07:00
2079173dfa lock execution thread before exiting (#935) 2019-10-02 12:40:45 -07:00
018a1e7528 added proportional line follower 2019-10-02 12:26:51 -07:00
757f95d984 move master to v1.2.21 2019-10-02 10:59:50 -07:00
8bc3fdc8ba 1.2.21 2019-10-01 15:56:22 -07:00
e8a1e73cf5 1.2.20 2019-10-01 14:54:42 -07:00
e9b2b239ad More gyro lessons (#933)
* updated pauseuntil

* updated youtube link

* updated docs
2019-10-01 14:54:17 -07:00
5ad2288a9f updated docs 2019-10-01 13:38:33 -07:00
92d13ef343 updated bluetooth info 2019-10-01 13:36:53 -07:00
471ca5d915 1.2.19 2019-10-01 13:28:01 -07:00
f745079728 Don't reset threashold when resetting color mode (#932)
* don't reset threshold when changing modes

* updated docs
2019-10-01 13:27:37 -07:00
d179f18ef3 added gyro movies 2019-10-01 11:05:44 -07:00
805fc6c787 1.2.18 2019-10-01 10:12:21 -07:00
374bbb8304 Drift-compensated angle in gyro (#931)
* compute angle based on undrifted rate

* add is calibrating function

* fix integrator

* added example

* docs

* poll faster
2019-10-01 10:11:58 -07:00
78 changed files with 1758 additions and 1391 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ clients/**/bin/**
clients/**/obj/**
clients/electron/projects
libs/**/_locales/**
package-lock.json
videos/**

View File

@ -1,14 +0,0 @@
/// <reference path="../node_modules/pxt-core/built/pxtlib.d.ts" />
import * as fs from 'fs';
const deploy = require("./editor/deploy")
export function deployCoreAsync(resp: pxtc.CompileResult) {
return deploy.deployCoreAsync(resp, process.env["PXT_SERIAL"] ? false : true)
.then(() => {
fs.writeFileSync("built/full-" + pxtc.BINARY_UF2, resp.outfiles[pxtc.BINARY_UF2], {
encoding: "base64"
})
})
}

View File

@ -1,14 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"noImplicitAny": true,
"noImplicitReturns": true,
"declaration": true,
"outDir": "../built",
"module": "commonjs",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false,
"types": ["node"]
}
}

View File

@ -38,10 +38,7 @@ Next you need to enable the experimental features (this may change in the future
## Download over Bluetooth
* go to the **beta** editor https://makecode.mindstorms.com/beta
This feature is not yet released so make sure to use the beta editor.
* go to https://makecode.mindstorms.com/
* click on **Download** to start a file download as usual
* on the download dialog, you should see a **Bluetooth** button. Click on the
**Bluetooth** button to enable the mode.
@ -51,8 +48,9 @@ This feature is not yet released so make sure to use the beta editor.
## Choosing the correct serial port
Unfortunately, the browser dialog does not make it easy to select which serial port is the brick.
On Windows, it typically reads "Standard Serial over Bluetooth" and you may
have multiple of those if you've paired different bricks.
* On Windows, choose ``Standard Serial over Bluetooth``. There might be multiple of those but only one works. Try your luck! Once you know the COM port number, remember it for the next time around.
* On Mac OS, choose ``cu.BRICKNAME-SerialPort``
## Known issues

View File

@ -35,6 +35,13 @@ On the home page, scroll down to the **FLL / City Shaper** section for specific
Yes.
### Can I run the program again on the brick?
![EV3 Brick with Try in BrkProg_Save Folder in File Manager](/static/getting-started/try-in-file-manager.png)
Use the Brick Buttons and navigate to the File Manager tab. Open the **BrkProg_SAVE** folder,
select your program and click the center button to run it again.
### Does it work without internet?
No, the editor is cached in your browser cache. However, you can also download the [offline app](/offline-app) in case you need to install it on a computer.
@ -98,6 +105,12 @@ The official answer is currently no. That being said, we have **Experimental sup
https://youtu.be/VIq8-6Egtqs
## Are there YouTube videos on MakeCode for EV3?
The MakeCode has a [FLL / City Shaper YouTube Channel](https://www.youtube.com/watch?v=IqL0Pyeu5Ng&list=PLMMBk9hE-SeqkOObethhlZtBTEK6FYx3n) with useful videos.
https://youtu.be/-AirqwC9DL4
### Why can't I delete my program (*.uf2) files from the Brick?
There's a bug in the firmware which prevents you from deleting the programs (``*.uf2`` files) from your EV3 Brick. There isn't a firmware update to fix this yet.

View File

@ -73,6 +73,12 @@ Verify that the program you just created shows eyes on the Brick Display, and th
**Well done!**
## Run it Again
![EV3 Brick with Try in BrkProg_Save Folder in File Manager](/static/getting-started/try-in-file-manager.png)
Use the Brick Buttons and navigate to the File Manager tab. Open the **BrkProg_SAVE** folder, select **Try** and click the center button to run it again.
## Connect a Large Motor @unplugged
Now you will learn to control the Large Motor.

View File

@ -54,6 +54,12 @@ Verify that the program you just created shows eyes on the Brick Display, and th
**Well done!**
## Run it Again
![EV3 Brick with Try in BrkProg_Save Folder in File Manager](/static/getting-started/try-in-file-manager.png)
Use the Brick Buttons and navigate to the File Manager tab. Open the **BrkProg_SAVE** folder, select **Try** and click the center button to run it again.
## Connect a Large Motor
Now you will learn to control the Large Motor.

View File

@ -1,3 +1,3 @@
{
"appref": "v1.2.15"
"appref": "v1.2.26"
}

View File

@ -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.1.20/win64");
window.open("https://makecode.com/api/release/ev3/v1.2.26/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.1.20/mac64");
window.open("https://makecode.com/api/release/ev3/v1.2.26/mac64");
tickEvent("offlineapp.download", { "target": "ev3", "platform": "mac64" });
}
</script>

View File

@ -1,9 +1,88 @@
# Projects
Here are some cool projects that you can build with your @boardname@!
```codecard
[
{
"name": "Getting Started",
"url": "/getting-started",
"imageUrl": "/static/lessons/firmware.png"
},
{
"name": "Brick Tutorials",
"url": "/tutorials/brick",
"imageUrl": "/static/tutorials/wake-up.png"
},
{
"name": "Motor Tutorials",
"url": "/tutorials/motors",
"imageUrl": "/static/tutorials/run-motors.png"
},
{
"name": "Touch Sensor Tutorials",
"url": "/tutorials/touch-sensor",
"imageUrl": "/static/tutorials/touch-to-run.png"
},
{
"name": "Color Sensor Tutorials",
"url": "/tutorials/color-sensor",
"imageUrl": "/static/tutorials/what-color.png"
},
{
"name": "Ultrasonic Sensor Tutorials",
"url": "/tutorials/ultrasonic-sensor",
"imageUrl": "/static/tutorials/object-near.png"
},
{
"name": "Gyro Tutorials",
"url": "/tutorials/gyro",
"imageUrl": "/static/tutorials/calibrate-gyro.png"
},
{
"name": "Infrared Sensor Tutorials",
"url": "/tutorials/infrared-sensor",
"imageUrl": "/static/tutorials/security-alert.png"
},
{
"name": "FLL / City Shaper",
"url": "/tutorials/city-shaper",
"imageUrl": "/static/tutorials/city-shaper/robot1.jpg"
},
{
"name": "Design Engineering",
"url": "/design-engineering",
"imageUrl": "/static/lessons/make-it-move-without-wheels.png"
},
{
"name": "Coding",
"url": "/coding",
"imageUrl": "/static/lessons/autonomous-parking.png"
},
{
"name": "Maker",
"url": "/maker",
"imageUrl": "/static/lessons/make-a-sound-machine.png"
},
{
"name": "Tutorial Videos",
"url": "/videos",
"imageUrl": "https://legoeducation.videomarketingplatform.co/27288170/35719444/5d009e5f93fbf479c2e5ed2bf87a7990/thumbnail.png"
}
]
```
## Basic
## See Also
Basic projects to build with your EV3 Brick.
[Getting Started](/getting-started),
[Brick Tutorials](/tutorials/brick),
[Motor Tutorials](/tutorials/motors),
[Touch Sensor Tutorials](/tutorials/touch-sensor),
[Color Sensor Tutorials](/tutorials/color-sensor),
[Ultrasonic Sensor Tutorials](/tutorials/ultrasonic-sensor),
[Gyro Tutorials](/tutorials/gyro),
[Infrared Sensor Tutorials](/tutorials/infrared-sensor),
[FLL / City Shaper](/tutorials/city-shaper),
[Design Engineering](/design-engineering),
[Coding](/coding),
[Maker](/maker),
[Tutorial Videos](/videos)
Coming soon.

69
docs/projects/SUMMARY.md Normal file
View File

@ -0,0 +1,69 @@
# Projects
* [Getting Started](/getting-started)
* [Prepare](https://makecode.mindstorms.com/troubleshoot)
* [Try](/getting-started/try)
* [Use](/getting-started/use)
* [First LEGO League](/fll)
* [Brick Tutorials](/tutorials/brick)
* [Wake Up!](/tutorials/wake-up)
* [Make an Animation](/tutorials/make-an-animation)
* [What Animal Am I?](/tutorials/what-animal-am-i)
* [Music Brick](/tutorials/music-brick)
* [Pause On Start](/tutorials/pause-on-start)
* [Motor Tutorials](/tutorials/motors)
* [Run Motors](/tutorials/run-motors)
* [Spin Turn](/tutorials/spin-turn)
* [Pivot Turn](/tutorials/pivot-turn)
* [Smooth Turn](/tutorials/smooth-turn)
* [Tank ZigZag](/tutorials/tank-zigzag)
* [Coast Or Brake](/tutorials/coast-or-brake)
* [Turtle](/tutorials/turtle)
* [Touch Sensor Tutorials](/tutorials/touch-sensor)
* [Touch to Run](/tutorials/touch-to-run)
* [Sensor Values](/tutorials/touch-sensor-values)
* [Stop At Object](/tutorials/stop-at-object)
* [Color Sensor Tutorials](/tutorials/color-sensor)
* [What Color?](/tutorials/what-color)
* [Line Following](/tutorials/line-following)
* [Red Light, Green Light](/tutorials/redlight-greenlight)
* [Move To Color](/tutorials/move-to-color)
* [Reflected Light Measure](/tutorials/reflected-light-measure)
* [Reflected Light Calibration](/tutorials/reflected-light-calibration)
* [Ultrasonic Sensor Tutorials](/tutorials/ultrasonic-sensor)
* [Object Near](/tutorials/object-near)
* [Wall Follower](/tutorials/wall-follower)
* [Gyro Tutorials](/tutorials/gyro)
* [Calibrate](/tutorials/calibrate-gyro)
* [Turn](/tutorials/turn-with-gyro)
* [Move Straight](/tutorials/move-straight-with-gyro)
* [Drifter](/tutorials/drifter)
* [Infrared Sensor Tutorials](/tutorials/infrared-sensor)
* [Security Alert](/tutorials/security-alert)
* [FLL / City Shaper](/tutorials/city-shaper)
* [Crane Mission / Robot 1](/tutorials/city-shaper/robot-1)
* [Crane Mission / Robot 2](/tutorials/city-shaper/robot-2)
* [Crane Mission / Video 1](https://youtu.be/IqL0Pyeu5Ng)
* [Bluetooth download (beta)](https://youtu.be/VIq8-6Egtqs)
* [Turn with Gyro](https://youtu.be/I7ncuXAfBwk)
* [Moving with Gyro](https://youtu.be/ufiOPvW37xc)
* [Line following with 1 color sensor](https://youtu.be/_LeduyKQVjg)
* [Proportional line following with 1 color sensor](https://youtu.be/-AirqwC9DL4)
* [Proportional line following with 2 color sensors](https://youtu.be/QWOflBuu9Oo)
* [Design Engineering](/design-engineering)
* [Make It Move Without Wheels](/design-engineering/make-it-move)
* [Make It Smarter and Faster](/design-engineering/make-it-smarter)
* [Make a System that Communicates](/design-engineering/make-it-communicate)
* [Coding](/coding)
* [Autonomous Parking](/coding/autonomous-parking)
* [Object Detection](/coding/object-detection)
* [Line Detection](/coding/line-detection)
* [Maker](/maker)
* [Make A Sound Machine](/maker/sound-machine)
* [Make A Security Gadget](/maker/security-gadget)
* [Tutorial Videos](/videos)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=5d009e5f93fbf479c2e5ed2bf87a7990&source=embed&photo%5fid=35719444)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=2008a566f1fb034d58d5ebe19ba8621f&source=embed&photo%5fid=35719467)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=629730c938e452f0fd7653fbc4708166&source=embed&photo%5fid=35719470)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=3513a83b87fe536b2dc512237465fd1b&source=embed&photo%5fid=35719471)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=5c594c2373367f7870196f519f3bfc7a&source=embed&photo%5fid=35719472)

View File

@ -0,0 +1,18 @@
# exit Program
Stops the program and returns to the brick menu
```sig
brick.exitProgram();
```
## Example
Do a sequence of motor commands and stop the program.
```blocks
motors.largeA.run(50)
pause(500)
motors.stopAll()
brick.exitProgram();
```

3
docs/stable-ref.json Normal file
View File

@ -0,0 +1,3 @@
{
"appref": "v1.2"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
docs/static/tutorials/drifter.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/static/tutorials/turn-with-gyro.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -24,6 +24,26 @@
"name": "Bluetooth download (beta)",
"description": "EXPERIMENTAL! Learn how to download code via Bluetooth.",
"youTubeId": "VIq8-6Egtqs"
}, {
"name": "Turn with Gyro",
"description": "Use the gyro for precise turns.",
"youTubeId": "I7ncuXAfBwk"
}, {
"name": "Moving with Gyro",
"description": "Use the gyro for correct the robot trajectory.",
"youTubeId": "ufiOPvW37xc"
}, {
"name": "Line following with 1 color sensor",
"description": "Simple line following using the color sensor.",
"youTubeId": "_LeduyKQVjg"
}, {
"name": "Proportional line following with 1 color sensor",
"description": "Proportional line following using the color sensor.",
"youTubeId": "-AirqwC9DL4"
}, {
"name": "Proportional line following with 2 color sensors",
"description": "Proportional line following using two color sensor.",
"youTubeId": "QWOflBuu9Oo"
}]
```

34
docs/tutorials/drifter.md Normal file
View File

@ -0,0 +1,34 @@
# Drifter
Use this program to try out the gyro sensor and the effect of drifting.
```typescript
// this loop shows the rate, angle and drift of the robot
forever(() => {
brick.showValue("rate", sensors.gyro2.rate(), 1)
brick.showValue("angle", sensors.gyro2.angle(), 2)
brick.showValue("drift", sensors.gyro2.drift(), 3)
})
// this loop shows wheter the sensor is calibrating
forever(() => {
brick.showString(sensors.gyro2.isCalibrating() ? "calibrating..." : "", 4)
})
// instructions on how to use the buttons
brick.showString("ENTER: calibrate", 7)
brick.showString(" (reset+drift)", 8)
brick.showString("LEFT: reset", 9)
brick.showString("RIGHT: compute drift", 10)
// enter -> calibrate
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.calibrate()
})
// right -> compute drift
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.computeDrift()
})
// left -> reset
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.reset()
})
```

View File

@ -9,11 +9,23 @@
"cardType": "tutorial",
"url":"/tutorials/calibrate-gyro",
"imageUrl":"/static/tutorials/calibrate-gyro.png"
}, {
"name": "Turn",
"description": "Use the gyro to turn precisely",
"cardType": "tutorial",
"url":"/tutorials/turn-with-gyro",
"imageUrl":"/static/tutorials/turn-with-gyro.png"
}, {
"name": "Move Straight",
"description": "Use the gyro to correct the trajectory of the robot",
"cardType": "tutorial",
"url":"/tutorials/move-straight-with-gyro",
"imageUrl":"/static/tutorials/move-straight-with-gyro.png"
}, {
"name": "Drifter",
"description": "Explore how the gyro is drifting",
"cardType": "example",
"url":"/tutorials/drifter",
"imageUrl":"/static/tutorials/drifter.png"
}]
```

View File

@ -0,0 +1,43 @@
# Turn With Gyro
## Introduction @fullscreen
Use the gyro to measure how much the robot is turning, regardless if your wheels are slipping.
## Step 1 Calibrate
Add the ``||sensors:calibrate gyro||`` block to make sure your gyro is ready to use.
```blocks
sensors.gyro2.calibrate()
```
## Step 2 Turn
Use motor blocks to make the robot turn. Don't go too fast!
```blocks
sensors.gyro2.calibrate()
motors.largeBC.steer(200, 20)
```
## Step 3 Pause for turn
Use the ``||sensors:pause until rotated||`` block to wait until the desired amount of rotation has occured.
```blocks
sensors.gyro2.calibrate()
motors.largeBC.steer(200, 20)
sensors.gyro2.pauseUntilRotated(90)
```
## Step 4 Stop
Stop the motors!
```blocks
sensors.gyro2.calibrate()
motors.largeBC.steer(200, 20)
sensors.gyro2.pauseUntilRotated(90)
motors.stopAll()
```

View File

@ -3,20 +3,15 @@
import UF2 = pxtc.UF2;
import { Ev3Wrapper } from "./wrap";
import { bluetoothTryAgainAsync } from "./dialogs";
export let ev3: Ev3Wrapper;
let confirmAsync: (options: any) => Promise<number>;
export function setConfirmAsync(cf: (options: any) => Promise<number>) {
confirmAsync = cf;
}
export function debug() {
return initHidAsync()
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
}
// Web Serial API https://wicg.github.io/serial/
// chromium bug https://bugs.chromium.org/p/chromium/issues/detail?id=884928
// Under experimental features in Chrome Desktop 77+
@ -167,7 +162,7 @@ export function initAsync(): Promise<void> {
useHID = false
} else {
const nodehid = /nodehid/i.test(window.location.href);
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && nodehid)
if (pxt.BrowserUtils.isLocalHost() && pxt.Cloud.localToken && nodehid)
useHID = true;
}
@ -283,22 +278,9 @@ export function deployCoreAsync(resp: pxtc.CompileResult) {
return w.reconnectAsync(false)
.catch(e => {
// user easily forgets to stop robot
if (confirmAsync)
return confirmAsync({
header: lf("Bluetooth download failed..."),
htmlBody:
`<ul>
<li>${lf("Make sure to stop your program or exit portview on the EV3.")}</li>
<li>${lf("Check your battery level.")}</li>
<li>${lf("Close EV3 LabView or other MakeCode editor tabs.")}
</ul>`,
hasCloseIcon: true,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("Try again"),
}).then(() => w.disconnectAsync())
.then(() => Promise.delay(1000))
.then(() => w.reconnectAsync());
bluetoothTryAgainAsync().then(() => w.disconnectAsync())
.then(() => Promise.delay(1000))
.then(() => w.reconnectAsync());
// nothing we can do
return Promise.reject(e);

145
editor/dialogs.tsx Normal file
View File

@ -0,0 +1,145 @@
import * as React from "react";
import { canUseWebSerial, enableWebSerialAsync } from "./deploy";
import { projectView } from "./extension";
let confirmAsync: (options: any) => Promise<number>;
export function bluetoothTryAgainAsync(): Promise<void> {
return confirmAsync({
header: lf("Bluetooth download failed..."),
jsx: <ul>
<li>{lf("Make sure to stop your program or exit portview on the EV3.")}</li>
<li>{lf("Check your battery level.")}</li>
<li>{lf("Close EV3 LabView or other MakeCode editor tabs.")}</li>
</ul>,
hasCloseIcon: false,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("Try again")
}).then(r => {});
}
function enableWebSerialAndCompileAsync() {
return enableWebSerialAsync()
.then(() => Promise.delay(500))
.then(() => projectView.compile());
}
let bluetoothDialogShown = false;
function explainWebSerialPairingAsync(): Promise<void> {
if (!confirmAsync || bluetoothDialogShown) return Promise.resolve();
bluetoothDialogShown = true;
return confirmAsync({
header: lf("Bluetooth pairing"),
hasCloseIcon: false,
hideCancel: true,
buttons: [{
label: lf("Help"),
icon: "question circle",
className: "lightgrey",
url: "/bluetooth"
}],
jsx: <p>
{lf("You will be prompted to select a serial port.")}
{pxt.BrowserUtils.isWindows()
? lf("Look for 'Standard Serial over Bluetooth link'.")
: lf("Loop for 'cu.EV3-SerialPort'.")}
{lf("If you have paired multiple EV3, you might have to try out multiple ports until you find the correct one.")}
</p>
}).then(() => { })
}
export function showUploadDialogAsync(fn: string, url: string, _confirmAsync: (options: any) => Promise<number>): Promise<void> {
confirmAsync = _confirmAsync;
// https://msdn.microsoft.com/en-us/library/cc848897.aspx
// "For security reasons, data URIs are restricted to downloaded resources.
// Data URIs cannot be used for navigation, for scripting, or to populate frame or iframe elements"
const downloadAgain = !pxt.BrowserUtils.isIE() && !pxt.BrowserUtils.isEdge();
const docUrl = pxt.appTarget.appTheme.usbDocs;
const jsx =
<div className="ui grid stackable">
<div className="column five wide" style={{ backgroundColor: "#E2E2E2" }}>
<div className="ui header">{lf("First time here?")}</div>
<strong style={{ fontSize: "small" }}>{lf("You must have version 1.10E or above of the firmware")}</strong>
<div style={{ justifyContent: "center", display: "flex", padding: "1rem" }}>
<img className="ui image" src="/static/download/firmware.png" style={{ height: "100px" }} />
</div>
<a href="/troubleshoot" target="_blank">{lf("Check your firmware version here and update if needed")}</a>
</div>
<div className="column eleven wide">
<div className="ui grid">
<div className="row">
<div className="column">
<div className="ui two column grid padded">
<div className="column">
<div className="ui">
<div className="image">
<img className="ui medium rounded image" src="/static/download/connect.svg" style={{ height: "109px", width: "261px", marginBottom: "1rem" }} />
</div>
<div className="content">
<div className="description">
<span className="ui yellow circular label">1</span>
<strong>{lf("Connect the EV3 to your computer with a USB cable")}</strong>
<br />
<span style={{ fontSize: "small" }}>{lf("Use the miniUSB port on the top of the EV3 Brick")}</span>
</div>
</div>
</div>
</div>
<div className="column">
<div className="ui">
<div className="image">
<img className="ui medium rounded image" src="/static/download/transfer.svg" style={{ height: "109px", width: "261px", marginBottom: "1rem" }} />
</div>
<div className="content">
<div className="description">
<span className="ui yellow circular label">2</span>
<strong>{lf("Move the .uf2 file to the EV3 Brick")}</strong>
<br />
<span style={{ fontSize: "small" }}>{lf("Locate the downloaded .uf2 file and drag it to the EV3 USB drive")}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>;
return confirmAsync({
header: lf("Download to your EV3"),
jsx,
hasCloseIcon: true,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("I got it"),
className: 'downloaddialog',
buttons: [canUseWebSerial() ? {
label: lf("Bluetooth"),
icon: "bluetooth",
className: "bluetooth focused",
onclick: () => {
pxt.tickEvent("bluetooth.enable");
explainWebSerialPairingAsync()
.then(() => enableWebSerialAndCompileAsync())
.done();
}
} : undefined, downloadAgain ? {
label: fn,
icon: "download",
className: "lightgrey focused",
url,
fileName: fn
} : undefined, docUrl ? {
label: lf("Help"),
icon: "help",
className: "lightgrey",
url: docUrl
} : undefined]
//timeout: 20000
}).then(() => { });
}

View File

@ -1,144 +1,26 @@
/// <reference path="../node_modules/pxt-core/localtypings/pxtarget.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxtcompiler.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxtlib.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
import { deployCoreAsync, initAsync, canUseWebSerial, enableWebSerialAsync, setConfirmAsync } from "./deploy";
import { deployCoreAsync, initAsync } from "./deploy";
import { showUploadDialogAsync } from "./dialogs";
export let projectView: pxt.editor.IProjectView;
let bluetoothDialogShown = false;
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
const projectView = opts.projectView;
pxt.debug('loading pxt-ev3 target extensions...')
function enableWebSerialAndCompileAsync() {
return enableWebSerialAsync()
.then(() => Promise.delay(500))
.then(() => projectView.compile());
}
projectView = opts.projectView;
const res: pxt.editor.ExtensionResult = {
deployCoreAsync,
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
setConfirmAsync(confirmAsync);
// https://msdn.microsoft.com/en-us/library/cc848897.aspx
// "For security reasons, data URIs are restricted to downloaded resources.
// Data URIs cannot be used for navigation, for scripting, or to populate frame or iframe elements"
const downloadAgain = !pxt.BrowserUtils.isIE() && !pxt.BrowserUtils.isEdge();
const docUrl = pxt.appTarget.appTheme.usbDocs;
const htmlBody = `
<div class="ui grid stackable">
<div class="column five wide" style="background-color: #E2E2E2;">
<div class="ui header">${lf("First time here?")}</div>
<strong style="font-size:small">${lf("You must have version 1.10E or above of the firmware")}</strong>
<div style="justify-content: center;display: flex;padding: 1rem;">
<img class="ui image" src="/static/download/firmware.png" style="height:100px;" />
</div>
<a href="/troubleshoot" target="_blank">${lf("Check your firmware version here and update if needed")}</a>
</div>
<div class="column eleven wide">
<div class="ui grid">
<div class="row">
<div class="column">
<div class="ui two column grid padded">
<div class="column">
<div class="ui">
<div class="image">
<img class="ui medium rounded image" src="/static/download/connect.svg" style="height:109px;width:261px;margin-bottom:1rem;" />
</div>
<div class="content">
<div class="description">
<span class="ui yellow circular label">1</span>
<strong>${lf("Connect the EV3 to your computer with a USB cable")}</strong>
<br />
<span style="font-size:small">${lf("Use the miniUSB port on the top of the EV3 Brick")}</span>
</div>
</div>
</div>
</div>
<div class="column">
<div class="ui">
<div class="image">
<img class="ui medium rounded image" src="/static/download/transfer.svg" style="height:109px;width:261px;margin-bottom:1rem;" />
</div>
<div class="content">
<div class="description">
<span class="ui yellow circular label">2</span>
<strong>${lf("Move the .uf2 file to the EV3 Brick")}</strong>
<br />
<span style="font-size:small">${lf("Locate the downloaded .uf2 file and drag it to the EV3 USB drive")}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>`;
return confirmAsync({
header: lf("Download to your EV3"),
htmlBody,
hasCloseIcon: true,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("I got it"),
className: 'downloaddialog',
buttons: [canUseWebSerial() ? {
label: lf("Bluetooth"),
icon: "bluetooth",
className: "bluetooth focused",
onclick: () => {
pxt.tickEvent("bluetooth.enable");
if (bluetoothDialogShown) {
enableWebSerialAndCompileAsync().done();
} else {
bluetoothDialogShown = true;
confirmAsync({
header: lf("Bluetooth pairing"),
hasCloseIcon: true,
hideCancel: true,
buttons: [{
label: lf("Help"),
icon: "question circle",
className: "lightgrey",
url: "/bluetooth"
}],
htmlBody: `<p>
${lf("You will be prompted to select a serial port.")}
${pxt.BrowserUtils.isWindows()
? lf("Look for 'Standard Serial over Bluetooth link'.")
: lf("Loop for 'cu.EV3-SerialPort'.")}
${lf("If you have paired multiple EV3, you might have to try out multiple ports until you find the correct one.")}
</p>
`
}).then(() => enableWebSerialAndCompileAsync())
}
}
} : undefined, downloadAgain ? {
label: fn,
icon: "download",
className: "lightgrey focused",
url,
fileName: fn
} : undefined, docUrl ? {
label: lf("Help"),
icon: "help",
className: "lightgrey",
url: docUrl
} : undefined]
//timeout: 20000
}).then(() => { });
}
deployAsync: deployCoreAsync,
showUploadInstructionsAsync: showUploadDialogAsync
};
initAsync().catch(e => {
// probably no HID - we'll try this again upon deployment
})
return Promise.resolve<pxt.editor.ExtensionResult>(res);
}
// When require()d from node, bind the global pxt namespace
// namespace pxt {
// export const dummyExport = 1;
// }
// eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")

View File

@ -1,14 +1,17 @@
{
"compilerOptions": {
"target": "es5",
"noImplicitAny": false,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"isolatedModules": false,
"outDir": "../built/editor",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false,
"allowSyntheticDefaultImports": true,
"declaration": true
"jsx": "react"
}
}

View File

@ -3,9 +3,7 @@
import { FieldPorts } from "./field_ports";
import { FieldMotors } from "./field_motors";
import { FieldSpeed } from "./field_speed";
import { FieldBrickButtons } from "./field_brickbuttons";
import { FieldTurnRatio } from "./field_turnratio";
import { FieldColorEnum } from "./field_color";
import { FieldMusic } from "./field_music";
@ -19,15 +17,9 @@ pxt.editor.initFieldExtensionsAsync = function (opts: pxt.editor.FieldExtensionO
}, {
selector: "motors",
editor: FieldMotors
}, {
selector: "speed",
editor: FieldSpeed
}, {
selector: "brickbuttons",
editor: FieldBrickButtons
}, {
selector: "turnratio",
editor: FieldTurnRatio
}, {
selector: "colorenum",
editor: FieldColorEnum

View File

@ -121,17 +121,17 @@ export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.
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 };
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let 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;
let position = this.fieldGroup_.getBoundingClientRect();
let primaryX = position.left + bBox.width / 2;
let primaryY = position.top + bBox.height;
let secondaryX = primaryX;
let 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).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
}
@ -152,9 +152,10 @@ export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.
* 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');
Blockly.DropDownDiv.getContentDiv().style.width = '';
const content = Blockly.DropDownDiv.getContentDiv();
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
(content as HTMLElement).style.width = '';
};
}

View File

@ -44,7 +44,7 @@ export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Block
* @return {string} Current colour in '#rrggbb' format.
*/
getValue(opt_asHex?: boolean) {
var colour = this.mapColour(this.colour_);
const colour = this.mapColour(this.value_);
if (!opt_asHex && colour.indexOf('#') > -1) {
return `0x${colour.replace(/^#/, '')}`;
}
@ -56,13 +56,13 @@ export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Block
* @param {string} colour The new colour in '#rrggbb' format.
*/
setValue(colorStr: string) {
var colour = this.mapEnum(colorStr);
let colour = this.mapEnum(colorStr);
if (this.sourceBlock_ && Blockly.Events.isEnabled() &&
this.colour_ != colour) {
this.value_ != colour) {
Blockly.Events.fire(new (Blockly as any).Events.BlockChange(
this.sourceBlock_, 'field', this.name, this.colour_, colour));
this.sourceBlock_, 'field', this.name, this.value_, colour));
}
this.colour_ = colour;
this.value_ = colour;
if (this.sourceBlock_) {
this.sourceBlock_.setColour(colour, colour, colour);
}

View File

@ -46,31 +46,31 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
(this as any).arrowX_ = 0;
/** @type {Number} */
this.arrowY_ = 11;
this.arrow_ = Blockly.utils.createSvgElement('image', {
this.arrow_ = Blockly.utils.dom.createSvgElement('image', {
'height': (this as any).arrowSize_ + 'px',
'width': (this as any).arrowSize_ + 'px'
});
}, null);
this.arrow_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', (Blockly.FieldDropdown as any).DROPDOWN_SVG_DATAURI);
this.arrow2_ = Blockly.utils.createSvgElement('image', {
this.arrow2_ = <SVGImageElement>Blockly.utils.dom.createSvgElement('image', {
'height': (this as any).arrowSize_ + 'px',
'width': (this as any).arrowSize_ + 'px'
});
}, null);
this.arrow2_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', (Blockly.FieldDropdown as any).DROPDOWN_SVG_DATAURI);
(this as any).className_ += ' blocklyDropdownText';
// Build the DOM.
this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
this.fieldGroup_ = Blockly.utils.dom.createSvgElement('g', {}, null);
if (!this.visible_) {
(this.fieldGroup_ as any).style.display = 'none';
}
// Adjust X to be flipped for RTL. Position is relative to horizontal start of source block.
var size = this.getSize();
var fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2;
let size = this.getSize();
let fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2;
/** @type {!Element} */
this.textElement_ = Blockly.utils.createSvgElement('text',
this.textElement_ = Blockly.utils.dom.createSvgElement('text',
{
'class': (this as any).className_,
'x': fieldX,
@ -79,7 +79,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
},
this.fieldGroup_);
fieldX += 10; // size of first group.
this.textElement2_ = Blockly.utils.createSvgElement('text',
this.textElement2_ = <SVGTextElement>Blockly.utils.dom.createSvgElement('text',
{
'class': (this as any).className_,
'x': fieldX,
@ -89,17 +89,17 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
this.fieldGroup_);
this.updateEditable();
this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
(this.sourceBlock_ as Blockly.BlockSvg).getSvgRoot().appendChild(this.fieldGroup_);
// Force a render.
this.render_();
this.size_.width = 0;
this.isDirty_ = true;
(this as any).mouseDownWrapper_ =
Blockly.bindEventWithChecks_((this as any).getClickTarget_(), 'mousedown', this,
(this as any).onMouseDown_);
// Add second dropdown
if (this.shouldShowRect_()) {
this.box_ = Blockly.utils.createSvgElement('rect', {
this.box_ = <SVGRectElement>Blockly.utils.dom.createSvgElement('rect', {
'rx': (Blockly.BlockSvg as any).CORNER_RADIUS,
'ry': (Blockly.BlockSvg as any).CORNER_RADIUS,
'x': 0,
@ -112,7 +112,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
'fill-opacity': 1
}, null);
this.fieldGroup_.insertBefore(this.box_, this.textElement_);
this.box2_ = Blockly.utils.createSvgElement('rect', {
this.box2_ = <SVGRectElement>Blockly.utils.dom.createSvgElement('rect', {
'rx': (Blockly.BlockSvg as any).CORNER_RADIUS,
'ry': (Blockly.BlockSvg as any).CORNER_RADIUS,
'x': 0,
@ -128,7 +128,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
}
// Force a reset of the text to add the arrow.
var text = this.text_;
let text = this.text_;
this.text_ = null;
this.setText(text);
}
@ -149,9 +149,9 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
getFirstValueI11n(value: string) {
const firstValue = this.getFirstValue(value);
const motorOptions = {
'medium motor': lf('medium motor'),
'large motor': lf('large motor'),
'large motors': lf('large motors')
'medium motor': lf("medium motor"),
'large motor': lf("large motor"),
'large motors': lf("large motors")
}
return motorOptions[firstValue];
}
@ -179,7 +179,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// Not rendered yet.
return;
}
var text = this.text_;
let text = this.text_;
if (text.length > this.maxDisplayLength) {
// Truncate displayed string and add an ellipsis ('...').
text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
@ -200,11 +200,11 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// Prevent the field from disappearing if empty.
text = Blockly.Field.NBSP;
}
var textNode = document.createTextNode(text);
let textNode = document.createTextNode(text);
this.textElement2_.appendChild(textNode);
// Cached width is obsolete. Clear it.
this.size_.width = 0;
this.isDirty_ = true;
};
patchDualMotorText(text: string) {
@ -233,8 +233,8 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
if (this.textElement2_) {
this.textElement2_.parentNode.appendChild(this.arrow2_);
}
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
this.sourceBlock_.render();
if (this.sourceBlock_ && (<Blockly.BlockSvg>this.sourceBlock_).rendered) {
(<Blockly.BlockSvg>this.sourceBlock_).render();
this.sourceBlock_.bumpNeighbours_();
}
}
@ -244,7 +244,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
return 0;
}
var addedWidth = 0;
let addedWidth = 0;
if (this.sourceBlock_.RTL) {
(this as any).arrow2X_ = (this as any).arrowSize_ - (Blockly.BlockSvg as any).DROPDOWN_ARROW_PADDING;
addedWidth = (this as any).arrowSize_ + (Blockly.BlockSvg as any).DROPDOWN_ARROW_PADDING;
@ -263,10 +263,10 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
return addedWidth;
};
updateWidth() {
updateSize_() {
// Calculate width of field
var width = Blockly.Field.getCachedWidth(this.textElement_);
var width2 = Blockly.Field.getCachedWidth(this.textElement2_);
let width = Blockly.Field.getCachedWidth(this.textElement_);
let width2 = Blockly.Field.getCachedWidth(this.textElement2_);
// Add padding to left and right of text.
if (this.EDITABLE) {
@ -311,15 +311,15 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// First dropdown
// Use one of the following options, medium motor, large motor or large motors (translated)
const textNode1 = document.createTextNode(this.getFirstValueI11n(this.value_));
const textNode1 = document.createTextNode(this.getFirstValueI11n(<string>this.value_));
this.textElement_.appendChild(textNode1);
// Second dropdown, no need to translate. Only port numbers
if (this.textElement2_) {
const textNode2 = document.createTextNode(this.getSecondValue(this.value_));
const textNode2 = document.createTextNode(this.getSecondValue(<string>this.value_));
this.textElement2_.appendChild(textNode2);
}
this.updateWidth();
this.updateSize_();
// Update text centering, based on newly calculated width.
let centerTextX = ((this as any).width1 - this.arrowWidth_) / 2;
@ -347,8 +347,8 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
}
// Apply new text element x position.
var width = Blockly.Field.getCachedWidth(this.textElement_);
var newX = centerTextX - width / 2;
let width = Blockly.Field.getCachedWidth(this.textElement_);
let newX = centerTextX - width / 2;
this.textElement_.setAttribute('x', `${newX}`);
// Update text centering, based on newly calculated width.
@ -377,8 +377,8 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
}
// Apply new text element x position.
var width2 = Blockly.Field.getCachedWidth(this.textElement2_);
var newX2 = centerTextX2 - width2 / 2;
let width2 = Blockly.Field.getCachedWidth(this.textElement2_);
let newX2 = centerTextX2 - width2 / 2;
this.textElement2_.setAttribute('x', `${newX2 + (this as any).width1 + Blockly.BlockSvg.BOX_FIELD_PADDING}`);
}
@ -401,7 +401,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
this.isFirst_ = e.clientX - this.getScaledBBox_().left < ((this as any).width1 * this.sourceBlock_.workspace.scale);
this.isFirst_ = e.clientX - this.getScaledBBox_().left < ((this as any).width1 * (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale);
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
@ -411,14 +411,14 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
let options = this.getOptions();
const foptions = this.getOptions(); // [img info, text]
let opts = {};
let conts = {};
let vals = {};
// Go through all option values and split them into groups
for (let opt = 0; opt < options.length; opt++) {
const value = options[opt][1];
for (let opt = 0; opt < foptions.length; opt++) {
const value = foptions[opt][1];
const motorValue = value.substring(value.indexOf('.') + 1);
const typeValue = motorValue.indexOf('large') == 0 ? 'large' : 'medium';
const portValue = motorValue.indexOf('large') == 0 ? motorValue.substring(5) : motorValue.substring(6);
@ -429,13 +429,14 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
if (!opts[key]) opts[key] = [];
opts[key].push(portValue);
conts[text] = options[opt][0];
conts[text] = foptions[opt][0];
vals[text] = value;
}
const currentFirst = this.getFirstValue(this.value_);
const currentSecond = this.getSecondValue(this.value_);
const currentFirst = this.getFirstValue(<string>this.value_);
//const currentSecond = this.getSecondValue(<string>this.value_);
let options: string[];
if (!this.isFirst_) {
options = opts[currentFirst];
} else {
@ -526,7 +527,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
Blockly.DropDownDiv.setColour(this.backgroundColour_, this.borderColour_);
// Calculate positioning based on the field position.
let scale = this.sourceBlock_.workspace.scale;
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let width = this.isFirst_ ? (this as any).width1 : (this as any).width2;
let bBox = { width: this.size_.width, height: this.size_.height };
width *= scale;
@ -538,7 +539,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
let secondaryX = primaryX;
let 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).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
@ -561,10 +562,11 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
* Callback for when the drop-down is hidden.
*/
protected onHide_() {
Blockly.DropDownDiv.content_.removeAttribute('role');
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
Blockly.DropDownDiv.getContentDiv().style.width = '';
const content = Blockly.DropDownDiv.getContentDiv();
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
(content as HTMLElement).style.width = '';
if (this.isFirst_ && this.box_) {
this.box_.setAttribute('fill', this.sourceBlock_.getColour());
} else if (!this.isFirst_ && this.box2_) {

View File

@ -26,7 +26,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
this.width_ = parseInt(options.width) || 380;
this.setText = Blockly.FieldDropdown.prototype.setText;
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
if (!pxt.BrowserUtils.isIE() && !soundCache) {
@ -78,14 +78,14 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
contentDiv.style.width = (this as any).width_ + 'px';
contentDiv.style.cssFloat = 'left';
dropdownDiv.style.maxHeight = `410px`;
(dropdownDiv as HTMLElement).style.maxHeight = `410px`;
dropdownDiv.appendChild(categoriesDiv);
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
// Calculate positioning based on the field position.
let scale = this.sourceBlock_.workspace.scale;
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
@ -95,7 +95,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
let secondaryX = primaryX;
let 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).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
@ -236,7 +236,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
protected onHide_() {
super.onHide_();
Blockly.DropDownDiv.getContentDiv().style.maxHeight = '';
(Blockly.DropDownDiv.getContentDiv() as HTMLElement).style.maxHeight = '';
this.stopSounds();
}

View File

@ -17,7 +17,7 @@ export class FieldPorts extends pxtblockly.FieldImages implements Blockly.FieldC
this.width_ = parseInt(options.width) || 300;
this.setText = Blockly.FieldDropdown.prototype.setText;
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
}

View File

@ -1,98 +0,0 @@
/// <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",
width: "170"
});
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', 'dominant-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) {
let x = parseFloat(value) || 0;
this.updateSpeed(x);
// Update reporter
this.reporter.textContent = `${x}%`;
}
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;
}
}

View File

@ -1,108 +0,0 @@
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
export interface FieldTurnRatioOptions extends Blockly.FieldCustomOptions {
}
export class FieldTurnRatio extends Blockly.FieldSlider implements Blockly.FieldCustom {
public isFieldCustom_ = true;
private params: any;
private path_: SVGPathElement;
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: FieldTurnRatioOptions, opt_validator?: Function) {
super(String(value_), '-200', '200', null, '10', 'TurnRatio', opt_validator);
this.params = params;
(this as any).sliderColor_ = '#a8aaa8';
}
static HALF = 80;
static HANDLE_RADIUS = 30;
static RADIUS = FieldTurnRatio.HALF - FieldTurnRatio.HANDLE_RADIUS - 1;
createLabelDom_(labelText: string) {
let labelContainer = document.createElement('div');
let svg = Blockly.utils.createSvgElement('svg', {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:html': 'http://www.w3.org/1999/xhtml',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'version': '1.1',
'height': (FieldTurnRatio.HALF + FieldTurnRatio.HANDLE_RADIUS + 10) + 'px',
'width': (FieldTurnRatio.HALF * 2) + 'px'
}, labelContainer);
let defs = Blockly.utils.createSvgElement('defs', {}, svg);
let marker = Blockly.utils.createSvgElement('marker', {
'id': 'head',
'orient': "auto",
'markerWidth': '2',
'markerHeight': '4',
'refX': '0.1', 'refY': '1.5'
}, defs);
let markerPath = Blockly.utils.createSvgElement('path', {
'd': 'M0,0 V3 L1.5,1.5 Z',
'fill': '#f12a21'
}, marker);
this.reporter_ = pxsim.svg.child(svg, "text", {
'x': FieldTurnRatio.HALF, 'y': 96,
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number'
}) as SVGTextElement;
this.path_ = Blockly.utils.createSvgElement('path', {
'x1': FieldTurnRatio.HALF,
'y1': FieldTurnRatio.HALF,
'marker-end': 'url(#head)',
'style': 'fill: none; stroke: #f12a21; stroke-width: 10'
}, svg);
this.updateGraph_();
let readout = document.createElement('span');
readout.setAttribute('class', 'blocklyFieldSliderReadout');
return [labelContainer, readout];
};
updateGraph_() {
if (!this.path_) {
return;
}
let v = goog.math.clamp(parseFloat(this.getText()), -200, 200);
if (isNaN(v)) {
v = 0;
}
const x = v / 100;
const nx = Math.max(-1, Math.min(1, x));
const theta = Math.max(nx) * Math.PI / 2;
const r = FieldTurnRatio.RADIUS - 6;
let cx = FieldTurnRatio.HALF;
const cy = FieldTurnRatio.HALF - 22;
if (Math.abs(x) > 1) {
cx -= (x - (x > 0 ? 1 : -1)) * r / 2; // move center of circle
}
const alpha = 0.2 + Math.abs(nx) * 0.5;
const y1 = r * alpha;
const y2 = r * Math.sin(Math.PI / 2 - theta);
const x2 = r * Math.cos(Math.PI / 2 - theta);
const y3 = y2 - r * alpha * Math.cos(2 * theta);
const x3 = x2 - r * alpha * Math.sin(2 * theta);
const d = `M ${cx} ${cy} C ${cx} ${cy - y1} ${cx + x3} ${cy - y3} ${cx + x2} ${cy - y2}`;
this.path_.setAttribute('d', d);
this.reporter_.textContent = `${v}`;
}
setReadout_(readout: Element, value: string) {
this.updateGraph_();
}
}

View File

@ -22,13 +22,8 @@
}
declare const enum ValType {
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
declare const enum PerfCounters {
GC = 0,
}
// Auto-generated. Do not edit. Really.

76
libs/base/shims.d.ts vendored
View File

@ -4,6 +4,18 @@
//% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte
declare interface Buffer {
/**
* Reads an unsigned byte at a particular location
*/
//% shim=BufferMethods::getUint8
getUint8(off: int32): int32;
/**
* Writes an unsigned byte at a particular location
*/
//% shim=BufferMethods::setUint8
setUint8(off: int32, v: int32): void;
/**
* Write a number in specified format in the buffer.
*/
@ -42,6 +54,12 @@ declare interface Buffer {
//% start.defl=0 length.defl=-1 shim=BufferMethods::shift
shift(offset: int32, start?: int32, length?: int32): void;
/**
* Convert a buffer to string assuming UTF8 encoding
*/
//% shim=BufferMethods::toString
toString(): string;
/**
* Convert a buffer to its hexadecimal representation.
*/
@ -72,6 +90,13 @@ declare namespace control {
*/
//% shim=control::createBuffer
function createBuffer(size: int32): Buffer;
/**
* Create a new buffer with UTF8-encoded string
* @param str the string to put in the buffer
*/
//% shim=control::createBufferFromUTF8
function createBufferFromUTF8(str: string): Buffer;
}
declare namespace loops {
@ -101,6 +126,12 @@ declare namespace control {
//% blockId=control_running_time block="millis (ms)" shim=control::millis
function millis(): int32;
/**
* Gets current time in microseconds. Overflows every ~18 minutes.
*/
//% shim=control::micros
function micros(): int32;
/**
* Used internally
*/
@ -143,11 +174,54 @@ declare namespace control {
//% help=control/device-serial-number shim=control::deviceSerialNumber
function deviceSerialNumber(): int32;
/**
* Derive a unique, consistent 64-bit serial number of this device from internal data.
*/
//% blockId="control_device_long_serial_number" block="device long serial number" weight=9
//% help=control/device-long-serial-number shim=control::deviceLongSerialNumber
function deviceLongSerialNumber(): Buffer;
/**
*
*/
//% shim=control::__log
function __log(text: string): void;
function __log(prority: int32, text: string): void;
/**
* Dump internal information about a value.
*/
//% shim=control::dmesgValue
function dmesgValue(v: any): void;
/**
* Force GC and dump basic information about heap.
*/
//% shim=control::gc
function gc(): void;
/**
* Force GC and halt waiting for debugger to do a full heap dump.
*/
//% shim=control::heapDump
function heapDump(): void;
/**
* Set flags used when connecting an external debugger.
*/
//% shim=control::setDebugFlags
function setDebugFlags(flags: int32): void;
/**
* Record a heap snapshot to debug memory leaks.
*/
//% shim=control::heapSnapshot
function heapSnapshot(): void;
/**
* Return true if profiling is enabled in the current build.
*/
//% shim=control::profilingEnabled
function profilingEnabled(): boolean;
}
// Auto-generated. Do not edit. Really.

View File

@ -1,5 +1,4 @@
const enum ColorSensorMode {
None = 0,
//% block="reflected light intensity"
ReflectedLightIntensity = 0,
//% block="ambient light intensity"
@ -59,7 +58,6 @@ namespace sensors {
constructor(port: number) {
super(port)
this._setMode(ColorSensorMode.None);
this.thresholdDetector = new sensors.ThresholdDetector(this.id());
this.calibrating = false;
}
@ -73,13 +71,7 @@ namespace sensors {
}
setMode(m: ColorSensorMode) {
if (m == ColorSensorMode.AmbientLightIntensity) {
this.thresholdDetector.setLowThreshold(5);
this.thresholdDetector.setHighThreshold(20);
} else {
this.thresholdDetector.setLowThreshold(20);
this.thresholdDetector.setHighThreshold(80);
}
// don't change threshold after initialization
this._setMode(m)
}
@ -187,6 +179,22 @@ namespace sensors {
return this.getNumber(NumberFormat.UInt8LE, 0)
}
/**
* Checks the color is being detected
* @param color the color to detect
*/
//% help=sensors/color-sensor/is-color-detected
//% block="is **color sensor** %this|detected|%color=colorEnumPicker"
//% blockId=colorisColorDetectedDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% this.fieldEditor="ports"
//% weight=99 blockGap=8
//% group="Color Sensor"
isColorDetected(color: number) {
return this.color() == color;
}
/**
* Get the current raw rgb values as an array from the color sensor.
* @param sensor the color sensor to query the request

View File

@ -0,0 +1,30 @@
# Is Color Detected
Checks the color is detected
```sig
let b = sensors.color1.isColorDetected(ColorSensorColor.Blue)
```
The [color](/reference/sensors/color) you choose to look for is one of the colors that the sensor can detect. If you want to use colors for tracking, it's best to use a color that is the same or very close to the ones the sensor detects.
## Parameters
* **color**: the [color](/reference/sensors/color) to watch for.
## Example
Wait for the sensor to see ``blue``. Then, show an expression on the screen.
```blocks
brick.showString("Waiting for blue", 1)
while(!sensors.color1.isColorDetected(ColorSensorColor.Blue)) {
pause(20)
}
brick.clearScreen()
brick.showImage(images.expressionsSick)
```
## See also
[on color detected](/reference/sensors/color-sensor/on-color-detected), [color](/reference/sensors/color)

View File

@ -26,6 +26,7 @@ namespace brick {
*/
//% blockId=brickBatteryProperty block="battery %property"
//% group="Battery"
//% blockGap=8
//% help=brick/battery-property
export function batteryInfo(property: BatteryProperty): number {
const info = sensors.internal.getBatteryInfo();

View File

@ -85,7 +85,7 @@ namespace brick {
//% blockId=buttonIsPressed
//% parts="brick"
//% blockNamespace=brick
//% weight=81 blockGap=8
//% weight=81
//% group="Buttons"
//% button.fieldEditor="brickbuttons"
isPressed() {

View File

@ -1,40 +0,0 @@
#include "pxt.h"
namespace control {
/**
* Announce that an event happened to registered handlers.
* @param src ID of the Component that generated the event
* @param value Component specific code indicating the cause of the event.
* @param mode optional definition of how the event should be processed after construction.
*/
//% weight=21 blockGap=12 blockId="control_raise_event"
//% block="raise event|from %src|with value %value" blockExternalInputs=1
//% help=control/raise-event
void raiseEvent(int src, int value) {
pxt::raiseEvent(src, value);
}
/**
* Allocates the next user notification event
*/
//% help=control/allocate-notify-event
int allocateNotifyEvent() {
return pxt::allocateNotifyEvent();
}
/** Write data to DMESG debugging buffer. */
//%
void dmesg(String s) {
DMESG("# %s", s->data);
}
}
namespace serial {
/** Send DMESG debug buffer over serial. */
//%
void writeDmesg() {
pxt::dumpDmesg();
}
}

407
libs/core/dal.d.ts vendored
View File

@ -1,6 +1,305 @@
// Auto-generated. Do not edit.
declare const enum DAL {
// built/dockermake/pxtapp/ev3const.h
// /pxtapp/configkeys.h
CFG_PIN_NAME_MSK = 65535,
CFG_PIN_CONFIG_MSK = 4294901760,
CFG_PIN_CONFIG_ACTIVE_LO = 65536,
CFG_MAGIC0 = 513675505,
CFG_MAGIC1 = 539130489,
CFG_PIN_ACCELEROMETER_INT = 1,
CFG_PIN_ACCELEROMETER_SCL = 2,
CFG_PIN_ACCELEROMETER_SDA = 3,
CFG_PIN_BTN_A = 4,
CFG_PIN_BTN_B = 5,
CFG_PIN_BTN_SLIDE = 6,
CFG_PIN_DOTSTAR_CLOCK = 7,
CFG_PIN_DOTSTAR_DATA = 8,
CFG_PIN_FLASH_CS = 9,
CFG_PIN_FLASH_MISO = 10,
CFG_PIN_FLASH_MOSI = 11,
CFG_PIN_FLASH_SCK = 12,
CFG_PIN_LED = 13,
CFG_PIN_LIGHT = 14,
CFG_PIN_MICROPHONE = 15,
CFG_PIN_MIC_CLOCK = 16,
CFG_PIN_MIC_DATA = 17,
CFG_PIN_MISO = 18,
CFG_PIN_MOSI = 19,
CFG_PIN_NEOPIXEL = 20,
CFG_PIN_RX = 21,
CFG_PIN_RXLED = 22,
CFG_PIN_SCK = 23,
CFG_PIN_SCL = 24,
CFG_PIN_SDA = 25,
CFG_PIN_SPEAKER_AMP = 26,
CFG_PIN_TEMPERATURE = 27,
CFG_PIN_TX = 28,
CFG_PIN_TXLED = 29,
CFG_PIN_IR_OUT = 30,
CFG_PIN_IR_IN = 31,
CFG_PIN_DISPLAY_SCK = 32,
CFG_PIN_DISPLAY_MISO = 33,
CFG_PIN_DISPLAY_MOSI = 34,
CFG_PIN_DISPLAY_CS = 35,
CFG_PIN_DISPLAY_DC = 36,
CFG_DISPLAY_WIDTH = 37,
CFG_DISPLAY_HEIGHT = 38,
CFG_DISPLAY_CFG0 = 39,
CFG_DISPLAY_CFG1 = 40,
CFG_DISPLAY_CFG2 = 41,
CFG_DISPLAY_CFG3 = 42,
CFG_PIN_DISPLAY_RST = 43,
CFG_PIN_DISPLAY_BL = 44,
CFG_PIN_SERVO_1 = 45,
CFG_PIN_SERVO_2 = 46,
CFG_PIN_BTN_LEFT = 47,
CFG_PIN_BTN_RIGHT = 48,
CFG_PIN_BTN_UP = 49,
CFG_PIN_BTN_DOWN = 50,
CFG_PIN_BTN_MENU = 51,
CFG_PIN_LED_R = 52,
CFG_PIN_LED_G = 53,
CFG_PIN_LED_B = 54,
CFG_PIN_LED1 = 55,
CFG_PIN_LED2 = 56,
CFG_PIN_LED3 = 57,
CFG_PIN_LED4 = 58,
CFG_SPEAKER_VOLUME = 59,
CFG_PIN_JACK_TX = 60,
CFG_PIN_JACK_SENSE = 61,
CFG_PIN_JACK_HPEN = 62,
CFG_PIN_JACK_BZEN = 63,
CFG_PIN_JACK_PWREN = 64,
CFG_PIN_JACK_SND = 65,
CFG_PIN_JACK_BUSLED = 66,
CFG_PIN_JACK_COMMLED = 67,
CFG_PIN_BTN_SOFT_RESET = 69,
CFG_ACCELEROMETER_TYPE = 70,
CFG_PIN_BTNMX_LATCH = 71,
CFG_PIN_BTNMX_CLOCK = 72,
CFG_PIN_BTNMX_DATA = 73,
CFG_PIN_BTN_MENU2 = 74,
CFG_PIN_BATTSENSE = 75,
CFG_PIN_VIBRATION = 76,
CFG_PIN_PWREN = 77,
CFG_DISPLAY_TYPE = 78,
CFG_PIN_ROTARY_ENCODER_A = 79,
CFG_PIN_ROTARY_ENCODER_B = 80,
CFG_ACCELEROMETER_SPACE = 81,
CFG_PIN_WIFI_MOSI = 82,
CFG_PIN_WIFI_MISO = 83,
CFG_PIN_WIFI_SCK = 84,
CFG_PIN_WIFI_TX = 85,
CFG_PIN_WIFI_RX = 86,
CFG_PIN_WIFI_CS = 87,
CFG_PIN_WIFI_BUSY = 88,
CFG_PIN_WIFI_RESET = 89,
CFG_PIN_WIFI_GPIO0 = 90,
CFG_PIN_WIFI_AT_TX = 91,
CFG_PIN_WIFI_AT_RX = 92,
CFG_PIN_USB_POWER = 93,
ACCELEROMETER_TYPE_LIS3DH = 50,
ACCELEROMETER_TYPE_LIS3DH_ALT = 48,
ACCELEROMETER_TYPE_MMA8453 = 56,
ACCELEROMETER_TYPE_FXOS8700 = 60,
ACCELEROMETER_TYPE_MMA8653 = 58,
ACCELEROMETER_TYPE_MSA300 = 76,
ACCELEROMETER_TYPE_MPU6050 = 104,
DISPLAY_TYPE_ST7735 = 7735,
DISPLAY_TYPE_ILI9341 = 9341,
CFG_PIN_A0 = 100,
CFG_PIN_A1 = 101,
CFG_PIN_A2 = 102,
CFG_PIN_A3 = 103,
CFG_PIN_A4 = 104,
CFG_PIN_A5 = 105,
CFG_PIN_A6 = 106,
CFG_PIN_A7 = 107,
CFG_PIN_A8 = 108,
CFG_PIN_A9 = 109,
CFG_PIN_A10 = 110,
CFG_PIN_A11 = 111,
CFG_PIN_A12 = 112,
CFG_PIN_A13 = 113,
CFG_PIN_A14 = 114,
CFG_PIN_A15 = 115,
CFG_PIN_A16 = 116,
CFG_PIN_A17 = 117,
CFG_PIN_A18 = 118,
CFG_PIN_A19 = 119,
CFG_PIN_A20 = 120,
CFG_PIN_A21 = 121,
CFG_PIN_A22 = 122,
CFG_PIN_A23 = 123,
CFG_PIN_A24 = 124,
CFG_PIN_A25 = 125,
CFG_PIN_A26 = 126,
CFG_PIN_A27 = 127,
CFG_PIN_A28 = 128,
CFG_PIN_A29 = 129,
CFG_PIN_A30 = 130,
CFG_PIN_A31 = 131,
CFG_PIN_D0 = 150,
CFG_PIN_D1 = 151,
CFG_PIN_D2 = 152,
CFG_PIN_D3 = 153,
CFG_PIN_D4 = 154,
CFG_PIN_D5 = 155,
CFG_PIN_D6 = 156,
CFG_PIN_D7 = 157,
CFG_PIN_D8 = 158,
CFG_PIN_D9 = 159,
CFG_PIN_D10 = 160,
CFG_PIN_D11 = 161,
CFG_PIN_D12 = 162,
CFG_PIN_D13 = 163,
CFG_PIN_D14 = 164,
CFG_PIN_D15 = 165,
CFG_PIN_D16 = 166,
CFG_PIN_D17 = 167,
CFG_PIN_D18 = 168,
CFG_PIN_D19 = 169,
CFG_PIN_D20 = 170,
CFG_PIN_D21 = 171,
CFG_PIN_D22 = 172,
CFG_PIN_D23 = 173,
CFG_PIN_D24 = 174,
CFG_PIN_D25 = 175,
CFG_PIN_D26 = 176,
CFG_PIN_D27 = 177,
CFG_PIN_D28 = 178,
CFG_PIN_D29 = 179,
CFG_PIN_D30 = 180,
CFG_PIN_D31 = 181,
CFG_NUM_NEOPIXELS = 200,
CFG_NUM_DOTSTARS = 201,
CFG_DEFAULT_BUTTON_MODE = 202,
CFG_SWD_ENABLED = 203,
CFG_FLASH_BYTES = 204,
CFG_RAM_BYTES = 205,
CFG_SYSTEM_HEAP_BYTES = 206,
CFG_LOW_MEM_SIMULATION_KB = 207,
CFG_BOOTLOADER_BOARD_ID = 208,
CFG_UF2_FAMILY = 209,
CFG_PINS_PORT_SIZE = 210,
CFG_BOOTLOADER_PROTECTION = 211,
CFG_POWER_DEEPSLEEP_TIMEOUT = 212,
CFG_ANALOG_BUTTON_THRESHOLD = 213,
CFG_CPU_MHZ = 214,
CFG_CONTROLLER_LIGHT_MAX_BRIGHTNESS = 215,
CFG_PIN_B0 = 300,
CFG_PIN_B1 = 301,
CFG_PIN_B2 = 302,
CFG_PIN_B3 = 303,
CFG_PIN_B4 = 304,
CFG_PIN_B5 = 305,
CFG_PIN_B6 = 306,
CFG_PIN_B7 = 307,
CFG_PIN_B8 = 308,
CFG_PIN_B9 = 309,
CFG_PIN_B10 = 310,
CFG_PIN_B11 = 311,
CFG_PIN_B12 = 312,
CFG_PIN_B13 = 313,
CFG_PIN_B14 = 314,
CFG_PIN_B15 = 315,
CFG_PIN_B16 = 316,
CFG_PIN_B17 = 317,
CFG_PIN_B18 = 318,
CFG_PIN_B19 = 319,
CFG_PIN_B20 = 320,
CFG_PIN_B21 = 321,
CFG_PIN_B22 = 322,
CFG_PIN_B23 = 323,
CFG_PIN_B24 = 324,
CFG_PIN_B25 = 325,
CFG_PIN_B26 = 326,
CFG_PIN_B27 = 327,
CFG_PIN_B28 = 328,
CFG_PIN_B29 = 329,
CFG_PIN_B30 = 330,
CFG_PIN_B31 = 331,
CFG_PIN_C0 = 350,
CFG_PIN_C1 = 351,
CFG_PIN_C2 = 352,
CFG_PIN_C3 = 353,
CFG_PIN_C4 = 354,
CFG_PIN_C5 = 355,
CFG_PIN_C6 = 356,
CFG_PIN_C7 = 357,
CFG_PIN_C8 = 358,
CFG_PIN_C9 = 359,
CFG_PIN_C10 = 360,
CFG_PIN_C11 = 361,
CFG_PIN_C12 = 362,
CFG_PIN_C13 = 363,
CFG_PIN_C14 = 364,
CFG_PIN_C15 = 365,
CFG_PIN_C16 = 366,
CFG_PIN_C17 = 367,
CFG_PIN_C18 = 368,
CFG_PIN_C19 = 369,
CFG_PIN_C20 = 370,
CFG_PIN_C21 = 371,
CFG_PIN_C22 = 372,
CFG_PIN_C23 = 373,
CFG_PIN_C24 = 374,
CFG_PIN_C25 = 375,
CFG_PIN_C26 = 376,
CFG_PIN_C27 = 377,
CFG_PIN_C28 = 378,
CFG_PIN_C29 = 379,
CFG_PIN_C30 = 380,
CFG_PIN_C31 = 381,
CFG_PIN_P0 = 400,
CFG_PIN_P1 = 401,
CFG_PIN_P2 = 402,
CFG_PIN_P3 = 403,
CFG_PIN_P4 = 404,
CFG_PIN_P5 = 405,
CFG_PIN_P6 = 406,
CFG_PIN_P7 = 407,
CFG_PIN_P8 = 408,
CFG_PIN_P9 = 409,
CFG_PIN_P10 = 410,
CFG_PIN_P11 = 411,
CFG_PIN_P12 = 412,
CFG_PIN_P13 = 413,
CFG_PIN_P14 = 414,
CFG_PIN_P15 = 415,
CFG_PIN_P16 = 416,
CFG_PIN_P17 = 417,
CFG_PIN_P18 = 418,
CFG_PIN_P19 = 419,
CFG_PIN_P20 = 420,
CFG_PIN_P21 = 421,
CFG_PIN_P22 = 422,
CFG_PIN_P23 = 423,
CFG_PIN_P24 = 424,
CFG_PIN_P25 = 425,
CFG_PIN_P26 = 426,
CFG_PIN_P27 = 427,
CFG_PIN_P28 = 428,
CFG_PIN_P29 = 429,
CFG_PIN_P30 = 430,
CFG_PIN_P31 = 431,
CFG_PIN_LORA_MISO = 1001,
CFG_PIN_LORA_MOSI = 1002,
CFG_PIN_LORA_SCK = 1003,
CFG_PIN_LORA_CS = 1004,
CFG_PIN_LORA_BOOT = 1005,
CFG_PIN_LORA_RESET = 1006,
CFG_PIN_IRRXLED = 1007,
CFG_PIN_IRTXLED = 1008,
CFG_PIN_LCD_RESET = 1009,
CFG_PIN_LCD_ENABLE = 1010,
CFG_PIN_LCD_DATALINE4 = 1011,
CFG_PIN_LCD_DATALINE5 = 1012,
CFG_PIN_LCD_DATALINE6 = 1013,
CFG_PIN_LCD_DATALINE7 = 1014,
CFG_NUM_LCD_COLUMNS = 1015,
CFG_NUM_LCD_ROWS = 1016,
// /pxtapp/ev3const.h
NUM_INPUTS = 4,
NUM_OUTPUTS = 4,
LCD_WIDTH = 178,
@ -46,44 +345,66 @@ declare const enum DAL {
CONN_OUTPUT_TACHO = 125,
CONN_NONE = 126,
CONN_ERROR = 127,
opProgramStart = 0x03,
opOutputGetType = 0xA0,
opOutputSetType = 0xA1,
opOutputReset = 0xA2,
opOutputStop = 0xA3,
opOutputPower = 0xA4,
opOutputSpeed = 0xA5,
opOutputStart = 0xA6,
opOutputPolarity = 0xA7,
opOutputRead = 0xA8,
opOutputTest = 0xA9,
opOutputReady = 0xAA,
opOutputPosition = 0xAB,
opOutputStepPower = 0xAC,
opOutputTimePower = 0xAD,
opOutputStepSpeed = 0xAE,
opOutputTimeSpeed = 0xAF,
opOutputStepSync = 0xB0,
opOutputTimeSync = 0xB1,
opOutputClearCount = 0xB2,
opOutputGetCount = 0xB3,
opOutputProgramStop = 0xB4,
BUTTON_ID_UP = 0x01,
BUTTON_ID_ENTER = 0x02,
BUTTON_ID_DOWN = 0x04,
BUTTON_ID_RIGHT = 0x08,
BUTTON_ID_LEFT = 0x10,
BUTTON_ID_ESCAPE = 0x20,
// built/dockermake/pxtapp/pxt.h
opProgramStart = 3,
opOutputGetType = 160,
opOutputSetType = 161,
opOutputReset = 162,
opOutputStop = 163,
opOutputPower = 164,
opOutputSpeed = 165,
opOutputStart = 166,
opOutputPolarity = 167,
opOutputRead = 168,
opOutputTest = 169,
opOutputReady = 170,
opOutputPosition = 171,
opOutputStepPower = 172,
opOutputTimePower = 173,
opOutputStepSpeed = 174,
opOutputTimeSpeed = 175,
opOutputStepSync = 176,
opOutputTimeSync = 177,
opOutputClearCount = 178,
opOutputGetCount = 179,
opOutputProgramStop = 180,
BUTTON_ID_UP = 1,
BUTTON_ID_ENTER = 2,
BUTTON_ID_DOWN = 4,
BUTTON_ID_RIGHT = 8,
BUTTON_ID_LEFT = 16,
BUTTON_ID_ESCAPE = 32,
// /pxtapp/platform.h
PXT_GC_THREAD_LIST = 1,
// /pxtapp/pxt.h
DEVICE_EVT_ANY = 0,
DEVICE_ID_NOTIFY = 10000,
DEVICE_ID_NOTIFY_ONE = 10001,
// built/dockermake/pxtapp/pxtbase.h
PXT_REF_TAG_STRING = 1,
PXT_REF_TAG_BUFFER = 2,
PXT_REF_TAG_IMAGE = 3,
PXT_REF_TAG_NUMBER = 32,
PXT_REF_TAG_ACTION = 33,
IMAGE_BITS = 1,
// /pxtapp/pxtbase.h
PXT32 = 1,
PXT64 = 1,
PXT_VTABLE_SHIFT = 2,
PXT_REFCNT_FLASH = 65534,
VTABLE_MAGIC = 249,
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
BoxedString = 1,
BoxedNumber = 2,
BoxedBuffer = 3,
RefAction = 4,
RefImage = 5,
RefCollection = 6,
RefRefLocal = 7,
RefMap = 8,
RefMImage = 9,
MMap = 10,
User0 = 16,
PXT_IOS_HEAP_ALLOC_BITS = 20,
IMAGE_HEADER_MAGIC = 135,
Int8LE = 1,
UInt8LE = 2,
Int16LE = 3,
@ -100,12 +421,10 @@ declare const enum DAL {
Float64LE = 14,
Float32BE = 15,
Float64BE = 16,
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
// built/dockermake/pxtapp/pxtconfig.h
// built/dockermake/pxtapp/pxtcore.h
NUM_TRY_FRAME_REGS = 3,
GC = 0,
// /pxtapp/pxtconfig.h
PXT_GC = 1,
// /pxtapp/pxtcore.h
PXT_HARD_FLOAT = 1,
}

View File

@ -87,7 +87,7 @@ 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));
this.poller = new Poller(25, () => this.query(), (prev, curr) => this.update(prev, curr));
}
poke() {
@ -121,10 +121,9 @@ namespace sensors.internal {
powerMM = control.mmap("/dev/lms_power", 2, 0)
devPoller = new Poller(500, () => { return hashDevices(); },
(prev, curr) => {
detectDevices();
});
devPoller = new Poller(500,
() => { return hashDevices(); },
(prev, curr) => { detectDevices(); });
}
export function getActiveSensors(): Sensor[] {
@ -208,12 +207,14 @@ namespace sensors.internal {
}
}
export function getBatteryInfo(): {
export interface BatteryInfo {
level: number;
Ibatt: number,
Vbatt: number,
Imotor: number
} {
}
export function getBatteryInfo(): BatteryInfo {
init();
if (!batteryInfo) updateBatteryInfo();
const CinCnt = batteryInfo.CinCnt;
@ -275,22 +276,25 @@ void cUiUpdatePower(void)
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let r = 0;
for (let i = 0; i < conns.length; ++i) {
r = (r << 8 | conns[i]);
r = conns[i] + (r << 6) + (r << 16) - r;
}
return r;
}
let nonActivated = 0;
function detectDevices() {
//control.dmesg(`detect devices (${nonActivated} na)`)
control.dmesg(`detect devices (hash ${hashDevices()})`)
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let numChanged = 0;
const uartSensors: SensorInfo[] = [];
for (const sensorInfo of sensorInfos) {
const newConn = conns[sensorInfo.port]
if (newConn == sensorInfo.connType) {
// control.dmesg(`connection unchanged ${newConn} at ${sensorInfo.port}`)
if (newConn == sensorInfo.connType
&& sensorInfo.sensor
&& sensorInfo.sensor.isActive()) {
if (newConn == DAL.CONN_INPUT_UART)
uartSensors.push(sensorInfo);
continue;
}
numChanged++
@ -321,7 +325,8 @@ void cUiUpdatePower(void)
for (const sensorInfo of uartSensors) {
let uinfo = readUartInfo(sensorInfo.port, 0)
sensorInfo.devType = uinfo[TypesOff.Type]
control.dmesg(`UART type ${sensorInfo.devType}`)
const mode = uinfo[TypesOff.Mode];
control.dmesg(`UART type ${sensorInfo.devType} mode ${mode}`)
}
}
@ -425,11 +430,11 @@ void cUiUpdatePower(void)
constructor(port: number) {
super(port)
this.mode = 0
this.realmode = 0
this.realmode = -1
}
_activated() {
this.realmode = 0
this.realmode = -1
this._setMode(this.mode)
}
@ -456,7 +461,7 @@ void cUiUpdatePower(void)
reset() {
if (this.isActive()) uartReset(this._port);
this.realmode = 0;
this.realmode = -1;
}
}
@ -534,8 +539,6 @@ void cUiUpdatePower(void)
function uartClearChange(port: number) {
control.dmesg(`UART clear change`);
const UART_DATA_READY = 8
const UART_PORT_CHANGED = 1
while (true) {
let status = getUartStatus(port)
if (port < 0) break
@ -556,7 +559,7 @@ void cUiUpdatePower(void)
}
function setUartModes() {
control.dmesg(`UART set modes`)
control.dmesg(`UART set modes 0x${devcon.toHex()}`)
uartMM.ioctl(IO.UART_SET_CONN, devcon)
const ports: number[] = [];
for (let port = 0; port < DAL.NUM_INPUTS; ++port) {
@ -568,22 +571,26 @@ void cUiUpdatePower(void)
while (ports.length) {
const port = ports.pop();
const status = waitNonZeroUartStatus(port)
control.dmesg(`UART set mode ${status} at ${port}`);
control.dmesg(`UART status ${status} at ${port}`);
if (!(status & UART_DATA_READY))
setUartMode(port, devcon[DevConOff.Mode + port]);
}
}
function updateUartMode(port: number, mode: number) {
control.dmesg(`UART set mode to ${mode} at ${port}`)
control.dmesg(`UART update mode to ${mode} at ${port}`)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_INPUT_UART)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 33)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode)
}
const UART_PORT_CHANGED = 1
const UART_DATA_READY = 8
function setUartMode(port: number, mode: number) {
const UART_PORT_CHANGED = 1
while (true) {
if (port < 0) return
updateUartMode(port, mode);
control.dmesg(`UART set mode 0x${devcon.toHex()}`)
uartMM.ioctl(IO.UART_SET_CONN, devcon)
let status = waitNonZeroUartStatus(port)
if (status & UART_PORT_CHANGED) {
@ -591,7 +598,8 @@ void cUiUpdatePower(void)
uartClearChange(port)
} else {
control.dmesg(`UART status ${status}`);
break;
if (status & UART_DATA_READY)
break;
}
pause(10)
}
@ -814,10 +822,10 @@ namespace sensors {
export class ThresholdDetector {
public id: number;
public min: number;
public max: number;
public lowThreshold: number;
public highThreshold: number;
private min: number;
private max: number;
private lowThreshold: number;
private highThreshold: number;
public level: number;
public state: ThresholdState;

26
libs/core/integrator.ts Normal file
View File

@ -0,0 +1,26 @@
namespace control {
export class EulerIntegrator {
public value: number;
private t: number;
private v: number;
constructor() {
this.reset();
}
public integrate(derivative: number): void {
let now = control.millis();
let dt = (now -this.t) / 1000.0;
this.value += dt * (this.v + derivative) / 2;
this.t = now;
this.v = derivative;
}
public reset() {
this.value = 0;
this.v = 0;
this.t = control.millis();
}
}
}

View File

@ -1,588 +0,0 @@
#include "pxt.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <cstdarg>
#include <pthread.h>
#include <unistd.h>
#include <dirent.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "ev3const.h"
#define THREAD_DBG(...)
#define MALLOC_LIMIT (8 * 1024 * 1024)
#define MALLOC_CHECK_PERIOD (1024 * 1024)
void *xmalloc(size_t sz) {
static size_t allocBytes = 0;
allocBytes += sz;
if (allocBytes >= MALLOC_CHECK_PERIOD) {
allocBytes = 0;
auto info = mallinfo();
DMESG("malloc used: %d kb", info.uordblks / 1024);
if (info.uordblks > MALLOC_LIMIT) {
target_panic(904);
}
}
auto r = malloc(sz);
if (r == NULL)
target_panic(905); // shouldn't happen
return r;
}
void *operator new(size_t size) {
return xmalloc(size);
}
void *operator new[](size_t size) {
return xmalloc(size);
}
void operator delete(void *p) {
free(p);
}
void operator delete[](void *p) {
free(p);
}
namespace pxt {
static int startTime;
static pthread_mutex_t execMutex;
static pthread_mutex_t eventMutex;
static pthread_cond_t newEventBroadcast;
struct Thread {
struct Thread *next;
Action act;
TValue arg0;
pthread_t pid;
pthread_cond_t waitCond;
int waitSource;
int waitValue;
TValue data0;
TValue data1;
};
static struct Thread *allThreads;
static struct Event *eventHead, *eventTail;
static int usbFD;
static int dmesgPtr;
static int dmesgSerialPtr;
static char dmesgBuf[4096];
struct Event {
struct Event *next;
int source;
int value;
};
Event lastEvent;
Event *mkEvent(int source, int value) {
auto res = new Event();
memset(res, 0, sizeof(Event));
res->source = source;
res->value = value;
return res;
}
#define USB_MAGIC 0x3d3f
#define USB_SERIAL 1
#define USB_RESTART 2
#define USB_DMESG 3
struct UsbPacket {
uint16_t size;
uint16_t msgcount;
uint16_t magic;
uint16_t code;
char buf[1024 - 8];
};
void *usbThread(void *) {
UsbPacket pkt;
UsbPacket resp;
while (true) {
int len = read(usbFD, &pkt, sizeof(pkt));
if (len <= 4) {
sleep_core_us(20000);
continue;
}
resp.msgcount = pkt.msgcount;
if (pkt.magic == USB_MAGIC) {
if (pkt.code == USB_RESTART) {
target_reset();
} else if (pkt.code == USB_DMESG) {
dumpDmesg();
}
/*
resp.magic = pkt.magic;
resp.code = pkt.code;
resp.size = 8;
write(usbFD, &resp, sizeof(resp));
*/
} else {
resp.magic = 0xffff;
resp.size = 4;
write(usbFD, &resp, sizeof(resp));
}
sleep_core_us(1000);
}
}
static void startUsb() {
usbFD = open("/dev/lms_usbdev", O_RDWR, 0666);
pthread_t pid;
pthread_create(&pid, NULL, usbThread, NULL);
pthread_detach(pid);
}
static void *exitThread(void *) {
int fd = open("/dev/lms_ui", O_RDWR, 0666);
if (fd < 0)
return 0;
uint8_t *data =
(uint8_t *)mmap(NULL, NUM_BUTTONS, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
return 0;
}
for (;;) {
if (data[5])
target_reset();
sleep_core_us(50000);
}
}
static void startExitThread() {
pthread_t pid;
pthread_create(&pid, NULL, exitThread, NULL);
pthread_detach(pid);
}
void sendUsb(uint16_t code, const char *data, int len) {
while (len > 0) {
int sz = len;
if (sz > 1000)
sz = 1000;
UsbPacket pkt = {(uint16_t)(6 + sz), 0, USB_MAGIC, code, {}};
memcpy(pkt.buf, data, sz);
write(usbFD, &pkt, sizeof(pkt));
len -= sz;
data += sz;
}
}
void sendSerial(const char *data, int len) {
sendUsb(USB_SERIAL, data, len);
}
volatile bool paniced;
extern "C" void drawPanic(int code);
extern "C" void target_panic(int error_code) {
char buf[50];
paniced = true;
pthread_mutex_trylock(&execMutex);
snprintf(buf, sizeof(buf), "\nPANIC %d\n", error_code);
drawPanic(error_code);
DMESG("PANIC %d", error_code);
for (int i = 0; i < 10; ++i) {
sendSerial(buf, strlen(buf));
sleep_core_us(500 * 1000);
}
target_reset();
}
void startUser() {
pthread_mutex_lock(&execMutex);
}
void stopUser() {
pthread_mutex_unlock(&execMutex);
}
void sleep_core_us(uint64_t us) {
struct timespec ts;
ts.tv_sec = us / 1000000;
ts.tv_nsec = (us % 1000000) * 1000;
while (nanosleep(&ts, &ts))
;
}
void sleep_ms(uint32_t ms) {
stopUser();
sleep_core_us(ms * 1000);
startUser();
}
void sleep_us(uint64_t us) {
if (us > 50000) {
sleep_ms(us / 1000);
}
sleep_core_us(us);
}
uint64_t currTime() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
int current_time_ms() {
return currTime() - startTime;
}
void disposeThread(Thread *t) {
if (allThreads == t) {
allThreads = t->next;
} else {
for (auto tt = allThreads; tt; tt = tt->next) {
if (tt->next == t) {
tt->next = t->next;
break;
}
}
}
decr(t->act);
decr(t->arg0);
decr(t->data0);
decr(t->data1);
pthread_cond_destroy(&t->waitCond);
delete t;
}
static void runAct(Thread *thr) {
startUser();
pxt::runAction1(thr->act, thr->arg0);
stopUser();
disposeThread(thr);
}
static void mainThread(Thread *) {}
void setupThread(Action a, TValue arg = 0, void (*runner)(Thread *) = NULL, TValue d0 = 0,
TValue d1 = 0) {
if (runner == NULL)
runner = runAct;
auto thr = new Thread();
memset(thr, 0, sizeof(Thread));
thr->next = allThreads;
allThreads = thr;
thr->act = incr(a);
thr->arg0 = incr(arg);
thr->data0 = incr(d0);
thr->data1 = incr(d1);
pthread_cond_init(&thr->waitCond, NULL);
if (runner == mainThread) {
thr->pid = pthread_self();
} else {
pthread_create(&thr->pid, NULL, (void *(*)(void *))runner, thr);
THREAD_DBG("setup thread: %p (pid %p)", thr, thr->pid);
pthread_detach(thr->pid);
}
}
void releaseFiber() {
stopUser();
pthread_exit(NULL);
}
void runInParallel(Action a) {
setupThread(a);
}
static void runFor(Thread *t) {
startUser();
while (true) {
pxt::runAction0(t->act);
sleep_ms(20);
}
}
void runForever(Action a) {
setupThread(a, 0, runFor);
}
void waitForEvent(int source, int value) {
THREAD_DBG("waitForEv: %d %d", source, value);
auto self = pthread_self();
for (auto t = allThreads; t; t = t->next) {
THREAD_DBG("t: %p", t);
if (t->pid == self) {
pthread_mutex_lock(&eventMutex);
t->waitSource = source;
t->waitValue = value;
stopUser();
// spourious wake ups may occur they say
while (t->waitSource) {
pthread_cond_wait(&t->waitCond, &eventMutex);
}
pthread_mutex_unlock(&eventMutex);
startUser();
return;
}
}
DMESG("current thread not registered!");
target_panic(901);
}
static void dispatchEvent(Event &e) {
lastEvent = e;
auto curr = findBinding(e.source, e.value);
if (curr)
setupThread(curr->action, fromInt(e.value));
curr = findBinding(e.source, DEVICE_EVT_ANY);
if (curr)
setupThread(curr->action, fromInt(e.value));
}
static void *evtDispatcher(void *dummy) {
pthread_mutex_lock(&eventMutex);
while (true) {
pthread_cond_wait(&newEventBroadcast, &eventMutex);
while (eventHead != NULL) {
if (paniced)
return 0;
Event *ev = eventHead;
eventHead = ev->next;
if (eventHead == NULL)
eventTail = NULL;
for (auto thr = allThreads; thr; thr = thr->next) {
if (paniced)
return 0;
if (thr->waitSource == 0)
continue;
if (thr->waitValue != ev->value && thr->waitValue != DEVICE_EVT_ANY)
continue;
if (thr->waitSource == ev->source) {
thr->waitSource = 0; // once!
pthread_cond_broadcast(&thr->waitCond);
} else if (thr->waitSource == DEVICE_ID_NOTIFY &&
ev->source == DEVICE_ID_NOTIFY_ONE) {
thr->waitSource = 0; // once!
pthread_cond_broadcast(&thr->waitCond);
break; // do not wake up any other threads
}
}
dispatchEvent(*ev);
delete ev;
}
}
}
int allocateNotifyEvent() {
static volatile int notifyId;
pthread_mutex_lock(&eventMutex);
int res = ++notifyId;
pthread_mutex_unlock(&eventMutex);
return res;
}
void raiseEvent(int id, int event) {
auto e = mkEvent(id, event);
pthread_mutex_lock(&eventMutex);
if (eventTail == NULL) {
if (eventHead != NULL)
target_panic(902);
eventHead = eventTail = e;
} else {
eventTail->next = e;
eventTail = e;
}
pthread_cond_broadcast(&newEventBroadcast);
pthread_mutex_unlock(&eventMutex);
}
void registerWithDal(int id, int event, Action a, int flags) {
// TODO support flags
setBinding(id, event, a);
}
static void runPoller(Thread *thr) {
Action query = thr->data0;
auto us = (uint64_t)toInt(thr->data1) * 1000;
// note that this is run without the user mutex held - it should not modify any state!
TValue prev = pxt::runAction0(query);
startUser();
pxt::runAction2(thr->act, prev, prev);
stopUser();
while (true) {
sleep_core_us(us);
if (paniced)
break;
TValue curr = pxt::runAction0(query);
if (curr != prev) {
startUser();
pxt::runAction2(thr->act, prev, curr);
stopUser();
if (paniced)
break;
decr(prev);
prev = curr;
}
}
// disposeThread(thr);
}
uint32_t afterProgramPage() {
return 0;
}
void dumpDmesg() {
auto len = dmesgPtr - dmesgSerialPtr;
if (len == 0)
return;
sendSerial(dmesgBuf + dmesgSerialPtr, len);
dmesgSerialPtr = dmesgPtr;
}
int lmsPid;
void stopLMS() {
struct dirent *ent;
DIR *dir;
dir = opendir("/proc");
if (dir == NULL)
return;
while ((ent = readdir(dir)) != NULL) {
int pid = atoi(ent->d_name);
if (!pid)
continue;
char namebuf[100];
snprintf(namebuf, 1000, "/proc/%d/cmdline", pid);
FILE *f = fopen(namebuf, "r");
if (f) {
fread(namebuf, 1, 99, f);
if (strcmp(namebuf, "./lms2012") == 0) {
lmsPid = pid;
}
fclose(f);
if (lmsPid)
break;
}
}
closedir(dir);
lmsPid = 0; // disable SIGSTOP for now - rethink if problems with I2C (runs on a thread)
if (lmsPid) {
DMESG("SIGSTOP to lmsPID=%d", lmsPid);
if (kill(lmsPid, SIGSTOP))
DMESG("SIGSTOP failed");
}
}
void runLMS() {
DMESG("re-starting LMS2012");
kill(lmsPid, SIGCONT);
sleep_core_us(200000);
exit(0);
/*
chdir("/home/root/lms2012/sys");
for (int fd = 3; fd < 9999; ++fd)
close(fd);
execl("lms2012", "./lms2012");
exit(100); // should not be reached
*/
}
void stopMotors() {
uint8_t cmd[3] = {opOutputStop, 0x0F, 0};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 3);
close(fd);
}
void stopProgram() {
uint8_t cmd[1] = {opOutputProgramStop};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 1);
close(fd);
}
extern "C" void target_reset() {
stopMotors();
stopProgram();
if (lmsPid)
runLMS();
else
exit(0);
}
void screen_init();
void initRuntime() {
// daemon(1, 1);
startTime = currTime();
DMESG("runtime starting...");
stopLMS();
startUsb();
startExitThread();
pthread_t disp;
pthread_create(&disp, NULL, evtDispatcher, NULL);
pthread_detach(disp);
setupThread(0, 0, mainThread);
target_init();
screen_init();
startUser();
}
static FILE *dmesgFile;
void dmesgRaw(const char *buf, uint32_t len) {
if (!dmesgFile) {
dmesgFile = fopen("/tmp/dmesg.txt", "w");
if (!dmesgFile)
dmesgFile = stderr;
}
if (len > sizeof(dmesgBuf) / 2)
return;
if (dmesgPtr + len > sizeof(dmesgBuf)) {
dmesgPtr = 0;
dmesgSerialPtr = 0;
}
memcpy(dmesgBuf + dmesgPtr, buf, len);
dmesgPtr += len;
fwrite(buf, 1, len, dmesgFile);
}
void dmesg(const char *format, ...) {
char buf[500];
snprintf(buf, sizeof(buf), "[%8d] ", current_time_ms());
dmesgRaw(buf, strlen(buf));
va_list arg;
va_start(arg, format);
vsnprintf(buf, sizeof(buf), format, arg);
va_end(arg);
dmesgRaw(buf, strlen(buf));
dmesgRaw("\n", 1);
fflush(dmesgFile);
fdatasync(fileno(dmesgFile));
}
} // namespace pxt

View File

@ -25,13 +25,20 @@ PXT_VTABLE_CTOR(MMap) {
}
void MMap::print() {
DMESG("MMap %p r=%d len=%d fd=%d data=%p", this, refcnt, length, fd, data);
DMESG("MMap %p len=%d fd=%d data=%p", this, length, fd, data);
}
void MMap::destroy() {
munmap(data, length);
close(fd);
}
void MMap::scan(MMap *) {}
unsigned MMap::gcsize(MMap *) {
return TOWORDS(sizeof(MMap));
}
}
namespace control {
@ -39,8 +46,8 @@ namespace control {
/** Create new file mapping in memory */
//%
MMap *mmap(String filename, int size, int offset) {
DMESG("mmap %s len=%d off=%d", filename->data, size, offset);
int fd = open(filename->data, O_RDWR, 0);
DMESG("mmap %s len=%d off=%d", filename->getUTF8Data(), size, offset);
int fd = open(filename->getUTF8Data(), O_RDWR, 0);
if (fd < 0)
return 0;

View File

@ -159,6 +159,7 @@ namespace motors {
private _accelerationTime: number;
private _decelerationSteps: number;
private _decelerationTime: number;
private _inverted: boolean;
protected static output_types: number[] = [0x7, 0x7, 0x7, 0x7];
@ -176,6 +177,7 @@ namespace motors {
this._accelerationTime = 0;
this._decelerationSteps = 0;
this._decelerationTime = 0;
this._inverted = false;
}
/**
@ -225,9 +227,11 @@ namespace motors {
//% help=motors/motor/set-inverted
setInverted(inverted: boolean) {
this.init();
const b = mkCmd(this._port, DAL.opOutputPolarity, 1)
b.setNumber(NumberFormat.Int8LE, 2, inverted ? 0 : 1);
writePWM(b)
this._inverted = inverted;
}
protected invertedFactor(): number {
return this._inverted ? -1 : 1;
}
/**
@ -264,8 +268,9 @@ namespace motors {
// allow 500ms for robot to settle
if (this._brake && this._brakeSettleTime > 0)
pause(this._brakeSettleTime);
else
pause(1); // give a tiny breather
else {
pause(1);
}
}
protected pauseOnRun(stepsOrTime: number) {
@ -275,7 +280,6 @@ namespace motors {
// allow robot to settle
this.settle();
} else {
// give a breather to the event system in tight loops
pause(1);
}
}
@ -294,8 +298,9 @@ namespace motors {
}
private normalizeSchedule(speed: number, step1: number, step2: number, step3: number, unit: MoveUnit): MoveSchedule {
// motor polarity is not supported at the firmware level for sync motor operations
const r: MoveSchedule = {
speed: Math.clamp(-100, 100, speed >> 0),
speed: Math.clamp(-100, 100, speed | 0) * this.invertedFactor(),
useSteps: true,
steps: [step1 || 0, step2 || 0, step3 || 0]
}
@ -562,6 +567,7 @@ namespace motors {
private __init() {
this.setOutputType(this._large);
this.setInverted(false);
}
/**
@ -741,7 +747,7 @@ namespace motors {
//% help=motors/synced/steer
steer(turnRatio: number, speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
speed = Math.clamp(-100, 100, speed >> 0) * this.invertedFactor();
if (!speed) {
this.stop();
return;
@ -918,9 +924,3 @@ namespace motors {
writePWM(b)
}
}
interface Buffer {
[index: number]: number;
// rest defined in buffer.cpp
}

236
libs/core/platform.cpp Normal file
View File

@ -0,0 +1,236 @@
#include "pxt.h"
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <signal.h>
#include <dirent.h>
#include "ev3const.h"
namespace pxt {
static int usbFD;
#define USB_MAGIC 0x3d3f
#define USB_SERIAL 1
#define USB_RESTART 2
#define USB_DMESG 3
struct UsbPacket {
uint16_t size;
uint16_t msgcount;
uint16_t magic;
uint16_t code;
char buf[1024 - 8];
};
void *usbThread(void *) {
UsbPacket pkt;
UsbPacket resp;
while (true) {
int len = read(usbFD, &pkt, sizeof(pkt));
if (len <= 4) {
sleep_core_us(20000);
continue;
}
resp.msgcount = pkt.msgcount;
if (pkt.magic == USB_MAGIC) {
if (pkt.code == USB_RESTART) {
target_reset();
} else if (pkt.code == USB_DMESG) {
dumpDmesg();
}
/*
resp.magic = pkt.magic;
resp.code = pkt.code;
resp.size = 8;
write(usbFD, &resp, sizeof(resp));
*/
} else {
resp.magic = 0xffff;
resp.size = 4;
write(usbFD, &resp, sizeof(resp));
}
sleep_core_us(1000);
}
}
static void startUsb() {
usbFD = open("/dev/lms_usbdev", O_RDWR, 0666);
pthread_t pid;
pthread_create(&pid, NULL, usbThread, NULL);
pthread_detach(pid);
}
static void *exitThread(void *) {
int fd = open("/dev/lms_ui", O_RDWR, 0666);
if (fd < 0)
return 0;
uint8_t *data =
(uint8_t *)mmap(NULL, NUM_BUTTONS, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
return 0;
}
for (;;) {
if (data[5])
target_reset();
sleep_core_us(50000);
}
}
static void startExitThread() {
pthread_t pid;
pthread_create(&pid, NULL, exitThread, NULL);
pthread_detach(pid);
}
void sendUsb(uint16_t code, const char *data, int len) {
while (len > 0) {
int sz = len;
if (sz > 1000)
sz = 1000;
UsbPacket pkt = {(uint16_t)(6 + sz), 0, USB_MAGIC, code, {}};
memcpy(pkt.buf, data, sz);
write(usbFD, &pkt, sizeof(pkt));
len -= sz;
data += sz;
}
}
void sendSerial(const char *data, int len) {
sendUsb(USB_SERIAL, data, len);
}
int lmsPid;
void stopLMS() {
struct dirent *ent;
DIR *dir;
dir = opendir("/proc");
if (dir == NULL)
return;
while ((ent = readdir(dir)) != NULL) {
int pid = atoi(ent->d_name);
if (!pid)
continue;
char namebuf[100];
snprintf(namebuf, 100, "/proc/%d/cmdline", pid);
FILE *f = fopen(namebuf, "r");
if (f) {
fread(namebuf, 1, 99, f);
if (strcmp(namebuf, "./lms2012") == 0) {
lmsPid = pid;
}
fclose(f);
if (lmsPid)
break;
}
}
closedir(dir);
lmsPid = 0; // disable SIGSTOP for now - rethink if problems with I2C (runs on a thread)
if (lmsPid) {
DMESG("SIGSTOP to lmsPID=%d", lmsPid);
if (kill(lmsPid, SIGSTOP))
DMESG("SIGSTOP failed");
}
}
void runLMS() {
DMESG("re-starting LMS2012");
kill(lmsPid, SIGCONT);
sleep_core_us(200000);
exit(0);
/*
chdir("/home/root/lms2012/sys");
for (int fd = 3; fd < 9999; ++fd)
close(fd);
execl("lms2012", "./lms2012");
exit(100); // should not be reached
*/
}
void stopMotors() {
uint8_t cmd[3] = {opOutputStop, 0x0F, 0};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 3);
close(fd);
}
void stopProgram() {
uint8_t cmd[1] = {opOutputProgramStop};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 1);
close(fd);
}
extern "C" void target_reset() {
tryLockUser();
stopMotors();
stopProgram();
if (lmsPid)
runLMS();
else
exit(0);
}
void target_exit() {
target_reset();
}
void target_startup() {
stopLMS();
startUsb();
startExitThread();
}
void initKeys() {}
static const char *progPath = "/mnt/ramdisk/prjs/BrkProg_SAVE";
// These are disabled except when building File_manager.pdf
// %
void deletePrjFile(String filename) {
const char *d = filename->getUTF8Data();
if (strlen(d) > 500 || strchr(d, '/'))
return;
char buf[1024];
snprintf(buf, sizeof(buf), "%s/%s", progPath, d);
unlink(buf);
}
// %
RefCollection *listPrjFiles() {
auto res = Array_::mk();
registerGCObj(res);
auto dp = opendir(progPath);
for (;;) {
dirent *ep = dp ? readdir(dp) : NULL;
if (!ep)
break;
if (ep->d_name[0] == '.')
continue;
auto str = mkString(ep->d_name, -1);
registerGCObj(str);
res->head.push((TValue)str);
unregisterGCObj(str);
}
if (dp)
closedir(dp);
unregisterGCObj(res);
return res;
}
}

View File

@ -1 +1,3 @@
// leave empty
#define PXT_GC_THREAD_LIST 1
#define PXT_IN_ISR() false

View File

@ -11,6 +11,7 @@ int allocateNotifyEvent();
void sleep_core_us(uint64_t us);
void startUser();
void stopUser();
int tryLockUser();
class Button;
typedef Button *Button_;
@ -27,9 +28,13 @@ class MMap : public RefObject {
MMap();
void destroy();
void print();
static void scan(MMap *);
static unsigned gcsize(MMap *);
};
extern volatile bool paniced;
void target_exit();
// Buffer, Sound, and Image share representation.
typedef Buffer Sound;

View File

@ -25,11 +25,20 @@
"dal.d.ts",
"icons.jres",
"ns.ts",
"platform.h"
"platform.h",
"platform.cpp",
"dmesg.cpp",
"integrator.ts"
],
"testFiles": [
"test.ts"
],
"dalDTS": {
"includeDirs": [
"pxtapp"
]
},
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/core---linux",
"npmDependencies": {},
"public": true,
"dependencies": {

View File

@ -1,16 +0,0 @@
#ifndef __PXTCORE_H
#define __PXTCORE_H
#include <stdio.h>
namespace pxt {
void dmesg(const char *fmt, ...);
#define DMESG pxt::dmesg
}
static inline void itoa(int v, char *dst) {
snprintf(dst, 30, "%d", v);
}
#endif

View File

@ -85,10 +85,9 @@ void updateScreen(Image_ img) {
lastImg = img;
}
if (lastImg && lastImg->isDirty() && mappedFrameBuffer != MAP_FAILED) {
if (lastImg && mappedFrameBuffer != MAP_FAILED) {
if (lastImg->bpp() != 1 || lastImg->width() != LCD_WIDTH || lastImg->height() != LCD_HEIGHT)
target_panic(906);
lastImg->clearDirty();
bitBufferToFrameBufferSwap(lastImg->pix(), mappedFrameBuffer);
}
}

View File

@ -25,8 +25,8 @@ struct hci_dev_list_req {
hci_dev_req dev_req[2];
};
static uint32_t bt_addr() {
uint32_t res = -1;
static uint64_t bt_addr() {
uint64_t res = -1;
int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
if (fd < 0) {
@ -50,11 +50,8 @@ static uint32_t bt_addr() {
goto done;
}
memcpy(&res, di.bdaddr, 4);
res *= 0x1000193;
res += di.bdaddr[4];
res *= 0x1000193;
res += di.bdaddr[5];
res = 0;
memcpy(&res, di.bdaddr, 6);
done:
close(fd);
@ -63,14 +60,10 @@ done:
namespace pxt {
int getSerialNumber() {
static int serial;
if (serial != 0)
return serial;
serial = bt_addr() & 0x7fffffff;
uint64_t getLongSerialNumber() {
static uint64_t serial;
if (serial == 0)
serial = bt_addr();
return serial;
}

View File

@ -68,6 +68,12 @@ declare namespace control {
/** Write data to DMESG debugging buffer. */
//% shim=control::dmesg
function dmesg(s: string): void;
/**
* Determines if the USB has been enumerated.
*/
//% shim=control::isUSBInitialized
function isUSBInitialized(): boolean;
}
declare namespace serial {

13
libs/ev3/brick.ts Normal file
View File

@ -0,0 +1,13 @@
namespace brick {
/**
* Exits the program to the main menu. (in the simulator restarts it)
*/
//% blockId=loopstop block="exit program"
//% help=reference/brick/exit-program
//% weight=10
//% blockGap=8
//% group="Buttons"
export function exitProgram() {
control.reset();
}
}

View File

@ -20,7 +20,7 @@ namespace console._screen {
lines = [];
console.addListener(log);
brick.buttonUp.onEvent(ButtonEvent.Bumped, () => scroll(-3))
brick.buttonDown.onEvent(ButtonEvent.Bumped, () => scroll(3))
brick.buttonDown.onEvent(ButtonEvent.Bumped, () => scroll(3))
}
}
@ -44,7 +44,7 @@ namespace console._screen {
printLog();
}
function log(msg: string): void {
function log(priority: ConsolePriority, msg: string): void {
lines.push(msg);
if (lines.length + 5 > maxLines) {
lines.splice(0, maxLines - lines.length);

View File

@ -1,6 +1,6 @@
//% color="#68C3E2" weight=100 icon="\uf106"
//% groups='["Buttons", "Screen", "Battery"]'
//% groups='["Buttons", "Screen", "Power"]'
//% labelLineWidth=60
namespace brick {
}

View File

@ -2,8 +2,9 @@
"name": "ev3",
"description": "The EV3 library",
"files": [
"README.md",
"README.md",
"ns.ts",
"brick.ts",
"startup.ts",
"images.jres",
"images.ts",

View File

@ -1,10 +1,6 @@
// This is the last thing executed before user code
console.addListener(function(msg: string) {
console.addListener(function(priority: ConsolePriority, msg: string) {
control.dmesg(msg.substr(0, msg.length - 1))
})
// pulse green, play startup sound, turn off light
brick.setStatusLight(StatusLight.GreenPulse);
// We pause for 100ms to give time to read sensor values, so they work in on_start block
pause(400)
// and we're ready
brick.setStatusLight(StatusLight.Off);
// boot sequence
brick.showBoot();

View File

@ -0,0 +1,21 @@
# Pause Until Rotated
Pauses the program until the gyro sensors detect that the desired amount of rotation
has been acheived.
```
sensors.gyro2.pauseUntilRotated(90)
```
## Example
This program performs a square turn left, then right.
```blocks
sensors.gyro2.calibrate()
motors.largeBC.steer(200, 10)
sensors.gyro2.pauseUntilRotated(90)
motors.largeBC.steer(-200, 10)
sensors.gyro2.pauseUntilRotated(-90)
motors.largeBC.stop()
```

View File

@ -7,12 +7,15 @@ const enum GyroSensorMode {
namespace sensors {
//% fixedInstances
export class GyroSensor extends internal.UartSensor {
private calibrating: boolean;
private _calibrating: boolean;
private _drift: number;
private _angle: control.EulerIntegrator;
constructor(port: number) {
super(port)
this.calibrating = false;
this._calibrating = false;
this._drift = 0;
this._angle = new control.EulerIntegrator();
this._setMode(GyroSensorMode.Rate);
this.setMode(GyroSensorMode.Rate);
}
@ -21,13 +24,17 @@ namespace sensors {
}
_query(): number {
return this.getNumber(NumberFormat.Int16LE, 0);
const v = this.getNumber(NumberFormat.Int16LE, 0);
this._angle.integrate(v - this._drift);
return v;
}
setMode(m: GyroSensorMode) {
if (m == GyroSensorMode.Rate && this.mode != m)
this._drift = 0;
this._setMode(m)
// decrecated
}
isCalibrating(): boolean {
return this._calibrating;
}
/**
@ -40,15 +47,14 @@ namespace sensors {
//% parts="gyroscope"
//% blockNamespace=sensors
//% this.fieldEditor="ports"
//% weight=64
//% weight=64 blockGap=8
//% group="Gyro Sensor"
angle(): number {
this.poke();
if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000);
if (this._calibrating)
pauseUntil(() => !this._calibrating, 2000);
this.setMode(GyroSensorMode.Angle);
return this._query();
return Math.round(this._angle.value);
}
/**
@ -65,10 +71,8 @@ namespace sensors {
//% group="Gyro Sensor"
rate(): number {
this.poke();
if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000);
this.setMode(GyroSensorMode.Rate);
if (this._calibrating)
pauseUntil(() => !this._calibrating, 2000);
return this._query() - this._drift;
}
@ -85,12 +89,12 @@ namespace sensors {
//% weight=51 blockGap=8
//% group="Gyro Sensor"
calibrate(): void {
if (this.calibrating) return; // already in calibration mode
if (this._calibrating) return; // already in calibration mode
const statusLight = brick.statusLight(); // save current status light
brick.setStatusLight(StatusLight.Orange);
this.calibrating = true;
this._calibrating = true;
// may be triggered by a button click,
// give time for robot to settle
pause(700);
@ -104,7 +108,8 @@ namespace sensors {
brick.setStatusLight(statusLight); // resture previous light
// and we're done
this.calibrating = false;
this._angle.reset();
this._calibrating = false;
return;
}
@ -116,22 +121,22 @@ namespace sensors {
// wait till sensor is live
pauseUntil(() => this.isActive(), 7000);
// mode toggling
this.setMode(GyroSensorMode.Rate);
this.setMode(GyroSensorMode.Angle);
this._setMode(GyroSensorMode.Rate);
this._setMode(GyroSensorMode.Angle);
this._setMode(GyroSensorMode.Rate);
// check sensor is ready
if (!this.isActive()) {
brick.setStatusLight(StatusLight.RedFlash); // didn't work
pause(2000);
brick.setStatusLight(statusLight); // restore previous light
this.calibrating = false;
this._angle.reset();
this._calibrating = false;
return;
}
// switch to rate mode
this.computeDriftNoCalibration();
// switch back to the desired mode
this.setMode(this.mode);
// and done
brick.setStatusLight(StatusLight.Green); // success
@ -139,7 +144,8 @@ namespace sensors {
brick.setStatusLight(statusLight); // resture previous light
// and we're done
this.calibrating = false;
this._angle.reset();
this._calibrating = false;
}
/**
@ -154,13 +160,37 @@ namespace sensors {
//% weight=50 blockGap=8
//% group="Gyro Sensor"
reset(): void {
if (this.calibrating) return; // already in calibration mode
if (this._calibrating) return; // already in calibration mode
this._calibrating = true;
const statusLight = brick.statusLight(); // save current status light
brick.setStatusLight(StatusLight.Orange);
this.calibrating = true;
// send a reset command
super.reset();
this._drift = 0;
this._angle.reset();
pauseUntil(() => this.isActive(), 7000);
// check sensor is ready
if (!this.isActive()) {
brick.setStatusLight(StatusLight.RedFlash); // didn't work
pause(2000);
brick.setStatusLight(statusLight); // restore previous light
this._angle.reset();
this._calibrating = false;
return;
}
this._setMode(GyroSensorMode.Rate);
// and done
this.calibrating = false;
brick.setStatusLight(StatusLight.Green); // success
pause(1000);
brick.setStatusLight(statusLight); // resture previous light
// and done
this._angle.reset();
this._calibrating = false;
}
/**
@ -190,15 +220,35 @@ namespace sensors {
//% weight=10 blockGap=8
//% group="Gyro Sensor"
computeDrift() {
if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000);
if (this._calibrating)
pauseUntil(() => !this._calibrating, 2000);
pause(1000); // let the robot settle
this.computeDriftNoCalibration();
}
/**
* Pauses the program until the gyro detected
* that the angle changed by the desired amount of degrees.
* @param degrees the degrees to turn
*/
//% help=sensors/gyro/pause-until-rotated
//% block="pause until **gyro** %this|rotated %degrees=rotationPicker|degrees"
//% blockId=gyroPauseUntilRotated
//% parts="gyroscope"
//% blockNamespace=sensors
//% this.fieldEditor="ports"
//% degrees.defl=90
//% weight=63
//% group="Gyro Sensor"
pauseUntilRotated(degrees: number, timeOut?: number): void {
let a = this.angle();
const end = a + degrees;
const direction = (end - a) > 0 ? 1 : -1;
pauseUntil(() => (end - this.angle()) * direction <= 0, timeOut);
}
private computeDriftNoCalibration() {
// clear drift
this.setMode(GyroSensorMode.Rate);
this._drift = 0;
const n = 10;
let d = 0;
@ -207,22 +257,17 @@ namespace sensors {
pause(20);
}
this._drift = d / n;
this._angle.reset();
}
_info(): string {
if (this.calibrating)
if (this._calibrating)
return "cal...";
switch (this.mode) {
case GyroSensorMode.Angle:
return `${this._query()}>`;
case GyroSensorMode.Rate:
let r = `${this._query()}r`;
if (this._drift != 0)
r += `-${this._drift | 0}`;
return r;
}
return "";
let r = `${this._query()}r`;
if (this._drift != 0)
r += `-${this._drift | 0}`;
return r;
}
}
@ -237,4 +282,17 @@ namespace sensors {
//% fixedInstance whenUsed block="4" jres=icons.port4
export const gyro4: GyroSensor = new GyroSensor(4)
/**
* Get the rotation angle field editor
* @param degrees angle in degrees, eg: 90
*/
//% blockId=rotationPicker block="%degrees"
//% blockHidden=true shim=TD_ID
//% colorSecondary="#FFFFFF"
//% degrees.fieldEditor="numberdropdown" degrees.fieldOptions.decompileLiterals=true
//% degrees.fieldOptions.data='[["30", 30], ["45", 45], ["60", 60], ["90", 90], ["180", 180], ["-30", -30], ["-45", -45], ["-60", -60], ["-90", -90], ["-180", -180]]'
export function __rotationPicker(degrees: number): number {
return degrees;
}
}

View File

@ -0,0 +1,10 @@
namespace image {
/**
* Get the screen image
*/
//%
export function screenImage(): Image {
return screen;
}
}

View File

@ -15,7 +15,7 @@ declare interface Image {
height: int32;
/**
* True iff the image is monochromatic (black and white)
* True if the image is monochromatic (black and white)
*/
//% property shim=ImageMethods::isMono
isMono: boolean;
@ -45,6 +45,18 @@ declare interface Image {
//% shim=ImageMethods::fill
fill(c: int32): void;
/**
* Copy row(s) of pixel from image to buffer (8 bit per pixel).
*/
//% shim=ImageMethods::getRows
getRows(x: int32, dst: Buffer): void;
/**
* Copy row(s) of pixel from buffer to image.
*/
//% shim=ImageMethods::setRows
setRows(x: int32, src: Buffer): void;
/**
* Return a copy of the current image
*/

View File

@ -129,8 +129,8 @@ namespace brick {
screenMode = ScreenMode.Ports;
renderPorts();
control.runInParallel(function() {
while(screenMode == ScreenMode.Ports) {
control.runInParallel(function () {
while (screenMode == ScreenMode.Ports) {
renderPorts();
pause(50);
}
@ -140,8 +140,18 @@ namespace brick {
function renderPorts() {
const col = 44;
const lineHeight8 = image.font8.charHeight + 2;
const h = screen.height;
clearScreen();
for (let i = 0; i < 4; ++i) {
const x = i * col + 2;
screen.print("ABCD"[i], x, 1 * lineHeight8, 1, image.font8)
screen.print((i + 1).toString(), x, h - lineHeight8, 1, image.font8)
}
screen.drawLine(0, 5 * lineHeight8, screen.width, 5 * lineHeight8, 1);
screen.drawLine(0, h - 5 * lineHeight8, screen.width, h - 5 * lineHeight8, 1)
function scale(x: number) {
if (Math.abs(x) >= 5000) {
const k = Math.floor(x / 1000);
@ -155,27 +165,38 @@ namespace brick {
const datas = motors.getAllMotorData();
for (let i = 0; i < datas.length; ++i) {
const data = datas[i];
const x = i * col + 2;
if (!data.actualSpeed && !data.count) continue;
const x = i * col;
screen.print("ABCD"[i], x + 2, 1 * lineHeight8, 1, image.font8)
screen.print(`${scale(data.actualSpeed)}%`, x + 2, 3 * lineHeight8, 1, image.font8)
screen.print(`${scale(data.count)}>`, x + 2, 4 * lineHeight8, 1, image.font8)
screen.print(`${scale(data.actualSpeed)}%`, x, 3 * lineHeight8, 1, image.font8)
screen.print(`${scale(data.count)}>`, x, 4 * lineHeight8, 1, image.font8)
}
screen.drawLine(0, 5 * lineHeight8, screen.width, 5 * lineHeight8, 1);
// sensors
const sis = sensors.internal.getActiveSensors();
const h = screen.height;
screen.drawLine(0, h - 5 * lineHeight8, screen.width, h - 5 * lineHeight8, 1)
for (let i = 0; i < sis.length; ++i) {
const si = sis[i];
const x = (si.port() - 1) * col;
const x = (si.port() - 1) * col + 2;
const inf = si._info();
screen.print(si.port() + "", x, h - 4 * lineHeight8, 1, image.font8)
screen.print(inf, x, h - 2 * lineHeight8, 1, inf.length > 4 ? image.font5 : image.font8);
if (inf)
screen.print(inf, x, h - 2 * lineHeight8, 1, inf.length > 4 ? image.font5 : image.font8);
}
}
export function showBoot() {
// pulse green, play startup sound, turn off light
brick.setStatusLight(StatusLight.GreenPulse);
// We pause for 100ms to give time to read sensor values, so they work in on_start block
pause(400)
// and we're ready
brick.setStatusLight(StatusLight.Off);
// always show port by default if no UI is set
control.runInParallel(function () {
// show ports if nothing is has been shown
if (screenMode != ScreenMode.None) return;
showPorts();
})
}
/**
* An image
* @param image the image

View File

@ -1,12 +1,13 @@
{
"name": "pxt-ev3",
"version": "1.2.17",
"version": "1.4.8",
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
"private": false,
"keywords": [
"JavaScript",
"education",
"lego",
"LEGO",
"EV3",
"pxt",
"MakeCode",
"Microsoft"
@ -32,15 +33,20 @@
],
"devDependencies": {
"typescript": "2.6.1",
"react": "16.8.3",
"semantic-ui-less": "2.2.14",
"@types/bluebird": "2.0.33",
"@types/marked": "0.3.0",
"@types/node": "8.0.53",
"webfonts-generator": "^0.4.0"
"webfonts-generator": "^0.4.0",
"@types/jquery": "3.2.16",
"@types/react": "16.0.25",
"@types/react-dom": "16.0.3",
"@types/web-bluetooth": "0.0.4"
},
"dependencies": {
"pxt-common-packages": "0.23.61",
"pxt-core": "4.0.11"
"pxt-common-packages": "6.16.18",
"pxt-core": "5.28.8"
},
"scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@ -22,7 +22,7 @@
],
"simulator": {
"autoRun": true,
"streams": true,
"autoRunLight": false,
"aspectRatio": 0.5,
"parts": false,
"enableTrace": true,
@ -50,15 +50,16 @@
"flashCodeAlign": 256,
"floatingPoint": true,
"taggedInts": true,
"stackAlign": 2
"stackAlign": 2,
"gc": true
},
"serial": {
"vendorId": "0x0694",
"productId": "0x0005",
"rawHID": true,
"useEditor": true,
"log": true
},
"vendorId": "0x0694",
"productId": "0x0005",
"rawHID": true,
"useEditor": true,
"log": true
},
"runtime": {
"mathBlocks": true,
"loopsBlocks": true,
@ -72,7 +73,9 @@
"onStartColor": "#58AB41",
"pauseUntilBlock": {
"category": "loops"
}
},
"bannedCategories": [
]
},
"compileService": {
"buildEngine": "dockermake",
@ -82,7 +85,7 @@
"appTheme": {
"accentColor": "#0089BF",
"logoWide": true,
"logoUrl": "https://education.lego.com/",
"logoUrl": "https://education.lego.com/",
"logo": "./static/lego_education_logo.png",
"docsLogo": "./static/lego_education_logo.png",
"portraitLogo": "./static/lego_education_logo.png",
@ -151,6 +154,12 @@
"usbHelp": [],
"extendEditor": true,
"extendFieldEditors": true,
"scriptManager": true,
"importExtensionFiles": true,
"experiments": [
"python",
"alwaysGithubItemBlocks"
],
"disableBlockIcons": true,
"blocklyOptions": {
"grid": {
@ -186,7 +195,11 @@
"editor.background": "#f9f9f9"
},
"fileNameExclusiveFilter": "[^a-zA-Z0-9]",
"qrCode": true,
"shareFinishedTutorials": true,
"nameProjectFirst": true,
"alwaysGithubItem": true,
"enableTrace": true
},
"ignoreDocsErrors": true
}
}

97
scripts/file_manager.ts Normal file
View File

@ -0,0 +1,97 @@
//% shim=pxt::listPrjFiles
function getPrjs() {
let programs = [
"pxt",
"my amazing robot",
]
for (let i = 1; i < 6; ++i)
programs.push("Untitled-" + i)
return programs
}
//% shim=pxt::deletePrjFile
function delPrj(fn: string) {
return
}
const programs = getPrjs()
.filter(s => s.substr(s.length - 4, 4) == ".rbf")
.map(s => s.substr(0, s.length - 4))
.filter(s => s != "File_manager")
programs.push("")
programs.push("Cancel")
programs.push("Delete 0 files")
let todel: boolean[] = []
let scrollTop = 0
let cursor = 0
let confirm = false
function showMenu() {
if (cursor < scrollTop + 2)
scrollTop = cursor - 2
else if (cursor > scrollTop + 11)
scrollTop = cursor - 11
if (scrollTop < 0)
scrollTop = 0
let num = 0
for (let i = 0; i < todel.length; ++i)
if (todel[i]) num++
programs[programs.length - 1] =
confirm ? "Enter to confirm" : "Delete " + num + " file(s)"
brick.clearScreen()
const h = brick.lineHeight()
for (let i = 0; i < 13; ++i) {
const y = i * h
const idx = scrollTop + i
const fg = idx == cursor ? 0 : 1
const bg = idx == cursor ? 1 : 0
// screen.fillRect(0, y, screen.width, h, bg);
let text = (idx == cursor ? ">" : " ")
+ (todel[idx] ? "*" : " ")
+ " "
+ (programs[scrollTop + i] || "")
screen.print(text, 0, y, fg, brick.font);
}
}
function move(d: number) {
confirm = false
const nc = cursor + d
if (0 <= nc && nc < programs.length)
cursor = nc
showMenu()
}
brick.buttonDown.onEvent(ButtonEvent.Pressed, () => move(1))
brick.buttonUp.onEvent(ButtonEvent.Pressed, () => move(-1))
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
if (cursor < programs.length - 3) {
todel[cursor] = !todel[cursor]
move(1)
} else if (cursor == programs.length - 3) {
// nothing
} else if (cursor == programs.length - 2) {
control.reset()
} else if (cursor == programs.length - 1) {
if (todel.every(x => !x))
return
if (confirm) {
brick.clearScreen()
brick.showString("deleting...", 6)
for (let i = 0; i < todel.length; ++i) {
if (todel[i]) {
delPrj(programs[i] + ".elf")
delPrj(programs[i] + ".rbf")
}
}
pause(1000)
control.reset()
} else {
confirm = true
showMenu()
}
}
})
showMenu()

View File

@ -5,6 +5,7 @@
namespace pxsim {
export class EV3Board extends CoreBoard {
viewHost: visuals.BoardHost;
view: SVGSVGElement;
outputState: EV3OutputState;
@ -83,7 +84,8 @@ namespace pxsim {
highContrast: msg.highContrast,
light: msg.light
};
const viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
this.viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
boardDef,
visual: boardDef.visual,
highContrast: msg.highContrast,
light: msg.light
@ -91,7 +93,7 @@ namespace pxsim {
document.body.innerHTML = ""; // clear children
document.body.className = msg.light ? "light" : "";
document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement);
document.body.appendChild(this.view = this.viewHost.getView() as SVGSVGElement);
this.inputNodes = [];
this.outputNodes = [];
@ -102,8 +104,8 @@ namespace pxsim {
return Promise.resolve();
}
screenshot(): string {
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
screenshotAsync(width?: number): Promise<ImageData> {
return this.viewHost.screenshotAsync(width);
}
getBrickNode() {

View File

@ -3,7 +3,6 @@
namespace pxsim {
export enum ColorSensorMode {
None = -1,
Reflected = 0,
Ambient = 1,
Colors = 2,

View File

@ -1,6 +1,5 @@
namespace pxsim {
export const enum GyroSensorMode {
None = -1,
Angle = 0,
Rate = 1,
}
@ -8,7 +7,6 @@ namespace pxsim {
export class GyroSensorNode extends UartSensorNode {
id = NodeType.GyroSensor;
private angle: number = 0;
private rate: number = 0;
constructor(port: number) {
@ -19,14 +17,6 @@ namespace pxsim {
return DAL.DEVICE_TYPE_GYRO;
}
setAngle(angle: number) {
angle = angle | 0;
if (this.angle != angle) {
this.angle = angle;
this.setChangedState();
}
}
setRate(rate: number) {
rate = rate | 0;
if (this.rate != rate) {
@ -35,9 +25,12 @@ namespace pxsim {
}
}
getRate() {
return this.rate;
}
getValue() {
return this.mode == GyroSensorMode.Angle ? this.angle :
this.mode == GyroSensorMode.Rate ? this.rate : 0;
return this.getRate();
}
}
}

View File

@ -44,7 +44,6 @@ namespace pxsim {
}
export enum InfraredSensorMode {
None = -1,
Proximity = 0,
Seek = 1,
RemoteControl = 2

View File

@ -9,7 +9,6 @@ namespace pxsim {
private angle: number = 0;
private tacho: number = 0;
private speed: number = 0;
private polarity: number = 1; // -1, 1 or -1
private started: boolean;
private speedCmd: DAL;
@ -31,7 +30,7 @@ namespace pxsim {
}
getSpeed() {
return Math.round(this.speed * (!this._synchedMotor && this.polarity == 0 ? -1 : 1));
return Math.round(this.speed);
}
getAngle() {
@ -82,16 +81,6 @@ namespace pxsim {
return this.id == NodeType.LargeMotor;
}
setPolarity(polarity: number) {
// Either 1 or 255 (reverse)
/*
-1 : Motor will run backward
0 : Motor will run opposite direction
1 : Motor will run forward
*/
this.polarity = polarity;
}
reset() {
// not sure what reset does...
}

View File

@ -119,11 +119,7 @@ namespace pxsim {
return 2;
}
case DAL.opOutputPolarity: {
// reverse
const port = buf.data[1];
const polarity = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.setPolarity(polarity));
console.error("opOutputPolarity not supported");
return 2;
}
case DAL.opOutputSetType: {

View File

@ -1,7 +1,7 @@
namespace pxsim.music {
export function fromWAV(buf: RefBuffer) {
return incr(buf)
return buf
}
export function stopAllSounds() {
@ -13,7 +13,7 @@ namespace pxsim.SoundMethods {
let audio: HTMLAudioElement;
export function buffer(buf: RefBuffer) {
return incr(buf)
return buf
}
export function play(buf: RefBuffer) {

View File

@ -176,7 +176,7 @@ namespace pxsim.visuals {
const dalBoard = board();
dalBoard.updateSubscribers.push(() => this.updateState());
if (props && props.wireframe)
svg.addClass(this.element, "sim-wireframe");
U.addClass(this.element, "sim-wireframe");
if (props && props.theme)
this.updateTheme();

View File

@ -2,12 +2,11 @@
namespace pxsim.visuals {
const MAX_RATE = 40;
const MAX_ANGLE = 360;
export class RotationSliderControl extends ControlView<GyroSensorNode> {
private group: SVGGElement;
private slider: SVGGElement;
private text: SVGTextElement;
private rateText: SVGTextElement;
private static SLIDER_WIDTH = 70;
//private static SLIDER_HEIGHT = 78;
@ -26,8 +25,8 @@ namespace pxsim.visuals {
pxsim.svg.child(this.slider, "circle", { 'cx': 9, 'cy': 50, 'r': 13, 'style': 'fill: #f12a21' });
pxsim.svg.child(this.slider, "circle", { 'cx': 9, 'cy': 50, 'r': 12.5, 'style': 'fill: none;stroke: #b32e29' });
this.text = pxsim.svg.child(this.group, "text", {
'x': RotationSliderControl.SLIDER_WIDTH / 2,
this.rateText = pxsim.svg.child(this.group, "text", {
'x': this.getInnerWidth() / 2,
'y': RotationSliderControl.SLIDER_WIDTH * 1.2,
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 16px',
@ -72,17 +71,10 @@ namespace pxsim.visuals {
return;
}
const node = this.state;
let percentage = 50;
if (node.getMode() == GyroSensorMode.Rate) {
const rate = node.getValue();
this.text.textContent = `${rate}°/s`
// cap rate at 40deg/s
percentage = 50 + Math.sign(rate) * Math.min(MAX_RATE, Math.abs(rate)) / MAX_RATE * 50;
} else { //angle
const angle = node.getValue();
this.text.textContent = `${angle}°`
percentage = 50 + Math.sign(angle) * Math.min(MAX_ANGLE, Math.abs(angle)) / MAX_ANGLE * 50;
}
const rate = node.getRate();
this.rateText.textContent = `${rate}°/s`
// cap rate at 40deg/s
const percentage = 50 + Math.sign(rate) * Math.min(MAX_RATE, Math.abs(rate)) / MAX_RATE * 50;
const x = RotationSliderControl.SLIDER_WIDTH * percentage / 100;
const y = Math.abs((percentage - 50) / 50) * 10;
this.slider.setAttribute("transform", `translate(${x}, ${y})`);
@ -97,11 +89,7 @@ namespace pxsim.visuals {
t = -(t - 0.5) * 2; // [-1,1]
const state = this.state;
if (state.getMode() == GyroSensorMode.Rate) {
state.setRate(MAX_RATE * t);
} else {
state.setAngle(MAX_ANGLE * t)
}
state.setRate(MAX_RATE * t);
}
}

View File

@ -24,7 +24,7 @@ namespace pxsim.visuals {
protected buildDomCore() {
// Setup buttons
this.buttons = this.btnids.map(n => this.content.getElementById(this.normalizeId(n)) as SVGElement);
this.buttons.forEach(b => svg.addClass(b, "sim-button"));
this.buttons.forEach(b => U.addClass(b, "sim-button"));
this.light = this.content.getElementById(this.normalizeId(BrickView.EV3_LIGHT_ID)) as SVGElement;
}

View File

@ -18,15 +18,15 @@
"Touch Sensor Tutorials": "tutorials/touch-sensor",
"Color Sensor Tutorials": "tutorials/color-sensor",
"Ultrasonic Sensor Tutorials": "tutorials/ultrasonic-sensor",
"Gyro Sensor Tutorials": "tutorials/gyro",
"Gyro Tutorials": "tutorials/gyro",
"Infrared Sensor Tutorials": "tutorials/infrared-sensor",
"FLL / City Shaper": "tutorials/city-shaper",
"Design Engineering": "design-engineering",
"Coding": "coding",
"Maker": "maker",
"Videos": "videos"
"Tutorial Videos": "videos"
},
"electronManifest": {
"latest": "v1.1.20"
"latest": "v1.2.26"
}
}

View File

@ -16,6 +16,10 @@
/*******************************
Add your custom CSS here
*******************************/
.simframe.ui.embed {
background: transparent;
}
/* Open Sans font */
@font-face {