Compare commits

..

2 Commits

Author SHA1 Message Date
e0aad7227f Add file listing and delete apis 2019-09-03 16:00:46 -07:00
aca1b4a764 Improve naming of command line programs 2019-09-03 16:00:33 -07:00
124 changed files with 1844 additions and 3614 deletions

1
.gitignore vendored
View File

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

14
cmds/cmds.ts Normal file
View File

@ -0,0 +1,14 @@
/// <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"
})
})
}

14
cmds/tsconfig.json Normal file
View File

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

View File

@ -4,8 +4,6 @@
* [Troubleshoot](/troubleshoot)
* [EV3 Manager](https://ev3manager.education.lego.com/)
* [Bluetooth](/bluetooth)
* [Forum](https://forum.makecode.com)
* [LEGO Support](https://www.lego.com/service/)
* [FIRST LEGO League](/fll)

View File

@ -28,7 +28,7 @@ program to a **.uf2** file, which you then copy to the **@drivename@** drive. Th
### ~ hint
**Experimental support** for Bluetooth download is now available. Please read the [Bluetooth](/bluetooth) page for more information.
Not seeing the **@drivename@** drive? Make sure to upgrade your firmware at https://ev3manager.education.lego.com/. Try these [troubleshooting](/troubleshoot) tips if you still have trouble getting the drive to appear.
### ~

View File

@ -1,62 +0,0 @@
# Bluetooth
This page describes the procedure to download MakeCode program to the EV3 brick
over Bluetooth.
## ~ hint
### WARNING: EXPERIMENTAL FEATURES AHEAD!
Support for Bluetooth download relies on [Web Serial](https://wicg.github.io/serial/),
an experimental browser feature. Web Serial is a work [in progress](https://www.chromestatus.com/feature/6577673212002304);
it may change or be removed in future versions without notice.
By enabling these experimental browser features, you could lose browser data or compromise your device security
or privacy.
## ~
https://youtu.be/VIq8-6Egtqs
## Supported browsers
* Chrome desktop, version 77 and higher, Windows 10 or Mac OS.
* [Edge Insider desktop](https://www.microsoftedgeinsider.com), version 77 and higher, Windows 10 or Mac OS.
To make sure your browser is up to date, go to the '...' menu, click "Help" then "About".
Next you need to enable the experimental features (this may change in the future)
* go to **chrome://flags/#enable-experimental-web-platform-features** and **enable**
**Experimental Web Platform features**
![A screenshot of the flags page in chrome](/static/bluetooth/experimental.png)
## Machine Setup
* pair your EV3 brick with your computer over Bluetooth. This is the usual pairing procedure.
## Download over Bluetooth
* 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.
* **make sure the EV3 brick is not running a program**
* click on **Download** again to download over bluetooth.
## Choosing the correct serial port
Unfortunately, the browser dialog does not make it easy to select which serial port is the brick.
* 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
* We do not detect properly that the program is running on the brick. Make sure to stop the program before starting the download procedure.
* The list of programs on the brick screen is not updated when uploading via bluetooth.
## Feedback
Please send us your feedback through https://forum.makecode.com.

View File

@ -212,12 +212,6 @@ Here are some fun programs for your @boardname@!
"description": "Keep your brick entertained and happy",
"url":"/examples/happy-unhappy",
"cardType": "example"
}, {
{
"name": "Turtle",
"description": "Encode moves and run them on a driving base",
"url":"/examples/turtle",
"cardType": "example"
}, {
"name": "Distance Measurer",
"description": "Use a motor to measure angle and distance",

Binary file not shown.

View File

@ -2,9 +2,7 @@
![FIRST LEGO League logo](/static/fll/fll-logo.png)
**For teams participating in City Shaper challenge**, you can use MakeCode for your challenge (see [City Shaper Challenge, page 7 bottom](https://firstinspiresst01.blob.core.windows.net/fll/2020/city-shaper-game-guide-pdf.pdf)!
Weve compiled a list of resources and information that we hope will be helpful for you.
**For teams participating in the Open Software Platform Pilot utilizing MakeCode**, weve compiled a list of resources and information that we hope will be helpful for you.
* **Got a question? Post it on the forums** at https://forum.makecode.com/
@ -19,33 +17,23 @@ If you found a bug, please try if it hasn't been fixed yet! Go to https://makeco
* You will need to install the latest EV3 firmware on your brick. Instructions on how to do that are located here: https://makecode.mindstorms.com/troubleshoot.
* You will need a computer with a USB port to connect to the EV3 in order to download your programs.
* You will need internet access and a browser on your computer to get to https://makecode.mindstorms.com.
* You can [install the app](/offline-app) to use the editor offline.
### I know LabView, how is MakeCode different?
We have compiled a guide for EV3 LabView users at https://makecode.mindstorms.com/labview.
### Whats the best way to get started with MakeCode?
Go to https://makecode.mindstorms.com. The home screen is filled with videos, tutorials and examples that might be relevant for your missions.
Watch some of the videos at https://makecode.mindstorms.com (at the bottom of the page).
Try some of the provided tutorials:
On the home page, scroll down to the **FLL / City Shaper** section for specific lessons related to Mission 2.
* [Wake Up!](@homeurl@#tutorial:tutorials/wake-up) show your EV3 brick waking up
* [Animation](@homeurl@#tutorial:tutorials/make-an-animation) create a custom animation to show
* [Music Brick](@homeurl@#tutorial:tutorials/music-brick) transform your EV3 into a musical instrument
* [Run Motors](@homeurl@#tutorial:tutorials/run-motors) control the motors of your robot
* [Red Light, Green Light](@homeurl@#tutorial:tutorials/redlight-greenlight) play red light, green light with the color sensor
* [Line Following](@homeurl@#tutorial:tutorials/line-following) have your robot follow a line
### Can I load both LEGO MINDSTORMS EV3 Software and MakeCode programs onto my EV3?
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.
### How do I figure out what a block does?
You can right-click on any block and select “Help” in the context menu to open the documentation page describing what that block does.
@ -99,25 +87,20 @@ You can share your projects by clicking on the **share** button in the top left
Sharing programs is also shown in the [Tips and Tricks](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=5c594c2373367f7870196f519f3bfc7a&source=embed&photo%5fid=35719472) video.
### Can I use Bluetooth to transfer my program?
The official answer is currently no. That being said, we have **Experimental support** for Bluetooth download. Please read the [Bluetooth](/bluetooth) page for more information.
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.
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. As a workaround, you can temporarily downgrade your firmware version, delete the files, and then upgrade back to the version that works with MakeCode.
We have prepared a special program that lets you delete UF2 files from the brick.
Download [these PDF instructions](/file-manager.pdf) and drop the PDF on the brick drive.
This will present you with an menu for deleting files.
Follow these steps to downgrade your firmware version, delete the files, and uprgade back again:
1. Go into **EV3 LabVIEW** - if it's not installed get it [here](https://education.lego.com/en-us/downloads/mindstorms-ev3/software)
2. Plug in your EV3 Brick and start a new project
3. Go to the **Tools** menu in the upper right corner, select **Firmware Update**
4. In the **Firmware Update** dialog box, click on the **Show Details** button
5. From the **Available Firmware Files** list, select **EV3 Firmware V1.09E**
6. Click the **Update Firmware** button and wait for the update to complete
Now the firmware version on the EV3 Brick will be **V1.09E**. Also, in the process, the downgrade deleted all of the saved programs from the EV3 Brick. To continue to use MakeCode, the firmware version must be at **V1.10E** or above. So, the Brick firmware needs to be upgraded again. If you don't know or do remember how to do this, see the **Upgrade your @drivename@** section in the [troubleshooting](/troubleshoot) page.
For other common questions, try the FAQ page https://makecode.mindstorms.com/faq.

View File

@ -54,12 +54,6 @@ 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.22"
"appref": "v1.0.11"
}

View File

@ -61,12 +61,6 @@ motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations);
motors.largeBC.stop();
```
### ~ hint
The **turn ratio range is -200, 200** unlike LabView who used -100,100.
### ~
## Tank
The **tank** blocks control the speed of two motors. These are commonly used for a differential drive robot. The blocks can also specify the duration, angle, or number of rotations.

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

View File

@ -3,3 +3,9 @@
## Offline app #target-app
The MakeCode editor is available as app which you can install on a computer with Windows or Mac OS. Once installed, the **[MakeCode Offline App](/offline-app)** lets you create, run, and download your projects to the @boardname@. It works the same as the Web application does in your browser but it's a stand-alone application that will work when a connection to the internet is restricted or not available.
### ~ hint
The [MakeCode Offline App](/offline-app) is currently in development and is made available as a **pre-release** version.
### ~

View File

@ -1,88 +1,9 @@
# Projects
```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"
}
]
```
Here are some cool projects that you can build with your @boardname@!
## See Also
## Basic
[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)
Basic projects to build with your EV3 Brick.
Coming soon.

View File

@ -1,69 +0,0 @@
# 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

@ -1,47 +0,0 @@
# battery Property
Return the information about the battery
```sig
brick.batteryInfo(BatteryProperty.Level)
```
## Parameters
* property: the kind of information
## Returns
* a [number](/types/number) which represents the value of the property requested.
## Example
Show the battery level percentage on the screen. Also, show a green light if the battery level is above 15%. If the battery level is below 15% but above 5%, show a orange light. But, if the battery level is below 5%, show a pulsing red light.
```blocks
let battery = 0;
forever(function() {
brick.showString("Battery level:", 1)
brick.showNumber(battery, 2)
battery = brick.batteryInfo(BatteryProperty.Level);
if (battery > 15)
{
brick.setStatusLight(StatusLight.Green);
} else if (battery > 5) {
brick.setStatusLight(StatusLight.Orange);
} else {
brick.setStatusLight(StatusLight.RedPulse)
}
pause(30000)
})
```
Or see all the values
```blocks
forever(function () {
brick.showValue("bat V", brick.batteryInfo(BatteryProperty.Voltage), 1)
brick.showValue("bat %", brick.batteryInfo(BatteryProperty.Level), 2)
brick.showValue("bat I", brick.batteryInfo(BatteryProperty.Current), 3)
})
```

View File

@ -10,12 +10,12 @@ You can find out what's connected to the ports on the brick and show its status.
## Example
Show the status of the ports on the brick. Resets all motors when ENTER is pressed.
Show the status of the ports on the brick when the ``enter`` button is pressed.
```blocks
brick.showPorts()
brick.showString("Press ENTER for port status", 1)
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
motors.resetAll()
brick.showPorts()
})
```

View File

@ -1,59 +0,0 @@
# Ramp
Schedules an acceleration, constant and deceleration phase at a given speed.
```sig
motors.largeA.ramp(50, 100, 500, 100)
```
The speed setting is a percentage of the motor's full speed. Full speed is the speed that the motor runs when the brick supplies maximum output voltage to the port.
## Parameters
* **speed**: a [number](/types/number) that is the percentage of full speed. A negative value runs the motor in the reverse direction.
* **acceleration**: the [number](/types/number) of movement units to rotate for while accelerating.
* **value**: the [number](/types/number) of movement units to rotate for.
* **deceleration**: the [number](/types/number) of movement units to rotate for while decelerating.
* **unit**: the movement unit of rotation. This can be `milliseconds`, `seconds`, `degrees`, or `rotations`. If the number for **value** is `0`, this parameter isn't used.
## Example
This is an interactive program that lets you change the values of
the acceleration and deceleration and see the effects.
```blocks
let steady = 0
let dec = 0
let acc = 0
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
acc += -100
})
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
motors.largeB.ramp(50, steady, MoveUnit.MilliSeconds, acc, dec)
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
acc += 100
})
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
dec += 100
})
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
dec += -100
})
acc = 500
steady = 1000
acc = 500
forever(function () {
brick.showValue("acc", acc, 1)
brick.showValue("steady", steady, 2)
brick.showValue("dec", dec, 3)
brick.showString("acc: left/right", 5)
brick.showString("dec: up/down", 6)
brick.showString("run large B: enter", 7)
})
```
## See also
[tank](/reference/motors/synced/tank), [steer](/reference/motors/synced/steer), [stop](/reference/motors/motor/stop)

View File

@ -0,0 +1,22 @@
# Schedule
Schedules an acceleration, constant and deceleration phase at a given speed.
```sig
motors.largeA.schedule(50, 100, 500, 100)
```
The speed setting is a percentage of the motor's full speed. Full speed is the speed that the motor runs when the brick supplies maximum output voltage to the port.
## Parameters
* **speed**: a [number](/types/number) that is the percentage of full speed. A negative value runs the motor in the reverse direction.
* **acceleration**: the [number](/types/number) of movement units to rotate for while accelerating.
* **value**: the [number](/types/number) of movement units to rotate for.
* **deceleration**: the [number](/types/number) of movement units to rotate for while decelerating.
* **unit**: the movement unit of rotation. This can be `milliseconds`, `seconds`, `degrees`, or `rotations`. If the number for **value** is `0`, this parameter isn't used.
## See also
[tank](/reference/motors/synced/tank), [steer](/reference/motors/synced/steer), [stop](/reference/motors/motor/stop)

View File

@ -1,30 +0,0 @@
# set Brake Settle Time
Set the time to wait after a motor stopped to allow it settle
when brake is enabled. Default is 10ms.
```sig
motors.largeA.setBrakeSettleTime(200)
```
When a the motor is stopped and brake is applied, it can still wiggle for a little while. You can use the settle time to automatically way after stopping and let the robot settle.
## Parameters
* **time**: a [number](/types/number) value which represents the number of milliseconds to wait after braking.
## Example
Set the brake mode and the settle time to 500ms. Run the motor connected to port **A** for 2 seconds at a speed of `30` and stop after 2s.
```blocks
motors.largeA.setBrake(true)
motors.largeA.setBrakeSettleTime(500)
motors.largeA.run(30)
pause(2000)
motors.largeA.stop()
```
## See also
[stop](/reference/motors/motor/stop)

View File

@ -1,28 +1,28 @@
# set Brake
Set the brake on the motor so it will brake when it finishes a brake command.
Set the brake on the motor so it won't turn when it has no power.
```sig
motors.largeA.setBrake(false)
```
When a the motor is stopped, it can still rotate if an external force is applied to it. This can happen, for example, if you're tanking your brick on a inclined surface and stop the motors. Gravity will push down on the brick and might cause it to start rolling again. You can prevent this movement by setting the brake.
When a the motor is stopped, it can still rotate if an external force is applied to it. This can happen, for example, if your're tanking your brick on a inclined surface and stop the motors. Gravity will push down on the brick and might cause it to start rolling again. You can prevent this movement by setting the brake.
Also, you can use the brake to do simple skid steering for your brick.
## Parameters
## Paramters
* **brake**: a [boolean](/types/boolean) value which is either `true` to set the brake on or `false` to set the brake off.
## Example
Run the motor connected to port **A** for 2 seconds at a speed of `30` and stop after 2s.
Run the motor connected to port **A** for 2 seconds at a speed of `30`. Stop and set the brake.
```blocks
motors.largeA.setBrake(true)
motors.largeA.run(30)
pause(2000)
motors.largeA.stop()
motors.largeA.setBrake(true)
```
## See also

View File

@ -0,0 +1,20 @@
# Set Run Acceleration Ramp
```sig
motors.largeD.setRunAccelerationRamp(1, MoveUnit.Seconds)
```
## Examples
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
motors.largeB.run(50, 6, MoveUnit.Rotations)
})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
motors.largeC.run(50, 6, MoveUnit.Seconds)
})
motors.largeB.setRunAccelerationRamp(360, MoveUnit.Degrees)
motors.largeB.setRunDecelerationRamp(360, MoveUnit.Degrees)
motors.largeC.setRunAccelerationRamp(2, MoveUnit.Seconds)
motors.largeC.setRunDecelerationRamp(2, MoveUnit.Seconds)
```

View File

@ -0,0 +1,20 @@
# Set Run Deceleration Ramp
```sig
motors.largeD.setRunDecelerationRamp(1, MoveUnit.Seconds)
```
## Examples
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
motors.largeB.run(50, 6, MoveUnit.Rotations)
})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
motors.largeC.run(50, 6, MoveUnit.Seconds)
})
motors.largeB.setRunAccelerationRamp(360, MoveUnit.Degrees)
motors.largeB.setRunDecelerationRamp(360, MoveUnit.Degrees)
motors.largeC.setRunAccelerationRamp(2, MoveUnit.Seconds)
motors.largeC.setRunDecelerationRamp(2, MoveUnit.Seconds)
```

View File

@ -1,26 +0,0 @@
# Set Run Phase
Allows to specify an acceleration or deceleration phases for run commands.
```sig
motors.largeD.setRunPhase(MovePhase.Acceleration, 1, MoveUnit.Seconds)
```
Once the run phase is specified on a motor (or pair of motors),
it will be automatically applied to [run](/reference/motors/run) commands.
## Time vs Rotation
The phases specified for time units (seconds, milliseconds) only apply to run with time
moves. Similarly, the phases specified for rotation units (# rotation, degrees) only
apply to run with rotation units.
## Examples
```blocks
motors.largeB.setRunPhase(MovePhase.Acceleration, 0.5, MoveUnit.Seconds)
motors.largeB.setRunPhase(MovePhase.Deceleration, 0.2, MoveUnit.Seconds)
forever(function () {
motors.largeB.run(50, 1, MoveUnit.Seconds)
})
```

View File

@ -1,25 +0,0 @@
# reset All Motors
Reset all motors currently running on the brick.
```sig
motors.resetAll();
```
The motors counters are resetted.
## Example
Tank the EV3 Brick forward at half speed for 5 seconds and then stop.
```blocks
motors.largeAB.tank(50, 50);
pause(5000);
motors.stopAll();
motors.resetAll();
```
## See also
[stop all](/reference/motors/motor/stop-all),
[reset](/reference/motors/motor/reset)

View File

@ -22,5 +22,4 @@ motors.stopAll();
[stop](/reference/motors/motor/stop),
[reset](/reference/motors/motor/reset),
[reset-all](/reference/motors/motor/reset-all),
[set brake](/reference/motors/motor/set-brake)

View File

@ -22,7 +22,7 @@ If you decide to use a **value** of rotation distance, you need to choose a type
## Parameters
* **turnRatio**: a [number](/types/number) that is the percentage of speed of the drive motor. The follower motor runs at this speed. A negative number steers to the left and a positive number steers to the right. This is a number between `-200` and `200`.
* **turnRatio**: a [number](/types/number) that is the percentage of speed of the drive motor. The follower motor runs at this speed. A negative number steers to the left and a positive number steers to the right. This is a number between `-100` and `100`.
* **speed**: a [number](/types/number) that is the percentage of full speed. A negative value runs the motors in the reverse direction. This is the speed that the drive motor runs at.
* **value**: the [number](/types/number) of movement units to rotate for. A value of `0` means run the motor continuously.
* **unit**: the movement unit of rotation. This can be `milliseconds`, `seconds`, `degrees`, or `rotations`. If the number for **value** is `0`, this parameter isn't used.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -25,16 +25,6 @@ Step by step guides to coding your @boardname@.
"description": "Use the color sensor to follow line or detect colors",
"url":"/tutorials/color-sensor",
"imageUrl":"/static/tutorials/what-color.png"
}, {
"name": "Gyro",
"description": "Drive straight or turn more precisely with the gyro",
"url":"/tutorials/gyro",
"imageUrl":"/static/tutorials/calibrate-gyro.png"
}, {
"name": "Ultrasonic Sensor",
"description": "Use the ultrasonic sensor to detect obstacles",
"url":"/tutorials/ultrasonic-sensor",
"imageUrl":"/static/tutorials/object-near.png"
}, {
"name": "Infrared Sensor",
"description": "Use the infrared sensor to detect objects",

View File

@ -27,12 +27,6 @@
"cardType": "tutorial",
"url":"/tutorials/music-brick",
"imageUrl":"/static/tutorials/music-brick.png"
}, {
"name": "Pause On Start",
"description": "Don't start running immediately!",
"cardType": "tutorial",
"url":"/tutorials/pause-on-start",
"imageUrl":"/static/tutorials/pause-on-start.png"
}]
```

