Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8fd0bf92fa | |||
| a33bff0520 | |||
| 7549f865d6 | |||
| a5de9d88bb | |||
| 0437df10de | |||
| 2079173dfa | |||
| 018a1e7528 | |||
| 757f95d984 | |||
| 8bc3fdc8ba | |||
| e8a1e73cf5 | |||
| e9b2b239ad | |||
| 5ad2288a9f | |||
| 92d13ef343 | |||
| 471ca5d915 | |||
| f745079728 | |||
| d179f18ef3 | |||
| 805fc6c787 | |||
| 374bbb8304 | |||
| 25452efc92 | |||
| 80b9c715b2 | |||
| cb816c91ad | |||
| 3012068986 | |||
| 4c9c7d6a69 | |||
| ad3652c290 | |||
| d8971829e3 | |||
| 8ca4558fc2 | |||
| d85b5c5129 | |||
| 95b1c6a50f | |||
| 4dc2872286 | |||
| 6c9ff804c8 | |||
| 7581b5af9e | |||
| 07504027f9 | |||
| a0e133864a | |||
| 0285711954 | |||
| 91be998d7e | |||
| e862869327 | |||
| 8047cb2612 | |||
| 8e2ffefd2c | |||
| 092e7b0522 | |||
| 42454ecd57 | |||
| 2563fd6031 | |||
| e0c1f2dca0 | |||
| c80574ed3f | |||
| b28b5cb6b7 | |||
| d1f11059db | |||
| 6de5c6afdf | |||
| b72c7c0c4f | |||
| 352c1ca5ec | |||
| 6d940a9ec7 | |||
| c070173346 | |||
| 6fcf68f174 | |||
| 02f2a85d28 | |||
| f63196908e | |||
| ad48ee12ca | |||
| 83aeb24a98 | |||
| fc5ecd9f10 | |||
| 0b3b840ac1 | |||
| 60c09809e7 | |||
| 148067a143 | |||
| 6f34887c94 | |||
| 64a9930c2e | |||
| 5815e16647 | |||
| c4f5e425c2 | |||
| 361ae7a2d2 | |||
| 3769402ade | |||
| 5c3c83eb52 | |||
| 0f07a89981 | |||
| 11d887a213 | |||
| 8150b2dbb0 | |||
| 89f41f23da | |||
| 6b78e08053 | |||
| 186c86d2b1 | |||
| 181c71d2dd | |||
| be05da3232 | |||
| 5958157a76 | |||
| f72b825377 | |||
| d76af5e5af | |||
| 23f48db20b | |||
| 2975bf2b55 | |||
| 5314515619 | |||
| 75b2db9f67 | |||
| cf2e39f1b2 | |||
| 6d15d69aa1 | |||
| 72a0940235 | |||
| b08dd8a7d2 | |||
| def648a98b | |||
| 32a92789b3 | |||
| 9956bb06fb | |||
| 83b9aecd7a | |||
| 17ab24eaa9 | |||
| 9c5d5f9a86 | |||
| 43a13e0877 | |||
| fe0915484d | |||
| 87a65aa38f | |||
| 1317da8904 | |||
| 62b2881e2a | |||
| bf482a2ac9 | |||
| 243600ad8f | |||
| 349caa4aed | |||
| 56bbcde299 | |||
| 7e9cc791ec | |||
| d5194b8d28 | |||
| 12b1eb349b | |||
| 68dc195ea4 | |||
| 0251b914f2 | |||
| 1fc818767c | |||
| 9aeaec477f | |||
| 7fc796d2cb | |||
| cb1cd2a4b4 | |||
| 39bd7aa0eb | |||
| 140ba64462 | |||
| 42fe96aa5a | |||
| 1a5b42026d | |||
| 9fe649aa3c | |||
| a97dfb17b2 | |||
| 277c9903bb |
@@ -6,13 +6,13 @@
|
|||||||
<a class="item" href="https://makecode.com/privacy" target="_blank" rel="noopener">Privacy & Cookies</a>
|
<a class="item" href="https://makecode.com/privacy" target="_blank" rel="noopener">Privacy & Cookies</a>
|
||||||
<a class="item" href="https://makecode.com/termsofuse" target="_blank" rel="noopener"> Terms Of Use</a>
|
<a class="item" href="https://makecode.com/termsofuse" target="_blank" rel="noopener"> Terms Of Use</a>
|
||||||
<a class="item" href="https://makecode.com/trademarks" target="_blank" rel="noopener">Trademarks</a>
|
<a class="item" href="https://makecode.com/trademarks" target="_blank" rel="noopener">Trademarks</a>
|
||||||
<div class="item">© 2018 Microsoft</div>
|
<div class="item">©2019 Microsoft</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui container horizontal small divided link list">
|
<div class="ui container horizontal small divided link list">
|
||||||
<a class="ui centered item" href="https://makecode.com/" title="Microsoft MakeCode" target="_blank" rel="noopener">Powered by Microsoft MakeCode</a>
|
<a class="ui centered item" href="https://makecode.com/" title="Microsoft MakeCode" target="_blank" rel="noopener">Powered by Microsoft MakeCode</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui centered container small list">
|
<div class="ui centered container small list">
|
||||||
<p class="item">LEGO, the LEGO logo, MINDSTORMS and the MINDSTORMS EV3 logo are trademarks and/ or copyrights of the LEGO Group. ©2018 The LEGO Group. All rights reserved.</p>
|
<p class="item">@copyrightText@</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
* [Troubleshoot](/troubleshoot)
|
* [Troubleshoot](/troubleshoot)
|
||||||
* [EV3 Manager](https://ev3manager.education.lego.com/)
|
* [EV3 Manager](https://ev3manager.education.lego.com/)
|
||||||
|
* [Bluetooth](/bluetooth)
|
||||||
|
* [Forum](https://forum.makecode.com)
|
||||||
* [LEGO Support](https://www.lego.com/service/)
|
* [LEGO Support](https://www.lego.com/service/)
|
||||||
* [FIRST LEGO League](/fll)
|
* [FIRST LEGO League](/fll)
|
||||||
|
|
||||||
@@ -19,11 +21,14 @@
|
|||||||
* [What Animal Am I?](/tutorials/what-animal-am-i)
|
* [What Animal Am I?](/tutorials/what-animal-am-i)
|
||||||
* [Music Brick](/tutorials/music-brick)
|
* [Music Brick](/tutorials/music-brick)
|
||||||
* [Run Motors](/tutorials/run-motors)
|
* [Run Motors](/tutorials/run-motors)
|
||||||
|
* [Tank ZigZag](/tutorials/tank-zigzag)
|
||||||
* [Touch to Run](/tutorials/touch-to-run)
|
* [Touch to Run](/tutorials/touch-to-run)
|
||||||
* [Touch Sensor Values](/tutorials/touch-sensor-values)
|
* [Touch Sensor Values](/tutorials/touch-sensor-values)
|
||||||
* [What Color?](/tutorials/what-color)
|
* [What Color?](/tutorials/what-color)
|
||||||
* [Line Following](/tutorials/line-following)
|
* [Line Following](/tutorials/line-following)
|
||||||
* [Red Light, Green Light](/tutorials/redlight-greenlight)
|
* [Red Light, Green Light](/tutorials/redlight-greenlight)
|
||||||
|
* [Reflected Light Measure](/tutorials/reflected-light-measure)
|
||||||
|
* [Reflected Light Calibration](/tutorials/reflected-light-calibration)
|
||||||
* [Object Near?](/tutorials/object-near)
|
* [Object Near?](/tutorials/object-near)
|
||||||
* [Security Alert](/tutorials/security-alert)
|
* [Security Alert](/tutorials/security-alert)
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ program to a **.uf2** file, which you then copy to the **@drivename@** drive. Th
|
|||||||
|
|
||||||
### ~ hint
|
### ~ hint
|
||||||
|
|
||||||
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.
|
**Experimental support** for Bluetooth download is now available. Please read the [Bluetooth](/bluetooth) page for more information.
|
||||||
|
|
||||||
### ~
|
### ~
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# 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**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -212,6 +212,12 @@ Here are some fun programs for your @boardname@!
|
|||||||
"description": "Keep your brick entertained and happy",
|
"description": "Keep your brick entertained and happy",
|
||||||
"url":"/examples/happy-unhappy",
|
"url":"/examples/happy-unhappy",
|
||||||
"cardType": "example"
|
"cardType": "example"
|
||||||
|
}, {
|
||||||
|
{
|
||||||
|
"name": "Turtle",
|
||||||
|
"description": "Encode moves and run them on a driving base",
|
||||||
|
"url":"/examples/turtle",
|
||||||
|
"cardType": "example"
|
||||||
}, {
|
}, {
|
||||||
"name": "Distance Measurer",
|
"name": "Distance Measurer",
|
||||||
"description": "Use a motor to measure angle and distance",
|
"description": "Use a motor to measure angle and distance",
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Extensions
|
||||||
|
|
||||||
|
## #gallery
|
||||||
|
|
||||||
|
## Using Extensions
|
||||||
|
|
||||||
|
In the web editor, click on ``Settings`` then ``Extensions`` to search and add extensions to the project.
|
||||||
|
The Blocks and JavaScript definitions will be automatically loaded in the editor.
|
||||||
|
|
||||||
|
## Custom extensions
|
||||||
|
|
||||||
|
The [Build Your Own Extension](https://makecode.com/extensions/getting-started) manual is for advanced users who want to publish their own extension.
|
||||||
|
|
||||||
|
## ~ hint
|
||||||
|
|
||||||
|
**Extensions** were previously called **Packages** in MakeCode.
|
||||||
|
|
||||||
|
## ~
|
||||||
@@ -2,32 +2,43 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
For teams participating in the Open Software Platform Pilot utilizing MakeCode, we’ve compiled a list of resources and information that we hope will be helpful for you.
|
**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)!
|
||||||
|
|
||||||
|
We’ve 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/
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
|
### I found a bug what do I do?
|
||||||
|
|
||||||
|
If you found a bug, please try if it hasn't been fixed yet! Go to https://makecode.mindstorms.com/beta and try if the bug is corrected. Otherwise, please tell us at https://forum.makecode.com/.
|
||||||
|
|
||||||
### How do I use MakeCode with my EV3?
|
### How do I use MakeCode with my EV3?
|
||||||
|
|
||||||
* 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 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 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 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.
|
||||||
|
|
||||||
### What’s the best way to get started with MakeCode?
|
### What’s the best way to get started with MakeCode?
|
||||||
|
|
||||||
Watch some of the videos at https://makecode.mindstorms.com (at the bottom of the page).
|
Go to https://makecode.mindstorms.com. The home screen is filled with videos, tutorials and examples that might be relevant for your missions.
|
||||||
Try some of the provided tutorials:
|
|
||||||
|
|
||||||
* [Wake Up!](@homeurl@#tutorial:tutorials/wake-up) – show your EV3 brick waking up
|
On the home page, scroll down to the **FLL / City Shaper** section for specific lessons related to Mission 2.
|
||||||
* [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?
|
### Can I load both LEGO MINDSTORMS EV3 Software and MakeCode programs onto my EV3?
|
||||||
|
|
||||||
Yes.
|
Yes.
|
||||||
|
|
||||||
|
### 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?
|
### 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.
|
You can right-click on any block and select “Help” in the context menu to open the documentation page describing what that block does.
|
||||||
@@ -81,20 +92,19 @@ 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.
|
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
|
||||||
|
|
||||||
### Why can't I delete my program (*.uf2) files from the Brick?
|
### 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. As a workaround, you can temporarily downgrade your firmware version, delete the files, and then upgrade back to the version that works with MakeCode.
|
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.
|
||||||
|
|
||||||
Follow these steps to downgrade your firmware version, delete the files, and uprgade back again:
|
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.
|
||||||
1. Go into **EV3 LabVIEW** - if it's not installed get it [here](https://education.lego.com/en-us/downloads/mindstorms-ev3/software)
|
This will present you with an menu for deleting files.
|
||||||
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.
|
For other common questions, try the FAQ page https://makecode.mindstorms.com/faq.
|
||||||
|
|
||||||
@@ -104,9 +114,3 @@ For other common questions, try the FAQ page https://makecode.mindstorms.com/faq
|
|||||||
|
|
||||||
>* Description: Unable to delete program files from the EV3 brick after downloading them
|
>* Description: Unable to delete program files from the EV3 brick after downloading them
|
||||||
>* Status: LEGO Education team is working on a fix, no estimated date yet
|
>* Status: LEGO Education team is working on a fix, no estimated date yet
|
||||||
|
|
||||||
## Community connection
|
|
||||||
|
|
||||||
For questions, issues, feedback and community for the Open Software Platform Pilot:
|
|
||||||
|
|
||||||
We are using a messaging service called **Slack**. Slack can be accessed via an app you download to your computer or mobile device, and via a web interface. For more information about Slack, click [here](https://slack.com/). Anyone in the pilot can participate by signing up with Slack first, and then clicking this [FIRST LEGO League Robot SW](https://fllrobotsw.slack.com/join/shared_invite/enQtNDgxOTQ5MDc2OTkyLTg2ZTRkYzQ4OGMyZTg1OTZmMDFhMWNlOTQ1OWRlNDdmNzNmMjlhMmZiM2M3OWUxYjU1ODEwY2FmODJkNjZkOTA) link to join the Slack workspace.
|
|
||||||
|
|||||||
@@ -25,6 +25,12 @@
|
|||||||
"description": "Build a robot and drive into the world of robotics!",
|
"description": "Build a robot and drive into the world of robotics!",
|
||||||
"url": "/getting-started/use",
|
"url": "/getting-started/use",
|
||||||
"cardType": "side"
|
"cardType": "side"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "First LEGO League",
|
||||||
|
"imageUrl": "/static/fll/fll-big.png",
|
||||||
|
"description": "Information about using MakeCode in FLL competitions",
|
||||||
|
"url": "/fll"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"appref": "v1.0.11"
|
"appref": "v1.2.22"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations);
|
|||||||
motors.largeBC.stop();
|
motors.largeBC.stop();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ~ hint
|
||||||
|
|
||||||
|
The **turn ratio range is -200, 200** unlike LabView who used -100,100.
|
||||||
|
|
||||||
|
### ~
|
||||||
|
|
||||||
## Tank
|
## 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.
|
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.
|
||||||
|
|||||||
@@ -3,11 +3,20 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>LEGO® MINDSTORMS® Education EV3 Offline App</title>
|
<title>@name@ Offline App</title>
|
||||||
<meta name="Description" content="A MakeCode for LEGO® MINDSTORMS® Education EV3 offline app" />
|
<meta name="Description" content="A MakeCode for @name@ offline app" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<!-- @include indexhead.html -->
|
<link rel="stylesheet" data-rtl="/blb/rtlsemantic.css" href="/doccdn/semantic.css" />
|
||||||
|
<link rel="stylesheet" href="/docfiles/style.css" />
|
||||||
|
<link rel="stylesheet" href="/docfiles/target.css" />
|
||||||
|
|
||||||
|
<script src="/doccdn/jquery.js" type="text/javascript"></script>
|
||||||
|
<script src="/doccdn/semantic.js" type="text/javascript"></script>
|
||||||
|
<script src="/docfiles/target.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@targetstyle@
|
||||||
|
</style>
|
||||||
<style>
|
<style>
|
||||||
p.item {
|
p.item {
|
||||||
color: rgba(0, 0, 0, 0.4);
|
color: rgba(0, 0, 0, 0.4);
|
||||||
@@ -55,6 +64,10 @@
|
|||||||
background-color: rgb(250, 250, 250);
|
background-color: rgb(250, 250, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#legal-segment {
|
||||||
|
background:white;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 800px) {
|
@media only screen and (max-width: 800px) {
|
||||||
.grid .column .image {
|
.grid .column .image {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -368,34 +381,61 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
var electronLatestVersion = "";
|
||||||
tickEvent = function (id, data) {
|
function tickEvent(id, data) {
|
||||||
if (!pxt.aiTrackEvent) return;
|
if (!pxt.aiTrackEvent) return;
|
||||||
if (!data) pxt.aiTrackEvent(id);
|
if (!data) pxt.aiTrackEvent(id);
|
||||||
else {
|
else {
|
||||||
var props = {};
|
var props = {};
|
||||||
var measures = {};
|
var measures = {};
|
||||||
for (var k in data)
|
for (var k in data)
|
||||||
if (typeof data[k] == "string") props[k] = data[k];
|
if (typeof data[k] == "string") props[k] = data[k];
|
||||||
else measures[k] = data[k];
|
else measures[k] = data[k];
|
||||||
pxt.aiTrackEvent(id, props, measures);
|
pxt.aiTrackEvent(id, props, measures);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
function agreeCheckboxChanged() {
|
function agreeCheckboxChanged() {
|
||||||
var downloadSegment = document.getElementById("download-segment");
|
showDownloads();
|
||||||
downloadSegment.classList.toggle("hidden");
|
}
|
||||||
|
|
||||||
|
function showAgree() {
|
||||||
|
$("#agree-segment").removeClass("hidden");
|
||||||
|
$("#read-segment").removeClass("hidden");
|
||||||
|
$("#legal-segment").removeClass("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNoDownloads() {
|
||||||
|
$("#no-download-segment").removeClass("hidden");
|
||||||
|
$("#read-segment").addClass("hidden");
|
||||||
|
$("#legal-segment").addClass("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDownloads() {
|
||||||
|
$("#download-win64").attr("href", "https://makecode.com/api/release/@targetid@/" + electronLatestVersion + "/win64");
|
||||||
|
$("#download-mac64").attr("href", "https://makecode.com/api/release/@targetid@/" + electronLatestVersion + "/mac64");
|
||||||
|
$("#download-segment").removeClass("hidden");
|
||||||
}
|
}
|
||||||
function downloadWin64() {
|
function downloadWin64() {
|
||||||
// TODO: Keep this link up-to-date with the desired release version
|
tickEvent("offlineapp.download", { "target": "@targetid@", "platform": "win64" });
|
||||||
window.open("https://makecode.com/api/release/ev3/v1.0.11/win64");
|
|
||||||
tickEvent("offlineapp.download", { "target": "ev3", "platform": "win64" });
|
|
||||||
}
|
}
|
||||||
function downloadMac64() {
|
function downloadMac64() {
|
||||||
// TODO: Keep this link up-to-date with the desired release version
|
tickEvent("offlineapp.download", { "target": "@targetid@", "platform": "mac64" });
|
||||||
window.open("https://makecode.com/api/release/ev3/v1.0.11/mac64");
|
|
||||||
tickEvent("offlineapp.download", { "target": "ev3", "platform": "mac64" });
|
|
||||||
}
|
}
|
||||||
|
$(function () {
|
||||||
|
$.getJSON("https://makecode.com/api/config/@targetid@/targetconfig")
|
||||||
|
.then(function (data) {
|
||||||
|
if (data && data.electronManifest && data.electronManifest.latest) {
|
||||||
|
electronLatestVersion = data.electronManifest.latest;
|
||||||
|
showAgree();
|
||||||
|
} else {
|
||||||
|
showNoDownloads();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
showNoDownloads();
|
||||||
|
})
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@@ -406,35 +446,39 @@
|
|||||||
|
|
||||||
<div class="ui grid topbar">
|
<div class="ui grid topbar">
|
||||||
<div class="three wide column">
|
<div class="three wide column">
|
||||||
<img class="ui small image left" src="/static//lego_education_logo_white.png" />
|
<img class="ui small image left" src="@cardLogo@" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ten wide column">
|
<div class="ten wide column">
|
||||||
<h1 class="ui inverted welcomeheader">MakeCode Offline App</h1>
|
<h1 class="ui inverted welcomeheader">@name@ Offline App</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="three wide column">
|
<div class="three wide column">
|
||||||
<img class="ui small image right" src="/static//Microsoft-logo_rgb_c-white.png" />
|
<img class="ui small image right" src="/static/Microsoft-logo_rgb_c-white.png" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui compact segments terms-container">
|
<div id="segments" class="ui compact segments terms-container">
|
||||||
<div class="ui secondary center aligned segment">
|
<div id="read-segment" class="ui secondary center aligned segment hidden">
|
||||||
Please read and accept the following terms to download the app.
|
Please read and accept the following terms to download the app.
|
||||||
</div>
|
</div>
|
||||||
<div class="ui left aligned segment terms">
|
<div id="legal-segment" class="ui left aligned segment terms hidden">
|
||||||
|
<div id="loader" class="ui active loader"></div>
|
||||||
<div class="c17">
|
<div class="c17">
|
||||||
<p class="c11">
|
<p class="c11">
|
||||||
<span class="c4 c1">MICROSOFT PRE-RELEASE SOFTWARE LICENSE TERMS</span>
|
<span class="c4 c1">MICROSOFT PRE-RELEASE SOFTWARE LICENSE TERMS</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c11">
|
<p class="c11">
|
||||||
<span class="c4 c1">MICROSOFT MAKECODE FOR LEGO MINDSTORMS EDUCATION EV3</span>
|
<span class="c4 c1">MICROSOFT MAKECODE SOFTWARE FOR @name@</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c7">
|
<p class="c7">
|
||||||
<span class="c4 c1"></span>
|
<span class="c4 c1"></span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c11">
|
<p class="c11">
|
||||||
<span class="c3 c1">These license terms are an agreement between Microsoft Corporation (or based on where you live, one
|
<span class="c3 c1">These license terms are an agreement between Microsoft Corporation (or based
|
||||||
of its affiliates) and you. They apply to the software named above. The terms also apply to any
|
on where you live, one
|
||||||
Microsoft services or updates for the software, except to the extent those have additional terms.</span>
|
of its affiliates) and you. They apply to the software named above. The terms also apply to
|
||||||
|
any
|
||||||
|
Microsoft services or updates for the software, except to the extent those have additional
|
||||||
|
terms.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c7">
|
<p class="c7">
|
||||||
<span class="c3 c1"></span>
|
<span class="c3 c1"></span>
|
||||||
@@ -446,27 +490,35 @@
|
|||||||
<span class="c5 c1">1.</span>
|
<span class="c5 c1">1.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">INSTALLATION AND USE RIGHTS. </span>
|
<span class="c5 c1">INSTALLATION AND USE RIGHTS. </span>
|
||||||
<span class="c3 c1">You may install and use any number of copies of the software to evaluate it as you develop and test
|
<span class="c3 c1">You may install and use any number of copies of the software to evaluate it
|
||||||
your software applications for use with Lego Mindstorms Education EV3 hardware.</span>
|
as you develop and test
|
||||||
|
your software applications for use with @name@ hardware.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">2.</span>
|
<span class="c5 c1">2.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">PRE-RELEASE SOFTWARE. </span>
|
<span class="c5 c1">PRE-RELEASE SOFTWARE. </span>
|
||||||
<span class="c3 c1">The software is a pre-release version. It may not work the way a final version of the software will.
|
<span class="c3 c1">The software is a pre-release version. It may not work the way a final
|
||||||
Microsoft may change it for the final, commercial version. We also may not release a commercial
|
version of the software will.
|
||||||
version. Microsoft is not obligated to provide maintenance, technical support or updates to you
|
Microsoft may change it for the final, commercial version. We also may not release a
|
||||||
|
commercial
|
||||||
|
version. Microsoft is not obligated to provide maintenance, technical support or updates to
|
||||||
|
you
|
||||||
for the software.</span>
|
for the software.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">3.</span>
|
<span class="c5 c1">3.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">ASSOCIATED ONLINE SERVICES.</span>
|
<span class="c5 c1">ASSOCIATED ONLINE SERVICES.</span>
|
||||||
<span class="c1"> Some features of the software provide access
|
<span class="c1"> Some features of the software
|
||||||
to, or rely on, online services to provide you information about updates to the software or extensions,
|
provide access
|
||||||
or to enable you to retrieve content, collaborate with others, or otherwise supplement your development
|
to, or rely on, online services to provide you information about updates to the software or
|
||||||
experience. As used throughout these license terms, the term <q>software</q> includes these online
|
extensions,
|
||||||
services and features. By using these online services and features you consent to the to the
|
or to enable you to retrieve content, collaborate with others, or otherwise supplement your
|
||||||
|
development
|
||||||
|
experience. As used throughout these license terms, the term <q>software</q> includes these
|
||||||
|
online
|
||||||
|
services and features. By using these online services and features you consent to the to the
|
||||||
transmission of information as described in Section 5, DATA.
|
transmission of information as described in Section 5, DATA.
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -474,9 +526,11 @@
|
|||||||
<span class="c5 c1">4.</span>
|
<span class="c5 c1">4.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">LICENSES FOR OTHER COMPONENTS.</span>
|
<span class="c5 c1">LICENSES FOR OTHER COMPONENTS.</span>
|
||||||
<span class="c3 c1"> The software may include third party components with separate legal notices or governed by
|
<span class="c3 c1"> The software may include third party components with separate legal
|
||||||
|
notices or governed by
|
||||||
other agreements, as described in the ThirdPartyNotices file accompanying the software. Even
|
other agreements, as described in the ThirdPartyNotices file accompanying the software. Even
|
||||||
if such components are governed by other agreements, the disclaimers and the limitations on and
|
if such components are governed by other agreements, the disclaimers and the limitations on
|
||||||
|
and
|
||||||
exclusions of damages below also apply.</span>
|
exclusions of damages below also apply.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
@@ -488,26 +542,35 @@
|
|||||||
<span class="c5 c1">a.</span>
|
<span class="c5 c1">a.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c1 c5">Data Collection. </span>
|
<span class="c1 c5">Data Collection. </span>
|
||||||
<span class="c1">The software may collect information about you and your use of the software, and send that to Microsoft.
|
<span class="c1">The software may collect information about you and your use of the software,
|
||||||
Microsoft may use this information to provide services and improve our products and services.
|
and send that to Microsoft.
|
||||||
You may opt out of many of these scenarios, but not all, as described in the product documentation.
|
Microsoft may use this information to provide services and improve our products and
|
||||||
In using the software, you must comply with applicable law. You can learn more about data collection
|
services.
|
||||||
|
You may opt out of many of these scenarios, but not all, as described in the product
|
||||||
|
documentation.
|
||||||
|
In using the software, you must comply with applicable law. You can learn more about data
|
||||||
|
collection
|
||||||
and use in the help documentation and the privacy statement at </span>
|
and use in the help documentation and the privacy statement at </span>
|
||||||
<span class="c14 c1">
|
<span class="c14 c1">
|
||||||
<a class="c9" href="http://go.microsoft.com/fwlink/?LinkId=398505">http://go.microsoft.com/fwlink/?LinkId=398505</a>
|
<a class="c9"
|
||||||
|
href="http://go.microsoft.com/fwlink/?LinkId=398505">http://go.microsoft.com/fwlink/?LinkId=398505</a>
|
||||||
</span>
|
</span>
|
||||||
<span class="c1">.</span>
|
<span class="c1">.</span>
|
||||||
<span class="c3 c1"> Your use of the software operates as your consent to these practices.</span>
|
<span class="c3 c1"> Your use of the software operates as your consent to these
|
||||||
|
practices.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c5 c1">b.</span>
|
<span class="c5 c1">b.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">Processing of Personal Data. </span>
|
<span class="c5 c1">Processing of Personal Data. </span>
|
||||||
<span class="c1">To the extent Microsoft is a processor or subprocessor of personal data in connection with the software,
|
<span class="c1">To the extent Microsoft is a processor or subprocessor of personal data in
|
||||||
Microsoft makes the commitments in the European Union General Data Protection Regulation Terms
|
connection with the software,
|
||||||
|
Microsoft makes the commitments in the European Union General Data Protection Regulation
|
||||||
|
Terms
|
||||||
of the Online Services Terms to all customers effective May 25, 2018, at </span>
|
of the Online Services Terms to all customers effective May 25, 2018, at </span>
|
||||||
<span class="c1 c14">
|
<span class="c1 c14">
|
||||||
<a class="c9" href="http://go.microsoft.com/?linkid=9840733">http://go.microsoft.com/?linkid=9840733</a>
|
<a class="c9"
|
||||||
|
href="http://go.microsoft.com/?linkid=9840733">http://go.microsoft.com/?linkid=9840733</a>
|
||||||
</span>
|
</span>
|
||||||
<span class="c3 c1">.</span>
|
<span class="c3 c1">.</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -515,48 +578,62 @@
|
|||||||
<span class="c5 c1">6.</span>
|
<span class="c5 c1">6.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">FEEDBACK. </span>
|
<span class="c5 c1">FEEDBACK. </span>
|
||||||
<span class="c3 c1">If you give feedback about the software to Microsoft, you give to Microsoft, without charge, the
|
<span class="c3 c1">If you give feedback about the software to Microsoft, you give to Microsoft,
|
||||||
|
without charge, the
|
||||||
right to use, share and commercialize your feedback in any way and for any purpose. You will
|
right to use, share and commercialize your feedback in any way and for any purpose. You will
|
||||||
not give feedback that is subject to a license that requires Microsoft to license its software
|
not give feedback that is subject to a license that requires Microsoft to license its
|
||||||
or documentation to third parties because we include your feedback in them. These rights survive
|
software
|
||||||
|
or documentation to third parties because we include your feedback in them. These rights
|
||||||
|
survive
|
||||||
this agreement.</span>
|
this agreement.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">7.</span>
|
<span class="c5 c1">7.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">SCOPE OF LICENSE.</span>
|
<span class="c5 c1">SCOPE OF LICENSE.</span>
|
||||||
<span class="c3 c1"> The software is licensed, not sold. This agreement only gives you some rights to use the software.
|
<span class="c3 c1"> The software is licensed, not sold. This agreement only gives you some
|
||||||
Microsoft reserves all other rights. Unless applicable law gives you more rights despite this
|
rights to use the software.
|
||||||
|
Microsoft reserves all other rights. Unless applicable law gives you more rights despite
|
||||||
|
this
|
||||||
limitation, you may use the software only as expressly permitted in this agreement. In
|
limitation, you may use the software only as expressly permitted in this agreement. In
|
||||||
doing so, you must comply with any technical limitations in the software that only allow you
|
doing so, you must comply with any technical limitations in the software that only allow you
|
||||||
to use it in certain ways. You may not:</span>
|
to use it in certain ways. You may not:</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c3 c1">- work around any technical limitations in the software;</span>
|
<span class="c3 c1">- work around any technical limitations in the
|
||||||
|
software;</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c3 c1">- reverse engineer, decompile or disassemble the software, or attempt to do so, except
|
<span class="c3 c1">- reverse engineer, decompile or disassemble the software, or
|
||||||
|
attempt to do so, except
|
||||||
and only to the extent required by third party licensing terms governing use of certain open
|
and only to the extent required by third party licensing terms governing use of certain open
|
||||||
source components that may be included with the software;</span>
|
source components that may be included with the software;</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c3 c1">- remove, minimize, block or modify any notices of Microsoft or its suppliers in the
|
<span class="c3 c1">- remove, minimize, block or modify any notices of Microsoft
|
||||||
|
or its suppliers in the
|
||||||
software;
|
software;
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c3 c1">- use the software in any way that is against the law; or</span>
|
<span class="c3 c1">- use the software in any way that is against the law;
|
||||||
|
or</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c3 c1">- share, publish, rent or lease the software, or provide the software as a stand-alone
|
<span class="c3 c1">- share, publish, rent or lease the software, or provide the
|
||||||
|
software as a stand-alone
|
||||||
offering for others to use.</span>
|
offering for others to use.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">8. UPDATES. </span>
|
<span class="c5 c1">8. UPDATES. </span>
|
||||||
<span class="c3 c1">The software may periodically check for updates and download and install them for you. You may obtain
|
<span class="c3 c1">The software may periodically check for updates and download and install
|
||||||
updates only from Microsoft or authorized sources. Microsoft may need to update your system to
|
them for you. You may obtain
|
||||||
provide you with updates. You agree to receive these automatic updates without any additional
|
updates only from Microsoft or authorized sources. Microsoft may need to update your system
|
||||||
notice. Updates may not include or support all existing software features, services, or peripheral
|
to
|
||||||
|
provide you with updates. You agree to receive these automatic updates without any
|
||||||
|
additional
|
||||||
|
notice. Updates may not include or support all existing software features, services, or
|
||||||
|
peripheral
|
||||||
devices.
|
devices.
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -564,57 +641,74 @@
|
|||||||
<span class="c5 c1">9.</span>
|
<span class="c5 c1">9.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">EXPORT RESTRICTIONS.</span>
|
<span class="c5 c1">EXPORT RESTRICTIONS.</span>
|
||||||
<span class="c3 c1"> You must comply with all domestic and international export laws and regulations that apply
|
<span class="c3 c1"> You must comply with all domestic and international export laws and
|
||||||
to the software, which include restrictions on destinations, end users and end use. For further
|
regulations that apply
|
||||||
|
to the software, which include restrictions on destinations, end users and end use. For
|
||||||
|
further
|
||||||
information on export restrictions, visit (aka.ms/exporting).</span>
|
information on export restrictions, visit (aka.ms/exporting).</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">10.</span>
|
<span class="c5 c1">10.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">SUPPORT SERVICES. </span>
|
<span class="c5 c1">SUPPORT SERVICES. </span>
|
||||||
<span class="c3 c1">Because the software is “as is,” we may not provide support services for it.</span>
|
<span class="c3 c1">Because the software is “as is,” we may not provide support
|
||||||
|
services for it.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">11.</span>
|
<span class="c5 c1">11.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">ENTIRE AGREEMENT.</span>
|
<span class="c5 c1">ENTIRE AGREEMENT.</span>
|
||||||
<span class="c3 c1"> This agreement, and the terms for supplements, updates, Internet-based services and support
|
<span class="c3 c1"> This agreement, and the terms for supplements, updates, Internet-based
|
||||||
services that you use, are the entire agreement for the software and support services.</span>
|
services and support
|
||||||
|
services that you use, are the entire agreement for the software and support
|
||||||
|
services.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">12.</span>
|
<span class="c5 c1">12.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">APPLICABLE LAW. </span>
|
<span class="c5 c1">APPLICABLE LAW. </span>
|
||||||
<span class="c3 c1">If you acquired the software in the United States, Washington State law applies to interpretation
|
<span class="c3 c1">If you acquired the software in the United States, Washington State law
|
||||||
of and claims for breach of this agreement, and the laws of the state where you live apply to
|
applies to interpretation
|
||||||
|
of and claims for breach of this agreement, and the laws of the state where you live apply
|
||||||
|
to
|
||||||
all other claims. If you acquired the software in any other country, its laws apply.</span>
|
all other claims. If you acquired the software in any other country, its laws apply.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">13.</span>
|
<span class="c5 c1">13.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">CONSUMER RIGHTS; REGIONAL VARIATIONS. </span>
|
<span class="c5 c1">CONSUMER RIGHTS; REGIONAL VARIATIONS. </span>
|
||||||
<span class="c3 c1">This agreement describes certain legal rights. You may have other rights, including consumer rights,
|
<span class="c3 c1">This agreement describes certain legal rights. You may have other rights,
|
||||||
under the laws of your state or country. Separate and apart from your relationship with Microsoft,
|
including consumer rights,
|
||||||
you may also have rights with respect to the party from which you acquired the software. This
|
under the laws of your state or country. Separate and apart from your relationship with
|
||||||
agreement does not change those other rights if the laws of your state or country do not permit
|
Microsoft,
|
||||||
it to do so. For example, if you acquired the software in one of the below regions, or mandatory
|
you may also have rights with respect to the party from which you acquired the software.
|
||||||
|
This
|
||||||
|
agreement does not change those other rights if the laws of your state or country do not
|
||||||
|
permit
|
||||||
|
it to do so. For example, if you acquired the software in one of the below regions, or
|
||||||
|
mandatory
|
||||||
country law applies, then the following provisions apply to you:</span>
|
country law applies, then the following provisions apply to you:</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c5 c1">a.</span>
|
<span class="c5 c1">a.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">Australia. </span>
|
<span class="c5 c1">Australia. </span>
|
||||||
<span class="c3 c1">You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is
|
<span class="c3 c1">You have statutory guarantees under the Australian Consumer Law and nothing
|
||||||
|
in this agreement is
|
||||||
intended to affect those rights.</span>
|
intended to affect those rights.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
<span class="c5 c1">b.</span>
|
<span class="c5 c1">b.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">Canada. </span>
|
<span class="c5 c1">Canada. </span>
|
||||||
<span class="c3 c1">If you acquired the software in Canada, you may stop receiving updates by turning off the automatic
|
<span class="c3 c1">If you acquired the software in Canada, you may stop receiving updates by
|
||||||
update feature, disconnecting your device from the Internet (if and when you re-connect to the
|
turning off the automatic
|
||||||
Internet, however, the software will resume checking for and installing updates), or uninstalling
|
update feature, disconnecting your device from the Internet (if and when you re-connect to
|
||||||
the software. The product documentation, if any, may also specify how to turn off updates for
|
the
|
||||||
|
Internet, however, the software will resume checking for and installing updates), or
|
||||||
|
uninstalling
|
||||||
|
the software. The product documentation, if any, may also specify how to turn off updates
|
||||||
|
for
|
||||||
your specific device or software.</span>
|
your specific device or software.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c8">
|
<p class="c8">
|
||||||
@@ -627,8 +721,10 @@
|
|||||||
<span class="c5 c1">(i)</span>
|
<span class="c5 c1">(i)</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">Warranty</span>
|
<span class="c5 c1">Warranty</span>
|
||||||
<span class="c3 c1">. The properly licensed software will perform substantially as described in any Microsoft materials
|
<span class="c3 c1">. The properly licensed software will perform substantially as described in
|
||||||
that accompany the software. However, Microsoft gives no contractual guarantee in relation to
|
any Microsoft materials
|
||||||
|
that accompany the software. However, Microsoft gives no contractual guarantee in relation
|
||||||
|
to
|
||||||
the licensed software.</span>
|
the licensed software.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c6">
|
<p class="c6">
|
||||||
@@ -638,74 +734,103 @@
|
|||||||
<span class="c5 c1">(ii)</span>
|
<span class="c5 c1">(ii)</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">Limitation of Liability</span>
|
<span class="c5 c1">Limitation of Liability</span>
|
||||||
<span class="c3 c1">. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as
|
<span class="c3 c1">. In case of intentional conduct, gross negligence, claims based on the
|
||||||
well as, in case of death or personal or physical injury, Microsoft is liable according to the
|
Product Liability Act, as
|
||||||
|
well as, in case of death or personal or physical injury, Microsoft is liable according to
|
||||||
|
the
|
||||||
statutory law.</span>
|
statutory law.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c10">
|
<p class="c10">
|
||||||
<span class="c3 c1">Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft
|
<span class="c3 c1">Subject to the foregoing clause (ii), Microsoft will only be liable for
|
||||||
is in breach of such material contractual obligations, the fulfillment of which facilitate the
|
slight negligence if Microsoft
|
||||||
due performance of this agreement, the breach of which would endanger the purpose of this agreement
|
is in breach of such material contractual obligations, the fulfillment of which facilitate
|
||||||
and the compliance with which a party may constantly trust in (so-called "cardinal obligations").
|
the
|
||||||
In other cases of slight negligence, Microsoft will not be liable for slight negligence.</span>
|
due performance of this agreement, the breach of which would endanger the purpose of this
|
||||||
|
agreement
|
||||||
|
and the compliance with which a party may constantly trust in (so-called "cardinal
|
||||||
|
obligations").
|
||||||
|
In other cases of slight negligence, Microsoft will not be liable for slight
|
||||||
|
negligence.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">14.</span>
|
<span class="c5 c1">14.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c5 c1">LEGAL EFFECT.</span>
|
<span class="c5 c1">LEGAL EFFECT.</span>
|
||||||
<span class="c3 c1"> This agreement describes certain legal rights. You may have other rights under the laws of
|
<span class="c3 c1"> This agreement describes certain legal rights. You may have other
|
||||||
your country. You may also have rights with respect to the party from whom you acquired the software.
|
rights under the laws of
|
||||||
This agreement does not change your rights under the laws of your country if the laws of your
|
your country. You may also have rights with respect to the party from whom you acquired the
|
||||||
|
software.
|
||||||
|
This agreement does not change your rights under the laws of your country if the laws of
|
||||||
|
your
|
||||||
country do not permit it to do so.</span>
|
country do not permit it to do so.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">15.</span>
|
<span class="c5 c1">15.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c4 c1">DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.” YOU BEAR THE RISK OF
|
<span class="c4 c1">DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.”
|
||||||
USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED
|
YOU BEAR THE RISK OF
|
||||||
|
USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT
|
||||||
|
PERMITTED
|
||||||
UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.</span>
|
FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c5 c1">16.</span>
|
<span class="c5 c1">16.</span>
|
||||||
<span class="c1"> </span>
|
<span class="c1"> </span>
|
||||||
<span class="c4 c1">LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT
|
<span class="c4 c1">LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND
|
||||||
DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST
|
ITS SUPPLIERS ONLY DIRECT
|
||||||
|
DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL,
|
||||||
|
LOST
|
||||||
PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.</span>
|
PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c0">
|
<p class="c0">
|
||||||
<span class="c3 c1">This limitation applies to (a) anything related to the software, services, content (including code)
|
<span class="c3 c1">This limitation applies to (a) anything related to the software, services,
|
||||||
on third party Internet sites, or third party programs; and (b) claims for breach of contract,
|
content (including code)
|
||||||
breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the
|
on third party Internet sites, or third party programs; and (b) claims for breach of
|
||||||
|
contract,
|
||||||
|
breach of warranty, guarantee or condition, strict liability, negligence, or other tort to
|
||||||
|
the
|
||||||
extent permitted by applicable law.</span>
|
extent permitted by applicable law.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c0">
|
<p class="c0">
|
||||||
<span class="c3 c1">It also applies even if Microsoft knew or should have known about the possibility of the damages.
|
<span class="c3 c1">It also applies even if Microsoft knew or should have known about the
|
||||||
The above limitation or exclusion may not apply to you because your country may not allow the
|
possibility of the damages.
|
||||||
|
The above limitation or exclusion may not apply to you because your country may not allow
|
||||||
|
the
|
||||||
exclusion or limitation of incidental, consequential or other damages.</span>
|
exclusion or limitation of incidental, consequential or other damages.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c12">
|
<p class="c12">
|
||||||
<span class="c4 c1">Please note: As the software is distributed in Quebec, Canada, some of the clauses in this agreement
|
<span class="c4 c1">Please note: As the software is distributed in Quebec, Canada, some of the
|
||||||
|
clauses in this agreement
|
||||||
are provided below in French.</span>
|
are provided below in French.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c12">
|
<p class="c12">
|
||||||
<span class="c4 c1">Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses
|
<span class="c4 c1">Remarque : Ce logiciel étant distribué au Québec,
|
||||||
|
Canada, certaines des clauses
|
||||||
dans ce contrat sont fournies ci-dessous en français.</span>
|
dans ce contrat sont fournies ci-dessous en français.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c11">
|
<p class="c11">
|
||||||
<span class="c5 c1">EXONÉRATION DE GARANTIE.</span>
|
<span class="c5 c1">EXONÉRATION DE GARANTIE.</span>
|
||||||
<span class="c1 c3"> Le logiciel visé par une licence est offert « tel quel ». Toute utilisation
|
<span class="c1 c3"> Le logiciel visé par une licence est offert « tel quel
|
||||||
de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune
|
». Toute utilisation
|
||||||
autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu
|
de ce logiciel est à votre seule risque et péril. Microsoft n’accorde
|
||||||
du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles
|
aucune
|
||||||
sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation
|
autre garantie expresse. Vous pouvez bénéficier de droits additionnels en
|
||||||
|
vertu
|
||||||
|
du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou
|
||||||
|
elles
|
||||||
|
sont permises par le droit locale, les garanties implicites de qualité marchande,
|
||||||
|
d’adéquation
|
||||||
à un usage particulier et d’absence de contrefaçon sont exclues.
|
à un usage particulier et d’absence de contrefaçon sont exclues.
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c11">
|
<p class="c11">
|
||||||
<span class="c5 c1">LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES.</span>
|
<span class="c5 c1">LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE
|
||||||
<span class="c3 c1"> Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages
|
RESPONSABILITÉ POUR LES DOMMAGES.</span>
|
||||||
directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune
|
<span class="c3 c1"> Vous pouvez obtenir de Microsoft et de ses fournisseurs une
|
||||||
|
indemnisation en cas de dommages
|
||||||
|
directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à
|
||||||
|
aucune
|
||||||
indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou
|
indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou
|
||||||
accessoires et pertes de bénéfices.</span>
|
accessoires et pertes de bénéfices.</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -713,26 +838,36 @@
|
|||||||
<span class="c3 c1">Cette limitation concerne :</span>
|
<span class="c3 c1">Cette limitation concerne :</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c3 c1">- tout ce qui est relié au logiciel, aux services ou au contenu
|
<span class="c3 c1">- tout ce qui est relié au logiciel, aux
|
||||||
(y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et</span>
|
services ou au contenu
|
||||||
|
(y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ;
|
||||||
|
et</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c2">
|
<p class="c2">
|
||||||
<span class="c3 c1">- les réclamations au titre de violation de contrat ou de garantie,
|
<span class="c3 c1">- les réclamations au titre de violation
|
||||||
ou au titre de responsabilité stricte, de négligence ou d’une autre faute
|
de contrat ou de garantie,
|
||||||
|
ou au titre de responsabilité stricte, de négligence ou d’une autre
|
||||||
|
faute
|
||||||
dans la limite autorisée par la loi en vigueur.</span>
|
dans la limite autorisée par la loi en vigueur.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c12">
|
<p class="c12">
|
||||||
<span class="c3 c1">Elle s’applique également, même si Microsoft connaissait ou devrait connaître
|
<span class="c3 c1">Elle s’applique également, même si Microsoft connaissait
|
||||||
|
ou devrait connaître
|
||||||
l’éventualité d’un tel dommage. Si votre pays n’autorise pas
|
l’éventualité d’un tel dommage. Si votre pays n’autorise pas
|
||||||
l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires
|
l’exclusion ou la limitation de responsabilité pour les dommages indirects,
|
||||||
ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus
|
accessoires
|
||||||
|
ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion
|
||||||
|
ci-dessus
|
||||||
ne s’appliquera pas à votre égard.</span>
|
ne s’appliquera pas à votre égard.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c16">
|
<p class="c16">
|
||||||
<span class="c5 c1">EFFET JURIDIQUE.</span>
|
<span class="c5 c1">EFFET JURIDIQUE.</span>
|
||||||
<span class="c3 c1"> Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres
|
<span class="c3 c1"> Le présent contrat décrit certains droits juridiques.
|
||||||
droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les
|
Vous pourriez avoir d’autres
|
||||||
droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.</span>
|
droits prévus par les lois de votre pays. Le présent contrat ne modifie pas
|
||||||
|
les
|
||||||
|
droits que vous confèrent les lois de votre pays si celles-ci ne le permettent
|
||||||
|
pas.</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="c15">
|
<p class="c15">
|
||||||
<span class="c3 c1"></span>
|
<span class="c3 c1"></span>
|
||||||
@@ -740,9 +875,9 @@
|
|||||||
<p class="c16">
|
<p class="c16">
|
||||||
<span class="c3 c1"> </span>
|
<span class="c3 c1"> </span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="c11">
|
<p class="c11">
|
||||||
<span class="c3 c1">LEGO, the LEGO logo, MINDSTORMS and the MINDSTORMS EV3 logo are trademarks and/ or copyrights of
|
<span class="c3 c1">@copyrightText@</span>
|
||||||
the LEGO Group. ©2018 The LEGO Group. All rights reserved.</span>
|
|
||||||
</p>
|
</p>
|
||||||
<p class="c11">
|
<p class="c11">
|
||||||
<span class="c3 c1"> </span>
|
<span class="c3 c1"> </span>
|
||||||
@@ -755,27 +890,30 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui center aligned segment">
|
<div id="agree-segment" class="ui center aligned segment hidden">
|
||||||
<input id="agree-checkbox" type="checkbox" autocomplete="off" onchange="agreeCheckboxChanged(this)">
|
<input id="agree-checkbox" type="checkbox" autocomplete="off" onchange="agreeCheckboxChanged(this)">
|
||||||
<label for="agree-checkbox">I agree to these Microsoft Software License Terms and to the
|
<label for="agree-checkbox">I agree to these Microsoft Software License Terms and to the
|
||||||
<a href="https://privacy.microsoft.com/en-us/privacystatement">Microsoft Privacy Statement.</a>
|
<a href="https://privacy.microsoft.com/en-us/privacystatement">Microsoft Privacy Statement.</a>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="no-download-segment" class="ui center aligned segment hidden">
|
||||||
|
<p>Sorry, there is no Offline App available for this editor.</p>
|
||||||
|
</div>
|
||||||
<div id="download-segment" class="ui center aligned segment hidden">
|
<div id="download-segment" class="ui center aligned segment hidden">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<h3 class="ui">Windows</h3>
|
<h3 class="ui">Windows</h3>
|
||||||
<button class="ui icon button" onclick="downloadWin64()">
|
<a id="download-win64" class="ui icon button" onclick="downloadWin64()">
|
||||||
<i class="download icon"></i>
|
<i class="download icon"></i>
|
||||||
makecode-ev3-setup-win64.exe
|
makecode-@targetid@-setup-win64.exe
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<h3 class="ui">Mac OS</h3>
|
<h3 class="ui">Mac OS</h3>
|
||||||
<button class="ui icon button" onclick="downloadMac64()">
|
<a id="download-mac64" class="ui icon button" onclick="downloadMac64()">
|
||||||
<i class="download icon"></i>
|
<i class="download icon"></i>
|
||||||
makecode-ev3-mac64.zip
|
makecode-@targetid@-mac64.zip
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -788,4 +926,4 @@
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -3,9 +3,3 @@
|
|||||||
## Offline app #target-app
|
## 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.
|
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.
|
|
||||||
|
|
||||||
### ~
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"redirect": "/extensions"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"redirect": "https://makecode.com/extensions/approval"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"redirect": "https://makecode.com/extensions/getting-started"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"redirect": "https://makecode.com/extensions/versioning"
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# 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)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -10,12 +10,12 @@ You can find out what's connected to the ports on the brick and show its status.
|
|||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Show the status of the ports on the brick when the ``enter`` button is pressed.
|
Show the status of the ports on the brick. Resets all motors when ENTER is pressed.
|
||||||
|
|
||||||
```blocks
|
```blocks
|
||||||
brick.showString("Press ENTER for port status", 1)
|
brick.showPorts()
|
||||||
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
|
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
|
||||||
brick.showPorts()
|
motors.resetAll()
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# 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)
|
||||||
@@ -6,9 +6,9 @@ Set the rotation speed of the motor as a percentage of maximum speed.
|
|||||||
motors.largeA.run(50)
|
motors.largeA.run(50)
|
||||||
```
|
```
|
||||||
|
|
||||||
The speed setting is a pecentage 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.
|
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.
|
||||||
|
|
||||||
If you use just the **speed** number, the motor runs continously and won't stop unless you tell it to. You can also give a value for a certain amount of distance you want the motor to rotate for. The **value** can be an amount of time, a turn angle in degrees, or a number of full rotations.
|
If you use just the **speed** number, the motor runs continuously and won't stop unless you tell it to. You can also give a value for a certain amount of distance you want the motor to rotate for. The **value** can be an amount of time, a turn angle in degrees, or a number of full rotations.
|
||||||
|
|
||||||
If you decide to use a **value** of rotation distance, you need to choose a type of movement **unit**.
|
If you decide to use a **value** of rotation distance, you need to choose a type of movement **unit**.
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@ Here is how you use each different movement unit to run the motor for a fixed ro
|
|||||||
// Run motor for 700 Milliseconds.
|
// Run motor for 700 Milliseconds.
|
||||||
motors.largeA.run(25, 700, MoveUnit.MilliSeconds);
|
motors.largeA.run(25, 700, MoveUnit.MilliSeconds);
|
||||||
|
|
||||||
// Run motor for 700 Milliseconds again but no units specified.
|
// Run motors B and C for 700 Milliseconds again but no units specified.
|
||||||
motors.largeA.run(25, 700);
|
motors.largeBC.run(25, 700);
|
||||||
|
|
||||||
// Run the motor for 45 seconds
|
// Run the motor for 45 seconds
|
||||||
motors.largeA.run(50, 45, MoveUnit.Seconds);
|
motors.largeA.run(50, 45, MoveUnit.Seconds);
|
||||||
@@ -61,6 +61,14 @@ motors.largeB.run(-25)
|
|||||||
|
|
||||||
## ~
|
## ~
|
||||||
|
|
||||||
|
## Multiple motors
|
||||||
|
|
||||||
|
When using **run** with multiple motors, there is no guarantee that their speed will stay in sync. Use [tank](/reference/motors/tank) or [steer](/reference/motors/steer) for synchronized motor operations.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
motors.largeBC.run(50)
|
||||||
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Drive the motor for 20 seconds
|
### Drive the motor for 20 seconds
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# 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)
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
# set Brake
|
# set Brake
|
||||||
|
|
||||||
Set the brake on the motor so it won't turn when it has no power.
|
Set the brake on the motor so it will brake when it finishes a brake command.
|
||||||
|
|
||||||
```sig
|
```sig
|
||||||
motors.largeA.setBrake(false)
|
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 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.
|
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.
|
||||||
|
|
||||||
Also, you can use the brake to do simple skid steering for your brick.
|
Also, you can use the brake to do simple skid steering for your brick.
|
||||||
|
|
||||||
## Paramters
|
## Parameters
|
||||||
|
|
||||||
* **brake**: a [boolean](/types/boolean) value which is either `true` to set the brake on or `false` to set the brake off.
|
* **brake**: a [boolean](/types/boolean) value which is either `true` to set the brake on or `false` to set the brake off.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Run the motor connected to port **A** for 2 seconds at a speed of `30`. Stop and set the brake.
|
Run the motor connected to port **A** for 2 seconds at a speed of `30` and stop after 2s.
|
||||||
|
|
||||||
```blocks
|
```blocks
|
||||||
|
motors.largeA.setBrake(true)
|
||||||
motors.largeA.run(30)
|
motors.largeA.run(30)
|
||||||
pause(2000)
|
pause(2000)
|
||||||
motors.largeA.stop()
|
motors.largeA.stop()
|
||||||
motors.largeA.setBrake(true)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# 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)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# 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)
|
||||||
@@ -22,4 +22,5 @@ motors.stopAll();
|
|||||||
|
|
||||||
[stop](/reference/motors/motor/stop),
|
[stop](/reference/motors/motor/stop),
|
||||||
[reset](/reference/motors/motor/reset),
|
[reset](/reference/motors/motor/reset),
|
||||||
|
[reset-all](/reference/motors/motor/reset-all),
|
||||||
[set brake](/reference/motors/motor/set-brake)
|
[set brake](/reference/motors/motor/set-brake)
|
||||||
@@ -22,7 +22,7 @@ If you decide to use a **value** of rotation distance, you need to choose a type
|
|||||||
|
|
||||||
## Parameters
|
## 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 `-100` and `100`.
|
* **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`.
|
||||||
* **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.
|
* **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.
|
* **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.
|
* **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.
|
||||||
@@ -86,6 +86,51 @@ for (let i = 0; i < 4; i++) {
|
|||||||
motors.stopAll()
|
motors.stopAll()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Steer tester
|
||||||
|
|
||||||
|
This program lets you change the values of speed and turn ratio with the buttons.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
let speed = 0;
|
||||||
|
let turnRatio = 0;
|
||||||
|
|
||||||
|
brick.showString(`steer tester`, 1)
|
||||||
|
brick.showString(`connect motors BC`, 7)
|
||||||
|
brick.showString(`up/down for speed`, 8)
|
||||||
|
brick.showString(`left/right for turn ratio`, 9)
|
||||||
|
|
||||||
|
forever(function () {
|
||||||
|
brick.showString(`motor B speed ${motors.largeB.speed()}%`, 4)
|
||||||
|
brick.showString(`motor C speed ${motors.largeC.speed()}%`, 5)
|
||||||
|
pause(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
function updateSteer() {
|
||||||
|
motors.largeBC.steer(turnRatio, speed);
|
||||||
|
brick.showString(`speed ${speed}%`, 2)
|
||||||
|
brick.showString(`turnRatio ${turnRatio}`, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
speed += 10
|
||||||
|
updateSteer()
|
||||||
|
})
|
||||||
|
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
speed -= 10
|
||||||
|
updateSteer()
|
||||||
|
})
|
||||||
|
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
turnRatio -= 10
|
||||||
|
updateSteer()
|
||||||
|
})
|
||||||
|
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
turnRatio += 10
|
||||||
|
updateSteer()
|
||||||
|
})
|
||||||
|
|
||||||
|
updateSteer()
|
||||||
|
```
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
[tank](/reference/motors/synced/tank), [run](/reference/motors/motor/run)
|
[tank](/reference/motors/synced/tank), [run](/reference/motors/motor/run)
|
||||||
@@ -82,6 +82,51 @@ pause(5000)
|
|||||||
motors.stopAll()
|
motors.stopAll()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Tank tester
|
||||||
|
|
||||||
|
This program lets you change the tank values using the brick buttons.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
let tankB = 0;
|
||||||
|
let tankC = 0;
|
||||||
|
|
||||||
|
brick.showString(`tank tester`, 1)
|
||||||
|
brick.showString(`connect motors BC`, 7)
|
||||||
|
brick.showString(`up/down for tank B`, 8)
|
||||||
|
brick.showString(`left/right for tank C`, 9)
|
||||||
|
|
||||||
|
forever(function () {
|
||||||
|
brick.showString(`motor B speed ${motors.largeB.speed()}%`, 4)
|
||||||
|
brick.showString(`motor C speed ${motors.largeC.speed()}%`, 5)
|
||||||
|
pause(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
function updateTank() {
|
||||||
|
brick.showString(`tank A: ${tankB}%`, 2)
|
||||||
|
brick.showString(`tank B: ${tankC}%`, 3)
|
||||||
|
motors.largeBC.tank(tankB, tankC);
|
||||||
|
}
|
||||||
|
|
||||||
|
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
tankB += 10
|
||||||
|
updateTank();
|
||||||
|
})
|
||||||
|
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
tankB -= 10
|
||||||
|
updateTank();
|
||||||
|
})
|
||||||
|
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
tankC += 10
|
||||||
|
updateTank();
|
||||||
|
})
|
||||||
|
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
tankC -= 10
|
||||||
|
updateTank();
|
||||||
|
})
|
||||||
|
|
||||||
|
updateTank();
|
||||||
|
```
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
[steer](/reference/motors/synced/steer), [run](/reference/motors/motor/run)
|
[steer](/reference/motors/synced/steer), [run](/reference/motors/motor/run)
|
||||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -2,113 +2,51 @@
|
|||||||
|
|
||||||
Step by step guides to coding your @boardname@.
|
Step by step guides to coding your @boardname@.
|
||||||
|
|
||||||
## Brick
|
## Tutorials
|
||||||
|
|
||||||
```codecard
|
```codecard
|
||||||
[{
|
[{
|
||||||
"name": "Wake Up!",
|
"name": "Brick",
|
||||||
"description": "Show different moods on the screen. Is it tired, sleepy, or awake?",
|
"description": "Learn how to use the screen and the buttons",
|
||||||
"cardType": "tutorial",
|
"url":"/tutorials/brick",
|
||||||
"url":"/tutorials/wake-up",
|
|
||||||
"imageUrl":"/static/tutorials/wake-up.png"
|
"imageUrl":"/static/tutorials/wake-up.png"
|
||||||
}, {
|
}, {
|
||||||
"name": "Make an Animation",
|
"name": "Motors",
|
||||||
"description": "Create a custom animation on your EV3 Brick Display.",
|
"description": "User motors to move the brick.",
|
||||||
"cardType": "tutorial",
|
"url":"/tutorials/motors",
|
||||||
"url":"/tutorials/make-an-animation",
|
|
||||||
"imageUrl":"/static/tutorials/make-an-animation.png"
|
|
||||||
}, {
|
|
||||||
"name": "What Animal Am I?",
|
|
||||||
"description": "Create different animal effects and have someone guess what the animal is.",
|
|
||||||
"cardType": "tutorial",
|
|
||||||
"url":"/tutorials/what-animal-am-i",
|
|
||||||
"imageUrl":"/static/tutorials/what-animal-am-i.png"
|
|
||||||
}, {
|
|
||||||
"name": "Music Brick",
|
|
||||||
"description": "Transform the brick into a musical instrument!",
|
|
||||||
"cardType": "tutorial",
|
|
||||||
"url":"/tutorials/music-brick",
|
|
||||||
"imageUrl":"/static/tutorials/music-brick.png"
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Motors
|
|
||||||
|
|
||||||
```codecard
|
|
||||||
[{
|
|
||||||
"name": "Run Motors",
|
|
||||||
"description": "Use the EV3 Brick buttons to start and stop the Large Motor and Medium Motor.",
|
|
||||||
"cardType": "tutorial",
|
|
||||||
"url":"/tutorials/run-motors",
|
|
||||||
"imageUrl":"/static/tutorials/run-motors.png"
|
"imageUrl":"/static/tutorials/run-motors.png"
|
||||||
}]
|
}, {
|
||||||
```
|
"name": "Touch Sensor",
|
||||||
|
"description": "Use touch sensors in your robot.",
|
||||||
## Touch Sensor
|
"url":"/tutorials/touch-sensor",
|
||||||
|
|
||||||
```codecard
|
|
||||||
[{
|
|
||||||
"name": "Touch to Run",
|
|
||||||
"description": "Press the Touch Sensor and run a motor.",
|
|
||||||
"cardType": "tutorial",
|
|
||||||
"url":"/tutorials/touch-to-run",
|
|
||||||
"imageUrl":"/static/tutorials/touch-to-run.png"
|
"imageUrl":"/static/tutorials/touch-to-run.png"
|
||||||
}, {
|
}, {
|
||||||
"name": "Touch Sensor Values",
|
"name": "Color Sensor",
|
||||||
"description": "Check the value of a Touch Sensor and stop a motor if pressed.",
|
"description": "Use the color sensor to follow line or detect colors",
|
||||||
"cardType": "tutorial",
|
"url":"/tutorials/color-sensor",
|
||||||
"url":"/tutorials/touch-sensor-values",
|
|
||||||
"imageUrl":"/static/tutorials/touch-sensor-values.png"
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Color Sensor
|
|
||||||
|
|
||||||
```codecard
|
|
||||||
[{
|
|
||||||
"name": "What Color?",
|
|
||||||
"description": "Use the Color Sensor to detect different colors.",
|
|
||||||
"cardType": "tutorial",
|
|
||||||
"url":"/tutorials/what-color",
|
|
||||||
"imageUrl":"/static/tutorials/what-color.png"
|
"imageUrl":"/static/tutorials/what-color.png"
|
||||||
}, {
|
}, {
|
||||||
"name": "Line Following",
|
"name": "Gyro",
|
||||||
"description": "Use the Color Sensor to make a robot follow a line.",
|
"description": "Drive straight or turn more precisely with the gyro",
|
||||||
"cardType": "tutorial",
|
"url":"/tutorials/gyro",
|
||||||
"url":"/tutorials/line-following",
|
"imageUrl":"/static/tutorials/calibrate-gyro.png"
|
||||||
"imageUrl":"/static/tutorials/line-following.png"
|
|
||||||
}, {
|
}, {
|
||||||
"name": "Red Light, Green Light",
|
"name": "Ultrasonic Sensor",
|
||||||
"description": "Play Red Light, Green Light using the Color Sensor and the robot.",
|
"description": "Use the ultrasonic sensor to detect obstacles",
|
||||||
"cardType": "tutorial",
|
"url":"/tutorials/ultrasonic-sensor",
|
||||||
"url":"/tutorials/redlight-greenlight",
|
"imageUrl":"/static/tutorials/object-near.png"
|
||||||
"imageUrl":"/static/tutorials/redlight-greenlight.png"
|
}, {
|
||||||
}]
|
"name": "Infrared Sensor",
|
||||||
```
|
"description": "Use the infrared sensor to detect objects",
|
||||||
|
"url":"/tutorials/infrared-sensor",
|
||||||
## Infrared Sensor
|
|
||||||
|
|
||||||
```codecard
|
|
||||||
[{
|
|
||||||
"name": "Security Alert",
|
|
||||||
"description": "Build an security alert using the Infrared Sensor.",
|
|
||||||
"cardType": "tutorial",
|
|
||||||
"url":"/tutorials/security-alert",
|
|
||||||
"imageUrl":"/static/tutorials/security-alert.png"
|
"imageUrl":"/static/tutorials/security-alert.png"
|
||||||
}]
|
}]
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
||||||
[Wake Up!](/tutorials/wake-up),
|
[Brick tutorials](/tutorials/brick),
|
||||||
[Make An Animation](/tutorials/make-an-animation),
|
[Motors tutorials](/tutorials/motors),
|
||||||
[What Animal Am I?](/tutorials/what-animal-am-i),
|
[Touch sensor tutorials](/tutorials/touch-sensor),
|
||||||
[Music Brick](/tutorials/music-brick),
|
[Color sensor tutorials](/tutorials/color-sensor),
|
||||||
[Run Motors](/tutorials/run-motors),
|
[Infrared sensor tutorials](/tutorials/infrared-sensor)
|
||||||
[Touch to Run](/tutorials/touch-to-run),
|
|
||||||
[Touch Sensor Values](/tutorials/touch-sensor-values),
|
|
||||||
[What Color?](/tutorials/what-color),
|
|
||||||
[Line Following](/tutorials/line-following),
|
|
||||||
[Red Light, Green Light](/tutorials/redlight-greenlight),
|
|
||||||
[Object Near?](/tutorials/object-near),
|
|
||||||
[Security Alert](/tutorials/security-alert)
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Brick Tutorials
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
```codecard
|
||||||
|
[{
|
||||||
|
"name": "Wake Up!",
|
||||||
|
"description": "Show different moods on the screen. Is it tired, sleepy, or awake?",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/wake-up",
|
||||||
|
"imageUrl":"/static/tutorials/wake-up.png"
|
||||||
|
}, {
|
||||||
|
"name": "Make an Animation",
|
||||||
|
"description": "Create a custom animation on your EV3 Brick Display.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/make-an-animation",
|
||||||
|
"imageUrl":"/static/tutorials/make-an-animation.png"
|
||||||
|
}, {
|
||||||
|
"name": "What Animal Am I?",
|
||||||
|
"description": "Create different animal effects and have someone guess what the animal is.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/what-animal-am-i",
|
||||||
|
"imageUrl":"/static/tutorials/what-animal-am-i.png"
|
||||||
|
}, {
|
||||||
|
"name": "Music Brick",
|
||||||
|
"description": "Transform the brick into a musical instrument!",
|
||||||
|
"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"
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[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),
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# 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)
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# 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)
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# 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)
|
||||||
|
```
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# Robot 2 Lesson
|
||||||
|
|
||||||
|
## Step 1 - Build Your Driving Base Robot @unplugged
|
||||||
|
|
||||||
|
Build the robot driving base:
|
||||||
|
|
||||||
|
[](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.
|
||||||
|
|
||||||
|
[](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!
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Coast or Brake
|
||||||
|
|
||||||
|
This code example will set the brake when button **A** is pressed or let the motor coast (turn freely when not running) when button **B** is pressed. The motor is turned by one rotation to cause motion.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
// tell motor to brake once the run command is done
|
||||||
|
motors.largeB.setBrake(true)
|
||||||
|
motors.largeB.run(100, 1, MoveUnit.Rotations)
|
||||||
|
})
|
||||||
|
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
// tell motor to coast once the run command is done
|
||||||
|
motors.largeB.setBrake(false)
|
||||||
|
motors.largeB.run(100, 1, MoveUnit.Rotations)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Color Sensor
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
```codecard
|
||||||
|
[{
|
||||||
|
"name": "What Color?",
|
||||||
|
"description": "Use the Color Sensor to detect different colors.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/what-color",
|
||||||
|
"imageUrl":"/static/tutorials/what-color.png"
|
||||||
|
}, {
|
||||||
|
"name": "Line Following",
|
||||||
|
"description": "Use the Color Sensor to make a robot follow a line.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/line-following",
|
||||||
|
"imageUrl":"/static/tutorials/line-following.png"
|
||||||
|
}, {
|
||||||
|
"name": "Red Light, Green Light",
|
||||||
|
"description": "Play Red Light, Green Light using the Color Sensor and the robot.",
|
||||||
|
"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.",
|
||||||
|
"cardType": "example",
|
||||||
|
"url":"/tutorials/reflected-light-measure",
|
||||||
|
"imageUrl":"/static/tutorials/reflected-light-measure.png"
|
||||||
|
}, {
|
||||||
|
"name": "Reflected Light Calibration",
|
||||||
|
"description": "Use the auto-calibration feature to setup the dark and bright values.",
|
||||||
|
"cardType": "example",
|
||||||
|
"url":"/tutorials/reflected-light-calibration",
|
||||||
|
"imageUrl":"/static/tutorials/reflected-light-calibration.png"
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[What Color?](/tutorials/what-color),
|
||||||
|
[Line Following](/tutorials/line-following),
|
||||||
|
[Red Light, Green Light](/tutorials/redlight-greenlight),
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Drifter
|
||||||
|
|
||||||
|
Use this program to try out the gyro sensor and the effect of drifting.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// this loop shows the rate, angle and drift of the robot
|
||||||
|
forever(() => {
|
||||||
|
brick.showValue("rate", sensors.gyro2.rate(), 1)
|
||||||
|
brick.showValue("angle", sensors.gyro2.angle(), 2)
|
||||||
|
brick.showValue("drift", sensors.gyro2.drift(), 3)
|
||||||
|
})
|
||||||
|
// this loop shows wheter the sensor is calibrating
|
||||||
|
forever(() => {
|
||||||
|
brick.showString(sensors.gyro2.isCalibrating() ? "calibrating..." : "", 4)
|
||||||
|
})
|
||||||
|
// instructions on how to use the buttons
|
||||||
|
brick.showString("ENTER: calibrate", 7)
|
||||||
|
brick.showString(" (reset+drift)", 8)
|
||||||
|
brick.showString("LEFT: reset", 9)
|
||||||
|
brick.showString("RIGHT: compute drift", 10)
|
||||||
|
|
||||||
|
// enter -> calibrate
|
||||||
|
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
})
|
||||||
|
// right -> compute drift
|
||||||
|
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
sensors.gyro2.computeDrift()
|
||||||
|
})
|
||||||
|
// left -> reset
|
||||||
|
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
sensors.gyro2.reset()
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# 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"
|
||||||
|
}]
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Infrared sensor
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
```codecard
|
||||||
|
[{
|
||||||
|
"name": "Security Alert",
|
||||||
|
"description": "Build an security alert using the Infrared Sensor.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/security-alert",
|
||||||
|
"imageUrl":"/static/tutorials/security-alert.png"
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[Security Alert](/tutorials/security-alert)
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Motors
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
```codecard
|
||||||
|
[{
|
||||||
|
"name": "Run Motors",
|
||||||
|
"description": "Use the EV3 Brick buttons to start and stop the Large Motor and Medium Motor.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/run-motors",
|
||||||
|
"imageUrl":"/static/tutorials/run-motors.png"
|
||||||
|
}, {
|
||||||
|
"name": "Spin Turn",
|
||||||
|
"description": "Turn the driving base around its center.",
|
||||||
|
"cardType": "example",
|
||||||
|
"url":"/tutorials/spin-turn",
|
||||||
|
"imageUrl":"/static/tutorials/spin-turn.png"
|
||||||
|
}, {
|
||||||
|
"name": "Pivot Turn",
|
||||||
|
"description": "Turn the driving base around a wheel.",
|
||||||
|
"cardType": "example",
|
||||||
|
"url":"/tutorials/pivot-turn",
|
||||||
|
"imageUrl":"/static/tutorials/pivot-turn.png"
|
||||||
|
}, {
|
||||||
|
"name": "Smooth Turn",
|
||||||
|
"description": "Turn the driving base in a smooth, steering motion.",
|
||||||
|
"cardType": "example",
|
||||||
|
"url":"/tutorials/smooth-turn",
|
||||||
|
"imageUrl":"/static/tutorials/smooth-turn.png"
|
||||||
|
}, {
|
||||||
|
"name": "Tank ZigZag",
|
||||||
|
"description": "Use the tank block to keep motors in sync.",
|
||||||
|
"cardType": "example",
|
||||||
|
"url":"/tutorials/tank-zigzag",
|
||||||
|
"imageUrl":"/static/tutorials/tank-zigzag.png"
|
||||||
|
}, {
|
||||||
|
"name": "Coast Or Brake",
|
||||||
|
"description": "Tell motors to coast or brake once the run command is done.",
|
||||||
|
"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"
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[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)
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 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).
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# 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!
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# 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)
|
||||||
|
```
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Pause Until Pressed
|
||||||
|
|
||||||
|
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()
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Pivot Turn
|
||||||
|
|
||||||
|
A **pivot turn** happens when a [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf) turns around the wheel on the inside of the turn by spinning just the single wheel at the outside of the turn.
|
||||||
|
|
||||||
|
You can make a turn happen with either a ``||motors:tank||`` or a ``||motors:steer||`` block.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
forever(function() {
|
||||||
|
motors.largeBC.tank(50, 0, 2, MoveUnit.Rotations)
|
||||||
|
motors.largeBC.tank(0, 50, 2, MoveUnit.Rotations)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Reflected light calibration
|
||||||
|
|
||||||
|
The ``||sensors:calibrateLight||`` blocks allows you to calibrate the reflected light of the color sensor in one block. At the time you run the block, move the sensor over a dark surface and a bright surface; then stop moving it.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
sensors.color3.onLightDetected(LightIntensityMode.Reflected, Light.Dark, function () {
|
||||||
|
brick.showString("dark", 2)
|
||||||
|
})
|
||||||
|
sensors.color3.onLightDetected(LightIntensityMode.Reflected, Light.Bright, function () {
|
||||||
|
brick.showString("bright", 2)
|
||||||
|
})
|
||||||
|
console.sendToScreen()
|
||||||
|
console.log("move color sensor")
|
||||||
|
console.log("over DARK and BRIGHT color")
|
||||||
|
console.log("and stop moving when done")
|
||||||
|
console.log("press ENTER when ready")
|
||||||
|
brick.buttonEnter.pauseUntil(ButtonEvent.Pressed)
|
||||||
|
sensors.color3.calibrateLight(LightIntensityMode.Reflected)
|
||||||
|
brick.showValue("dark", sensors.color3.threshold(Light.Dark), 4)
|
||||||
|
brick.showValue("bright", sensors.color3.threshold(Light.Bright), 5)
|
||||||
|
forever(function () {
|
||||||
|
brick.showValue("reflected light", sensors.color3.light(LightIntensityMode.Reflected), 1)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Reflected light measure
|
||||||
|
|
||||||
|
This example uses a color sensor to measure the reflected light from a dark and light surface
|
||||||
|
and sets the light/dark thresholds.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
sensors.color3.onLightDetected(LightIntensityMode.Reflected, Light.Dark, function () {
|
||||||
|
brick.showString("dark", 2)
|
||||||
|
})
|
||||||
|
sensors.color3.onLightDetected(LightIntensityMode.Reflected, Light.Bright, function () {
|
||||||
|
brick.showString("bright", 2)
|
||||||
|
})
|
||||||
|
console.sendToScreen()
|
||||||
|
console.log("move color sensor")
|
||||||
|
console.log("over DARK color")
|
||||||
|
console.log("press ENTER when ready")
|
||||||
|
brick.buttonEnter.pauseUntil(ButtonEvent.Pressed)
|
||||||
|
sensors.color3.setThreshold(Light.Dark, sensors.color3.light(LightIntensityMode.Reflected) + 5)
|
||||||
|
console.logValue("dark", sensors.color3.threshold(Light.Dark))
|
||||||
|
console.log("move color sensor")
|
||||||
|
console.log("over BRIGHT color")
|
||||||
|
console.log("press ENTER when ready")
|
||||||
|
brick.buttonEnter.pauseUntil(ButtonEvent.Pressed)
|
||||||
|
sensors.color3.setThreshold(Light.Bright, sensors.color3.light(LightIntensityMode.Reflected) - 5)
|
||||||
|
console.logValue("bright", sensors.color3.threshold(Light.Bright))
|
||||||
|
forever(function () {
|
||||||
|
brick.showValue("reflected light", sensors.color3.light(LightIntensityMode.Reflected), 1)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Smooth Turn
|
||||||
|
|
||||||
|
A **smooth turn** happens when a [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf) makes a turn by spinning both both wheels but with each running at a different speed.
|
||||||
|
|
||||||
|
You can make a turn happen with either a ``||motors:tank||`` or a ``||motors:steer||`` block.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
forever(function() {
|
||||||
|
motors.largeBC.tank(50, 20, 2, MoveUnit.Rotations)
|
||||||
|
motors.largeBC.tank(20, 50, 2, MoveUnit.Rotations)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Spin Turn
|
||||||
|
|
||||||
|
A **spin turn** happens when a [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf) turns, or rotates, on a single spot by spinning both wheels, but with each turning in opposite directions.
|
||||||
|
|
||||||
|
You can make a turn happen with either a ``||motors:tank||`` or a ``||motors:steer||`` block.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
forever(function() {
|
||||||
|
motors.largeBC.tank(50, -50, 2, MoveUnit.Rotations)
|
||||||
|
motors.largeBC.tank(-50, 50, 2, MoveUnit.Rotations)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# 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()
|
||||||
|
```
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Tank ZigZag
|
||||||
|
|
||||||
|
This example shows how to use the [tank](/reference/motors/tank) block to keep the speed of 2 large motors synchronized. The [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
|
||||||
|
) will move in a zig zag pattern.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
/**
|
||||||
|
* Use the tank block to keep large motors synched.
|
||||||
|
Use this code with a EV3 driving base.
|
||||||
|
*/
|
||||||
|
forever(function () {
|
||||||
|
brick.showImage(images.eyesMiddleRight)
|
||||||
|
motors.largeBC.tank(50, 10, 2, MoveUnit.Rotations)
|
||||||
|
brick.showImage(images.eyesMiddleLeft)
|
||||||
|
motors.largeBC.tank(10, 50, 2, MoveUnit.Rotations)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Touch Sensor
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
```codecard
|
||||||
|
[{
|
||||||
|
"name": "Touch to Run",
|
||||||
|
"description": "Press the Touch Sensor and run a motor.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/touch-to-run",
|
||||||
|
"imageUrl":"/static/tutorials/touch-to-run.png"
|
||||||
|
}, {
|
||||||
|
"name": "Sensor Values",
|
||||||
|
"description": "Check the value of a Touch Sensor and stop a motor if pressed.",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/touch-sensor-values",
|
||||||
|
"imageUrl":"/static/tutorials/touch-sensor-values.png"
|
||||||
|
}, {
|
||||||
|
"name": "Stop At Object",
|
||||||
|
"description": "Waits for the sensor to be pressed before continuing the program",
|
||||||
|
"cardType": "tutorial",
|
||||||
|
"url":"/tutorials/stop-at-object",
|
||||||
|
"imageUrl":"/static/tutorials/pause-until-pressed.png"
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[Touch to Run](/tutorials/touch-to-run),
|
||||||
|
[Touch Sensor Values](/tutorials/touch-sensor-values),
|
||||||
|
[Stop At Object](/tutorials/stop-at-object)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Turn With Gyro
|
||||||
|
|
||||||
|
## Introduction @fullscreen
|
||||||
|
|
||||||
|
Use the gyro to measure how much the robot is turning, regardless if your wheels are slipping.
|
||||||
|
|
||||||
|
## Step 1 Calibrate
|
||||||
|
|
||||||
|
Add the ``||sensors:calibrate gyro||`` block to make sure your gyro is ready to use.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2 Turn
|
||||||
|
|
||||||
|
Use motor blocks to make the robot turn. Don't go too fast!
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
motors.largeBC.steer(200, 20)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3 Pause for turn
|
||||||
|
|
||||||
|
Use the ``||sensors:pause until rotated||`` block to wait until the desired amount of rotation has occured.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
motors.largeBC.steer(200, 20)
|
||||||
|
sensors.gyro2.pauseUntilRotated(90)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4 Stop
|
||||||
|
|
||||||
|
Stop the motors!
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
motors.largeBC.steer(200, 20)
|
||||||
|
sensors.gyro2.pauseUntilRotated(90)
|
||||||
|
motors.stopAll()
|
||||||
|
```
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# 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)
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 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)
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
# 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.
|
||||||
@@ -2,72 +2,236 @@
|
|||||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||||
|
|
||||||
import UF2 = pxtc.UF2;
|
import UF2 = pxtc.UF2;
|
||||||
|
import { Ev3Wrapper } from "./wrap";
|
||||||
|
|
||||||
export let ev3: pxt.editor.Ev3Wrapper
|
export let ev3: Ev3Wrapper;
|
||||||
|
let confirmAsync: (options: any) => Promise<number>;
|
||||||
|
|
||||||
|
export function setConfirmAsync(cf: (options: any) => Promise<number>) {
|
||||||
|
confirmAsync = cf;
|
||||||
|
}
|
||||||
|
|
||||||
export function debug() {
|
export function debug() {
|
||||||
return initAsync()
|
return initHidAsync()
|
||||||
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
|
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
|
||||||
}
|
}
|
||||||
|
|
||||||
function hf2Async() {
|
|
||||||
return pxt.HF2.mkPacketIOAsync()
|
// Web Serial API https://wicg.github.io/serial/
|
||||||
.then(h => {
|
// chromium bug https://bugs.chromium.org/p/chromium/issues/detail?id=884928
|
||||||
let w = new pxt.editor.Ev3Wrapper(h)
|
// Under experimental features in Chrome Desktop 77+
|
||||||
ev3 = w
|
enum ParityType {
|
||||||
return w.reconnectAsync(true)
|
"none",
|
||||||
.then(() => w)
|
"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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let noHID = false
|
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;
|
||||||
|
|
||||||
let initPromise: Promise<pxt.editor.Ev3Wrapper>
|
constructor(private port: SerialPort, private options: SerialOptions) {
|
||||||
export function initAsync() {
|
}
|
||||||
if (initPromise)
|
|
||||||
return initPromise
|
|
||||||
|
|
||||||
let canHID = false
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let useHID = false;
|
||||||
|
let useWebSerial = false;
|
||||||
|
export function initAsync(): Promise<void> {
|
||||||
if (pxt.U.isNodeJS) {
|
if (pxt.U.isNodeJS) {
|
||||||
// doesn't seem to work ATM
|
// doesn't seem to work ATM
|
||||||
canHID = false
|
useHID = false
|
||||||
} else {
|
} else {
|
||||||
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
|
const nodehid = /nodehid/i.test(window.location.href);
|
||||||
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && !forceHexDownload)
|
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && nodehid)
|
||||||
canHID = true
|
useHID = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noHID)
|
if (WebSerialPackageIO.isSupported())
|
||||||
canHID = false
|
pxt.tickEvent("webserial.supported");
|
||||||
|
|
||||||
if (canHID) {
|
return Promise.resolve();
|
||||||
initPromise = hf2Async()
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if (initPromise)
|
||||||
|
return initPromise
|
||||||
|
if (useHID) {
|
||||||
|
initPromise = cleanupAsync()
|
||||||
|
.then(() => hf2Async())
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
initPromise = null
|
initPromise = null
|
||||||
noHID = true
|
useHID = false;
|
||||||
return Promise.reject(err)
|
useWebSerial = false;
|
||||||
|
return Promise.reject(err);
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
noHID = true
|
useHID = false
|
||||||
|
useWebSerial = false;
|
||||||
initPromise = Promise.reject(new Error("no HID"))
|
initPromise = Promise.reject(new Error("no HID"))
|
||||||
}
|
}
|
||||||
|
return initPromise;
|
||||||
return initPromise
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this comes from aux/pxt.lms
|
// this comes from aux/pxt.lms
|
||||||
|
const fspath = "../prjs/BrkProg_SAVE/"
|
||||||
const rbfTemplate = `
|
const rbfTemplate = `
|
||||||
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
|
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
|
||||||
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
|
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
|
||||||
`
|
`
|
||||||
export function deployCoreAsync(resp: pxtc.CompileResult) {
|
export function deployCoreAsync(resp: pxtc.CompileResult) {
|
||||||
let w: pxt.editor.Ev3Wrapper
|
|
||||||
|
|
||||||
let filename = resp.downloadFileBaseName || "pxt"
|
let filename = resp.downloadFileBaseName || "pxt"
|
||||||
filename = filename.replace(/^lego-/, "")
|
filename = filename.replace(/^lego-/, "")
|
||||||
|
|
||||||
let fspath = "../prjs/BrkProg_SAVE/"
|
|
||||||
|
|
||||||
let elfPath = fspath + filename + ".elf"
|
let elfPath = fspath + filename + ".elf"
|
||||||
let rbfPath = fspath + filename + ".rbf"
|
let rbfPath = fspath + filename + ".rbf"
|
||||||
|
|
||||||
@@ -107,27 +271,54 @@ export function deployCoreAsync(resp: pxtc.CompileResult) {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noHID) return saveUF2Async()
|
if (!useHID) return saveUF2Async()
|
||||||
|
|
||||||
return initAsync()
|
pxt.tickEvent("webserial.flash");
|
||||||
|
let w: Ev3Wrapper;
|
||||||
|
return initHidAsync()
|
||||||
.then(w_ => {
|
.then(w_ => {
|
||||||
w = w_
|
w = w_
|
||||||
if (w.isStreaming)
|
if (w.isStreaming)
|
||||||
pxt.U.userError("please stop the program first")
|
pxt.U.userError("please stop the program first")
|
||||||
return w.stopAsync()
|
return w.reconnectAsync(false)
|
||||||
|
.catch(e => {
|
||||||
|
// user easily forgets to stop robot
|
||||||
|
if (confirmAsync)
|
||||||
|
return confirmAsync({
|
||||||
|
header: lf("Bluetooth download failed..."),
|
||||||
|
htmlBody:
|
||||||
|
`<ul>
|
||||||
|
<li>${lf("Make sure to stop your program or exit portview on the EV3.")}</li>
|
||||||
|
<li>${lf("Check your battery level.")}</li>
|
||||||
|
<li>${lf("Close EV3 LabView or other MakeCode editor tabs.")}
|
||||||
|
</ul>`,
|
||||||
|
hasCloseIcon: true,
|
||||||
|
hideCancel: true,
|
||||||
|
hideAgree: false,
|
||||||
|
agreeLbl: lf("Try again"),
|
||||||
|
}).then(() => w.disconnectAsync())
|
||||||
|
.then(() => Promise.delay(1000))
|
||||||
|
.then(() => w.reconnectAsync());
|
||||||
|
|
||||||
|
// nothing we can do
|
||||||
|
return Promise.reject(e);
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
.then(() => w.stopAsync())
|
||||||
.then(() => w.rmAsync(elfPath))
|
.then(() => w.rmAsync(elfPath))
|
||||||
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
|
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
|
||||||
.then(() => w.flashAsync(rbfPath, rbfBIN))
|
.then(() => w.flashAsync(rbfPath, rbfBIN))
|
||||||
.then(() => w.runAsync(rbfPath))
|
.then(() => w.runAsync(rbfPath))
|
||||||
|
.then(() => Promise.delay(500))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
pxt.tickEvent("webserial.success");
|
||||||
return w.disconnectAsync()
|
return w.disconnectAsync()
|
||||||
//return Promise.delay(1000).then(() => w.dmesgAsync())
|
//return Promise.delay(1000).then(() => w.dmesgAsync())
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
// if we failed to initalize, retry
|
pxt.tickEvent("webserial.fail");
|
||||||
if (noHID)
|
useHID = false;
|
||||||
return saveUF2Async()
|
useWebSerial = false;
|
||||||
else
|
// if we failed to initalize, tell the user to retry
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
|
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
|
||||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||||
|
|
||||||
import { deployCoreAsync, initAsync } from "./deploy";
|
import { deployCoreAsync, initAsync, canUseWebSerial, enableWebSerialAsync, setConfirmAsync } from "./deploy";
|
||||||
|
|
||||||
|
let bluetoothDialogShown = false;
|
||||||
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
||||||
|
const projectView = opts.projectView;
|
||||||
pxt.debug('loading pxt-ev3 target extensions...')
|
pxt.debug('loading pxt-ev3 target extensions...')
|
||||||
|
|
||||||
|
function enableWebSerialAndCompileAsync() {
|
||||||
|
return enableWebSerialAsync()
|
||||||
|
.then(() => Promise.delay(500))
|
||||||
|
.then(() => projectView.compile());
|
||||||
|
}
|
||||||
|
|
||||||
const res: pxt.editor.ExtensionResult = {
|
const res: pxt.editor.ExtensionResult = {
|
||||||
deployCoreAsync,
|
deployCoreAsync,
|
||||||
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
|
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
|
||||||
let resolve: (thenableOrResult?: void | PromiseLike<void>) => void;
|
setConfirmAsync(confirmAsync);
|
||||||
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
|
// https://msdn.microsoft.com/en-us/library/cc848897.aspx
|
||||||
// "For security reasons, data URIs are restricted to downloaded resources.
|
// "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"
|
// 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 downloadAgain = !pxt.BrowserUtils.isIE() && !pxt.BrowserUtils.isEdge();
|
||||||
const docUrl = pxt.appTarget.appTheme.usbDocs;
|
const docUrl = pxt.appTarget.appTheme.usbDocs;
|
||||||
const saveAs = pxt.BrowserUtils.hasSaveAs();
|
|
||||||
|
|
||||||
const htmlBody = `
|
const htmlBody = `
|
||||||
<div class="ui grid stackable">
|
<div class="ui grid stackable">
|
||||||
@@ -84,7 +84,38 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P
|
|||||||
hideAgree: false,
|
hideAgree: false,
|
||||||
agreeLbl: lf("I got it"),
|
agreeLbl: lf("I got it"),
|
||||||
className: 'downloaddialog',
|
className: 'downloaddialog',
|
||||||
buttons: [downloadAgain ? {
|
buttons: [canUseWebSerial() ? {
|
||||||
|
label: lf("Bluetooth"),
|
||||||
|
icon: "bluetooth",
|
||||||
|
className: "bluetooth focused",
|
||||||
|
onclick: () => {
|
||||||
|
pxt.tickEvent("bluetooth.enable");
|
||||||
|
if (bluetoothDialogShown) {
|
||||||
|
enableWebSerialAndCompileAsync().done();
|
||||||
|
} else {
|
||||||
|
bluetoothDialogShown = true;
|
||||||
|
confirmAsync({
|
||||||
|
header: lf("Bluetooth pairing"),
|
||||||
|
hasCloseIcon: true,
|
||||||
|
hideCancel: true,
|
||||||
|
buttons: [{
|
||||||
|
label: lf("Help"),
|
||||||
|
icon: "question circle",
|
||||||
|
className: "lightgrey",
|
||||||
|
url: "/bluetooth"
|
||||||
|
}],
|
||||||
|
htmlBody: `<p>
|
||||||
|
${lf("You will be prompted to select a serial port.")}
|
||||||
|
${pxt.BrowserUtils.isWindows()
|
||||||
|
? lf("Look for 'Standard Serial over Bluetooth link'.")
|
||||||
|
: lf("Loop for 'cu.EV3-SerialPort'.")}
|
||||||
|
${lf("If you have paired multiple EV3, you might have to try out multiple ports until you find the correct one.")}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
}).then(() => enableWebSerialAndCompileAsync())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} : undefined, downloadAgain ? {
|
||||||
label: fn,
|
label: fn,
|
||||||
icon: "download",
|
icon: "download",
|
||||||
className: "lightgrey focused",
|
className: "lightgrey focused",
|
||||||
|
|||||||
@@ -1,285 +1,286 @@
|
|||||||
namespace pxt.editor {
|
/**
|
||||||
import HF2 = pxt.HF2
|
* See https://www.lego.com/cdn/cs/set/assets/blt6879b00ae6951482/LEGO_MINDSTORMS_EV3_Communication_Developer_Kit.pdf
|
||||||
import U = pxt.U
|
* https://github.com/mindboards/ev3sources/blob/master/lms2012/lms2012/source/bytecodes.h#L146
|
||||||
|
*/
|
||||||
|
import HF2 = pxt.HF2
|
||||||
|
import U = pxt.U
|
||||||
|
|
||||||
function log(msg: string) {
|
function log(msg: string) {
|
||||||
pxt.log("EWRAP: " + msg)
|
pxt.log("serial: " + msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirEntry {
|
||||||
|
name: string;
|
||||||
|
md5?: string;
|
||||||
|
size?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const runTemplate = "C00882010084XX0060640301606400"
|
||||||
|
const usbMagic = 0x3d3f
|
||||||
|
const DIRECT_COMMAND_NO_REPLY = 0x80
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if (this.dataDump)
|
||||||
|
log("RECV: " + U.toHex(buf))
|
||||||
|
this.msgs.push(buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DirEntry {
|
private allocCore(addSize: number, replyType: number) {
|
||||||
name: string;
|
let len = 5 + addSize
|
||||||
md5?: string;
|
let buf = new Uint8Array(len)
|
||||||
size?: number;
|
HF2.write16(buf, 0, len - 2) // pktLen
|
||||||
|
HF2.write16(buf, 2, this.cmdSeq++) // msgCount
|
||||||
|
buf[4] = replyType
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
const runTemplate = "C00882010084XX0060640301606400"
|
private allocSystem(addSize: number, cmd: number, replyType = 1) {
|
||||||
const usbMagic = 0x3d3f
|
let buf = this.allocCore(addSize + 1, replyType)
|
||||||
|
buf[5] = cmd
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
export class Ev3Wrapper {
|
private allocCustom(code: number, addSize = 0) {
|
||||||
msgs = new U.PromiseBuffer<Uint8Array>()
|
let buf = this.allocCore(1 + 2 + addSize, 0)
|
||||||
private cmdSeq = U.randomUint32() & 0xffff;
|
HF2.write16(buf, 4, usbMagic)
|
||||||
private lock = new U.PromiseQueue();
|
HF2.write16(buf, 6, code)
|
||||||
isStreaming = false;
|
return buf
|
||||||
dataDump = false;
|
}
|
||||||
|
|
||||||
constructor(public io: pxt.HF2.PacketIO) {
|
stopAsync() {
|
||||||
io.onData = buf => {
|
return this.isVmAsync()
|
||||||
buf = buf.slice(0, HF2.read16(buf, 0) + 2)
|
.then(vm => {
|
||||||
if (HF2.read16(buf, 4) == usbMagic) {
|
if (vm) return Promise.resolve();
|
||||||
let code = HF2.read16(buf, 6)
|
log(`stopping PXT app`)
|
||||||
let payload = buf.slice(8)
|
let buf = this.allocCustom(2)
|
||||||
if (code == 1) {
|
return this.justSendAsync(buf)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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, 0)
|
|
||||||
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
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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(() => Promise.delay(500))
|
||||||
.then(loop)
|
})
|
||||||
return loop()
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
downloadFileAsync(path: string, cb: (d: Uint8Array) => void) {
|
lsAsync(path: string): Promise<DirEntry[]> {
|
||||||
return this.lock.enqueue("file", () =>
|
let lsReq = this.allocSystem(2 + path.length + 1, 0x99)
|
||||||
this.streamFileOnceAsync(path, cb))
|
HF2.write16(lsReq, 6, 1024) // maxRead
|
||||||
}
|
U.memcpy(lsReq, 8, U.stringToUint8Array(path))
|
||||||
|
|
||||||
|
|
||||||
private initAsync() {
|
return this.talkAsync(lsReq, 8)
|
||||||
return Promise.resolve()
|
.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetState() {
|
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> =>
|
||||||
reconnectAsync(first = false): Promise<void> {
|
this.lock.enqueue("file", () =>
|
||||||
this.resetState()
|
this.streamFileOnceAsync(path, cb))
|
||||||
if (first) return this.initAsync()
|
.then(() => Promise.delay(500))
|
||||||
log(`reconnect`);
|
.then(loop)
|
||||||
return this.io.reconnectAsync()
|
return loop()
|
||||||
.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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../pxt-common-packages"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../pxt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -76,9 +76,10 @@ export class FieldSpeed extends Blockly.FieldSlider implements Blockly.FieldCust
|
|||||||
};
|
};
|
||||||
|
|
||||||
setReadout_(readout: Element, value: string) {
|
setReadout_(readout: Element, value: string) {
|
||||||
this.updateSpeed(parseFloat(value));
|
let x = parseFloat(value) || 0;
|
||||||
|
this.updateSpeed(x);
|
||||||
// Update reporter
|
// Update reporter
|
||||||
this.reporter.textContent = `${value}%`;
|
this.reporter.textContent = `${x}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateSpeed(speed: number) {
|
private updateSpeed(speed: number) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class FieldTurnRatio extends Blockly.FieldSlider implements Blockly.Field
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor(value_: any, params: FieldTurnRatioOptions, opt_validator?: Function) {
|
constructor(value_: any, params: FieldTurnRatioOptions, opt_validator?: Function) {
|
||||||
super(String(value_), '-100', '100', null, '10', 'TurnRatio', opt_validator);
|
super(String(value_), '-200', '200', null, '10', 'TurnRatio', opt_validator);
|
||||||
this.params = params;
|
this.params = params;
|
||||||
(this as any).sliderColor_ = '#a8aaa8';
|
(this as any).sliderColor_ = '#a8aaa8';
|
||||||
}
|
}
|
||||||
@@ -76,26 +76,26 @@ export class FieldTurnRatio extends Blockly.FieldSlider implements Blockly.Field
|
|||||||
if (!this.path_) {
|
if (!this.path_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let v = goog.math.clamp(parseFloat(this.getText()), -100, 100);
|
let v = goog.math.clamp(parseFloat(this.getText()), -200, 200);
|
||||||
if (isNaN(v)) {
|
if (isNaN(v)) {
|
||||||
v = 0;
|
v = 0;
|
||||||
}
|
}
|
||||||
|
const x = v / 100;
|
||||||
const x = goog.math.clamp(parseFloat(this.getText()), -100, 100) / 100;
|
const nx = Math.max(-1, Math.min(1, x));
|
||||||
const theta = x * Math.PI / 2;
|
const theta = Math.max(nx) * Math.PI / 2;
|
||||||
const cx = FieldTurnRatio.HALF;
|
const r = FieldTurnRatio.RADIUS - 6;
|
||||||
const cy = FieldTurnRatio.HALF - 14;
|
let cx = FieldTurnRatio.HALF;
|
||||||
const gamma = Math.PI - 2 * theta;
|
const cy = FieldTurnRatio.HALF - 22;
|
||||||
const r = FieldTurnRatio.RADIUS;
|
if (Math.abs(x) > 1) {
|
||||||
const alpha = 0.2 + Math.abs(x) * 0.5;
|
cx -= (x - (x > 0 ? 1 : -1)) * r / 2; // move center of circle
|
||||||
const x1 = 0;
|
}
|
||||||
|
const alpha = 0.2 + Math.abs(nx) * 0.5;
|
||||||
const y1 = r * alpha;
|
const y1 = r * alpha;
|
||||||
const y2 = r * Math.sin(Math.PI / 2 - theta);
|
const y2 = r * Math.sin(Math.PI / 2 - theta);
|
||||||
const x2 = r * Math.cos(Math.PI / 2 - theta);
|
const x2 = r * Math.cos(Math.PI / 2 - theta);
|
||||||
const y3 = y2 - r * alpha * Math.cos(2 * theta);
|
const y3 = y2 - r * alpha * Math.cos(2 * theta);
|
||||||
const x3 = x2 - r * alpha * Math.sin(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}`;
|
const d = `M ${cx} ${cy} C ${cx} ${cy - y1} ${cx + x3} ${cy - y3} ${cx + x2} ${cy - y2}`;
|
||||||
this.path_.setAttribute('d', d);
|
this.path_.setAttribute('d', d);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "broadcast",
|
||||||
|
"description": "Broadcasting messages - beta",
|
||||||
|
"files": [
|
||||||
|
"broadcast.ts"
|
||||||
|
],
|
||||||
|
"public": true,
|
||||||
|
"dependencies": {
|
||||||
|
"core": "file:../core"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,13 +73,7 @@ namespace sensors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setMode(m: ColorSensorMode) {
|
setMode(m: ColorSensorMode) {
|
||||||
if (m == ColorSensorMode.AmbientLightIntensity) {
|
// don't change threshold after initialization
|
||||||
this.thresholdDetector.setLowThreshold(5);
|
|
||||||
this.thresholdDetector.setHighThreshold(20);
|
|
||||||
} else {
|
|
||||||
this.thresholdDetector.setLowThreshold(20);
|
|
||||||
this.thresholdDetector.setHighThreshold(80);
|
|
||||||
}
|
|
||||||
this._setMode(m)
|
this._setMode(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +105,9 @@ namespace sensors {
|
|||||||
"red",
|
"red",
|
||||||
"white",
|
"white",
|
||||||
"brown"][this._query()];
|
"brown"][this._query()];
|
||||||
|
case ColorSensorMode.AmbientLightIntensity:
|
||||||
|
case ColorSensorMode.ReflectedLightIntensity:
|
||||||
|
return `${this._query()}%`;
|
||||||
default:
|
default:
|
||||||
return this._query().toString();
|
return this._query().toString();
|
||||||
}
|
}
|
||||||
@@ -179,6 +176,7 @@ namespace sensors {
|
|||||||
//% group="Color Sensor"
|
//% group="Color Sensor"
|
||||||
//% blockGap=8
|
//% blockGap=8
|
||||||
color(): ColorSensorColor {
|
color(): ColorSensorColor {
|
||||||
|
this.poke();
|
||||||
this.setMode(ColorSensorMode.Color)
|
this.setMode(ColorSensorMode.Color)
|
||||||
return this.getNumber(NumberFormat.UInt8LE, 0)
|
return this.getNumber(NumberFormat.UInt8LE, 0)
|
||||||
}
|
}
|
||||||
@@ -196,6 +194,7 @@ namespace sensors {
|
|||||||
//% group="Color Sensor"
|
//% group="Color Sensor"
|
||||||
//% blockGap=8
|
//% blockGap=8
|
||||||
rgbRaw(): number[] {
|
rgbRaw(): number[] {
|
||||||
|
this.poke();
|
||||||
this.setMode(ColorSensorMode.RgbRaw);
|
this.setMode(ColorSensorMode.RgbRaw);
|
||||||
return [this.getNumber(NumberFormat.UInt16LE, 0), this.getNumber(NumberFormat.UInt16LE, 2), this.getNumber(NumberFormat.UInt16LE, 4)];
|
return [this.getNumber(NumberFormat.UInt16LE, 0), this.getNumber(NumberFormat.UInt16LE, 2), this.getNumber(NumberFormat.UInt16LE, 4)];
|
||||||
}
|
}
|
||||||
@@ -249,8 +248,9 @@ namespace sensors {
|
|||||||
//% weight=87 blockGap=8
|
//% weight=87 blockGap=8
|
||||||
//% group="Color Sensor"
|
//% group="Color Sensor"
|
||||||
light(mode: LightIntensityMode) {
|
light(mode: LightIntensityMode) {
|
||||||
|
this.poke();
|
||||||
this.setMode(<ColorSensorMode><number>mode)
|
this.setMode(<ColorSensorMode><number>mode)
|
||||||
switch(mode) {
|
switch (mode) {
|
||||||
case LightIntensityMode.ReflectedRaw:
|
case LightIntensityMode.ReflectedRaw:
|
||||||
return this.reflectedLightRaw();
|
return this.reflectedLightRaw();
|
||||||
default:
|
default:
|
||||||
@@ -279,6 +279,7 @@ namespace sensors {
|
|||||||
*/
|
*/
|
||||||
//%
|
//%
|
||||||
reflectedLightRaw(): number {
|
reflectedLightRaw(): number {
|
||||||
|
this.poke();
|
||||||
this.setMode(ColorSensorMode.RefRaw);
|
this.setMode(ColorSensorMode.RefRaw);
|
||||||
return this.getNumber(NumberFormat.UInt16LE, 0);
|
return this.getNumber(NumberFormat.UInt16LE, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,40 @@
|
|||||||
|
|
||||||
|
const enum BatteryProperty {
|
||||||
|
//% block="level (%)"
|
||||||
|
Level,
|
||||||
|
//% block="current (I)"
|
||||||
|
Current,
|
||||||
|
//% block="voltage (V)"
|
||||||
|
Voltage
|
||||||
|
}
|
||||||
|
|
||||||
namespace brick {
|
namespace brick {
|
||||||
/**
|
/**
|
||||||
* Returns the current battery level
|
* Returns the current battery level
|
||||||
*/
|
*/
|
||||||
//% blockId=brickBatteryLevel block="battery level"
|
//% blockId=brickBatteryLevel block="battery level"
|
||||||
//% group="More"
|
//% group="Battery"
|
||||||
//% help=brick/battery-level
|
//% help=brick/battery-level
|
||||||
|
//% deprecated blockHidden=1
|
||||||
export function batteryLevel(): number {
|
export function batteryLevel(): number {
|
||||||
const info = sensors.internal.getBatteryInfo();
|
const info = sensors.internal.getBatteryInfo();
|
||||||
return info.current;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,6 +55,10 @@ namespace brick {
|
|||||||
this._wasPressed = false
|
this._wasPressed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected poke() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//% hidden
|
//% hidden
|
||||||
_update(curr: boolean) {
|
_update(curr: boolean) {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
@@ -85,6 +89,7 @@ namespace brick {
|
|||||||
//% group="Buttons"
|
//% group="Buttons"
|
||||||
//% button.fieldEditor="brickbuttons"
|
//% button.fieldEditor="brickbuttons"
|
||||||
isPressed() {
|
isPressed() {
|
||||||
|
this.poke();
|
||||||
return this._isPressed
|
return this._isPressed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +107,7 @@ namespace brick {
|
|||||||
//% group="Buttons"
|
//% group="Buttons"
|
||||||
//% button.fieldEditor="brickbuttons"
|
//% button.fieldEditor="brickbuttons"
|
||||||
wasPressed() {
|
wasPressed() {
|
||||||
|
this.poke();
|
||||||
const r = this._wasPressed
|
const r = this._wasPressed
|
||||||
this._wasPressed = false
|
this._wasPressed = false
|
||||||
return r
|
return r
|
||||||
@@ -144,6 +150,7 @@ namespace brick {
|
|||||||
namespace brick {
|
namespace brick {
|
||||||
let btnsMM: MMap
|
let btnsMM: MMap
|
||||||
let buttons: DevButton[]
|
let buttons: DevButton[]
|
||||||
|
let buttonPoller: sensors.internal.Poller;
|
||||||
|
|
||||||
export namespace internal {
|
export namespace internal {
|
||||||
export function getBtnsMM() {
|
export function getBtnsMM() {
|
||||||
@@ -167,7 +174,7 @@ namespace brick {
|
|||||||
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
|
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
|
||||||
if (!btnsMM) control.fail("no buttons?")
|
if (!btnsMM) control.fail("no buttons?")
|
||||||
buttons = []
|
buttons = []
|
||||||
sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
|
buttonPoller = new sensors.internal.Poller(50, readButtons, (prev, curr) => {
|
||||||
for (let b of buttons)
|
for (let b of buttons)
|
||||||
b._update(!!(curr & b.mask))
|
b._update(!!(curr & b.mask))
|
||||||
})
|
})
|
||||||
@@ -182,6 +189,10 @@ namespace brick {
|
|||||||
initBtns()
|
initBtns()
|
||||||
buttons.push(this)
|
buttons.push(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected poke() {
|
||||||
|
buttonPoller.poke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initBtns() // always ON as it handles ESCAPE button
|
initBtns() // always ON as it handles ESCAPE button
|
||||||
|
|||||||
@@ -1,28 +1,51 @@
|
|||||||
namespace sensors.internal {
|
namespace sensors.internal {
|
||||||
//% shim=pxt::unsafePollForChanges
|
export class Poller {
|
||||||
export function unsafePollForChanges(
|
private query: () => number;
|
||||||
periodMs: number,
|
private update: (previous: number, current: number) => void;
|
||||||
query: () => number,
|
public interval: 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().
|
|
||||||
|
|
||||||
// This is implementation for the simulator.
|
private previousValue: number;
|
||||||
|
private currentValue: number;
|
||||||
|
private lastQuery: number; // track down the last time we did a query/update cycle
|
||||||
|
private lastPause: number; // track down the last time we pause in the sensor polling loop
|
||||||
|
|
||||||
control.runInParallel(() => {
|
constructor(interval: number, query: () => number, update: (previous: number, current: number) => void) {
|
||||||
let prev = query()
|
this.interval = interval | 0;
|
||||||
changeHandler(prev, prev)
|
this.query = query;
|
||||||
while (true) {
|
this.update = update;
|
||||||
pause(periodMs)
|
|
||||||
let curr = query()
|
this.poll();
|
||||||
if (prev !== curr) {
|
}
|
||||||
changeHandler(prev, curr)
|
|
||||||
prev = curr
|
poke(): void {
|
||||||
}
|
const now = control.millis();
|
||||||
|
if (now - this.lastQuery >= this.interval * 2)
|
||||||
|
this.queryAndUpdate(); // sensor poller is not allowed to run
|
||||||
|
if (now - this.lastPause >= this.interval * 5)
|
||||||
|
pause(1); // allow events to trigger
|
||||||
|
}
|
||||||
|
|
||||||
|
private queryAndUpdate() {
|
||||||
|
this.lastQuery = control.millis();
|
||||||
|
this.currentValue = this.query();
|
||||||
|
if (this.previousValue != this.currentValue) {
|
||||||
|
this.update(this.previousValue, this.currentValue);
|
||||||
|
this.previousValue = this.currentValue;
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
private poll() {
|
||||||
|
control.runInBackground(() => {
|
||||||
|
this.lastQuery = this.lastPause = control.millis();
|
||||||
|
this.previousValue = this.currentValue = this.query();
|
||||||
|
this.update(this.previousValue, this.currentValue);
|
||||||
|
while (true) {
|
||||||
|
this.lastPause = control.millis();
|
||||||
|
pause(this.interval);
|
||||||
|
this.queryAndUpdate();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bufferToString(buf: Buffer): string {
|
export function bufferToString(buf: Buffer): string {
|
||||||
@@ -36,8 +59,18 @@ namespace sensors.internal {
|
|||||||
let analogMM: MMap
|
let analogMM: MMap
|
||||||
let uartMM: MMap
|
let uartMM: MMap
|
||||||
let IICMM: MMap
|
let IICMM: MMap
|
||||||
|
let powerMM: MMap
|
||||||
let devcon: Buffer
|
let devcon: Buffer
|
||||||
let sensorInfos: SensorInfo[]
|
let devPoller: Poller
|
||||||
|
let sensorInfos: SensorInfo[];
|
||||||
|
|
||||||
|
let batteryInfo: {
|
||||||
|
CinCnt: number;
|
||||||
|
CoutCnt: number;
|
||||||
|
VinCnt: number;
|
||||||
|
};
|
||||||
|
let batteryVMin: number;
|
||||||
|
let batteryVMax: number;
|
||||||
|
|
||||||
class SensorInfo {
|
class SensorInfo {
|
||||||
port: number
|
port: number
|
||||||
@@ -46,6 +79,7 @@ namespace sensors.internal {
|
|||||||
connType: number
|
connType: number
|
||||||
devType: number
|
devType: number
|
||||||
iicid: string
|
iicid: string
|
||||||
|
poller: Poller;
|
||||||
|
|
||||||
constructor(p: number) {
|
constructor(p: number) {
|
||||||
this.port = p
|
this.port = p
|
||||||
@@ -53,6 +87,20 @@ namespace sensors.internal {
|
|||||||
this.devType = DAL.DEVICE_TYPE_NONE
|
this.devType = DAL.DEVICE_TYPE_NONE
|
||||||
this.iicid = ''
|
this.iicid = ''
|
||||||
this.sensors = []
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,19 +119,12 @@ namespace sensors.internal {
|
|||||||
IICMM = control.mmap("/dev/lms_iic", IICOff.Size, 0)
|
IICMM = control.mmap("/dev/lms_iic", IICOff.Size, 0)
|
||||||
if (!IICMM) control.fail("no iic sensor")
|
if (!IICMM) control.fail("no iic sensor")
|
||||||
|
|
||||||
unsafePollForChanges(500,
|
powerMM = control.mmap("/dev/lms_power", 2, 0)
|
||||||
() => { 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[] {
|
export function getActiveSensors(): Sensor[] {
|
||||||
@@ -110,18 +151,130 @@ namespace sensors.internal {
|
|||||||
return manufacturer + sensorType;
|
return manufacturer + sensorType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBatteryInfo(): { temp: number; current: number } {
|
const ADC_REF = 5000 //!< [mV] maximal value on ADC
|
||||||
init();
|
const ADC_RES = 4095 //!< [CNT] maximal count on ADC
|
||||||
return {
|
// see c_ui.c
|
||||||
temp: analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryTemp),
|
const SHUNT_IN = 0.11 // [Ohm]
|
||||||
current: Math.round(analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryCurrent) / 10)
|
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 function getBatteryInfo(): {
|
||||||
|
level: number;
|
||||||
|
Ibatt: number,
|
||||||
|
Vbatt: number,
|
||||||
|
Imotor: 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function hashDevices(): number {
|
function hashDevices(): number {
|
||||||
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
|
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
|
||||||
let r = 0;
|
let r = 0;
|
||||||
for(let i = 0; i < conns.length; ++i) {
|
for (let i = 0; i < conns.length; ++i) {
|
||||||
r = (r << 8 | conns[i]);
|
r = (r << 8 | conns[i]);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
@@ -129,7 +282,7 @@ namespace sensors.internal {
|
|||||||
|
|
||||||
let nonActivated = 0;
|
let nonActivated = 0;
|
||||||
function detectDevices() {
|
function detectDevices() {
|
||||||
control.dmesg(`detect devices (${nonActivated} na)`)
|
//control.dmesg(`detect devices (${nonActivated} na)`)
|
||||||
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
|
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
|
||||||
let numChanged = 0;
|
let numChanged = 0;
|
||||||
const uartSensors: SensorInfo[] = [];
|
const uartSensors: SensorInfo[] = [];
|
||||||
@@ -157,7 +310,7 @@ namespace sensors.internal {
|
|||||||
// TODO? for now assume touch
|
// TODO? for now assume touch
|
||||||
sensorInfo.devType = DAL.DEVICE_TYPE_TOUCH
|
sensorInfo.devType = DAL.DEVICE_TYPE_TOUCH
|
||||||
} else if (newConn == DAL.CONN_NONE || newConn == 0) {
|
} else if (newConn == DAL.CONN_NONE || newConn == 0) {
|
||||||
control.dmesg(`disconnect at port ${sensorInfo.port}`)
|
//control.dmesg(`disconnect at port ${sensorInfo.port}`)
|
||||||
} else {
|
} else {
|
||||||
control.dmesg(`unknown connection type: ${newConn} at ${sensorInfo.port}`)
|
control.dmesg(`unknown connection type: ${newConn} at ${sensorInfo.port}`)
|
||||||
}
|
}
|
||||||
@@ -175,7 +328,7 @@ namespace sensors.internal {
|
|||||||
if (numChanged == 0 && nonActivated == 0)
|
if (numChanged == 0 && nonActivated == 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
control.dmesg(`updating sensor status`)
|
//control.dmesg(`updating sensor status`)
|
||||||
nonActivated = 0;
|
nonActivated = 0;
|
||||||
for (const sensorInfo of sensorInfos) {
|
for (const sensorInfo of sensorInfos) {
|
||||||
if (sensorInfo.devType == DAL.DEVICE_TYPE_IIC_UNKNOWN) {
|
if (sensorInfo.devType == DAL.DEVICE_TYPE_IIC_UNKNOWN) {
|
||||||
@@ -198,7 +351,7 @@ namespace sensors.internal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
control.dmesg(`detect devices done`)
|
//control.dmesg(`detect devices done`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Sensor extends control.Component {
|
export class Sensor extends control.Component {
|
||||||
@@ -214,6 +367,11 @@ namespace sensors.internal {
|
|||||||
this.markUsed();
|
this.markUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
poke() {
|
||||||
|
if (this.isActive())
|
||||||
|
sensorInfos[this._port].poke();
|
||||||
|
}
|
||||||
|
|
||||||
markUsed() {
|
markUsed() {
|
||||||
sensors.__sensorUsed(this._port, this._deviceType());
|
sensors.__sensorUsed(this._port, this._deviceType());
|
||||||
}
|
}
|
||||||
@@ -656,10 +814,10 @@ namespace sensors {
|
|||||||
|
|
||||||
export class ThresholdDetector {
|
export class ThresholdDetector {
|
||||||
public id: number;
|
public id: number;
|
||||||
public min: number;
|
private min: number;
|
||||||
public max: number;
|
private max: number;
|
||||||
public lowThreshold: number;
|
private lowThreshold: number;
|
||||||
public highThreshold: number;
|
private highThreshold: number;
|
||||||
public level: number;
|
public level: number;
|
||||||
public state: ThresholdState;
|
public state: ThresholdState;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
namespace control {
|
||||||
|
export class EulerIntegrator {
|
||||||
|
public value: number;
|
||||||
|
private t: number;
|
||||||
|
private v: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public integrate(derivative: number): void {
|
||||||
|
let now = control.millis();
|
||||||
|
let dt = (now -this.t) / 1000.0;
|
||||||
|
this.value += dt * (this.v + derivative) / 2;
|
||||||
|
|
||||||
|
this.t = now;
|
||||||
|
this.v = derivative;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset() {
|
||||||
|
this.value = 0;
|
||||||
|
this.v = 0;
|
||||||
|
this.t = control.millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -446,11 +446,6 @@ static void runPoller(Thread *thr) {
|
|||||||
// disposeThread(thr);
|
// disposeThread(thr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//%
|
|
||||||
void unsafePollForChanges(int ms, Action query, Action handler) {
|
|
||||||
setupThread(handler, 0, runPoller, query, fromInt(ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t afterProgramPage() {
|
uint32_t afterProgramPage() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -529,6 +524,7 @@ void stopProgram() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void target_reset() {
|
extern "C" void target_reset() {
|
||||||
|
pthread_mutex_trylock(&execMutex);
|
||||||
stopMotors();
|
stopMotors();
|
||||||
stopProgram();
|
stopProgram();
|
||||||
if (lmsPid)
|
if (lmsPid)
|
||||||
|
|||||||
@@ -36,6 +36,13 @@ enum MoveUnit {
|
|||||||
MilliSeconds
|
MilliSeconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum MovePhase {
|
||||||
|
//% block="acceleration"
|
||||||
|
Acceleration,
|
||||||
|
//% block="deceleration"
|
||||||
|
Deceleration
|
||||||
|
}
|
||||||
|
|
||||||
namespace motors {
|
namespace motors {
|
||||||
let pwmMM: MMap
|
let pwmMM: MMap
|
||||||
let motorMM: MMap
|
let motorMM: MMap
|
||||||
@@ -55,7 +62,7 @@ namespace motors {
|
|||||||
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
|
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
|
||||||
if (!motorMM) control.fail("no motor file")
|
if (!motorMM) control.fail("no motor file")
|
||||||
|
|
||||||
resetAllMotors()
|
resetAll()
|
||||||
|
|
||||||
const buf = output.createBuffer(1)
|
const buf = output.createBuffer(1)
|
||||||
buf[0] = DAL.opProgramStart
|
buf[0] = DAL.opProgramStart
|
||||||
@@ -111,20 +118,31 @@ namespace motors {
|
|||||||
* Stops all motors
|
* Stops all motors
|
||||||
*/
|
*/
|
||||||
//% blockId=motorStopAll block="stop all motors"
|
//% blockId=motorStopAll block="stop all motors"
|
||||||
//% weight=1
|
//% weight=2
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
//% help=motors/stop-all
|
//% help=motors/stop-all
|
||||||
export function stopAll() {
|
export function stopAll() {
|
||||||
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
|
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
|
||||||
writePWM(b)
|
writePWM(b);
|
||||||
|
pause(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets all motors
|
* Resets all motors
|
||||||
*/
|
*/
|
||||||
|
//% blockId=motorResetAll block="reset all motors"
|
||||||
|
//% weight=1
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
export function resetAllMotors() {
|
//% help=motors/reset-all
|
||||||
|
export function resetAll() {
|
||||||
reset(Output.ALL)
|
reset(Output.ALL)
|
||||||
|
pause(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MoveSchedule {
|
||||||
|
speed: number;
|
||||||
|
useSteps: boolean;
|
||||||
|
steps: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
//% fixedInstances
|
//% fixedInstances
|
||||||
@@ -132,26 +150,32 @@ namespace motors {
|
|||||||
protected _port: Output;
|
protected _port: Output;
|
||||||
protected _portName: string;
|
protected _portName: string;
|
||||||
protected _brake: boolean;
|
protected _brake: boolean;
|
||||||
|
protected _regulated: boolean;
|
||||||
private _pauseOnRun: boolean;
|
private _pauseOnRun: boolean;
|
||||||
private _initialized: boolean;
|
private _initialized: boolean;
|
||||||
private _brakeSettleTime: number;
|
private _brakeSettleTime: number;
|
||||||
private _init: () => void;
|
private _init: () => void;
|
||||||
private _run: (speed: number) => void;
|
private _accelerationSteps: number;
|
||||||
private _move: (steps: boolean, stepsOrTime: number, speed: number) => void;
|
private _accelerationTime: number;
|
||||||
|
private _decelerationSteps: number;
|
||||||
|
private _decelerationTime: number;
|
||||||
|
|
||||||
protected static output_types: number[] = [0x7, 0x7, 0x7, 0x7];
|
protected static output_types: number[] = [0x7, 0x7, 0x7, 0x7];
|
||||||
|
|
||||||
constructor(port: Output, init: () => void, run: (speed: number) => void, move: (steps: boolean, stepsOrTime: number, speed: number) => void) {
|
constructor(port: Output, init: () => void) {
|
||||||
super();
|
super();
|
||||||
this._port = port;
|
this._port = port;
|
||||||
this._portName = outputToName(this._port);
|
this._portName = outputToName(this._port);
|
||||||
this._brake = false;
|
this._brake = false;
|
||||||
|
this._regulated = true;
|
||||||
this._pauseOnRun = true;
|
this._pauseOnRun = true;
|
||||||
this._initialized = false;
|
this._initialized = false;
|
||||||
this._brakeSettleTime = 10;
|
this._brakeSettleTime = 10;
|
||||||
this._init = init;
|
this._init = init;
|
||||||
this._run = run;
|
this._accelerationSteps = 0;
|
||||||
this._move = move;
|
this._accelerationTime = 0;
|
||||||
|
this._decelerationSteps = 0;
|
||||||
|
this._decelerationTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,6 +238,7 @@ namespace motors {
|
|||||||
//% weight=1 blockGap=8
|
//% weight=1 blockGap=8
|
||||||
//% group="Properties"
|
//% group="Properties"
|
||||||
//% millis.defl=200 millis.min=0 millis.max=500
|
//% millis.defl=200 millis.min=0 millis.max=500
|
||||||
|
//% help=motors/motor/set-brake-settle-time
|
||||||
setBrakeSettleTime(millis: number) {
|
setBrakeSettleTime(millis: number) {
|
||||||
this.init();
|
this.init();
|
||||||
// ensure in [0,500]
|
// ensure in [0,500]
|
||||||
@@ -239,6 +264,9 @@ namespace motors {
|
|||||||
// allow 500ms for robot to settle
|
// allow 500ms for robot to settle
|
||||||
if (this._brake && this._brakeSettleTime > 0)
|
if (this._brake && this._brakeSettleTime > 0)
|
||||||
pause(this._brakeSettleTime);
|
pause(this._brakeSettleTime);
|
||||||
|
else {
|
||||||
|
pause(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected pauseOnRun(stepsOrTime: number) {
|
protected pauseOnRun(stepsOrTime: number) {
|
||||||
@@ -247,6 +275,8 @@ namespace motors {
|
|||||||
this.pauseUntilReady();
|
this.pauseUntilReady();
|
||||||
// allow robot to settle
|
// allow robot to settle
|
||||||
this.settle();
|
this.settle();
|
||||||
|
} else {
|
||||||
|
pause(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +293,42 @@ namespace motors {
|
|||||||
reset(this._port);
|
reset(this._port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private normalizeSchedule(speed: number, step1: number, step2: number, step3: number, unit: MoveUnit): MoveSchedule {
|
||||||
|
const r: MoveSchedule = {
|
||||||
|
speed: Math.clamp(-100, 100, speed >> 0),
|
||||||
|
useSteps: true,
|
||||||
|
steps: [step1 || 0, step2 || 0, step3 || 0]
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
r.useSteps = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r.useSteps = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < r.steps.length; ++i)
|
||||||
|
r.steps[i] = Math.max(0, (r.steps[i] * scale) | 0);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the motor at a given speed for limited time or distance.
|
* Runs the motor at a given speed for limited time or distance.
|
||||||
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
|
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
|
||||||
@@ -277,41 +343,156 @@ namespace motors {
|
|||||||
//% help=motors/motor/run
|
//% help=motors/motor/run
|
||||||
run(speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
|
run(speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
|
||||||
this.init();
|
this.init();
|
||||||
speed = Math.clamp(-100, 100, speed >> 0);
|
const schedule = this.normalizeSchedule(speed, 0, value, 0, unit);
|
||||||
// stop if speed is 0
|
// stop if speed is 0
|
||||||
if (!speed) {
|
if (!schedule.speed) {
|
||||||
this.stop();
|
this.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// special: 0 is infinity
|
// special: 0 is infinity
|
||||||
if (value == 0) {
|
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
|
||||||
this._run(speed);
|
this._run(schedule.speed);
|
||||||
|
pause(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// timed motor moves
|
||||||
|
const steps = schedule.steps;
|
||||||
|
const useSteps = schedule.useSteps;
|
||||||
|
|
||||||
|
// compute ramp up and down
|
||||||
|
steps[0] = (useSteps ? this._accelerationSteps : this._accelerationTime) || 0;
|
||||||
|
steps[2] = (useSteps ? this._decelerationSteps : this._decelerationTime) || 0;
|
||||||
|
if (steps[0] + steps[2] > steps[1]) {
|
||||||
|
// rescale
|
||||||
|
const r = steps[1] / (steps[0] + steps[2]);
|
||||||
|
steps[0] = Math.floor(steps[0] * r);
|
||||||
|
steps[2] *= Math.floor(steps[2] * r);
|
||||||
|
}
|
||||||
|
steps[1] -= (steps[0] + steps[2]);
|
||||||
|
|
||||||
|
// send ramped command
|
||||||
|
this._schedule(schedule);
|
||||||
|
this.pauseOnRun(steps[0] + steps[1] + steps[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
//% blockId=motorSchedule block="ramp %motor at %speed=motorSpeedPicker|\\%|for %value|%unit||accelerate %acceleration|decelerate %deceleration"
|
||||||
|
//% weight=99 blockGap=8
|
||||||
|
//% group="Move"
|
||||||
|
//% motor.fieldEditor="motors"
|
||||||
|
//% help=motors/motor/ramp
|
||||||
|
//% inlineInputMode=inline
|
||||||
|
//% expandableArgumentMode=toggle
|
||||||
|
//% value.defl=500
|
||||||
|
ramp(speed: number, value: number = 500, unit: MoveUnit = MoveUnit.MilliSeconds, acceleration?: number, deceleration?: number) {
|
||||||
|
this.init();
|
||||||
|
const schedule = this.normalizeSchedule(speed, acceleration, value, deceleration, unit);
|
||||||
|
// stop if speed is 0
|
||||||
|
if (!schedule.speed) {
|
||||||
|
this.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// special case: do nothing
|
||||||
|
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// timed motor moves
|
// timed motor moves
|
||||||
let useSteps: boolean;
|
const steps = schedule.steps;
|
||||||
let stepsOrTime: number;
|
// send ramped command
|
||||||
|
this._schedule(schedule);
|
||||||
|
this.pauseOnRun(steps[0] + steps[1] + steps[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the amount of rotation or time for the acceleration
|
||||||
|
* of run commands.
|
||||||
|
*/
|
||||||
|
//% blockId=outputMotorsetRunRamp block="set %motor|run %ramp 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;
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case MoveUnit.Rotations:
|
case MoveUnit.Rotations:
|
||||||
stepsOrTime = (value * 360) >> 0;
|
temp = Math.max(0, (value * 360) | 0);
|
||||||
useSteps = true;
|
if (phase == MovePhase.Acceleration)
|
||||||
|
this._accelerationSteps = temp;
|
||||||
|
else
|
||||||
|
this._decelerationSteps = temp;
|
||||||
break;
|
break;
|
||||||
case MoveUnit.Degrees:
|
case MoveUnit.Degrees:
|
||||||
stepsOrTime = value >> 0;
|
temp = Math.max(0, value | 0);
|
||||||
useSteps = true;
|
if (phase == MovePhase.Acceleration)
|
||||||
|
this._accelerationSteps = temp;
|
||||||
|
else
|
||||||
|
this._decelerationSteps = temp;
|
||||||
break;
|
break;
|
||||||
case MoveUnit.Seconds:
|
case MoveUnit.Seconds:
|
||||||
stepsOrTime = (value * 1000) >> 0;
|
temp = Math.max(0, (value * 1000) | 0);
|
||||||
useSteps = false;
|
if (phase == MovePhase.Acceleration)
|
||||||
|
this._accelerationTime = temp;
|
||||||
|
else
|
||||||
|
this._decelerationTime = temp;
|
||||||
break;
|
break;
|
||||||
default:
|
case MoveUnit.MilliSeconds:
|
||||||
stepsOrTime = value;
|
temp = Math.max(0, value | 0);
|
||||||
useSteps = false;
|
if (phase == MovePhase.Acceleration)
|
||||||
|
this._accelerationTime = temp;
|
||||||
|
else
|
||||||
|
this._decelerationTime = temp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._move(useSteps, stepsOrTime, speed);
|
private _run(speed: number) {
|
||||||
this.pauseOnRun(stepsOrTime);
|
// ramp up acceleration
|
||||||
|
if (this._accelerationTime) {
|
||||||
|
this._schedule({ speed: speed, useSteps: false, steps: [this._accelerationTime, 100, 0] });
|
||||||
|
pause(this._accelerationTime);
|
||||||
|
}
|
||||||
|
// keep going
|
||||||
|
const b = mkCmd(this._port, this._regulated ? DAL.opOutputSpeed : DAL.opOutputPower, 1)
|
||||||
|
b.setNumber(NumberFormat.Int8LE, 2, speed)
|
||||||
|
writePWM(b)
|
||||||
|
if (speed) {
|
||||||
|
writePWM(mkCmd(this._port, DAL.opOutputStart, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _schedule(schedule: MoveSchedule) {
|
||||||
|
const p = {
|
||||||
|
useSteps: schedule.useSteps,
|
||||||
|
step1: schedule.steps[0],
|
||||||
|
step2: schedule.steps[1],
|
||||||
|
step3: schedule.steps[2],
|
||||||
|
speed: this._regulated ? schedule.speed : undefined,
|
||||||
|
power: this._regulated ? undefined : schedule.speed,
|
||||||
|
useBrake: this._brake
|
||||||
|
};
|
||||||
|
step(this._port, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the motor(s) speed should be regulated. Default is true.
|
||||||
|
* @param value true for regulated motor
|
||||||
|
*/
|
||||||
|
//% blockId=outputMotorSetRegulated block="set %motor|regulated %value=toggleOnOff"
|
||||||
|
//% motor.fieldEditor="motors"
|
||||||
|
//% weight=58 blockGap=8
|
||||||
|
//% group="Properties"
|
||||||
|
//% help=motors/motor/set-regulated
|
||||||
|
setRegulated(value: boolean) {
|
||||||
|
this._regulated = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -332,14 +513,18 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorPauseUntilRead block="pause until %motor|ready"
|
//% blockId=motorPauseUntilRead block="pause until %motor|ready"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
//% weight=90
|
//% weight=90 blockGap=8
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
pauseUntilReady(timeOut?: number) {
|
pauseUntilReady(timeOut?: number) {
|
||||||
pauseUntil(() => this.isReady(), timeOut);
|
pauseUntil(() => this.isReady(), timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRunSmoothness(accelerationPercent: number, decelerationPercent: number) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected setOutputType(large: boolean) {
|
protected setOutputType(large: boolean) {
|
||||||
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
||||||
if (this._port & (1 << i)) {
|
if (this._port & (1 << i)) {
|
||||||
// (0x07: Large motor, Medium motor = 0x08)
|
// (0x07: Large motor, Medium motor = 0x08)
|
||||||
MotorBase.output_types[i] = large ? 0x07 : 0x08;
|
MotorBase.output_types[i] = large ? 0x07 : 0x08;
|
||||||
@@ -364,12 +549,10 @@ namespace motors {
|
|||||||
//% fixedInstances
|
//% fixedInstances
|
||||||
export class Motor extends MotorBase {
|
export class Motor extends MotorBase {
|
||||||
private _large: boolean;
|
private _large: boolean;
|
||||||
private _regulated: boolean;
|
|
||||||
|
|
||||||
constructor(port: Output, large: boolean) {
|
constructor(port: Output, large: boolean) {
|
||||||
super(port, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
|
super(port, () => this.__init());
|
||||||
this._large = large;
|
this._large = large;
|
||||||
this._regulated = true;
|
|
||||||
this.markUsed();
|
this.markUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,44 +564,6 @@ namespace motors {
|
|||||||
this.setOutputType(this._large);
|
this.setOutputType(this._large);
|
||||||
}
|
}
|
||||||
|
|
||||||
private __setSpeed(speed: number) {
|
|
||||||
const b = mkCmd(this._port, this._regulated ? DAL.opOutputSpeed : DAL.opOutputPower, 1)
|
|
||||||
b.setNumber(NumberFormat.Int8LE, 2, speed)
|
|
||||||
writePWM(b)
|
|
||||||
if (speed) {
|
|
||||||
writePWM(mkCmd(this._port, DAL.opOutputStart, 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private __move(steps: boolean, stepsOrTime: number, speed: number) {
|
|
||||||
control.dmesg("motor.__move")
|
|
||||||
const p = {
|
|
||||||
useSteps: steps,
|
|
||||||
step1: 0,
|
|
||||||
step2: stepsOrTime,
|
|
||||||
step3: 0,
|
|
||||||
speed: this._regulated ? speed : undefined,
|
|
||||||
power: this._regulated ? undefined : speed,
|
|
||||||
useBrake: this._brake
|
|
||||||
};
|
|
||||||
control.dmesg("motor.1")
|
|
||||||
step(this._port, p)
|
|
||||||
control.dmesg("motor.__move end")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the motor speed should be regulated. Default is true.
|
|
||||||
* @param value true for regulated motor
|
|
||||||
*/
|
|
||||||
//% blockId=outputMotorSetRegulated block="set %motor|regulated %value=toggleOnOff"
|
|
||||||
//% motor.fieldEditor="motors"
|
|
||||||
//% weight=58 blockGap=8
|
|
||||||
//% group="Properties"
|
|
||||||
//% help=motors/motor/set-regulated
|
|
||||||
setRegulated(value: boolean) {
|
|
||||||
this._regulated = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets motor actual speed.
|
* Gets motor actual speed.
|
||||||
* @param motor the port which connects to the motor
|
* @param motor the port which connects to the motor
|
||||||
@@ -476,6 +621,33 @@ namespace motors {
|
|||||||
toString(): string {
|
toString(): string {
|
||||||
return `${this._large ? "" : "M"}${this._portName} ${this.speed()}% ${this.angle()}>`;
|
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
|
//% whenUsed fixedInstance block="large motor A" jres=icons.portA
|
||||||
@@ -506,7 +678,7 @@ namespace motors {
|
|||||||
export class SynchedMotorPair extends MotorBase {
|
export class SynchedMotorPair extends MotorBase {
|
||||||
|
|
||||||
constructor(ports: Output) {
|
constructor(ports: Output) {
|
||||||
super(ports, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
|
super(ports, () => this.__init());
|
||||||
this.markUsed();
|
this.markUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,24 +690,6 @@ namespace motors {
|
|||||||
this.setOutputType(true);
|
this.setOutputType(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private __setSpeed(speed: number) {
|
|
||||||
syncMotors(this._port, {
|
|
||||||
speed: speed,
|
|
||||||
turnRatio: 0, // same speed
|
|
||||||
useBrake: !!this._brake
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private __move(steps: boolean, stepsOrTime: number, speed: number) {
|
|
||||||
syncMotors(this._port, {
|
|
||||||
useSteps: steps,
|
|
||||||
speed: speed,
|
|
||||||
turnRatio: 0, // same speed
|
|
||||||
stepsOrTime: stepsOrTime,
|
|
||||||
useBrake: this._brake
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Move Tank block can make a robot drive forward, backward, turn, or stop.
|
* The Move Tank block can make a robot drive forward, backward, turn, or stop.
|
||||||
* Use the Move Tank block for robot vehicles that have two Large Motors,
|
* Use the Move Tank block for robot vehicles that have two Large Motors,
|
||||||
@@ -561,10 +715,12 @@ namespace motors {
|
|||||||
speedRight = Math.clamp(-100, 100, speedRight >> 0);
|
speedRight = Math.clamp(-100, 100, speedRight >> 0);
|
||||||
|
|
||||||
const speed = Math.abs(speedLeft) > Math.abs(speedRight) ? speedLeft : speedRight;
|
const speed = Math.abs(speedLeft) > Math.abs(speedRight) ? speedLeft : speedRight;
|
||||||
const turnRatio = speedLeft == speed
|
let turnRatio = speedLeft == speed
|
||||||
? (100 - speedRight / speedLeft * 100)
|
? speedLeft == 0 ? 0 : (100 - speedRight / speedLeft * 100)
|
||||||
: (speedLeft / speedRight * 100 - 100);
|
: speedRight == 0 ? 0 : (speedLeft / speedRight * 100 - 100);
|
||||||
|
turnRatio = Math.floor(turnRatio);
|
||||||
|
|
||||||
|
//control.dmesg(`tank ${speedLeft} ${speedRight} => ${turnRatio} ${speed}`)
|
||||||
this.steer(turnRatio, speed, value, unit);
|
this.steer(turnRatio, speed, value, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,7 +743,7 @@ namespace motors {
|
|||||||
this.init();
|
this.init();
|
||||||
speed = Math.clamp(-100, 100, speed >> 0);
|
speed = Math.clamp(-100, 100, speed >> 0);
|
||||||
if (!speed) {
|
if (!speed) {
|
||||||
stop(this._port, this._brake);
|
this.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,10 +752,18 @@ namespace motors {
|
|||||||
let stepsOrTime: number;
|
let stepsOrTime: number;
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case MoveUnit.Rotations:
|
case MoveUnit.Rotations:
|
||||||
|
if (value < 0) {
|
||||||
|
value = -value;
|
||||||
|
speed = -speed;
|
||||||
|
}
|
||||||
stepsOrTime = (value * 360) >> 0;
|
stepsOrTime = (value * 360) >> 0;
|
||||||
useSteps = true;
|
useSteps = true;
|
||||||
break;
|
break;
|
||||||
case MoveUnit.Degrees:
|
case MoveUnit.Degrees:
|
||||||
|
if (value < 0) {
|
||||||
|
value = -value;
|
||||||
|
speed = -speed;
|
||||||
|
}
|
||||||
stepsOrTime = value >> 0;
|
stepsOrTime = value >> 0;
|
||||||
useSteps = true;
|
useSteps = true;
|
||||||
break;
|
break;
|
||||||
@@ -743,26 +907,15 @@ namespace motors {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
speed = Math.clamp(-100, 100, speed)
|
speed = Math.clamp(-100, 100, speed)
|
||||||
control.dmesg('speed: ' + speed)
|
|
||||||
|
|
||||||
let b = mkCmd(out, op, 15)
|
let b = mkCmd(out, op, 15)
|
||||||
control.dmesg('STEP 5')
|
|
||||||
b.setNumber(NumberFormat.Int8LE, 2, speed)
|
b.setNumber(NumberFormat.Int8LE, 2, speed)
|
||||||
// note that b[3] is padding
|
// note that b[3] is padding
|
||||||
control.dmesg('STEP 1')
|
|
||||||
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 0, opts.step1)
|
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 0, opts.step1)
|
||||||
control.dmesg('STEP 2')
|
|
||||||
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 1, opts.step2)
|
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 1, opts.step2)
|
||||||
control.dmesg('STEP 3')
|
|
||||||
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 2, opts.step3)
|
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 2, opts.step3)
|
||||||
control.dmesg('STEP 4')
|
|
||||||
control.dmesg('br ' + opts.useBrake);
|
|
||||||
const br = !!opts.useBrake ? 1 : 0;
|
const br = !!opts.useBrake ? 1 : 0;
|
||||||
control.dmesg('Step 4.5 ' + br)
|
|
||||||
b.setNumber(NumberFormat.Int8LE, 4 + 4 * 3, br)
|
b.setNumber(NumberFormat.Int8LE, 4 + 4 * 3, br)
|
||||||
control.dmesg('STEP 5')
|
|
||||||
writePWM(b)
|
writePWM(b)
|
||||||
control.dmesg('end step')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
"dal.d.ts",
|
"dal.d.ts",
|
||||||
"icons.jres",
|
"icons.jres",
|
||||||
"ns.ts",
|
"ns.ts",
|
||||||
"platform.h"
|
"platform.h",
|
||||||
|
"integrator.ts"
|
||||||
],
|
],
|
||||||
"testFiles": [
|
"testFiles": [
|
||||||
"test.ts"
|
"test.ts"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
//% color="#68C3E2" weight=100 icon="\uf106"
|
//% color="#68C3E2" weight=100 icon="\uf106"
|
||||||
//% groups='["Buttons", "Screen"]'
|
//% groups='["Buttons", "Screen", "Battery"]'
|
||||||
//% labelLineWidth=60
|
//% labelLineWidth=60
|
||||||
namespace brick {
|
namespace brick {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// This is the last thing executed before user code
|
// This is the last thing executed before user code
|
||||||
|
console.addListener(function(msg: string) {
|
||||||
|
control.dmesg(msg.substr(0, msg.length - 1))
|
||||||
|
})
|
||||||
// pulse green, play startup sound, turn off light
|
// pulse green, play startup sound, turn off light
|
||||||
brick.setStatusLight(StatusLight.GreenPulse);
|
brick.setStatusLight(StatusLight.GreenPulse);
|
||||||
// We pause for 100ms to give time to read sensor values, so they work in on_start block
|
// We pause for 100ms to give time to read sensor values, so they work in on_start block
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
```cards
|
```cards
|
||||||
sensors.gyro1.angle();
|
sensors.gyro1.angle();
|
||||||
sensors.gyro1.rate();
|
sensors.gyro1.rate();
|
||||||
|
sensors.gyro1.calibrate();
|
||||||
sensors.gyro1.reset();
|
sensors.gyro1.reset();
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -10,4 +11,6 @@ sensors.gyro1.reset();
|
|||||||
|
|
||||||
[angle](/reference/sensors/gyro/angle),
|
[angle](/reference/sensors/gyro/angle),
|
||||||
[rate](/reference/sensors/gyro/rate),
|
[rate](/reference/sensors/gyro/rate),
|
||||||
|
[reset](/reference/sensors/gyro/calibrate),
|
||||||
[reset](/reference/sensors/gyro/reset)
|
[reset](/reference/sensors/gyro/reset)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ When the brick changes its position, it's moved in the direction of one of the a
|
|||||||
|
|
||||||
## Accuracy and calibration
|
## Accuracy and calibration
|
||||||
|
|
||||||
Gyro sensors aren't perfectly accurate. Sometimes, because of temperature and changes in the way elecricity behaves in the sensor, the gyro returns a small error in it's measurement. This causes the gyro sensor to return an incorrect value for the amount of angle it detected. This might make your robot drive off course and not go to where you want it to.
|
Gyro sensors aren't perfectly accurate. Sometimes, because of temperature and changes in the way electricity behaves in the sensor, the gyro returns a small error in it's measurement. This causes the gyro sensor to return an incorrect value for the amount of angle it detected. This might make your robot drive off course and not go to where you want it to.
|
||||||
|
|
||||||
### Drift
|
### Drift
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ If you want to turn the tank or robot you built to the left by 45 degrees, you m
|
|||||||
|
|
||||||
The problem is that when you need to read the angle measurement frequently, the amount of error in the angle measurement may continue to increase. If the sensor thought it moved by 45 degrees the first time instead of really 49 degrees, your second turn will put you at 98 degrees when the sensor said 90 degrees. If you want a robot to turn right 90 degrees and drive for 5 meters, it might actually turn 98 degrees and drive 0.7 meters off course before it stops. This error in the sensor's measurement is called _drift_.
|
The problem is that when you need to read the angle measurement frequently, the amount of error in the angle measurement may continue to increase. If the sensor thought it moved by 45 degrees the first time instead of really 49 degrees, your second turn will put you at 98 degrees when the sensor said 90 degrees. If you want a robot to turn right 90 degrees and drive for 5 meters, it might actually turn 98 degrees and drive 0.7 meters off course before it stops. This error in the sensor's measurement is called _drift_.
|
||||||
|
|
||||||
### Time to reset
|
### Calibration
|
||||||
|
|
||||||
If errors in the angle values returned by the gyro sensor are making your project not work right, then it's time to **[reset](/reference/sensors/gyro/reset)**. A reset will return the gyro sensor's current angle value back to `0` and _calibrate_ for drift. Calibration is the process of finding out how much error there is in a sensor's measurement and then removing the error from the value returned to your program.
|
If errors in the angle values returned by the gyro sensor are making your project not work right, then it's time to **[reset](/reference/sensors/gyro/reset)**. A reset will return the gyro sensor's current angle value back to `0` and _calibrate_ for drift. Calibration is the process of finding out how much error there is in a sensor's measurement and then removing the error from the value returned to your program.
|
||||||
|
|
||||||
@@ -42,7 +42,14 @@ Turn the brick and press ENTER to see the current rotation angle of `gyro 2`.
|
|||||||
|
|
||||||
```blocks
|
```blocks
|
||||||
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
|
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
|
||||||
brick.showNumber(sensors.gyro2.angle(), 1)
|
sensors.gyro2.reset()
|
||||||
|
})
|
||||||
|
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
})
|
||||||
|
forever(function () {
|
||||||
|
brick.showNumber(control.millis(), 1)
|
||||||
|
brick.showNumber(sensors.gyro2.angle(), 2)
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# calibrate
|
||||||
|
|
||||||
|
Detects if the gyro is drifting and performs a full reset if needed.
|
||||||
|
|
||||||
|
```sig
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
```
|
||||||
|
|
||||||
|
To make the gyro measure rotation angle from the current position of the brick, it is recalibrated. That is, the brick's current position is set to `0` degrees and rotation angle measurements start from there.
|
||||||
|
|
||||||
|
This function adds a few pauses to ensure that the robot is still. If you only want to reset the sensor, use [reset](/reference/gyro-sensor/reset).
|
||||||
|
|
||||||
|
|
||||||
|
## ~hint
|
||||||
|
|
||||||
|
The current position is considered to be the [_horizon_](https://en.wikipedia.org/wiki/Attitude_indicator) or a place that is the _plane of reference_ (this is possibly someplace that's flat for a horizontal reference).
|
||||||
|
|
||||||
|
## ~
|
||||||
|
|
||||||
|
## ~hint
|
||||||
|
|
||||||
|
**Important**
|
||||||
|
|
||||||
|
To properly reset the gyro, the brick must remain still (undisturbed) while the reset operation takes place.
|
||||||
|
|
||||||
|
## ~
|
||||||
|
|
||||||
|
## Calibration states
|
||||||
|
|
||||||
|
Calibration happens in the following phases and each phase is tracked by the brick status light.
|
||||||
|
|
||||||
|
* **orange**: sensor initialization. This phase ensures that the sensor is in the desired mode and ready to collect data.
|
||||||
|
* **orange pulse**: data collection. Light information is being collected, move the sensor over the various light sources to detect.
|
||||||
|
* **green**: calibration success. The calibration data has been saved.
|
||||||
|
* **red flash**: sensor failure. We were unable to connect to the sensor.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
Set the brick on a flat surface. Reset `gyro 2` and tilt the brick slightly. Reset it again while it's still tilted. Lay the brick down flat again and display the angle measurement.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
|
||||||
|
sensors.gyro2.calibrate()
|
||||||
|
})
|
||||||
|
forever(function() {
|
||||||
|
brick.showNumber(sensors.gyro2.angle(), 1)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
[angle](/reference/sensors/gyro/angle), [rate](/reference/sensors/gyro/rate)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# compute Drift
|
||||||
|
|
||||||
|
Called when the sensor is completely still, computes the current rate drift
|
||||||
|
```sig
|
||||||
|
sensors.gyro2.computeDrift()
|
||||||
|
```
|
||||||
|
|
||||||
|
The gyroscope sensor is subject to rate drifting. This means that the measurement reported by the sensor is off by a few degrees per second over time: it is drifting.
|
||||||
|
|
||||||
|
To counter the effect of drifting, call the ``||sensors:compute drift||`` block when the sensor is still to compute the current drift. The rate meansurements will automatically be corrected based on that drift.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This example uses a gyro sensor to
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let error = 0
|
||||||
|
sensors.gyro2.computeDrift()
|
||||||
|
while (sensors.color3.color() != ColorSensorColor.White) {
|
||||||
|
error = sensors.gyro2.rate() * -1
|
||||||
|
motors.largeBC.steer(error, 50)
|
||||||
|
}
|
||||||
|
motors.stopAll()
|
||||||
|
pause(1000)
|
||||||
|
sensors.gyro2.computeDrift()
|
||||||
|
while (sensors.color3.color() != ColorSensorColor.Blue) {
|
||||||
|
error = sensors.gyro2.rate() * -1
|
||||||
|
motors.largeBC.steer(error, 50)
|
||||||
|
}
|
||||||
|
motors.stopAll()
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[rate](/reference/sensors/gyro/rate),
|
||||||
|
[compute drift](/reference/sensors/gyro/compute-drift)
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# drift
|
||||||
|
|
||||||
|
Get the computed rate drift
|
||||||
|
|
||||||
|
```sig
|
||||||
|
sensors.gyro2.drift()
|
||||||
|
```
|
||||||
|
|
||||||
|
The gyroscope sensor is subject to rate drifting. This means that the measurement reported by the sensor is off by a few degrees per second over time: it is drifting.
|
||||||
|
|
||||||
|
To counter the effect of drifting, call the ``||sensors:compute drift||`` block when the sensor is still to compute the current drift. The rate meansurements will automatically be corrected based on that drift.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This example uses a gyro sensor to drive straight until while color is detected.
|
||||||
|
The robot is stopped, the drift is computed and another movement is done.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let error = 0
|
||||||
|
sensors.gyro2.computeDrift()
|
||||||
|
while (sensors.color3.color() != ColorSensorColor.White) {
|
||||||
|
error = sensors.gyro2.rate() * -1
|
||||||
|
motors.largeBC.steer(error, 50)
|
||||||
|
}
|
||||||
|
motors.stopAll()
|
||||||
|
pause(1000)
|
||||||
|
sensors.gyro2.computeDrift()
|
||||||
|
while (sensors.color3.color() != ColorSensorColor.Blue) {
|
||||||
|
error = sensors.gyro2.rate() * -1
|
||||||
|
motors.largeBC.steer(error, 50)
|
||||||
|
}
|
||||||
|
motors.stopAll()
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[rate](/reference/sensors/gyro/rate),
|
||||||
|
[compute drift](/reference/sensors/gyro/compute-drift)
|
||||||
|
|
||||||