View File

@ -1,37 +0,0 @@
# Calibrate Gyro
## Introduction @fullscreen
The gyroscope is a very useful sensor in the EV3 system. It detects the rotation rate
which can be very useful to correct the trajectory of the robot and do precise turns.
However, the sensor can be imprecise and subject to drifting. It is recommend to
calibrate your sensor at least once after starting your brick. You don't have to
recalibrate on every run.
* [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
* [EV3 Driving Base with Gyro](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-gyro-sensor-driving-base-a521f8ebe355c281c006418395309e15.pdf)
## Step 1 Show ports
Add the ``||brick:show ports||`` to see the status of the gyroscope.
```blocks
brick.showPorts()
```
## Step 2 Calibration
Add a ``||sensors:calibrate gyro||`` block to calibrate the gyro. The block
detects if the sensor is present and does a full reset of the sensor if necessary.
```blocks
brick.showPorts()
sensors.gyro2.calibrate()
```
## Step 3 Download and run @fullscreen
Download this program to your brick and press the ENTER button.

View File

@ -1,54 +0,0 @@
# City Shaper
## Tutorials
```codecard
[
{
"name": "Crane Mission / Robot 1",
"description": "Learn the basics and build your first robot driving base.",
"cardType": "tutorial",
"url":"/tutorials/city-shaper/robot-1",
"imageUrl": "/static/tutorials/city-shaper/robot1.jpg"
}, {
"name": "Crane Mission / Robot 2",
"description": "Program your robot to move in different ways.",
"cardType": "tutorial",
"url":"/tutorials/city-shaper/robot-2",
"imageUrl": "/static/tutorials/city-shaper/robot2.jpg"
}, {
"name": "Crane Mission / Video 1",
"description": "A video of the Robot 1 tutorial",
"youTubeId": "IqL0Pyeu5Ng"
}, {
"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"
}]
```
## See Also
[Robot 1](/tutorials/city-shaper/robot-1),
[Robot 2](/tutorials/city-shaper/robot-2)

View File

@ -1,27 +0,0 @@
# Crane Mission Lessons
The [Crane Mission Lessons](https://firstinspiresst01.blob.core.windows.net/fll/2020/fll-ev3-overview.pdf) adapted for MakeCode.
## Lessons
```codecard
[
{
"name": "Crane Mission / Robot 1",
"description": "Learn the basics and build your first robot driving base.",
"cardType": "tutorial",
"url":"/tutorials/city-shaper/robot-1"
}, {
"name": "Crane Mission / Robot 2",
"description": "Program your robot to move in different ways.",
"cardType": "tutorial",
"url":"/tutorials/city-shaper/robot-2"
}
]
```
## See Also
[Robot 1](/tutorials/city-shaper/robot-1),
[Robot 2](/tutorials/city-shaper/robot-2)

View File

@ -1,20 +0,0 @@
# Mission 2 Lesson
Use the program below to tell your robot how to solve the Crane Mission (Mission 2).
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
motors.largeBC.steer(0, 25, 2.25, MoveUnit.Rotations)
control.timer1.reset()
while (control.timer1.seconds() < 1.5) {
motors.largeBC.steer(sensors.color1.light(LightIntensityMode.Reflected) - 40, 50)
}
motors.largeBC.stop()
motors.largeBC.steer(0, 15, 0.25, MoveUnit.Rotations)
motors.mediumA.run(25, 60, MoveUnit.Degrees)
pause(2000)
motors.mediumA.run(-25, 1, MoveUnit.Seconds)
motors.largeBC.steer(0, -100, 4, MoveUnit.Rotations)
})
motors.largeBC.setBrake(true)
```

View File

@ -1,53 +0,0 @@
# Robot 1 Lesson
## Step 1 - Build Your Driving Base Robot @unplugged
Build the medium motor robot driving base:
* [Driving base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
* [Medium motor driving base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-medium-motor-driving-base-e66e2fc0d917485ef1aa023e8358e7a7.pdf)
If clicking on the image above doesn't open the instructions, right-click on the image and choose "Save link as..." to download the PDF.
## Step 2 - Show an image @fullscreen
At first, it's nice to know that your program is running. Plug in a ``||brick:show mood||`` from the **BRICK** toolbox drawer
into the ``||loops:on start||`` block. Change the image to something else if you want!
```blocks
brick.showMood(moods.sleeping)
```
## Step 3 - Try your code @fullscreen
Look at the simulator and check that your image is showing on the screen. When you are ready, press the **DOWNLOAD** button
and follow the instructions to transfer your code on the brick.
## Step 4 - Pause on Start @fullscreen
As you may have noticed, the code starts running immediately once you download it to the brick. To prevent the robot
from rolling away, add a ``||brick:pause until enter pressed||`` button to wait for the user to press enter.
```blocks
brick.showMood(moods.sleeping)
brick.buttonEnter.pauseUntil(ButtonEvent.Pressed)
```
## Step 5 - Steer motors @fullscreen
Drag a ``||motors:steer motors||`` block from the **MOTORS** toolbox drawer and snap it in under ``||brick:show mood||``.
Click on the **(+)** symbol and make sure to tell your motors to turn **1** rotation.
```blocks
brick.showMood(moods.sleeping)
brick.buttonEnter.pauseUntil(ButtonEvent.Pressed)
brick.showMood(moods.neutral)
motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations)
```
## Step 6 - Try your code @fullscreen
Whenever you make a code change, the simulator will restart so you can see what your latest change will do.
When you are ready, click **DOWNLOAD** and follow the instructions to transfer the code into your brick.
**Remember**: Take the driving base apart at the end of the session so that another group can build their robot too.

View File

@ -1,100 +0,0 @@
# Robot 2 Lesson
## Step 1 - Build Your Driving Base Robot @unplugged
Build the robot driving base:
[![EV3 Driving Base](/static/lessons/common/ev3-driving-base.jpg)](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
If clicking on the image above doesn't open the instructions, right-click on the image and choose "Save link as..." to download the PDF.
## Step 2 - Show an image and move @fullscreen
Add blocks to the ``||loops:on start||`` block to show an image and move the motors **B+C** for ``1`` rotation.
```blocks
brick.showMood(moods.neutral)
motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations)
```
## Step 3 - Brick button @fullscreen
Let's change the code so that your robot moves when the **UP** button is pressed.
Add an ``||brick:on button up||`` block and move ``||motors:steer motors||`` inside of it.
After downloading your code, press **UP** to move the robot.
```blocks
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
brick.showMood(moods.awake)
motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations)
})
brick.showMood(moods.neutral)
```
## Step 4 - Braking @fullscreen
When the motors are done turning, the robot keeps on moving for a short distance.
Turn on the **brakes** so that your robot stops immediately.
Drag a ``||motors:set brake||`` block into the ``||loops:on start||`` and set it to **ON** for the the **BC** motors.
```blocks
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
brick.showMood(moods.awake)
motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations)
})
brick.showMood(moods.neutral)
motors.largeBC.setBrake(true)
```
## Step 5 - Left and Right turn @fullscreen
Let's make the robot turn to the left when the **LEFT** button is pressed on the brick.
Find an ``||brick:on button||`` block and put a ``||motors:steer motors||`` in it. Make the turn ratio drive the motor to left.
Get another ``||brick:on button||`` and set it to run when the **RIGHT** is pressed.
Put a ``||motors:steer motors||`` in for that button and set the turn ratio to drive to the right.
```blocks
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
brick.showMood(moods.middleLeft)
motors.largeBC.steer(-50, 50, 1, MoveUnit.Rotations)
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
brick.showMood(moods.middleRight)
motors.largeBC.steer(50, 50, 1, MoveUnit.Rotations)
})
```
## Step 6 - Backwards @fullscreen
Let's make the robot go backwards when the **DOWN** button is pressed on the brick.
Add a ``||motors:steer motors||`` to an ``||brick:on button||`` block and change the speed to be negative. This will make the motor go backwards.
```blocks
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
brick.showMood(moods.knockedOut)
motors.largeBC.steer(0, -50, 1, MoveUnit.Rotations)
})
```
## Step 7 - Add an Ultrasonic sensor @fullscreen
Add an Ultrasonic sensor to your driving base.
[![EV3 Driving Base with Ultrasonic Sensor](/static/lessons/common/ev3-ultrasonic-sensor-driving-base.jpg)](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-ultrasonic-sensor-driving-base-61ffdfa461aee2470b8ddbeab16e2070.pdf)
If clicking on the image above doesn't open the instructions, right-click on the image and choose "Save link as..." to download the PDF.
## Step 8 - Stopping distance @fullscreen
Create a program that moves the Driving Base and makes it stop 6 cm from the Cuboid.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
brick.showMood(moods.dizzy)
motors.largeBC.steer(0, 50)
pauseUntil(() => sensors.ultrasonic4.distance() < 6)
motors.largeBC.stop()
})
```
Try sending your robot towards a wall!

View File

@ -21,12 +21,6 @@
"cardType": "tutorial",
"url":"/tutorials/redlight-greenlight",
"imageUrl":"/static/tutorials/redlight-greenlight.png"
}, {
"name": "Move To Color",
"description": "Move straight until a color is detected.",
"cardType": "tutorial",
"url":"/tutorials/move-to-color",
"imageUrl":"/static/tutorials/move-to-color.png"
}, {
"name": "Reflected Light Measure",
"description": "Teach the sensor what light or dark is.",

View File

@ -1,34 +0,0 @@
# 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

@ -1,31 +0,0 @@
# Gyro tutorials
## Tutorials
```codecard
[{
"name": "Calibrate",
"description": "Make sure you gyro sensor is ready to use",
"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

@ -4,6 +4,12 @@
```codecard
[{
"name": "Object Near",
"description": "Detect if objects are near.",
"cardType": "tutorial",
"url":"/tutorials/object-near",
"imageUrl":"/static/tutorials/object-near.png"
}, {
"name": "Security Alert",
"description": "Build an security alert using the Infrared Sensor.",
"cardType": "tutorial",
@ -14,4 +20,5 @@
## See Also
[Object Near?](/tutorials/object-near),
[Security Alert](/tutorials/security-alert)

View File

@ -39,11 +39,6 @@
"cardType": "example",
"url":"/tutorials/coast-or-brake",
"imageUrl":"/static/tutorials/coast-or-brake.png"
}, {
"name": "Turtle",
"description": "Encode moves and run them on a driving base",
"url":"/tutorials/turtle",
"cardType": "example"
}]
```
@ -54,5 +49,4 @@
[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)
[Coast Or Brake](/tutorials/coast-or-brake)

View File

@ -1,61 +0,0 @@
# Move Straight With Gyro
## Introduction @fullscreen
Rotating using a wheel is not precise. The wheel can slip or the motors
can be slightly different.
With the help of the gyro you can detect and correct deviations in your trajectory.
* [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
* [EV3 Driving Base with Gyro](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-gyro-sensor-driving-base-a521f8ebe355c281c006418395309e15.pdf)
## Step 1 Calibration
Add a ``||sensors:calibrate gyro||`` block in a ``||brick:on button enter pressed||`` block so that you can manually start a calibration process. Run the calibration
at least once after connecting the gyro.
```blocks
brick.showPorts()
sensors.gyro2.calibrate()
```
## Step 2 Compute the error
Make a new **error** variable and drag the ``||sensors:gyro rate||``
and multiply it by -1. Since the rate shows the rotation rate, we will
counter it by negating it.
```blocks
let error = 0
brick.showPorts()
sensors.gyro2.calibrate()
while (true) {
error = sensors.gyro2.rate() * -1
}
```
## Step 3 Steer with feedback
Drag a ``||motors:steer motors||`` block under the variable and pass
the **error** variable into the turn ratio section.
If the robot is turning right, the gyro will report a positive rotation rate
and the turn ratio will be negative which will the turn the robot left!
```blocks
let error = 0
brick.showPorts()
sensors.gyro2.calibrate()
while (true) {
error = sensors.gyro2.rate() * -1
motors.largeBC.steer(error, 50)
}
```
## Step 4 Run it!
Download to your brick and test out if the robot is going straight.
This kind of technique is called a proportional controller;
it corrects the inputs (motor speed) with a feedback proportional to the output (rotation rate).

View File

@ -1,65 +0,0 @@
# Move To Color
## Introduction @fullscreen
This tutorial shows how to move the EV3 driving base until the color sensor detects a color.
Here are the building instructions for the robot:
* [EV3 driving base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
* [Color sensor down](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-color-sensor-down-driving-base-d30ed30610c3d6647d56e17bc64cf6e2.pdf)
## Step 1 Run code on button pressed
Drag ``||brick:on button pressed||`` block so that your code starts when the enter button is pressed (and not at the start). We are also using the ``||brick:show string||``
message easily diagnose if the program does not work.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
brick.showString("started", 1)
})
```
## Step 2 Turn on the motors
Drag a ``||motors:steer motors B+C||`` block under the button pressed event. This will turn on both motors.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
brick.showString("started", 1)
motors.largeBC.steer(0, 50)
})
```
## Step 3 Pause until color
Drag a ``||sensors:pause until color detected||`` block after the steer and select the color you want to detect. This block will stop the program until the color is detected.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
brick.showString("started", 1)
motors.largeBC.steer(0, 50)
brick.showString("looking for red", 1)
sensors.color3.pauseUntilColorDetected(ColorSensorColor.Red)
})
```
### Step 4 Stop the motors!
Once the color is detected, the program will continue to run blocks. Drag a ``||motors:stop B+C motor||`` so that both motors stop.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
brick.showString("started", 1)
motors.largeBC.steer(0, 50)
brick.showString("looking for red", 1)
sensors.color3.pauseUntilColorDetected(ColorSensorColor.Red)
brick.showString("stop", 1)
motors.largeBC.stop()
})
```
## Step 5
Download your program to your brick and try it out!

View File

@ -1,33 +0,0 @@
# Pause On Start
## Introduction @unplugged
Sometimes you don't want your program to run right away... you can use a button to wait before moving the motors.
## Step 1
Let's start by showing an image on the screen so the user knows that the robot is ready and waiting.
```blocks
brick.showImage(images.informationStop1)
```
## Step 2
Drag the ``||brick:pause until enter pressed||`` button to wait for the user to press the Enter button.
```blocks
brick.showImage(images.informationStop1)
brick.buttonEnter.pauseUntil(ButtonEvent.Pressed)
```
## Step 3
Add all the motor and sensor code you want after those blocks!
```blocks
brick.showImage(images.informationStop1)
brick.buttonEnter.pauseUntil(ButtonEvent.Pressed)
brick.showImage(images.expressionsBigSmile)
motors.largeBC.tank(50, 50, 1, MoveUnit.Seconds)
```

View File

@ -1,9 +0,0 @@
# Stop At Object
This is a code example to detect contact or collision with another object. It uses a touch sensor to detect hitting a wall or other obstacle. The motors are run and then stopped when the sensor is pressed.
```blocks
motors.largeBC.tank(50, 50)
sensors.touch1.pauseUntil(ButtonEvent.Pressed)
motors.largeBC.stop()
```

View File

@ -16,10 +16,10 @@
"url":"/tutorials/touch-sensor-values",
"imageUrl":"/static/tutorials/touch-sensor-values.png"
}, {
"name": "Stop At Object",
"name": "Pause Until Pressed",
"description": "Waits for the sensor to be pressed before continuing the program",
"cardType": "tutorial",
"url":"/tutorials/stop-at-object",
"url":"/tutorials/pause-until-pressed",
"imageUrl":"/static/tutorials/pause-until-pressed.png"
}]
```
@ -28,4 +28,4 @@
[Touch to Run](/tutorials/touch-to-run),
[Touch Sensor Values](/tutorials/touch-sensor-values),
[Stop At Object](/tutorials/stop-at-object)
[Pause Until Pressed](/tutorials/pause-until-pressed)

View File

@ -1,43 +0,0 @@
# 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

@ -1,52 +0,0 @@
# Turtle
A fun interactive program where the user enters a sequence of moves using the buttons and the robot executes it.
```blocks
/**
* Run this program with a driving base.
**/
let indent = ""
let command = ""
let c = ""
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
command = command + "L"
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
command = command + "R"
})
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
command = command + "F"
})
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
command = command + "B"
})
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
indent = ""
for (let index = 0; index <= command.length; index++) {
c = command[index]
brick.showString("" + indent + c, 4)
indent = "" + indent + " "
if (c == "L") {
motors.largeBC.steer(-100, 50, 378, MoveUnit.Degrees)
} else if (c == "R") {
motors.largeBC.steer(100, 50, 378, MoveUnit.Degrees)
} else if (c == "F") {
motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations)
} else if (c == "B") {
motors.largeBC.steer(0, -50, 1, MoveUnit.Rotations)
}
}
command = ""
brick.showString("", 2)
})
motors.largeBC.setBrake(true)
forever(function () {
brick.showString("TURTLE", 1)
brick.showString(command, 3)
brick.showString("up/down: forward/backward", 8)
brick.showString("left/right: turn", 9)
brick.showString("enter: play commands", 10)
})
```

View File

@ -1,24 +0,0 @@
# Infrared sensor
## Tutorials
```codecard
[{
"name": "Object Near",
"description": "Detect if objects are near.",
"cardType": "tutorial",
"url":"/tutorials/object-near",
"imageUrl":"/static/tutorials/object-near.png"
}, {
"name": "Wall Follower",
"description": "Follow a wall at a distance using the ultrasonic sensor and a proportional controller.",
"cardType": "tutorial",
"url":"/tutorials/wall-follower",
"imageUrl":"/static/tutorials/wall-follower.png"
}]
```
## See Also
[Object Near?](/tutorials/object-near),
[Wall Follower](/tutorials/wall-follower)

View File

@ -1,191 +0,0 @@
# Wall Follower
## Introduction @unplugged
This tutorial shows you how to use the ultrasonic sensor to
move a [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
along a wall.
Your ultrasonic sensor should be placed horizontally, near the driving wheel, facing the wall.
## Step 1 Measure distance
Declare a new variable ``distance`` and store the distance from
the ultrasonic sensor on port 4.
```blocks
let distance = 0
forever(function () {
distance = sensors.ultrasonic4.distance()
})
```
## Step 2 Show distance
Use a ``||brick:show value||`` block to display the distance value on the screen.
This is **very** helpful when you are debugging your code on the robot.
Once your code is ready, download it to your robot and check that the measured distance looks ok.
```blocks
let distance = 0
forever(function () {
distance = sensors.ultrasonic4.distance()
brick.showValue("distance", distance, 1)
})
```
## Step 3 Goal
Declare a new variable ``goal`` and assign it to ``10`` in ``on start``.
The value should be the distance in centimeters between your robot and the wall.
```blocks
let goal = 0
goal = 10
```
## Step 4 Compute Error
Declare a new variable ``error`` and assign a difference between ``distance`` and ``goal``.
We will use this value to determine how much the robot needs to correct its trajectory.
```blocks
let distance = 0
let goal = 0
let error = 0
goal = 10
forever(function () {
distance = sensors.ultrasonic4.distance()
brick.showValue("distance", distance, 1)
error = distance - goal
brick.showValue("error", error, 2)
})
```
## Step 5 Show Error
Just like ``distance``, use ``||brick:show value||`` to display the value of the error (line 2).
This will allow you to debug your code while it is running on the robot.
Download your program to the robot and check that the error goes to ``0`` when
the robot is around 10cm from the wall.
```blocks
let distance = 0
let goal = 0
let error = 0
goal = 10
forever(function () {
distance = sensors.ultrasonic4.distance()
brick.showValue("distance", distance, 1)
error = distance - goal
brick.showValue("error", error, 2)
})
```
## Step 6 Kp
Declare a new variable ``kp`` and assign it to ``1``.
This number determines how to convert the error into a ``turn ratio`` for the steer block.
For starter, set it to 1 and we will go through the steps to tune its value later on.
As usual, also use ``||brick:show value||`` to display the value of ``kp`` on the screen (line 3).
```blocks
let distance = 0
let goal = 0
let error = 0
let kp = 0
goal = 10
kp = 1
forever(function () {
distance = sensors.ultrasonic4.distance()
brick.showValue("distance", distance, 1)
error = distance - goal
brick.showValue("error", error, 2)
brick.showValue("kp", kp, 3)
})
```
## Step 7 Turn ratio
Declare a new variable ``turnratio`` and store the product of ``error`` and ``kp`` in it.
Also use ``||brick:show value||`` to display its value on screen.
Download the program on the robot and try moving the robot around the wall. You should see
the value of ``turnratio`` change similarly to ``error``.
```blocks
let distance = 0
let goal = 0
let error = 0
let kp = 0
let turnratio = 0
goal = 10
kp = 1
forever(function () {
distance = sensors.ultrasonic4.distance()
brick.showValue("distance", distance, 1)
error = distance - goal
brick.showValue("error", error, 2)
brick.showValue("kp", kp, 3)
turnratio = error * kp
brick.showValue("turn", turnratio, 4)
})
```
## Step 8 Steering
Add a ``||motors:steer motors||`` block for ``large B+C`` at 35% and place the ``turnratio``
variable for the turn value.
Download the code to your robot and try it out. Does it follow the wall?...
Not really, this is because we need to tune the ``kp`` variable.
```blocks
let distance = 0
let goal = 0
let error = 0
let kp = 0
let turnratio = 0
goal = 10
kp = 1
forever(function () {
distance = sensors.ultrasonic4.distance()
brick.showValue("distance", distance, 1)
error = distance - goal
brick.showValue("error", error, 2)
brick.showValue("kp", kp, 3)
turnratio = error * kp
brick.showValue("turn", turnratio, 4)
motors.largeBC.steer(turnratio, 35)
})
```
## Step 9 Tuning kp
As mentioned in a previous step, we need to find the right value for kp so that the robot
follows the wall properly. This tuning can be tedious so we are going to the brick buttons
to speed up the process.
Add ``||brick:on button||`` blocks to handle the left and right button pressed. When left is pressed, change ``kp`` by ``-1``. When right is pressed, change ``kp`` by 1.
Download your code to the robot and change the values of ``kp`` until the robot follows the wall. (Tip try something around -5 / -10).
```blocks
let kp = 0
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
kp += -1
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
kp += 1
})
```
## Step 10 @unplugged
Well done! Your robot is using the ultrasonic distance
to correct is trajectory using a proportional controller!
The robot will be more precise if it goes slow... Try using a variable
and the brick up and down events to control the speed as well.

View File

@ -2,231 +2,74 @@
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
import UF2 = pxtc.UF2;
import { Ev3Wrapper } from "./wrap";
import { bluetoothTryAgainAsync } from "./dialogs";
export let ev3: Ev3Wrapper;
export let ev3: pxt.editor.Ev3Wrapper
export function debug() {
return initHidAsync()
return initAsync()
.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+
enum ParityType {
"none",
"even",
"odd",
"mark",
"space"
}
declare interface SerialOptions {
baudrate?: number;
databits?: number;
stopbits?: number;
parity?: ParityType;
buffersize?: number;
rtscts?: boolean;
xon?: boolean;
xoff?: boolean;
xany?: boolean;
}
type SerialPortInfo = pxt.Map<string>;
type SerialPortRequestOptions = any;
declare class SerialPort {
open(options?: SerialOptions): Promise<void>;
close(): void;
readonly readable: any;
readonly writable: any;
//getInfo(): SerialPortInfo;
}
declare interface Serial extends EventTarget {
onconnect: any;
ondisconnect: any;
getPorts(): Promise<SerialPort[]>
requestPort(options: SerialPortRequestOptions): Promise<SerialPort>;
}
class WebSerialPackageIO implements pxt.HF2.PacketIO {
onData: (v: Uint8Array) => void;
onError: (e: Error) => void;
onEvent: (v: Uint8Array) => void;
onSerial: (v: Uint8Array, isErr: boolean) => void;
sendSerialAsync: (buf: Uint8Array, useStdErr: boolean) => Promise<void>;
private _reader: any;
private _writer: any;
constructor(private port: SerialPort, private options: SerialOptions) {
}
async readSerialAsync() {
this._reader = this.port.readable.getReader();
let buffer: Uint8Array;
const reader = this._reader;
while (reader === this._reader) { // will change if we recycle the connection
const { done, value } = await this._reader.read()
if (!buffer) buffer = value;
else { // concat
let tmp = new Uint8Array(buffer.length + value.byteLength)
tmp.set(buffer, 0)
tmp.set(value, buffer.length)
buffer = tmp;
}
if (buffer && buffer.length >= 6) {
this.onData(new Uint8Array(buffer));
buffer = undefined;
}
}
}
static isSupported(): boolean {
return !!(<any>navigator).serial;
}
static async mkPacketIOAsync(): Promise<pxt.HF2.PacketIO> {
const serial = (<any>navigator).serial;
if (serial) {
try {
const requestOptions: SerialPortRequestOptions = {};
const port = await serial.requestPort(requestOptions);
const options: SerialOptions = {
baudrate: 460800,
buffersize: 4096
};
return new WebSerialPackageIO(port, options);
} catch (e) {
console.log(`connection error`, e)
}
}
throw new Error("could not open serial port");
}
error(msg: string): any {
console.error(msg);
throw new Error(lf("error on brick ({0})", msg))
}
private openAsync() {
console.log(`serial: opening port`)
if (!!this._reader) return Promise.resolve();
this._reader = undefined;
this._writer = undefined;
return this.port.open(this.options)
.then(() => {
this.readSerialAsync();
return Promise.resolve();
});
}
private closeAsync() {
console.log(`serial: closing port`);
this.port.close();
this._reader = undefined;
this._writer = undefined;
return Promise.delay(500);
}
reconnectAsync(): Promise<void> {
return this.openAsync();
}
disconnectAsync(): Promise<void> {
return this.closeAsync();
}
sendPacketAsync(pkt: Uint8Array): Promise<void> {
if (!this._writer)
this._writer = this.port.writable.getWriter();
return this._writer.write(pkt);
}
}
function hf2Async() {
const pktIOAsync: Promise<pxt.HF2.PacketIO> = useWebSerial
? WebSerialPackageIO.mkPacketIOAsync() : pxt.HF2.mkPacketIOAsync()
return pktIOAsync.then(h => {
let w = new Ev3Wrapper(h)
ev3 = w
return w.reconnectAsync(true)
.then(() => w)
})
return pxt.HF2.mkPacketIOAsync()
.then(h => {
let w = new pxt.editor.Ev3Wrapper(h)
ev3 = w
return w.reconnectAsync(true)
.then(() => w)
})
}
let useHID = false;
let useWebSerial = false;
export function initAsync(): Promise<void> {
if (pxt.U.isNodeJS) {
// doesn't seem to work ATM
useHID = false
} else {
const nodehid = /nodehid/i.test(window.location.href);
if (pxt.BrowserUtils.isLocalHost() && pxt.Cloud.localToken && nodehid)
useHID = true;
}
let noHID = false
if (WebSerialPackageIO.isSupported())
pxt.tickEvent("webserial.supported");
return Promise.resolve();
}
export function canUseWebSerial() {
return WebSerialPackageIO.isSupported();
}
export function enableWebSerialAsync() {
initPromise = undefined;
useWebSerial = WebSerialPackageIO.isSupported();
useHID = useWebSerial;
if (useWebSerial)
return initHidAsync().then(() => { });
else return Promise.resolve();
}
function cleanupAsync() {
if (ev3) {
console.log('cleanup previous port')
return ev3.disconnectAsync()
.catch(e => { })
.finally(() => { ev3 = undefined; });
}
return Promise.resolve();
}
let initPromise: Promise<Ev3Wrapper>
function initHidAsync() { // needs to run within a click handler
let initPromise: Promise<pxt.editor.Ev3Wrapper>
export function initAsync() {
if (initPromise)
return initPromise
if (useHID) {
initPromise = cleanupAsync()
.then(() => hf2Async())
let canHID = false
if (pxt.U.isNodeJS) {
// doesn't seem to work ATM
canHID = false
} else {
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && !forceHexDownload)
canHID = true
}
if (noHID)
canHID = false
if (canHID) {
initPromise = hf2Async()
.catch(err => {
console.error(err);
initPromise = null
useHID = false;
useWebSerial = false;
return Promise.reject(err);
noHID = true
return Promise.reject(err)
})
} else {
useHID = false
useWebSerial = false;
noHID = true
initPromise = Promise.reject(new Error("no HID"))
}
return initPromise;
return initPromise
}
// this comes from aux/pxt.lms
const fspath = "../prjs/BrkProg_SAVE/"
const rbfTemplate = `
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
`
export function deployCoreAsync(resp: pxtc.CompileResult) {
let filename = resp.downloadFileBaseName || "pxt"
let w: pxt.editor.Ev3Wrapper
const origElfUF2 = UF2.parseFile(pxt.U.stringToUint8Array(ts.pxtc.decodeBase64(resp.outfiles[pxt.outputName()])))
let filename = resp.downloadFileBaseName || (origElfUF2[0].filename || "").replace(/^Projects\//, "").replace(/\.elf$/, "") || "pxt"
filename = filename.replace(/^lego-/, "")
let fspath = "../prjs/BrkProg_SAVE/"
let elfPath = fspath + filename + ".elf"
let rbfPath = fspath + filename + ".rbf"
@ -236,8 +79,6 @@ export function deployCoreAsync(resp: pxtc.CompileResult) {
let rbfBIN = pxt.U.fromHex(rbfHex)
pxt.HF2.write16(rbfBIN, 4, rbfBIN.length)
let origElfUF2 = UF2.parseFile(pxt.U.stringToUint8Array(ts.pxtc.decodeBase64(resp.outfiles[pxt.outputName()])))
let mkFile = (ext: string, data: Uint8Array = null) => {
let f = UF2.newBlockFile()
f.filename = "Projects/" + filename + ext
@ -266,41 +107,27 @@ export function deployCoreAsync(resp: pxtc.CompileResult) {
return Promise.resolve();
}
if (!useHID) return saveUF2Async()
if (noHID) return saveUF2Async()
pxt.tickEvent("webserial.flash");
let w: Ev3Wrapper;
return initHidAsync()
return initAsync()
.then(w_ => {
w = w_
if (w.isStreaming)
pxt.U.userError("please stop the program first")
return w.reconnectAsync(false)
.catch(e => {
// user easily forgets to stop robot
bluetoothTryAgainAsync().then(() => w.disconnectAsync())
.then(() => Promise.delay(1000))
.then(() => w.reconnectAsync());
// nothing we can do
return Promise.reject(e);
})
return w.stopAsync()
})
.then(() => w.stopAsync())
.then(() => w.rmAsync(elfPath))
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
.then(() => w.flashAsync(rbfPath, rbfBIN))
.then(() => w.runAsync(rbfPath))
.then(() => Promise.delay(500))
.then(() => {
pxt.tickEvent("webserial.success");
return w.disconnectAsync()
//return Promise.delay(1000).then(() => w.dmesgAsync())
}).catch(e => {
pxt.tickEvent("webserial.fail");
useHID = false;
useWebSerial = false;
// if we failed to initalize, tell the user to retry
return Promise.reject(e)
// if we failed to initalize, retry
if (noHID)
return saveUF2Async()
else
return Promise.reject(e)
})
}

View File

@ -1,145 +0,0 @@
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,26 +1,113 @@
/// <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 } from "./deploy";
import { showUploadDialogAsync } from "./dialogs";
export let projectView: pxt.editor.IProjectView;
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
pxt.debug('loading pxt-ev3 target extensions...')
projectView = opts.projectView;
const res: pxt.editor.ExtensionResult = {
deployAsync: deployCoreAsync,
showUploadInstructionsAsync: showUploadDialogAsync
};
deployCoreAsync,
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
let resolve: (thenableOrResult?: void | PromiseLike<void>) => void;
let reject: (error: any) => void;
const deferred = new Promise<void>((res, rej) => {
resolve = res;
reject = rej;
});
const boardName = pxt.appTarget.appTheme.boardName || "???";
const boardDriveName = pxt.appTarget.appTheme.driveDisplayName || pxt.appTarget.compile.driveName || "???";
// 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 saveAs = pxt.BrowserUtils.hasSaveAs();
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: [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(() => { });
}
};
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,17 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"noImplicitAny": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"noImplicitThis": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"isolatedModules": false,
"outDir": "../built/editor",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false,
"jsx": "react"
"allowSyntheticDefaultImports": true,
"declaration": true
}
}

View File

@ -1,286 +1,285 @@
/**
* See https://www.lego.com/cdn/cs/set/assets/blt6879b00ae6951482/LEGO_MINDSTORMS_EV3_Communication_Developer_Kit.pdf
* https://github.com/mindboards/ev3sources/blob/master/lms2012/lms2012/source/bytecodes.h#L146
*/
import HF2 = pxt.HF2
import U = pxt.U
namespace pxt.editor {
import HF2 = pxt.HF2
import U = pxt.U
function log(msg: string) {
pxt.log("serial: " + msg)
}
function log(msg: string) {
pxt.log("EWRAP: " + msg)
}
export interface DirEntry {
name: string;
md5?: string;
size?: number;
}
export interface DirEntry {
name: string;
md5?: string;
size?: number;
}
const runTemplate = "C00882010084XX0060640301606400"
const usbMagic = 0x3d3f
const DIRECT_COMMAND_NO_REPLY = 0x80
const runTemplate = "C00882010084XX0060640301606400"
const usbMagic = 0x3d3f
export class Ev3Wrapper {
msgs = new U.PromiseBuffer<Uint8Array>()
private cmdSeq = U.randomUint32() & 0xffff;
private lock = new U.PromiseQueue();
isStreaming = false;
dataDump = /talkdbg=1/.test(window.location.href);
export class Ev3Wrapper {
msgs = new U.PromiseBuffer<Uint8Array>()
private cmdSeq = U.randomUint32() & 0xffff;
private lock = new U.PromiseQueue();
isStreaming = false;
dataDump = false;
constructor(public io: pxt.HF2.PacketIO) {
io.onData = buf => {
buf = buf.slice(0, HF2.read16(buf, 0) + 2)
if (HF2.read16(buf, 4) == usbMagic) {
let code = HF2.read16(buf, 6)
let payload = buf.slice(8)
if (code == 1) {
let str = U.uint8ArrayToString(payload)
if (U.isNodeJS)
pxt.debug("SERIAL: " + str.replace(/\n+$/, ""))
else
window.postMessage({
type: 'serial',
id: 'n/a', // TODO?
data: str
}, "*")
} else
pxt.debug("Magic: " + code + ": " + U.toHex(payload))
return
constructor(public io: pxt.HF2.PacketIO) {
io.onData = buf => {
buf = buf.slice(0, HF2.read16(buf, 0) + 2)
if (HF2.read16(buf, 4) == usbMagic) {
let code = HF2.read16(buf, 6)
let payload = buf.slice(8)
if (code == 1) {
let str = U.uint8ArrayToString(payload)
if (Util.isNodeJS)
console.log("SERIAL: " + str.replace(/\n+$/, ""))
else
window.postMessage({
type: 'serial',
id: 'n/a', // TODO?
data: str
}, "*")
} else
console.log("Magic: " + code + ": " + U.toHex(payload))
return
}
if (this.dataDump)
log("RECV: " + U.toHex(buf))
this.msgs.push(buf)
}
if (this.dataDump)
log("RECV: " + U.toHex(buf))
this.msgs.push(buf)
}
}
private allocCore(addSize: number, replyType: number) {
let len = 5 + addSize
let buf = new Uint8Array(len)
HF2.write16(buf, 0, len - 2) // pktLen
HF2.write16(buf, 2, this.cmdSeq++) // msgCount
buf[4] = replyType
return buf
}
private allocCore(addSize: number, replyType: number) {
let len = 5 + addSize
let buf = new Uint8Array(len)
HF2.write16(buf, 0, len - 2) // pktLen
HF2.write16(buf, 2, this.cmdSeq++) // msgCount
buf[4] = replyType
return buf
}
private allocSystem(addSize: number, cmd: number, replyType = 1) {
let buf = this.allocCore(addSize + 1, replyType)
buf[5] = cmd
return buf
}
private allocSystem(addSize: number, cmd: number, replyType = 1) {
let buf = this.allocCore(addSize + 1, replyType)
buf[5] = cmd
return buf
}
private allocCustom(code: number, addSize = 0) {
let buf = this.allocCore(1 + 2 + addSize, 0)
HF2.write16(buf, 4, usbMagic)
HF2.write16(buf, 6, code)
return buf
}
private allocCustom(code: number, addSize = 0) {
let buf = this.allocCore(1 + 2 + addSize, 0)
HF2.write16(buf, 4, usbMagic)
HF2.write16(buf, 6, code)
return buf
}
stopAsync() {
return this.isVmAsync()
.then(vm => {
if (vm) return Promise.resolve();
log(`stopping PXT app`)
let buf = this.allocCustom(2)
return this.justSendAsync(buf)
.then(() => Promise.delay(500))
})
}
dmesgAsync() {
log(`asking for DMESG buffer over serial`)
let buf = this.allocCustom(3)
return this.justSendAsync(buf)
}
runAsync(path: string) {
let codeHex = runTemplate.replace("XX", U.toHex(U.stringToUint8Array(path)))
let code = U.fromHex(codeHex)
let pkt = this.allocCore(2 + code.length, DIRECT_COMMAND_NO_REPLY)
HF2.write16(pkt, 5, 0x0800)
U.memcpy(pkt, 7, code)
log(`run ${path}`)
return this.justSendAsync(pkt)
}
justSendAsync(buf: Uint8Array) {
return this.lock.enqueue("talk", () => {
this.msgs.drain()
if (this.dataDump)
log("SEND: " + U.toHex(buf))
return this.io.sendPacketAsync(buf)
})
}
talkAsync(buf: Uint8Array, altResponse = 0) {
return this.lock.enqueue("talk", () => {
this.msgs.drain()
if (this.dataDump)
log("TALK: " + U.toHex(buf))
return this.io.sendPacketAsync(buf)
.then(() => this.msgs.shiftAsync(1000))
.then(resp => {
if (resp[2] != buf[2] || resp[3] != buf[3])
U.userError("msg count de-sync")
if (buf[4] == 1) {
if (altResponse != -1 && resp[5] != buf[5])
U.userError("cmd de-sync")
if (altResponse != -1 && resp[6] != 0 && resp[6] != altResponse)
U.userError("cmd error: " + resp[6])
}
return resp
stopAsync() {
return this.isVmAsync()
.then(vm => {
if (vm) return Promise.resolve();
log(`stopping PXT app`)
let buf = this.allocCustom(2)
return this.justSendAsync(buf)
.then(() => Promise.delay(500))
})
})
}
flashAsync(path: string, file: Uint8Array) {
log(`write ${file.length} bytes to ${path}`)
let handle = -1
let loopAsync = (pos: number): Promise<void> => {
if (pos >= file.length) return Promise.resolve()
let size = file.length - pos
if (size > 1000) size = 1000
let upl = this.allocSystem(1 + size, 0x93, 0x1)
upl[6] = handle
U.memcpy(upl, 6 + 1, file, pos, size)
return this.talkAsync(upl, 8) // 8=EOF
.then(() => loopAsync(pos + size))
}
let begin = this.allocSystem(4 + path.length + 1, 0x92)
HF2.write32(begin, 6, file.length) // fileSize
U.memcpy(begin, 10, U.stringToUint8Array(path))
return this.lock.enqueue("file", () =>
this.talkAsync(begin)
.then(resp => {
handle = resp[7]
return loopAsync(0)
}))
}
dmesgAsync() {
log(`asking for DMESG buffer over serial`)
let buf = this.allocCustom(3)
return this.justSendAsync(buf)
}
lsAsync(path: string): Promise<DirEntry[]> {
let lsReq = this.allocSystem(2 + path.length + 1, 0x99)
HF2.write16(lsReq, 6, 1024) // maxRead
U.memcpy(lsReq, 8, U.stringToUint8Array(path))
runAsync(path: string) {
let codeHex = runTemplate.replace("XX", U.toHex(U.stringToUint8Array(path)))
let code = U.fromHex(codeHex)
let pkt = this.allocCore(2 + code.length, 0)
HF2.write16(pkt, 5, 0x0800)
U.memcpy(pkt, 7, code)
log(`run ${path}`)
return this.justSendAsync(pkt)
}
return this.talkAsync(lsReq, 8)
.then(resp =>
U.uint8ArrayToString(resp.slice(12)).split(/\n/).map(s => {
if (!s) return null as DirEntry
let m = /^([A-F0-9]+) ([A-F0-9]+) ([^\/]*)$/.exec(s)
if (m)
return {
md5: m[1],
size: parseInt(m[2], 16),
name: m[3]
}
else
return {
name: s.replace(/\/$/, "")
}
}).filter(v => !!v))
}
rmAsync(path: string): Promise<void> {
log(`rm ${path}`)
let rmReq = this.allocSystem(path.length + 1, 0x9c)
U.memcpy(rmReq, 6, U.stringToUint8Array(path))
return this.talkAsync(rmReq, 5)
.then(resp => { })
}
isVmAsync(): Promise<boolean> {
let path = "/no/such/dir"
let mkdirReq = this.allocSystem(path.length + 1, 0x9b)
U.memcpy(mkdirReq, 6, U.stringToUint8Array(path))
return this.talkAsync(mkdirReq, -1)
.then(resp => {
let isVM = resp[6] == 0x05
log(`${isVM ? "PXT app" : "VM"} running`)
return isVM
justSendAsync(buf: Uint8Array) {
return this.lock.enqueue("talk", () => {
this.msgs.drain()
if (this.dataDump)
log("SEND: " + U.toHex(buf))
return this.io.sendPacketAsync(buf)
})
}
private streamFileOnceAsync(path: string, cb: (d: Uint8Array) => void) {
let fileSize = 0
let filePtr = 0
let handle = -1
let resp = (buf: Uint8Array): Promise<void> => {
if (buf[6] == 2) {
// handle not ready - file is missing
this.isStreaming = false
return Promise.resolve()
}
if (buf[6] != 0 && buf[6] != 8)
U.userError("bad response when streaming file: " + buf[6] + " " + U.toHex(buf))
this.isStreaming = true
fileSize = HF2.read32(buf, 7)
if (handle == -1) {
handle = buf[11]
log(`stream on, handle=${handle}`)
}
let data = buf.slice(12)
filePtr += data.length
if (data.length > 0)
cb(data)
if (buf[6] == 8) {
// end of file
this.isStreaming = false
return this.rmAsync(path)
}
let contFileReq = this.allocSystem(1 + 2, 0x97)
HF2.write16(contFileReq, 7, 1000) // maxRead
contFileReq[6] = handle
return Promise.delay(data.length > 0 ? 0 : 500)
.then(() => this.talkAsync(contFileReq, -1))
.then(resp)
}
let getFileReq = this.allocSystem(2 + path.length + 1, 0x96)
HF2.write16(getFileReq, 6, 1000) // maxRead
U.memcpy(getFileReq, 8, U.stringToUint8Array(path))
return this.talkAsync(getFileReq, -1).then(resp)
}
talkAsync(buf: Uint8Array, altResponse = 0) {
return this.lock.enqueue("talk", () => {
this.msgs.drain()
if (this.dataDump)
log("TALK: " + U.toHex(buf))
return this.io.sendPacketAsync(buf)
.then(() => this.msgs.shiftAsync(1000))
.then(resp => {
if (resp[2] != buf[2] || resp[3] != buf[3])
U.userError("msg count de-sync")
if (buf[4] == 1) {
if (altResponse != -1 && resp[5] != buf[5])
U.userError("cmd de-sync")
if (altResponse != -1 && resp[6] != 0 && resp[6] != altResponse)
U.userError("cmd error: " + resp[6])
}
return resp
})
})
}
streamFileAsync(path: string, cb: (d: Uint8Array) => void) {
let loop = (): Promise<void> =>
this.lock.enqueue("file", () =>
this.streamFileOnceAsync(path, cb))
.then(() => Promise.delay(500))
.then(loop)
return loop()
flashAsync(path: string, file: Uint8Array) {
log(`write ${file.length} bytes to ${path}`)
let handle = -1
let loopAsync = (pos: number): Promise<void> => {
if (pos >= file.length) return Promise.resolve()
let size = file.length - pos
if (size > 1000) size = 1000
let upl = this.allocSystem(1 + size, 0x93, 0x1)
upl[6] = handle
U.memcpy(upl, 6 + 1, file, pos, size)
return this.talkAsync(upl, 8) // 8=EOF
.then(() => loopAsync(pos + size))
}
let begin = this.allocSystem(4 + path.length + 1, 0x92)
HF2.write32(begin, 6, file.length) // fileSize
U.memcpy(begin, 10, U.stringToUint8Array(path))
return this.lock.enqueue("file", () =>
this.talkAsync(begin)
.then(resp => {
handle = resp[7]
return loopAsync(0)
}))
}
lsAsync(path: string): Promise<DirEntry[]> {
let lsReq = this.allocSystem(2 + path.length + 1, 0x99)
HF2.write16(lsReq, 6, 1024) // maxRead
U.memcpy(lsReq, 8, U.stringToUint8Array(path))
return this.talkAsync(lsReq, 8)
.then(resp =>
U.uint8ArrayToString(resp.slice(12)).split(/\n/).map(s => {
if (!s) return null as DirEntry
let m = /^([A-F0-9]+) ([A-F0-9]+) ([^\/]*)$/.exec(s)
if (m)
return {
md5: m[1],
size: parseInt(m[2], 16),
name: m[3]
}
else
return {
name: s.replace(/\/$/, "")
}
}).filter(v => !!v))
}
rmAsync(path: string): Promise<void> {
log(`rm ${path}`)
let rmReq = this.allocSystem(path.length + 1, 0x9c)
U.memcpy(rmReq, 6, U.stringToUint8Array(path))
return this.talkAsync(rmReq, 5)
.then(resp => { })
}
isVmAsync(): Promise<boolean> {
let path = "/no/such/dir"
let mkdirReq = this.allocSystem(path.length + 1, 0x9b)
U.memcpy(mkdirReq, 6, U.stringToUint8Array(path))
return this.talkAsync(mkdirReq, -1)
.then(resp => {
let isVM = resp[6] == 0x05
log(`${isVM ? "PXT app" : "VM"} running`)
return isVM
})
}
private streamFileOnceAsync(path: string, cb: (d: Uint8Array) => void) {
let fileSize = 0
let filePtr = 0
let handle = -1
let resp = (buf: Uint8Array): Promise<void> => {
if (buf[6] == 2) {
// handle not ready - file is missing
this.isStreaming = false
return Promise.resolve()
}
if (buf[6] != 0 && buf[6] != 8)
U.userError("bad response when streaming file: " + buf[6] + " " + U.toHex(buf))
this.isStreaming = true
fileSize = HF2.read32(buf, 7)
if (handle == -1) {
handle = buf[11]
log(`stream on, handle=${handle}`)
}
let data = buf.slice(12)
filePtr += data.length
if (data.length > 0)
cb(data)
if (buf[6] == 8) {
// end of file
this.isStreaming = false
return this.rmAsync(path)
}
let contFileReq = this.allocSystem(1 + 2, 0x97)
HF2.write16(contFileReq, 7, 1000) // maxRead
contFileReq[6] = handle
return Promise.delay(data.length > 0 ? 0 : 500)
.then(() => this.talkAsync(contFileReq, -1))
.then(resp)
}
let getFileReq = this.allocSystem(2 + path.length + 1, 0x96)
HF2.write16(getFileReq, 6, 1000) // maxRead
U.memcpy(getFileReq, 8, U.stringToUint8Array(path))
return this.talkAsync(getFileReq, -1).then(resp)
}
streamFileAsync(path: string, cb: (d: Uint8Array) => void) {
let loop = (): Promise<void> =>
this.lock.enqueue("file", () =>
this.streamFileOnceAsync(path, cb))
.then(() => Promise.delay(500))
.then(loop)
return loop()
}
downloadFileAsync(path: string, cb: (d: Uint8Array) => void) {
return this.lock.enqueue("file", () =>
this.streamFileOnceAsync(path, cb))
}
private initAsync() {
return Promise.resolve()
}
private resetState() {
}
reconnectAsync(first = false): Promise<void> {
this.resetState()
if (first) return this.initAsync()
log(`reconnect`);
return this.io.reconnectAsync()
.then(() => this.initAsync())
}
disconnectAsync() {
log(`disconnect`);
return this.io.disconnectAsync()
}
}
downloadFileAsync(path: string, cb: (d: Uint8Array) => void) {
return this.lock.enqueue("file", () =>
this.streamFileOnceAsync(path, cb))
}
private initAsync() {
return Promise.resolve()
}
private resetState() {
}
reconnectAsync(first = false): Promise<void> {
this.resetState()
if (first) return this.initAsync()
log(`reconnect`);
return this.io.reconnectAsync()
.then(() => this.initAsync())
}
disconnectAsync() {
log(`disconnect`);
return this.io.disconnectAsync()
}
}
}

View File

@ -3,7 +3,9 @@
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";
@ -17,9 +19,15 @@ 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.
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let bBox = { width: this.size_.width, height: this.size_.height };
var scale = this.sourceBlock_.workspace.scale;
var bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
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;
var position = this.fieldGroup_.getBoundingClientRect();
var primaryX = position.left + bBox.width / 2;
var primaryY = position.top + bBox.height;
var secondaryX = primaryX;
var secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
}
@ -152,10 +152,9 @@ export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.
* Callback for when the drop-down is hidden.
*/
private onHide_ = function () {
const content = Blockly.DropDownDiv.getContentDiv();
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
(content as HTMLElement).style.width = '';
Blockly.DropDownDiv.content_.removeAttribute('role');
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
Blockly.DropDownDiv.getContentDiv().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) {
const colour = this.mapColour(this.value_);
var colour = this.mapColour(this.colour_);
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) {
let colour = this.mapEnum(colorStr);
var colour = this.mapEnum(colorStr);
if (this.sourceBlock_ && Blockly.Events.isEnabled() &&
this.value_ != colour) {
this.colour_ != colour) {
Blockly.Events.fire(new (Blockly as any).Events.BlockChange(
this.sourceBlock_, 'field', this.name, this.value_, colour));
this.sourceBlock_, 'field', this.name, this.colour_, colour));
}
this.value_ = colour;
this.colour_ = 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.dom.createSvgElement('image', {
this.arrow_ = Blockly.utils.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_ = <SVGImageElement>Blockly.utils.dom.createSvgElement('image', {
this.arrow2_ = Blockly.utils.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.dom.createSvgElement('g', {}, null);
this.fieldGroup_ = Blockly.utils.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.
let size = this.getSize();
let fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2;
var size = this.getSize();
var fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2;
/** @type {!Element} */
this.textElement_ = Blockly.utils.dom.createSvgElement('text',
this.textElement_ = Blockly.utils.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_ = <SVGTextElement>Blockly.utils.dom.createSvgElement('text',
this.textElement2_ = Blockly.utils.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_ as Blockly.BlockSvg).getSvgRoot().appendChild(this.fieldGroup_);
this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
// Force a render.
this.render_();
this.isDirty_ = true;
this.size_.width = 0;
(this as any).mouseDownWrapper_ =
Blockly.bindEventWithChecks_((this as any).getClickTarget_(), 'mousedown', this,
(this as any).onMouseDown_);
// Add second dropdown
if (this.shouldShowRect_()) {
this.box_ = <SVGRectElement>Blockly.utils.dom.createSvgElement('rect', {
this.box_ = Blockly.utils.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_ = <SVGRectElement>Blockly.utils.dom.createSvgElement('rect', {
this.box2_ = Blockly.utils.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.
let text = this.text_;
var 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;
}
let text = this.text_;
var 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;
}
let textNode = document.createTextNode(text);
var textNode = document.createTextNode(text);
this.textElement2_.appendChild(textNode);
// Cached width is obsolete. Clear it.
this.isDirty_ = true;
this.size_.width = 0;
};
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_ && (<Blockly.BlockSvg>this.sourceBlock_).rendered) {
(<Blockly.BlockSvg>this.sourceBlock_).render();
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
this.sourceBlock_.render();
this.sourceBlock_.bumpNeighbours_();
}
}
@ -244,7 +244,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
return 0;
}
let addedWidth = 0;
var 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;
};
updateSize_() {
updateWidth() {
// Calculate width of field
let width = Blockly.Field.getCachedWidth(this.textElement_);
let width2 = Blockly.Field.getCachedWidth(this.textElement2_);
var width = Blockly.Field.getCachedWidth(this.textElement_);
var 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(<string>this.value_));
const textNode1 = document.createTextNode(this.getFirstValueI11n(this.value_));
this.textElement_.appendChild(textNode1);
// Second dropdown, no need to translate. Only port numbers
if (this.textElement2_) {
const textNode2 = document.createTextNode(this.getSecondValue(<string>this.value_));
const textNode2 = document.createTextNode(this.getSecondValue(this.value_));
this.textElement2_.appendChild(textNode2);
}
this.updateSize_();
this.updateWidth();
// 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.
let width = Blockly.Field.getCachedWidth(this.textElement_);
let newX = centerTextX - width / 2;
var width = Blockly.Field.getCachedWidth(this.textElement_);
var 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.
let width2 = Blockly.Field.getCachedWidth(this.textElement2_);
let newX2 = centerTextX2 - width2 / 2;
var width2 = Blockly.Field.getCachedWidth(this.textElement2_);
var 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 * (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale);
this.isFirst_ = e.clientX - this.getScaledBBox_().left < ((this as any).width1 * 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');
const foptions = this.getOptions(); // [img info, text]
let options = this.getOptions();
let opts = {};
let conts = {};
let vals = {};
// Go through all option values and split them into groups
for (let opt = 0; opt < foptions.length; opt++) {
const value = foptions[opt][1];
for (let opt = 0; opt < options.length; opt++) {
const value = options[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,14 +429,13 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
if (!opts[key]) opts[key] = [];
opts[key].push(portValue);
conts[text] = foptions[opt][0];
conts[text] = options[opt][0];
vals[text] = value;
}
const currentFirst = this.getFirstValue(<string>this.value_);
//const currentSecond = this.getSecondValue(<string>this.value_);
const currentFirst = this.getFirstValue(this.value_);
const currentSecond = this.getSecondValue(this.value_);
let options: string[];
if (!this.isFirst_) {
options = opts[currentFirst];
} else {
@ -527,7 +526,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 = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let scale = 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;
@ -539,7 +538,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((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
@ -562,11 +561,10 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
* Callback for when the drop-down is hidden.
*/
protected onHide_() {
const content = Blockly.DropDownDiv.getContentDiv();
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
(content as HTMLElement).style.width = '';
Blockly.DropDownDiv.content_.removeAttribute('role');
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
Blockly.DropDownDiv.getContentDiv().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.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
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 as HTMLElement).style.maxHeight = `410px`;
dropdownDiv.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 = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let scale = 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((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).setBoundsElement(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() as HTMLElement).style.maxHeight = '';
Blockly.DropDownDiv.getContentDiv().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.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
}

View File

@ -0,0 +1,97 @@
/// <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) {
this.updateSpeed(parseFloat(value));
// Update reporter
this.reporter.textContent = `${value}%`;
}
private updateSpeed(speed: number) {
let sign = this.sign(speed);
speed = (Math.abs(speed) / 100 * 50) + 50;
if (sign == -1) speed = 50 - speed;
let c = Math.PI * (90 * 2);
let pct = ((100 - speed) / 100) * c;
this.circleBar.setAttribute('stroke-dashoffset', `${pct}`);
}
// A re-implementation of Math.sign (since IE11 doesn't support it)
private sign(num: number) {
return num ? num < 0 ? -1 : 1 : 0;
}
}

View File

@ -0,0 +1,108 @@
/// <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_), '-100', '100', 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()), -100, 100);
if (isNaN(v)) {
v = 0;
}
const x = goog.math.clamp(parseFloat(this.getText()), -100, 100) / 100;
const theta = x * Math.PI / 2;
const cx = FieldTurnRatio.HALF;
const cy = FieldTurnRatio.HALF - 14;
const gamma = Math.PI - 2 * theta;
const r = FieldTurnRatio.RADIUS;
const alpha = 0.2 + Math.abs(x) * 0.5;
const x1 = 0;
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,8 +22,13 @@
}
declare const enum PerfCounters {
GC = 0,
declare const enum ValType {
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
}
// Auto-generated. Do not edit. Really.

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

@ -4,18 +4,6 @@
//% 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.
*/
@ -54,12 +42,6 @@ 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.
*/
@ -90,13 +72,6 @@ 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 {
@ -126,12 +101,6 @@ 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
*/
@ -174,54 +143,11 @@ 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(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;
function __log(text: string): void;
}
// Auto-generated. Do not edit. Really.

View File

@ -1,66 +0,0 @@
/**
* Message broadcasting
*/
//% weight=70
//% color="#58AB41"
namespace broadcast {
const broadcastEventId = control.allocateNotifyEvent();
const broadcastDoneEventId = control.allocateNotifyEvent();
function normalizeId(id: number) {
// upper ids are reserved for answer
return ((id + 1) | 0) & 0xffff;
}
/**
* An enum shim
*/
//% shim=ENUM_GET
//% blockId=msg_enum_shim
//% block="$arg"
//% enumName="Messages"
//% enumMemberName="message"
//% enumPromptHint="e.g. Move, Turn, ..."
//% enumInitialMembers="message1"
//% blockHidden=1
//% enumIsHash=1
export function __messageShim(arg: number) {
// This function should do nothing, but must take in a single
// argument of type number and return a number value.
return arg;
}
/**
* Register code to run when a message is received
*/
//% block="on %id=msg_enum_shim|received"
//% blockId=broadcastonreceived draggableParameters
export function onMessageReceived(message: number, body: () => void) {
const messageid = normalizeId(message);
control.onEvent(broadcastEventId, messageid, function () {
body();
control.raiseEvent(broadcastDoneEventId, messageid);
})
}
/**
* Sends a message to activate code
*/
//% block="send %id=msg_enum_shim"
//% blockId=broadcastsend draggableParameters
export function sendMessage(message: number) {
const messageid = normalizeId(message);
control.raiseEvent(broadcastEventId, messageid);
}
/**
* Sends a message and pauses until the handler to finishes.
*/
//% block="send %id=msg_enum_shim| and pause"
//% blockId=broadcastsendpause
export function sendMessageAndPause(message: number) {
const messageid = normalizeId(message);
control.raiseEvent(broadcastEventId, messageid);
control.waitForEvent(broadcastDoneEventId, messageid);
}
}

View File

@ -1,11 +0,0 @@
{
"name": "broadcast",
"description": "Broadcasting messages - beta",
"files": [
"broadcast.ts"
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

View File

@ -73,7 +73,13 @@ namespace sensors {
}
setMode(m: ColorSensorMode) {
// don't change threshold after initialization
if (m == ColorSensorMode.AmbientLightIntensity) {
this.thresholdDetector.setLowThreshold(5);
this.thresholdDetector.setHighThreshold(20);
} else {
this.thresholdDetector.setLowThreshold(20);
this.thresholdDetector.setHighThreshold(80);
}
this._setMode(m)
}
@ -105,9 +111,6 @@ namespace sensors {
"red",
"white",
"brown"][this._query()];
case ColorSensorMode.AmbientLightIntensity:
case ColorSensorMode.ReflectedLightIntensity:
return `${this._query()}%`;
default:
return this._query().toString();
}
@ -176,7 +179,6 @@ namespace sensors {
//% group="Color Sensor"
//% blockGap=8
color(): ColorSensorColor {
this.poke();
this.setMode(ColorSensorMode.Color)
return this.getNumber(NumberFormat.UInt8LE, 0)
}
@ -194,7 +196,6 @@ namespace sensors {
//% group="Color Sensor"
//% blockGap=8
rgbRaw(): number[] {
this.poke();
this.setMode(ColorSensorMode.RgbRaw);
return [this.getNumber(NumberFormat.UInt16LE, 0), this.getNumber(NumberFormat.UInt16LE, 2), this.getNumber(NumberFormat.UInt16LE, 4)];
}
@ -248,9 +249,8 @@ namespace sensors {
//% weight=87 blockGap=8
//% group="Color Sensor"
light(mode: LightIntensityMode) {
this.poke();
this.setMode(<ColorSensorMode><number>mode)
switch (mode) {
switch(mode) {
case LightIntensityMode.ReflectedRaw:
return this.reflectedLightRaw();
default:
@ -279,7 +279,6 @@ namespace sensors {
*/
//%
reflectedLightRaw(): number {
this.poke();
this.setMode(ColorSensorMode.RefRaw);
return this.getNumber(NumberFormat.UInt16LE, 0);
}

View File

@ -1,40 +1,13 @@
const enum BatteryProperty {
//% block="level (%)"
Level,
//% block="current (I)"
Current,
//% block="voltage (V)"
Voltage
}
namespace brick {
/**
* Returns the current battery level
*/
//% blockId=brickBatteryLevel block="battery level"
//% group="Battery"
//% group="More"
//% help=brick/battery-level
//% deprecated blockHidden=1
export function batteryLevel(): number {
const info = sensors.internal.getBatteryInfo();
return info.level;
}
/**
* Returns information about the battery
*/
//% blockId=brickBatteryProperty block="battery %property"
//% group="Battery"
//% help=brick/battery-property
export function batteryInfo(property: BatteryProperty): number {
const info = sensors.internal.getBatteryInfo();
switch(property) {
case BatteryProperty.Level: return info.level;
case BatteryProperty.Current: return info.Ibatt;
case BatteryProperty.Voltage: return info.Vbatt;
default: return 0;
}
return info.current;
}
}

View File

@ -55,10 +55,6 @@ namespace brick {
this._wasPressed = false
}
protected poke() {
}
//% hidden
_update(curr: boolean) {
if (this == null) return
@ -89,7 +85,6 @@ namespace brick {
//% group="Buttons"
//% button.fieldEditor="brickbuttons"
isPressed() {
this.poke();
return this._isPressed
}
@ -107,7 +102,6 @@ namespace brick {
//% group="Buttons"
//% button.fieldEditor="brickbuttons"
wasPressed() {
this.poke();
const r = this._wasPressed
this._wasPressed = false
return r
@ -150,7 +144,6 @@ namespace brick {
namespace brick {
let btnsMM: MMap
let buttons: DevButton[]
let buttonPoller: sensors.internal.Poller;
export namespace internal {
export function getBtnsMM() {
@ -174,7 +167,7 @@ namespace brick {
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
if (!btnsMM) control.fail("no buttons?")
buttons = []
buttonPoller = new sensors.internal.Poller(50, readButtons, (prev, curr) => {
sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
for (let b of buttons)
b._update(!!(curr & b.mask))
})
@ -189,10 +182,6 @@ namespace brick {
initBtns()
buttons.push(this)
}
protected poke() {
buttonPoller.poke();
}
}
initBtns() // always ON as it handles ESCAPE button

40
libs/core/control.cpp Normal file
View File

@ -0,0 +1,40 @@
#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,305 +1,6 @@
// Auto-generated. Do not edit.
declare const enum DAL {
// /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
// built/dockermake/pxtapp/ev3const.h
NUM_INPUTS = 4,
NUM_OUTPUTS = 4,
LCD_WIDTH = 178,
@ -345,66 +46,44 @@ declare const enum DAL {
CONN_OUTPUT_TACHO = 125,
CONN_NONE = 126,
CONN_ERROR = 127,
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
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
DEVICE_EVT_ANY = 0,
DEVICE_ID_NOTIFY = 10000,
DEVICE_ID_NOTIFY_ONE = 10001,
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,
// 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,
Int8LE = 1,
UInt8LE = 2,
Int16LE = 3,
@ -421,10 +100,12 @@ declare const enum DAL {
Float64LE = 14,
Float32BE = 15,
Float64BE = 16,
NUM_TRY_FRAME_REGS = 3,
GC = 0,
// /pxtapp/pxtconfig.h
PXT_GC = 1,
// /pxtapp/pxtcore.h
PXT_HARD_FLOAT = 1,
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
// built/dockermake/pxtapp/pxtconfig.h
// built/dockermake/pxtapp/pxtcore.h
}

View File

@ -1,51 +1,28 @@
namespace sensors.internal {
export class Poller {
private query: () => number;
private update: (previous: number, current: number) => void;
public interval: number;
//% shim=pxt::unsafePollForChanges
export function unsafePollForChanges(
periodMs: number,
query: () => number,
changeHandler: (prev: number, curr: number) => void
) {
// This is implemented in C++ without blocking the regular JS when query() is runnning
// which is generally unsafe. Query should not update globally visible state, and cannot
// call any yielding functions, like sleep().
private previousValue: number;
private currentValue: number;
private lastQuery: number; // track down the last time we did a query/update cycle
private lastPause: number; // track down the last time we pause in the sensor polling loop
// This is implementation for the simulator.
constructor(interval: number, query: () => number, update: (previous: number, current: number) => void) {
this.interval = interval | 0;
this.query = query;
this.update = update;
this.poll();
}
poke(): void {
const now = control.millis();
if (now - this.lastQuery >= this.interval * 2)
this.queryAndUpdate(); // sensor poller is not allowed to run
if (now - this.lastPause >= this.interval * 5)
pause(1); // allow events to trigger
}
private queryAndUpdate() {
this.lastQuery = control.millis();
this.currentValue = this.query();
if (this.previousValue != this.currentValue) {
this.update(this.previousValue, this.currentValue);
this.previousValue = this.currentValue;
}
}
private poll() {
control.runInBackground(() => {
this.lastQuery = this.lastPause = control.millis();
this.previousValue = this.currentValue = this.query();
this.update(this.previousValue, this.currentValue);
while (true) {
this.lastPause = control.millis();
pause(this.interval);
this.queryAndUpdate();
control.runInParallel(() => {
let prev = query()
changeHandler(prev, prev)
while (true) {
pause(periodMs)
let curr = query()
if (prev !== curr) {
changeHandler(prev, curr)
prev = curr
}
})
}
}
})
}
export function bufferToString(buf: Buffer): string {
@ -59,18 +36,8 @@ namespace sensors.internal {
let analogMM: MMap
let uartMM: MMap
let IICMM: MMap
let powerMM: MMap
let devcon: Buffer
let devPoller: Poller
let sensorInfos: SensorInfo[];
let batteryInfo: {
CinCnt: number;
CoutCnt: number;
VinCnt: number;
};
let batteryVMin: number;
let batteryVMax: number;
let sensorInfos: SensorInfo[]
class SensorInfo {
port: number
@ -79,7 +46,6 @@ namespace sensors.internal {
connType: number
devType: number
iicid: string
poller: Poller;
constructor(p: number) {
this.port = p
@ -87,20 +53,6 @@ namespace sensors.internal {
this.devType = DAL.DEVICE_TYPE_NONE
this.iicid = ''
this.sensors = []
this.poller = new Poller(25, () => this.query(), (prev, curr) => this.update(prev, curr));
}
poke() {
this.poller.poke();
}
private query() {
if (this.sensor) return this.sensor._query();
return 0;
}
private update(prev: number, curr: number) {
if (this.sensor) this.sensor._update(prev, curr)
}
}
@ -119,12 +71,19 @@ namespace sensors.internal {
IICMM = control.mmap("/dev/lms_iic", IICOff.Size, 0)
if (!IICMM) control.fail("no iic sensor")
powerMM = control.mmap("/dev/lms_power", 2, 0)
unsafePollForChanges(500,
() => { return hashDevices(); },
(prev, curr) => { detectDevices();
});
sensorInfos.forEach(info => {
unsafePollForChanges(50, () => {
if (info.sensor) return info.sensor._query()
return 0
}, (prev, curr) => {
if (info.sensor) info.sensor._update(prev, curr)
})
})
devPoller = new Poller(250, () => { return hashDevices(); },
(prev, curr) => {
detectDevices();
});
}
export function getActiveSensors(): Sensor[] {
@ -151,132 +110,18 @@ namespace sensors.internal {
return manufacturer + sensorType;
}
const ADC_REF = 5000 //!< [mV] maximal value on ADC
const ADC_RES = 4095 //!< [CNT] maximal count on ADC
// see c_ui.c
const SHUNT_IN = 0.11 // [Ohm]
const AMP_CIN = 22.0 // [Times]
const EP2_SHUNT_IN = 0.05 // [Ohm]
const EP2_AMP_CIN = 15.0 // [Times]
const SHUNT_OUT = 0.055 // [Ohm]
const AMP_COUT = 19.0 // [Times]
const VCE = 0.05 // [V]
const AMP_VIN = 0.5 // [Times]
const AVR_CIN = 300
const AVR_COUT = 30
const AVR_VIN = 30
// lms2012
const BATT_INDICATOR_HIGH = 7500 //!< Battery indicator high [mV]
const BATT_INDICATOR_LOW = 6200 //!< Battery indicator low [mV]
const ACCU_INDICATOR_HIGH = 7500 //!< Rechargeable battery indicator high [mV]
const ACCU_INDICATOR_LOW = 7100 //!< Rechargeable battery indicator low [mV]
function CNT_V(C: number) {
return ((C * ADC_REF) / (ADC_RES * 1000.0))
}
function updateBatteryInfo() {
let CinCnt = analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryCurrent);
let CoutCnt = analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.MotorCurrent);
let VinCnt = analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.Cell123456);
if (!batteryInfo) {
batteryVMin = BATT_INDICATOR_LOW;
batteryVMax = BATT_INDICATOR_HIGH;
if (powerMM) {
const accu = powerMM.getNumber(NumberFormat.UInt8LE, 0);
if (accu > 0) {
control.dmesg("rechargeable battery")
batteryVMin = ACCU_INDICATOR_LOW;
batteryVMax = ACCU_INDICATOR_HIGH;
}
}
batteryInfo = {
CinCnt: CinCnt,
CoutCnt: CoutCnt,
VinCnt: VinCnt
};
// update in background
control.runInParallel(() => forever(updateBatteryInfo));
} else {
CinCnt = batteryInfo.CinCnt = ((batteryInfo.CinCnt * (AVR_CIN - 1)) + CinCnt) / AVR_CIN;
CoutCnt = batteryInfo.CoutCnt = ((batteryInfo.CoutCnt * (AVR_COUT - 1)) + CoutCnt) / AVR_COUT;
VinCnt = batteryInfo.VinCnt = ((batteryInfo.VinCnt * (AVR_VIN - 1)) + VinCnt) / AVR_VIN;
}
}
export interface BatteryInfo {
level: number;
Ibatt: number,
Vbatt: number,
Imotor: number
}
export function getBatteryInfo(): BatteryInfo {
export function getBatteryInfo(): { temp: number; current: number } {
init();
if (!batteryInfo) updateBatteryInfo();
const CinCnt = batteryInfo.CinCnt;
const CoutCnt = batteryInfo.CoutCnt;
const VinCnt = batteryInfo.VinCnt;
/*
void cUiUpdatePower(void)
{
#ifndef Linux_X86
DATAF CinV;
DATAF CoutV;
if ((UiInstance.Hw == FINAL) || (UiInstance.Hw == FINALB))
{
CinV = CNT_V(UiInstance.CinCnt) / AMP_CIN;
UiInstance.Vbatt = (CNT_V(UiInstance.VinCnt) / AMP_VIN) + CinV + VCE;
UiInstance.Ibatt = CinV / SHUNT_IN;
CoutV = CNT_V(UiInstance.CoutCnt) / AMP_COUT;
UiInstance.Imotor = CoutV / SHUNT_OUT;
}
else
{
CinV = CNT_V(UiInstance.CinCnt) / EP2_AMP_CIN;
UiInstance.Vbatt = (CNT_V(UiInstance.VinCnt) / AMP_VIN) + CinV + VCE;
UiInstance.Ibatt = CinV / EP2_SHUNT_IN;
UiInstance.Imotor = 0;
}
#endif
#ifdef DEBUG_TEMP_SHUTDOWN
UiInstance.Vbatt = 7.0;
UiInstance.Ibatt = 5.0;
#endif
}
*/
const CinV = CNT_V(CinCnt) / AMP_CIN;
const Vbatt = CNT_V(VinCnt) / AMP_VIN + CinV + VCE;
const Ibatt = CinV / SHUNT_IN;
const CoutV = CNT_V(CoutCnt) / AMP_COUT;
const Imotor = CoutV / SHUNT_OUT;
const level = Math.max(0, Math.min(100, Math.floor((Vbatt * 1000.0 - batteryVMin)
/ (batteryVMax - batteryVMin) * 100)));
return {
level: level,
Vbatt: Vbatt,
Ibatt: Ibatt,
Imotor: Imotor
};
temp: analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryTemp),
current: Math.round(analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryCurrent) / 10)
}
}
function hashDevices(): number {
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let r = 0;
for (let i = 0; i < conns.length; ++i) {
for(let i = 0; i < conns.length; ++i) {
r = (r << 8 | conns[i]);
}
return r;
@ -312,7 +157,7 @@ void cUiUpdatePower(void)
// TODO? for now assume touch
sensorInfo.devType = DAL.DEVICE_TYPE_TOUCH
} else if (newConn == DAL.CONN_NONE || newConn == 0) {
//control.dmesg(`disconnect at port ${sensorInfo.port}`)
control.dmesg(`disconnect at port ${sensorInfo.port}`)
} else {
control.dmesg(`unknown connection type: ${newConn} at ${sensorInfo.port}`)
}
@ -330,7 +175,7 @@ void cUiUpdatePower(void)
if (numChanged == 0 && nonActivated == 0)
return
//control.dmesg(`updating sensor status`)
control.dmesg(`updating sensor status`)
nonActivated = 0;
for (const sensorInfo of sensorInfos) {
if (sensorInfo.devType == DAL.DEVICE_TYPE_IIC_UNKNOWN) {
@ -369,11 +214,6 @@ void cUiUpdatePower(void)
this.markUsed();
}
poke() {
if (this.isActive())
sensorInfos[this._port].poke();
}
markUsed() {
sensors.__sensorUsed(this._port, this._deviceType());
}
@ -816,10 +656,10 @@ namespace sensors {
export class ThresholdDetector {
public id: number;
private min: number;
private max: number;
private lowThreshold: number;
private highThreshold: number;
public min: number;
public max: number;
public lowThreshold: number;
public highThreshold: number;
public level: number;
public state: ThresholdState;

View File

@ -1,26 +0,0 @@
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();
}
}
}

659
libs/core/linux.cpp Normal file
View File

@ -0,0 +1,659 @@
#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)
namespace Array_ {
RefCollection *mk(unsigned flags);
}
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);
}
//%
void unsafePollForChanges(int ms, Action query, Action handler) {
setupThread(handler, 0, runPoller, query, fromInt(ms));
}
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, 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() {
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));
}
const char *progPath = "/mnt/ramdisk/prjs/BrkProg_SAVE";
//%
void deleteAllPrograms() {
char buf[1024];
struct dirent *ent;
DIR *dir;
dir = opendir(progPath);
if (dir == NULL)
return;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
snprintf(buf, sizeof(buf), "%s/%s", progPath, ent->d_name);
DMESG("FN: %s", ent->d_name);
// unlink(buf);
}
closedir(dir);
}
//%
void deletePrjFile(String filename) {
if (strlen(filename->data) > 500 || strchr(filename->data, '/'))
return;
char buf[1024];
snprintf(buf, sizeof(buf), "%s/%s", progPath, filename->data);
unlink(buf);
}
//%
RefCollection *listPrjFiles() {
auto res = Array_::mk(0);
//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->push((TValue)str);
//unregisterGCObj(str);
}
if (dp)
closedir(dp);
//unregisterGCObj(res);
return res;
}
} // namespace pxt

View File

@ -25,20 +25,13 @@ PXT_VTABLE_CTOR(MMap) {
}
void MMap::print() {
DMESG("MMap %p len=%d fd=%d data=%p", this, length, fd, data);
DMESG("MMap %p r=%d len=%d fd=%d data=%p", this, refcnt, 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 {
@ -46,8 +39,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->getUTF8Data(), size, offset);
int fd = open(filename->getUTF8Data(), O_RDWR, 0);
DMESG("mmap %s len=%d off=%d", filename->data, size, offset);
int fd = open(filename->data, O_RDWR, 0);
if (fd < 0)
return 0;

View File

@ -36,13 +36,6 @@ enum MoveUnit {
MilliSeconds
}
enum MovePhase {
//% block="acceleration"
Acceleration,
//% block="deceleration"
Deceleration
}
namespace motors {
let pwmMM: MMap
let motorMM: MMap
@ -62,7 +55,7 @@ namespace motors {
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
if (!motorMM) control.fail("no motor file")
resetAll()
resetAllMotors()
const buf = output.createBuffer(1)
buf[0] = DAL.opProgramStart
@ -118,25 +111,20 @@ namespace motors {
* Stops all motors
*/
//% blockId=motorStopAll block="stop all motors"
//% weight=2
//% weight=1
//% group="Move"
//% help=motors/stop-all
export function stopAll() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
writePWM(b);
pause(1);
writePWM(b)
}
/**
* Resets all motors
*/
//% blockId=motorResetAll block="reset all motors"
//% weight=1
//% group="Move"
//% help=motors/reset-all
export function resetAll() {
export function resetAllMotors() {
reset(Output.ALL)
pause(1);
}
interface MoveSchedule {
@ -238,7 +226,6 @@ namespace motors {
//% weight=1 blockGap=8
//% group="Properties"
//% millis.defl=200 millis.min=0 millis.max=500
//% help=motors/motor/set-brake-settle-time
setBrakeSettleTime(millis: number) {
this.init();
// ensure in [0,500]
@ -264,9 +251,6 @@ namespace motors {
// allow 500ms for robot to settle
if (this._brake && this._brakeSettleTime > 0)
pause(this._brakeSettleTime);
else {
pause(1);
}
}
protected pauseOnRun(stepsOrTime: number) {
@ -275,8 +259,6 @@ namespace motors {
this.pauseUntilReady();
// allow robot to settle
this.settle();
} else {
pause(1);
}
}
@ -297,24 +279,16 @@ namespace motors {
const r: MoveSchedule = {
speed: Math.clamp(-100, 100, speed >> 0),
useSteps: true,
steps: [step1 || 0, step2 || 0, step3 || 0]
steps: [step1, step2, step3]
}
let scale = 1;
switch (unit) {
case MoveUnit.Rotations:
scale = 360;
r.useSteps = true;
if (r.steps[1] < 0) {
r.speed = -r.speed;
r.steps[1] = -r.steps[1];
}
break;
case MoveUnit.Degrees:
r.useSteps = true;
if (r.steps[1] < 0) {
r.speed = -r.speed;
r.steps[1] = -r.steps[1];
}
break;
case MoveUnit.Seconds:
scale = 1000;
@ -352,7 +326,6 @@ namespace motors {
// special: 0 is infinity
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
this._run(schedule.speed);
pause(1);
return;
}
@ -379,20 +352,18 @@ namespace motors {
/**
* Schedules a run of the motor with an acceleration, constant and deceleration phase.
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value measured distance or rotation, eg: 500
* @param unit (optional) unit of the value, eg: MoveUnit.MilliSeconds
* @param acceleration acceleration phase measured distance or rotation, eg: 500
* @param deceleration deceleration phase measured distance or rotation, eg: 500
* @param acceleration acceleration phase measured distance or rotation
* @param value measured distance or rotation
* @param deceleration deceleration phase measured distance or rotation
* @param unit (optional) unit of the value
*/
//% blockId=motorSchedule block="ramp %motor at %speed=motorSpeedPicker|\\%|for %value|%unit||accelerate %acceleration|decelerate %deceleration"
//% blockId=motorSchedule block="schedule %motor at %speed=motorSpeedPicker|\\%|for %acceleration|%value|%deceleration||%unit"
//% weight=99 blockGap=8
//% group="Move"
//% motor.fieldEditor="motors"
//% help=motors/motor/ramp
//% help=motors/motor/schedule
//% inlineInputMode=inline
//% expandableArgumentMode=toggle
//% value.defl=500
ramp(speed: number, value: number = 500, unit: MoveUnit = MoveUnit.MilliSeconds, acceleration?: number, deceleration?: number) {
schedule(speed: number, acceleration: number, value: number, deceleration: number, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
const schedule = this.normalizeSchedule(speed, acceleration, value, deceleration, unit);
// stop if speed is 0
@ -415,41 +386,50 @@ namespace motors {
* Specifies the amount of rotation or time for the acceleration
* of run commands.
*/
//% blockId=outputMotorsetRunRamp block="set %motor|run %ramp to $value||$unit"
//% blockId=outputMotorsetRunAcceleration block="set %motor|run acceleration to $value||$unit"
//% motor.fieldEditor="motors"
//% weight=21 blockGap=8
//% group="Properties"
//% help=motors/motor/set-run-phase
setRunPhase(phase: MovePhase, value: number, unit: MoveUnit = MoveUnit.MilliSeconds) {
let temp: number;
//% help=motors/motor/set-run-acceleration-ramp
setRunAccelerationRamp(value: number, unit: MoveUnit = MoveUnit.MilliSeconds) {
switch (unit) {
case MoveUnit.Rotations:
temp = Math.max(0, (value * 360) | 0);
if (phase == MovePhase.Acceleration)
this._accelerationSteps = temp;
else
this._decelerationSteps = temp;
this._accelerationSteps = Math.max(0, (value * 360) | 0);
break;
case MoveUnit.Degrees:
temp = Math.max(0, value | 0);
if (phase == MovePhase.Acceleration)
this._accelerationSteps = temp;
else
this._decelerationSteps = temp;
this._accelerationSteps = Math.max(0, value | 0);
break;
case MoveUnit.Seconds:
temp = Math.max(0, (value * 1000) | 0);
if (phase == MovePhase.Acceleration)
this._accelerationTime = temp;
else
this._decelerationTime = temp;
this._accelerationTime = Math.max(0, (value * 1000) | 0);
break;
case MoveUnit.MilliSeconds:
temp = Math.max(0, value | 0);
if (phase == MovePhase.Acceleration)
this._accelerationTime = temp;
else
this._decelerationTime = temp;
this._accelerationTime = Math.max(0, value | 0);
break;
}
}
/**
* Specifies the amount of rotation or time for the acceleration
* of run commands.
*/
//% blockId=outputMotorsetRunDeceleration block="set %motor|run deceleration ramp to $value||$unit"
//% motor.fieldEditor="motors"
//% weight=20 blockGap=8
//% group="Properties"
//% help=motors/motor/set-run-deceleration-ramp
setRunDecelerationRamp(value: number, unit: MoveUnit = MoveUnit.MilliSeconds) {
switch (unit) {
case MoveUnit.Rotations:
this._decelerationSteps = Math.max(0, (value * 360) | 0);
break;
case MoveUnit.Degrees:
this._decelerationSteps = Math.max(0, value | 0);
break;
case MoveUnit.Seconds:
this._decelerationTime = Math.max(0, (value * 1000) | 0);
break;
case MoveUnit.MilliSeconds:
this._decelerationTime = Math.max(0, value | 0);
break;
}
}
@ -513,7 +493,7 @@ namespace motors {
*/
//% blockId=motorPauseUntilRead block="pause until %motor|ready"
//% motor.fieldEditor="motors"
//% weight=90 blockGap=8
//% weight=90
//% group="Move"
pauseUntilReady(timeOut?: number) {
pauseUntil(() => this.isReady(), timeOut);
@ -621,33 +601,6 @@ namespace motors {
toString(): string {
return `${this._large ? "" : "M"}${this._portName} ${this.speed()}% ${this.angle()}>`;
}
/**
* Pauses the program until the motor is stalled.
*/
//% blockId=motorPauseUntilStall block="pause until %motor|stalled"
//% motor.fieldEditor="motors"
//% weight=89
//% group="Move"
//% help=motors/motor/pause-until-stalled
pauseUntilStalled(timeOut?: number): void {
// let it start
pause(50);
let previous = this.angle();
let stall = 0;
pauseUntil(() => {
let current = this.angle();
if (Math.abs(current - previous) < 1) {
if (stall++ > 2) {
return true; // not moving
}
} else {
stall = 0;
previous = current;
}
return false;
}, timeOut)
}
}
//% whenUsed fixedInstance block="large motor A" jres=icons.portA
@ -743,7 +696,7 @@ namespace motors {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
this.stop();
stop(this._port, this._brake);
return;
}
@ -752,18 +705,10 @@ namespace motors {
let stepsOrTime: number;
switch (unit) {
case MoveUnit.Rotations:
if (value < 0) {
value = -value;
speed = -speed;
}
stepsOrTime = (value * 360) >> 0;
useSteps = true;
break;
case MoveUnit.Degrees:
if (value < 0) {
value = -value;
speed = -speed;
}
stepsOrTime = value >> 0;
useSteps = true;
break;
@ -918,3 +863,9 @@ namespace motors {
writePWM(b)
}
}
interface Buffer {
[index: number]: number;
// rest defined in buffer.cpp
}

View File

@ -1,199 +0,0 @@
#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, 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() {
tryLockUser();
stopMotors();
stopProgram();
if (lmsPid)
runLMS();
else
exit(0);
}
void target_exit() {
target_reset();
}
void target_startup() {
stopLMS();
startUsb();
startExitThread();
}
void initKeys() {}
}

View File

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

View File

@ -11,7 +11,6 @@ int allocateNotifyEvent();
void sleep_core_us(uint64_t us);
void startUser();
void stopUser();
int tryLockUser();
class Button;
typedef Button *Button_;
@ -28,13 +27,9 @@ 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,20 +25,11 @@
"dal.d.ts",
"icons.jres",
"ns.ts",
"platform.h",
"platform.cpp",
"dmesg.cpp",
"integrator.ts"
"platform.h"
],
"testFiles": [
"test.ts"
],
"dalDTS": {
"includeDirs": [
"pxtapp"
]
},
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/core---linux",
"npmDependencies": {},
"public": true,
"dependencies": {

16
libs/core/pxtcore.h Normal file
View File

@ -0,0 +1,16 @@
#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,9 +85,10 @@ void updateScreen(Image_ img) {
lastImg = img;
}
if (lastImg && mappedFrameBuffer != MAP_FAILED) {
if (lastImg && lastImg->isDirty() && 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 uint64_t bt_addr() {
uint64_t res = -1;
static uint32_t bt_addr() {
uint32_t res = -1;
int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
if (fd < 0) {
@ -50,8 +50,11 @@ static uint64_t bt_addr() {
goto done;
}
res = 0;
memcpy(&res, di.bdaddr, 6);
memcpy(&res, di.bdaddr, 4);
res *= 0x1000193;
res += di.bdaddr[4];
res *= 0x1000193;
res += di.bdaddr[5];
done:
close(fd);
@ -60,10 +63,14 @@ done:
namespace pxt {
uint64_t getLongSerialNumber() {
static uint64_t serial;
if (serial == 0)
serial = bt_addr();
int getSerialNumber() {
static int serial;
if (serial != 0)
return serial;
serial = bt_addr() & 0x7fffffff;
return serial;
}

View File

@ -68,12 +68,6 @@ 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 {

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(priority: ConsolePriority, msg: string): void {
function log(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"]'
//% labelLineWidth=60
namespace brick {
}

View File

@ -1,7 +1,5 @@
// This is the last thing executed before user code
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

View File

@ -1,6 +1,6 @@
# calibrate
Detects if the gyro is drifting and performs a full reset if needed.
Reset the zero reference for the gyro to current position of the brick.
```sig
sensors.gyro2.calibrate()

Some files were not shown because too many files have changed in this diff Show More