Compare commits

..

76 Commits

Author SHA1 Message Date
c4f5e425c2 1.2.3 2019-09-17 11:26:52 -07:00
361ae7a2d2 adding a few pause to allow motors to settle 2019-09-17 11:23:40 -07:00
3769402ade 1.2.2 2019-09-17 10:52:14 -07:00
5c3c83eb52 add a run to avoid tight loop hogging (#913) 2019-09-17 10:52:00 -07:00
0f07a89981 updated docs on offline-app 2019-09-11 19:37:26 -07:00
11d887a213 Releaseing offline app version 1.1.20 (#912) 2019-09-11 18:24:47 -07:00
8150b2dbb0 fix turtle link 2019-09-11 13:23:09 -07:00
89f41f23da 1.2.1 2019-09-10 07:13:42 -07:00
6b78e08053 Edits for 'citys shaper' robot tutorials (#911) 2019-09-09 16:59:12 -07:00
186c86d2b1 rev minor version 2019-09-09 09:09:27 -07:00
181c71d2dd 1.1.22 2019-09-09 09:08:50 -07:00
be05da3232 enabled green screen 2019-09-09 09:08:38 -07:00
5958157a76 1.1.21 2019-09-09 09:04:28 -07:00
f72b825377 reenable permanent storage (#909) 2019-09-09 09:04:12 -07:00
d76af5e5af moving turtle 2019-09-08 21:36:04 -07:00
23f48db20b added turtle program 2019-09-08 21:29:27 -07:00
2975bf2b55 Console to dmesg (#910)
* always pip console.log to dmesg

* trim new line

* restore storage

* restore new line

* remove unused variable
2019-09-07 18:22:39 -07:00
5314515619 Crane mission (#907)
* dummy page

* robot 1 lesson

* added lesson 2

* added mission 2

* added link
2019-09-06 16:10:33 -07:00
75b2db9f67 releasing to 1.1.20 (#908) 2019-09-06 14:24:19 -07:00
cf2e39f1b2 renamed tutorial 2019-09-06 08:47:56 -07:00
6d15d69aa1 updated fll page 2019-09-06 08:33:25 -07:00
72a0940235 1.1.20 2019-09-06 06:22:43 -07:00
b08dd8a7d2 Merge branch 'master' of https://github.com/microsoft/pxt-ev3 2019-09-06 06:22:36 -07:00
def648a98b Block updates follow blog post (#905)
* fix battery block comment

* update run phase blocks

* some more docs
2019-09-06 06:22:12 -07:00
32a92789b3 fix battery block comment 2019-09-06 05:23:48 -07:00
9956bb06fb adding wall follower 2019-09-04 21:42:08 -07:00
83b9aecd7a 1.1.19 2019-09-04 14:52:02 -07:00
17ab24eaa9 Ramp (#904)
* updated ramp block

* updated example
2019-09-04 14:51:45 -07:00
9c5d5f9a86 updated block 2019-09-04 13:50:13 -07:00
43a13e0877 added example 2019-09-04 13:42:29 -07:00
fe0915484d 1.1.18 2019-09-04 12:57:17 -07:00
87a65aa38f battery properties (#903)
* Battery params

* move category

* cleanup

* use property

* fix level

* fix battery computation

* fix level comp

* docs
2019-09-04 12:56:45 -07:00
1317da8904 Remove old instructions 2019-09-03 17:34:34 -07:00
62b2881e2a Embed file deleter in PDF file so we can serve it 2019-09-03 16:29:31 -07:00
bf482a2ac9 Do ZIP not UF2 for file manager 2019-09-03 16:11:02 -07:00
243600ad8f Adding file manager program 2019-09-03 16:08:56 -07:00
349caa4aed 1.1.17 2019-09-02 20:58:29 -07:00
56bbcde299 Support for smooth acceleration/deceleration in run (#900)
* removed logging

* removing more logging

* always use step for single/multiple motors

* refactored schedule

* account for accel ramp up and down

* added default acc/decel

* rounding speed/angle

* remove hack

* use acceleration time in run too

* handle missing case

* adding notes on motors

* adding sample

* fixed ramp simulation

* clear defaults

* some docs, more later

* adding basic examples

* remove debug msg

* clean json

* added move schedule

* docs

* basic docs
2019-09-02 20:57:23 -07:00
7e9cc791ec Some edits for the new sensor/motor examples (#901)
* Some edits for the new sensor/motor examples

* article typo

* dark and bright

* fix block styling

* I like 'on'

* more block styling
2019-09-02 04:20:42 -07:00
d5194b8d28 More samples (#896)
* coast or brake

* fix title

* added lesson
2019-08-31 06:05:36 -07:00
12b1eb349b 1.1.16 2019-08-30 16:53:03 -07:00
68dc195ea4 motor tutorials (#895)
* updated pivots

* redirect support to forum

* adding top level FLL link

* updated wording
2019-08-30 16:52:37 -07:00
0251b914f2 color sensor examples (#894)
* tank zigzag

* reflected light measure

* adding links

* added reflected light calibration

* updated summary
2019-08-30 15:03:09 -07:00
1fc818767c added vscode workspace 2019-08-30 13:57:27 -07:00
9aeaec477f updated FLL page 2019-08-30 12:03:43 -07:00
7fc796d2cb 1.1.15 2019-08-30 11:55:49 -07:00
cb1cd2a4b4 Tutorial category breakout (#437)
* Tutorial category breakout

* Put galleries in config

* restore default tutorial

* updated links

* update tutorials pages

* add infrared
2019-08-30 11:54:30 -07:00
39bd7aa0eb 1.1.14 2019-08-30 10:59:15 -07:00
140ba64462 Gyrofix (#893)
* disable drift correction by default

* disable drift correction

* better calibration sequence

* add comments

* updated comment about beta
2019-08-30 10:58:49 -07:00
42fe96aa5a fix link 2019-08-30 09:40:41 -07:00
1a5b42026d adding redirects for missing links 2019-08-30 09:39:28 -07:00
9fe649aa3c fixed extensions.md 2019-08-30 05:48:41 -07:00
a97dfb17b2 1.1.13 2019-08-30 05:41:11 -07:00
277c9903bb Tank fixes (#892)
* fixing turnration computation

* updated tank computation

* fix rendering glitch

* restore tank computation

* rounding errors
2019-08-30 05:40:51 -07:00
0de8a84de2 1.1.12 2019-08-29 13:11:34 -07:00
a302bbfc2b Single tank (#891)
* add note about dual blocks

* fix simulator to match hardware

* updated wording
2019-08-29 13:11:01 -07:00
bcb682b602 1.1.11 2019-08-28 23:00:03 -07:00
e4a7531541 add rgbraw (#890) 2019-08-28 22:59:41 -07:00
348964c888 1.1.10 2019-08-28 22:43:42 -07:00
8b3461bebd Add exitThread() polling the exit button (#888)
* Add exitThread() polling the exit button

* Add missing includes

* Remove redundant code
2019-08-28 22:43:21 -07:00
e511630c2e Update README.md 2019-08-28 16:27:03 -07:00
db156d5df0 Update README.md 2019-08-28 16:24:55 -07:00
93c6975400 1.1.9 2019-08-28 16:21:41 -07:00
abc93dd7da Multi sensor fix (#887)
* more logging

* set uart mode all at once

* updated logging

* pr feedback
2019-08-28 16:21:06 -07:00
85cfc86bf8 1.1.8 2019-08-28 13:36:50 -07:00
b66d4f2d64 enable storage extension (#886)
* enable storage extension

* fix -beta
2019-08-28 13:36:23 -07:00
5843deab11 1.1.7 2019-08-28 11:39:18 -07:00
8d5edc38bb bump common version (#885) 2019-08-28 11:30:59 -07:00
0309e50058 1.1.6 2019-08-28 08:52:24 -07:00
aa40e7b169 Endprogram (#884)
* moving end program logic to c++

* typo

* always stop on reset
2019-08-28 08:52:01 -07:00
75cf8da396 1.1.5 2019-08-27 17:57:44 -07:00
Max
db9b6a995b IIC added (#870)
* IIC added

* Fixed bug with not detecting device
2019-08-27 17:57:21 -07:00
fb255edafe 1.1.4 2019-08-27 17:28:57 -07:00
f4c39f74e8 1.1.3 2019-08-27 17:05:49 -07:00
3e56e2c3e2 Rgb raw tuning of blocs (#883)
* refactor blocks

* make non-private
2019-08-27 17:05:16 -07:00
79b5f8cc88 Add rgb and reflection raw mods for color sensor (#876)
* Add reflection raw value

Add reflection raw value for color sensor

* update

* Combined rgbraw and refraw
2019-08-27 16:45:12 -07:00
78 changed files with 2124 additions and 405 deletions

View File

@ -1,10 +1,8 @@
# LEGO® MINDSTORMS® Education EV3 for Microsoft MakeCode
[![Build Status](https://ci2.dot.net/buildStatus/icon?job=Private/pxt_project_rainbow/master/pxt-ev3_Push)](https://ci2.dot.net/job/Private/job/pxt_project_rainbow/job/master/job/pxt-ev3_Push/)
# LEGO® MINDSTORMS® Education EV3 for Microsoft MakeCode [![Build Status](https://travis-ci.org/microsoft/pxt-ev3.svg?branch=master)](https://travis-ci.org/microsoft/pxt-ev3)
This repo contains the editor target hosted at https://makecode.mindstorms.com
## Local Dev setup
## Local setup
These instructions assume familiarity with dev tools and languages.
@ -12,10 +10,6 @@ These instructions assume familiarity with dev tools and languages.
* install Docker; make sure `docker` command is in your `PATH`
* (optional) install [Visual Studio Code](https://code.visualstudio.com/)
In a common folder,
* clone https://github.com/Microsoft/pxt to ``pxt`` folder
* clone https://github.com/Microsoft/pxt-common-packages to ``pxt-common-packages`` folder
* clone https://github.com/Microsoft/pxt-ev3 to ``pxt-ev3`` folder
* go to ``pxt`` and run
@ -23,6 +17,18 @@ In a common folder,
npm install
```
* to run the local server,
```
pxt serve --cloud
```
## Local Dev setup
In the common folder,
* clone https://github.com/Microsoft/pxt to ``pxt`` folder
* clone https://github.com/Microsoft/pxt-common-packages to ``pxt-common-packages`` folder
* go to ``pxt-common-packages`` and run
```
@ -54,9 +60,6 @@ cd libs/core
pxt deploy
```
### Jenkins build
https://ci2.dot.net/job/Private/job/pxt_project_rainbow/job/master/
## License
MIT

View File

@ -4,6 +4,7 @@
* [Troubleshoot](/troubleshoot)
* [EV3 Manager](https://ev3manager.education.lego.com/)
* [Forum](https://forum.makecode.com)
* [LEGO Support](https://www.lego.com/service/)
* [FIRST LEGO League](/fll)
@ -19,11 +20,14 @@
* [What Animal Am I?](/tutorials/what-animal-am-i)
* [Music Brick](/tutorials/music-brick)
* [Run Motors](/tutorials/run-motors)
* [Tank ZigZag](/tutorials/tank-zigzag)
* [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)
* [Reflected Light Measure](/tutorials/reflected-light-measure)
* [Reflected Light Calibration](/tutorials/reflected-light-calibration)
* [Object Near?](/tutorials/object-near)
* [Security Alert](/tutorials/security-alert)

View File

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

18
docs/extensions.md Normal file
View File

@ -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.
## ~

BIN
docs/file-manager.pdf Normal file

Binary file not shown.

View File

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

View File

@ -25,6 +25,12 @@
"description": "Build a robot and drive into the world of robotics!",
"url": "/getting-started/use",
"cardType": "side"
},
{
"name": "First LEGO League",
"imageUrl": "/static/fll/fll-big.png",
"description": "Information about using MakeCode in FLL competitions",
"url": "/fll"
}
]
```

View File

@ -1,3 +1,3 @@
{
"appref": "v1.0.11"
"appref": "v1.1.20"
}

View File

@ -388,12 +388,12 @@
}
function downloadWin64() {
// TODO: Keep this link up-to-date with the desired release version
window.open("https://makecode.com/api/release/ev3/v1.0.11/win64");
window.open("https://makecode.com/api/release/ev3/v1.1.20/win64");
tickEvent("offlineapp.download", { "target": "ev3", "platform": "win64" });
}
function downloadMac64() {
// TODO: Keep this link up-to-date with the desired release version
window.open("https://makecode.com/api/release/ev3/v1.0.11/mac64");
window.open("https://makecode.com/api/release/ev3/v1.1.20/mac64");
tickEvent("offlineapp.download", { "target": "ev3", "platform": "mac64" });
}
</script>

View File

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

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

@ -0,0 +1,3 @@
{
"redirect": "/extensions"
}

View File

@ -0,0 +1,3 @@
{
"redirect": "https://makecode.com/extensions/approval"
}

View File

@ -0,0 +1,3 @@
{
"redirect": "https://makecode.com/extensions/getting-started"
}

View File

@ -0,0 +1,3 @@
{
"redirect": "https://makecode.com/extensions/versioning"
}

View File

@ -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)
})
```

View File

@ -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)

View File

@ -6,9 +6,9 @@ Set the rotation speed of the motor as a percentage of maximum speed.
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**.
@ -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.
motors.largeA.run(25, 700, MoveUnit.MilliSeconds);
// Run motor for 700 Milliseconds again but no units specified.
motors.largeA.run(25, 700);
// Run motors B and C for 700 Milliseconds again but no units specified.
motors.largeBC.run(25, 700);
// Run the motor for 45 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
### Drive the motor for 20 seconds

View File

@ -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)

View File

@ -1,28 +1,28 @@
# 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
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.
## Paramters
## Parameters
* **brake**: a [boolean](/types/boolean) value which is either `true` to set the brake on or `false` to set the brake off.
## Example
Run the motor connected to port **A** for 2 seconds at a speed of `30`. 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
motors.largeA.setBrake(true)
motors.largeA.run(30)
pause(2000)
motors.largeA.stop()
motors.largeA.setBrake(true)
```
## See also

View File

@ -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)
})
```

View File

@ -39,6 +39,13 @@ motors.largeBC.steer(-15, -75)
## ~
## ~ hint
Only one set of synchronized motors will run at the same time. Once you launch tank/steer, it will cancel any existing synchronized speed command.
## ~
## Examples
### Make a slight right
@ -79,6 +86,51 @@ for (let i = 0; i < 4; i++) {
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
[tank](/reference/motors/synced/tank), [run](/reference/motors/motor/run)

View File

@ -35,6 +35,12 @@ motors.largeBC.tank(-75, -75)
## ~
## ~ hint
Only one set of synchronized motors will run at the same time. Once you launch tank/steer, it will cancel any existing synchronized speed command.
## ~
## Examples
### Tank forward and backward
@ -76,6 +82,51 @@ pause(5000)
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
[steer](/reference/motors/synced/steer), [run](/reference/motors/motor/run)

BIN
docs/static/fll/fll-big.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
docs/static/tutorials/coast-or-brake.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/static/tutorials/tank-zigzag.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/static/tutorials/wall-follower.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -2,113 +2,46 @@
Step by step guides to coding your @boardname@.
## Brick
## Tutorials
```codecard
[{
"name": "Wake Up!",
"description": "Show different moods on the screen. Is it tired, sleepy, or awake?",
"cardType": "tutorial",
"url":"/tutorials/wake-up",
"name": "Brick",
"description": "Learn how to use the screen and the buttons",
"url":"/tutorials/brick",
"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"
}]
```
## 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",
"name": "Motors",
"description": "User motors to move the brick.",
"url":"/tutorials/motors",
"imageUrl":"/static/tutorials/run-motors.png"
}]
```
## Touch Sensor
```codecard
[{
"name": "Touch to Run",
"description": "Press the Touch Sensor and run a motor.",
"cardType": "tutorial",
"url":"/tutorials/touch-to-run",
}, {
"name": "Touch Sensor",
"description": "Use touch sensors in your robot.",
"url":"/tutorials/touch-sensor",
"imageUrl":"/static/tutorials/touch-to-run.png"
}, {
"name": "Touch 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"
}]
```
## Color Sensor
```codecard
[{
"name": "What Color?",
"description": "Use the Color Sensor to detect different colors.",
"cardType": "tutorial",
"url":"/tutorials/what-color",
"name": "Color Sensor",
"description": "Use the color sensor to follow line or detect colors",
"url":"/tutorials/color-sensor",
"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": "Ultrasonic Sensor",
"description": "Use the ultrasonic sensor to detect obstacles",
"url":"/tutorials/ultrasonic-sensor",
"imageUrl":"/static/tutorials/object-near.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"
}]
```
## Infrared Sensor
```codecard
[{
"name": "Security Alert",
"description": "Build an security alert using the Infrared Sensor.",
"cardType": "tutorial",
"url":"/tutorials/security-alert",
"name": "Infrared Sensor",
"description": "Use the infrared sensor to detect objects",
"url":"/tutorials/infrared-sensor",
"imageUrl":"/static/tutorials/security-alert.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),
[Run Motors](/tutorials/run-motors),
[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)
[Brick tutorials](/tutorials/brick),
[Motors tutorials](/tutorials/motors),
[Touch sensor tutorials](/tutorials/touch-sensor),
[Color sensor tutorials](/tutorials/color-sensor),
[Infrared sensor tutorials](/tutorials/infrared-sensor)

38
docs/tutorials/brick.md Normal file
View File

@ -0,0 +1,38 @@
# 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"
}]
```
## 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),

View File

@ -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)

View File

@ -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)
```

View File

@ -0,0 +1,40 @@
# Robot 1 Lesson
## Step 1 - Build Your Driving Base Robot @unplugged
Build the robot driving base:
[![EV3 Driving Base](/static/lessons/common/ev3-driving-base.jpg)](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
If clicking on the image above doesn't open the instructions, right-click on the image and choose "Save link as..." to download the PDF.
## Step 2 - Show an image @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.neutral)
```
## 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 - 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.neutral)
motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations)
```
## Step 5 - Try your code @fullscreen
Whenever you make a code change, the simulator will restart so you can see what your latest change will do.
When you are ready, click **DOWNLOAD** and follow the instructions to transfer the code into your brick.
**Remember**: Take the driving base apart at the end of the session so that another group can build their robot too.

View File

@ -0,0 +1,94 @@
# Robot 2 Lesson
## Step 1 - Build Your Driving Base Robot @unplugged
Build the robot driving base:
[![EV3 Driving Base](/static/lessons/common/ev3-driving-base.jpg)](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf)
If clicking on the image above doesn't open the instructions, right-click on the image and choose "Save link as..." to download the PDF.
## Step 2 - Show an image and move @fullscreen
Add blocks to the ``||loops:on start||`` block to show an image and move the motors **B+C** for ``1`` rotation.
```blocks
brick.showMood(moods.neutral)
motors.largeBC.steer(0, 50, 1, MoveUnit.Rotations)
```
## Step 3 - Brick button @fullscreen
Let's change the code so that your robot moves when the **UP** button is pressed.
Add an ``||brick:on button up||`` block and move ``||motors:steer motors||`` inside of it.
After downloading your code, press **UP** to move the robot.
```blocks
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
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 () {
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 () {
motors.largeBC.steer(-50, 50, 1, MoveUnit.Rotations)
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
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 () {
motors.largeBC.steer(0, -50, 1, MoveUnit.Rotations)
})
```
## Step 7 - Add an Ultrasonic sensor @fullscreen
Add an Ultrasonic sensor to your driving base.
[![EV3 Driving Base with Ultrasonic Sensor](/static/lessons/common/ev3-ultrasonic-sensor-driving-base.jpg)](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-ultrasonic-sensor-driving-base-61ffdfa461aee2470b8ddbeab16e2070.pdf)
If clicking on the image above doesn't open the instructions, right-click on the image and choose "Save link as..." to download the PDF.
## Step 8 - Stopping distance @fullscreen
Create a program that moves the Driving Base and makes it stop 6 cm from the Cuboid.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
motors.largeBC.steer(0, 50)
pauseUntil(() => sensors.ultrasonic4.distance() < 6)
motors.largeBC.stop()
})
```
Try sending your robot towards a wall!

View File

@ -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)
})
```

View File

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

View File

@ -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)

58
docs/tutorials/motors.md Normal file
View File

@ -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)

View File

@ -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()
```

View File

@ -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)
})
```

View File

@ -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)
})
```

View File

@ -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)
})
```

View File

@ -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)
})
```

View File

@ -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)
})
```

View File

@ -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()
```

View File

@ -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)
})
```

View File

@ -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)

52
docs/tutorials/turtle.md Normal file
View File

@ -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)
})
```

View File

@ -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)

View File

@ -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.

13
ev3.code-workspace Normal file
View File

@ -0,0 +1,13 @@
{
"folders": [
{
"path": "."
},
{
"path": "../pxt-common-packages"
},
{
"path": "../pxt"
}
]
}

View File

@ -15,7 +15,9 @@ enum LightIntensityMode {
//% block="reflected light"
Reflected = ColorSensorMode.ReflectedLightIntensity,
//% block="ambient light"
Ambient = ColorSensorMode.AmbientLightIntensity
Ambient = ColorSensorMode.AmbientLightIntensity,
//% block="reflected light (raw)"
ReflectedRaw = ColorSensorMode.RefRaw
}
const enum ColorSensorColor {
@ -93,6 +95,8 @@ namespace sensors {
|| this.mode == ColorSensorMode.AmbientLightIntensity
|| this.mode == ColorSensorMode.ReflectedLightIntensity)
return this.getNumber(NumberFormat.UInt8LE, 0)
if (this.mode == ColorSensorMode.RefRaw || this.mode == ColorSensorMode.RgbRaw)
return this.getNumber(NumberFormat.UInt16LE, 0)
return 0
}
@ -114,7 +118,7 @@ namespace sensors {
_update(prev: number, curr: number) {
if (this.calibrating) return; // simply ignore data updates while calibrating
if (this.mode == ColorSensorMode.Color)
if (this.mode == ColorSensorMode.Color || this.mode == ColorSensorMode.RgbRaw || this.mode == ColorSensorMode.RefRaw)
control.raiseEvent(this._id, this._colorEventValue(curr));
else
this.thresholdDetector.setLevel(curr);
@ -179,6 +183,23 @@ namespace sensors {
return this.getNumber(NumberFormat.UInt8LE, 0)
}
/**
* Get the current raw rgb values as an array from the color sensor.
* @param sensor the color sensor to query the request
*/
//% help=sensors/color-sensor/rgbraw
//% blockId=colorRgbRaw block="**color sensor** %this| RGB raw"
//% parts="colorsensor"
//% blockNamespace=sensors
//% this.fieldEditor="ports"
//% weight=1
//% group="Color Sensor"
//% blockGap=8
rgbRaw(): number[] {
this.setMode(ColorSensorMode.RgbRaw);
return [this.getNumber(NumberFormat.UInt16LE, 0), this.getNumber(NumberFormat.UInt16LE, 2), this.getNumber(NumberFormat.UInt16LE, 4)];
}
/**
* Registers code to run when the ambient light changes.
* @param condition the light condition
@ -225,11 +246,16 @@ namespace sensors {
//% parts="colorsensor"
//% blockNamespace=sensors
//% this.fieldEditor="ports"
//% weight=87
//% weight=87 blockGap=8
//% group="Color Sensor"
light(mode: LightIntensityMode) {
this.setMode(<ColorSensorMode><number>mode)
return this.getNumber(NumberFormat.UInt8LE, 0)
switch(mode) {
case LightIntensityMode.ReflectedRaw:
return this.reflectedLightRaw();
default:
return this.getNumber(NumberFormat.UInt8LE, 0)
}
}
/**
@ -248,6 +274,15 @@ namespace sensors {
return this.light(LightIntensityMode.Reflected);
}
/**
* Gets the raw reflection light value
*/
//%
reflectedLightRaw(): number {
this.setMode(ColorSensorMode.RefRaw);
return this.getNumber(NumberFormat.UInt16LE, 0);
}
/**
* Set a threshold value
* @param condition the dark or bright light condition

View File

@ -1,13 +1,40 @@
const enum BatteryProperty {
//% block="level (%)"
Level,
//% block="current (I)"
Current,
//% block="voltage (V)"
Voltage
}
namespace brick {
/**
* Returns the current battery level
*/
//% blockId=brickBatteryLevel block="battery level"
//% group="More"
//% group="Battery"
//% help=brick/battery-level
//% deprecated blockHidden=1
export function batteryLevel(): number {
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;
}
}
}

View File

@ -159,12 +159,6 @@ namespace brick {
if (sl[i])
ret |= 1 << i
}
// this needs to be done in query(), which is run without the main JS execution mutex
// otherwise, while(true){} will lock the device
if (ret & DAL.BUTTON_ID_ESCAPE) {
motors.stopAll(); // ensuring that all motors are off
control.reset()
}
return ret
}

View File

@ -25,10 +25,28 @@ namespace sensors.internal {
})
}
export function bufferToString(buf: Buffer): string {
let s = ''
for (let i = 0; i < buf.length; i++)
s += String.fromCharCode(buf[i])
return s
}
let analogMM: MMap
let uartMM: MMap
let IICMM: MMap
let powerMM: MMap
let devcon: Buffer
let sensorInfos: SensorInfo[]
let sensorInfos: SensorInfo[];
let batteryInfo: {
CinCnt: number;
CoutCnt: number;
VinCnt: number;
};
let batteryVMin: number;
let batteryVMax: number;
class SensorInfo {
port: number
@ -36,11 +54,13 @@ namespace sensors.internal {
sensors: Sensor[]
connType: number
devType: number
iicid: string
constructor(p: number) {
this.port = p
this.connType = DAL.CONN_NONE
this.devType = DAL.DEVICE_TYPE_NONE
this.iicid = ''
this.sensors = []
}
}
@ -57,22 +77,25 @@ namespace sensors.internal {
uartMM = control.mmap("/dev/lms_uart", UartOff.Size, 0)
if (!uartMM) control.fail("no uart sensor")
forever(() => {
detectDevices()
pause(500)
})
IICMM = control.mmap("/dev/lms_iic", IICOff.Size, 0)
if (!IICMM) control.fail("no iic sensor")
for (let info_ of sensorInfos) {
let info = info_
powerMM = control.mmap("/dev/lms_power", 2, 0)
unsafePollForChanges(500,
() => { return hashDevices(); },
(prev, curr) => {
detectDevices();
});
sensorInfos.forEach(info => {
unsafePollForChanges(50, () => {
if (info.sensor) return info.sensor._query()
return 0
}, (prev, curr) => {
if (info.sensor) info.sensor._update(prev, curr)
})
}
}
})
}
export function getActiveSensors(): Sensor[] {
init();
@ -89,60 +112,216 @@ namespace sensors.internal {
//serial.writeLine("UART " + port + " / " + mode + " - " + info)
}
export function getBatteryInfo(): { temp: number; current: number } {
init();
return {
temp: analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryTemp),
current: Math.round(analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryCurrent) / 10)
export function readIICID(port: number) {
const buf = output.createBuffer(IICStr.Size)
buf[IICStr.Port] = port
IICMM.ioctl(IO.IIC_READ_TYPE_INFO, buf)
const manufacturer = bufferToString(buf.slice(IICStr.Manufacturer, 8))
const sensorType = bufferToString(buf.slice(IICStr.SensorType, 8))
return manufacturer + sensorType;
}
const ADC_REF = 5000 //!< [mV] maximal value on ADC
const ADC_RES = 4095 //!< [CNT] maximal count on ADC
// see c_ui.c
const SHUNT_IN = 0.11 // [Ohm]
const AMP_CIN = 22.0 // [Times]
const EP2_SHUNT_IN = 0.05 // [Ohm]
const EP2_AMP_CIN = 15.0 // [Times]
const SHUNT_OUT = 0.055 // [Ohm]
const AMP_COUT = 19.0 // [Times]
const VCE = 0.05 // [V]
const AMP_VIN = 0.5 // [Times]
const AVR_CIN = 300
const AVR_COUT = 30
const AVR_VIN = 30
// lms2012
const BATT_INDICATOR_HIGH = 7500 //!< Battery indicator high [mV]
const BATT_INDICATOR_LOW = 6200 //!< Battery indicator low [mV]
const ACCU_INDICATOR_HIGH = 7500 //!< Rechargeable battery indicator high [mV]
const ACCU_INDICATOR_LOW = 7100 //!< Rechargeable battery indicator low [mV]
function CNT_V(C: number) {
return ((C * ADC_REF) / (ADC_RES * 1000.0))
}
function updateBatteryInfo() {
let CinCnt = analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryCurrent);
let CoutCnt = analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.MotorCurrent);
let VinCnt = analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.Cell123456);
if (!batteryInfo) {
batteryVMin = BATT_INDICATOR_LOW;
batteryVMax = BATT_INDICATOR_HIGH;
if (powerMM) {
const accu = powerMM.getNumber(NumberFormat.UInt8LE, 0);
if (accu > 0) {
control.dmesg("rechargeable battery")
batteryVMin = ACCU_INDICATOR_LOW;
batteryVMax = ACCU_INDICATOR_HIGH;
}
}
batteryInfo = {
CinCnt: CinCnt,
CoutCnt: CoutCnt,
VinCnt: VinCnt
};
// update in background
control.runInParallel(() => forever(updateBatteryInfo));
} else {
CinCnt = batteryInfo.CinCnt = ((batteryInfo.CinCnt * (AVR_CIN - 1)) + CinCnt) / AVR_CIN;
CoutCnt = batteryInfo.CoutCnt = ((batteryInfo.CoutCnt * (AVR_COUT - 1)) + CoutCnt) / AVR_COUT;
VinCnt = batteryInfo.VinCnt = ((batteryInfo.VinCnt * (AVR_VIN - 1)) + VinCnt) / AVR_VIN;
}
}
function detectDevices() {
let conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let numChanged = 0
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;
for (let info of sensorInfos) {
let newConn = conns[info.port]
if (newConn == info.connType)
continue
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 {
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let r = 0;
for (let i = 0; i < conns.length; ++i) {
r = (r << 8 | conns[i]);
}
return r;
}
let nonActivated = 0;
function detectDevices() {
//control.dmesg(`detect devices (${nonActivated} na)`)
const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let numChanged = 0;
const uartSensors: SensorInfo[] = [];
for (const sensorInfo of sensorInfos) {
const newConn = conns[sensorInfo.port]
if (newConn == sensorInfo.connType) {
// control.dmesg(`connection unchanged ${newConn} at ${sensorInfo.port}`)
continue;
}
numChanged++
info.connType = newConn
info.devType = DAL.DEVICE_TYPE_NONE
sensorInfo.connType = newConn
sensorInfo.devType = DAL.DEVICE_TYPE_NONE
if (newConn == DAL.CONN_INPUT_UART) {
control.dmesg(`new UART connection at ${info.port}`)
setUartMode(info.port, 0)
let uinfo = readUartInfo(info.port, 0)
info.devType = uinfo[TypesOff.Type]
control.dmesg(`UART type ${info.devType}`)
control.dmesg(`new UART connection at ${sensorInfo.port}`)
updateUartMode(sensorInfo.port, 0);
uartSensors.push(sensorInfo);
} else if (newConn == DAL.CONN_NXT_IIC) {
control.dmesg(`new IIC connection at ${sensorInfo.port}`)
sensorInfo.devType = DAL.DEVICE_TYPE_IIC_UNKNOWN
sensorInfo.iicid = readIICID(sensorInfo.port)
control.dmesg(`IIC ID ${sensorInfo.iicid.length}`)
} else if (newConn == DAL.CONN_INPUT_DUMB) {
control.dmesg(`new DUMB connection at ${info.port}`)
control.dmesg(`new DUMB connection at ${sensorInfo.port}`)
// TODO? for now assume touch
info.devType = DAL.DEVICE_TYPE_TOUCH
sensorInfo.devType = DAL.DEVICE_TYPE_TOUCH
} else if (newConn == DAL.CONN_NONE || newConn == 0) {
control.dmesg(`disconnect at ${info.port}`)
control.dmesg(`disconnect at port ${sensorInfo.port}`)
} else {
control.dmesg(`unknown connection type: ${newConn} at ${info.port}`)
control.dmesg(`unknown connection type: ${newConn} at ${sensorInfo.port}`)
}
}
if (numChanged == 0)
if (uartSensors.length > 0) {
setUartModes();
for (const sensorInfo of uartSensors) {
let uinfo = readUartInfo(sensorInfo.port, 0)
sensorInfo.devType = uinfo[TypesOff.Type]
control.dmesg(`UART type ${sensorInfo.devType}`)
}
}
if (numChanged == 0 && nonActivated == 0)
return
for (let si of sensorInfos) {
if (si.sensor && si.sensor._deviceType() != si.devType) {
si.sensor = null
}
if (si.devType != DAL.DEVICE_TYPE_NONE) {
// TODO figure out compiler problem when '|| null' is added here!
si.sensor = si.sensors.filter(s => s._deviceType() == si.devType)[0]
if (si.sensor == null) {
control.dmesg(`sensor not found for type=${si.devType} at ${si.port}`)
control.dmesg(`updating sensor status`)
nonActivated = 0;
for (const sensorInfo of sensorInfos) {
if (sensorInfo.devType == DAL.DEVICE_TYPE_IIC_UNKNOWN) {
sensorInfo.sensor = sensorInfo.sensors.filter(s => s._IICId() == sensorInfo.iicid)[0]
if (!sensorInfo.sensor) {
control.dmesg(`sensor not found for iicid=${sensorInfo.iicid} at ${sensorInfo.port}`)
nonActivated++;
} else {
control.dmesg(`sensor connected type=${si.devType} at ${si.port}`)
si.sensor._activated()
control.dmesg(`sensor connected iicid=${sensorInfo.iicid} at ${sensorInfo.port}`)
sensorInfo.sensor._activated()
}
} else if (sensorInfo.devType != DAL.DEVICE_TYPE_NONE) {
sensorInfo.sensor = sensorInfo.sensors.filter(s => s._deviceType() == sensorInfo.devType)[0]
if (!sensorInfo.sensor) {
control.dmesg(`sensor not found for type=${sensorInfo.devType} at ${sensorInfo.port}`)
nonActivated++;
} else {
control.dmesg(`sensor connected type=${sensorInfo.devType} at ${sensorInfo.port}`)
sensorInfo.sensor._activated()
}
}
}
//control.dmesg(`detect devices done`)
}
export class Sensor extends control.Component {
@ -187,6 +366,10 @@ namespace sensors.internal {
_deviceType() {
return 0
}
_IICId() {
return ''
}
}
export class AnalogSensor extends Sensor {
@ -242,6 +425,55 @@ namespace sensors.internal {
}
}
export class IICSensor extends Sensor {
protected mode: number // the mode user asked for
protected realmode: number // the mode the hardware is in
private readLength: number
constructor(port: number) {
super(port)
this.mode = 0
this.realmode = 0
this.readLength = 1;
}
_activated() {
this.realmode = 0
this._setMode(this.mode)
}
protected _setMode(m: number) {
let v = m | 0
this.mode = v
if (!this.isActive()) return
if (this.realmode != this.mode) {
this.realmode = v
setIICMode(this._port, this._deviceType(), v)
}
}
getBytes(): Buffer {
return getIICBytes(this.isActive() ? this._port : -1, this.readLength)
}
getNumber(fmt: NumberFormat, off: number) {
if (!this.isActive())
return 0
return getIICNumber(this.readLength, fmt, off, this._port)
}
transaction(deviceAddress: number, write: number[], read: number) {
this.readLength = read;
transactionIIC(this._port, deviceAddress, write, read)
}
_deviceType() {
return DAL.DEVICE_TYPE_IIC_UNKNOWN
}
}
export const iicsensor = new IICSensor(3)
function uartReset(port: number) {
if (port < 0) return
control.dmesg(`UART reset at ${port}`)
@ -266,6 +498,7 @@ namespace sensors.internal {
}
function uartClearChange(port: number) {
control.dmesg(`UART clear change`);
const UART_DATA_READY = 8
const UART_PORT_CHANGED = 1
while (true) {
@ -287,20 +520,43 @@ namespace sensors.internal {
}
}
function setUartModes() {
control.dmesg(`UART set modes`)
uartMM.ioctl(IO.UART_SET_CONN, devcon)
const ports: number[] = [];
for (let port = 0; port < DAL.NUM_INPUTS; ++port) {
if (devcon.getNumber(NumberFormat.Int8LE, DevConOff.Connection + port) == DAL.CONN_INPUT_UART) {
ports.push(port);
}
}
while (ports.length) {
const port = ports.pop();
const status = waitNonZeroUartStatus(port)
control.dmesg(`UART set mode ${status} at ${port}`);
}
}
function updateUartMode(port: number, mode: number) {
control.dmesg(`UART set mode to ${mode} at ${port}`)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_INPUT_UART)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 33)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode)
}
function setUartMode(port: number, mode: number) {
const UART_PORT_CHANGED = 1
while (true) {
if (port < 0) return
control.dmesg(`UART set mode to ${mode} at ${port}`)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_INPUT_UART)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 33)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode)
updateUartMode(port, mode);
uartMM.ioctl(IO.UART_SET_CONN, devcon)
let status = waitNonZeroUartStatus(port)
if (status & UART_PORT_CHANGED) {
control.dmesg(`UART clear changed at ${port}`)
uartClearChange(port)
} else {
break
control.dmesg(`UART status ${status}`);
break;
}
pause(10)
}
@ -321,6 +577,48 @@ namespace sensors.internal {
UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index + off)
}
export function setIICMode(port: number, type: number, mode: number) {
if (port < 0) return;
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_NXT_IIC)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, type)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode)
IICMM.ioctl(IO.IIC_SET_CONN, devcon)
}
export function transactionIIC(port: number, deviceAddress: number, writeBuf: number[], readLen: number) {
if (port < 0) return;
let iicdata = output.createBuffer(IICDat.Size)
iicdata.setNumber(NumberFormat.Int8LE, IICDat.Port, port)
iicdata.setNumber(NumberFormat.Int8LE, IICDat.Repeat, 0)
iicdata.setNumber(NumberFormat.Int16LE, IICDat.Time, 0)
iicdata.setNumber(NumberFormat.Int8LE, IICDat.WrLng, writeBuf.length + 1)
for (let i = 0; i < writeBuf.length; i++)
iicdata.setNumber(NumberFormat.Int8LE, IICDat.WrData + i + 1, writeBuf[i])
iicdata.setNumber(NumberFormat.Int8LE, IICDat.WrData, deviceAddress)
iicdata.setNumber(NumberFormat.Int8LE, IICDat.RdLng, readLen)
IICMM.ioctl(IO.IIC_SETUP, iicdata)
}
export function getIICBytes(port: number, length: number) {
if (port < 0) return output.createBuffer(length);
let index = IICMM.getNumber(NumberFormat.UInt16LE, IICOff.Actual + port * 2);
let buf = IICMM.slice(
IICOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index,
length
);
// Reverse
for (let i = 0; i < length / 2; i++) {
let c = buf[i]
buf[i] = buf[length - i - 1]
buf[length - i - 1] = c
}
return buf;
}
export function getIICNumber(length: number, format: NumberFormat, off: number, port: number) {
return getIICBytes(port, length).getNumber(format, off)
}
const enum NxtColOff {
Calibration = 0, // uint32[4][3]
@ -404,6 +702,52 @@ namespace sensors.internal {
Size = 58
}
const enum IICOff {
TypeData = 0, // Types[8][4]
Repeat = 1792, // uint16[300][4]
Raw = 4192, // int8[32][300][4]
Actual = 42592, // uint16[4]
LogIn = 42600, // uint16[4]
Status = 42608, // int8[4]
Output = 42612, // int8[32][4]
OutputLength = 42740, // int8[4]
Size = 42744
}
const enum IICCtlOff {
TypeData = 0, // Types
Port = 56, // int8
Mode = 57, // int8
Size = 58
}
const enum IICDat {
Result = 0, // result
Port = 4, // int8
Repeat = 5, // int8
Time = 6, // int16
WrLng = 8, // int8
WrData = 9, // int8[32]
RdLng = 41, // int8
RdData = 42, //int8[32]
Size = 74,
}
const enum IICStr {
Port = 0, // int8
Time = 2, // int16
Type = 4, // int8
Mode = 5, // int8
Manufacturer = 6, // int8[9]
SensorType = 15, // int[9]
SetupLng = 24, // int8
SetupString = 28, // ulong
PollLng = 32, // int8
PollString = 36, // ulong
ReadLng = 40, // int8
Size = 44
}
const enum IO {
UART_SET_CONN = 0xc00c7500,
UART_READ_MODE_INFO = 0xc03c7501,
@ -471,7 +815,7 @@ namespace sensors {
}
public threshold(t: ThresholdState): number {
switch(t) {
switch (t) {
case ThresholdState.High: return this.highThreshold;
case ThresholdState.Low: return this.lowThreshold;
default: return (this.max - this.min) / 2;
@ -511,5 +855,5 @@ namespace sensors {
break;
}
}
}
}
}

View File

@ -14,6 +14,9 @@
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "ev3const.h"
#define THREAD_DBG(...)
@ -144,6 +147,29 @@ static void startUsb() {
pthread_detach(pid);
}
static void *exitThread(void *) {
int fd = open("/dev/lms_ui", O_RDWR, 0666);
if (fd < 0)
return 0;
uint8_t *data =
(uint8_t *)mmap(NULL, NUM_BUTTONS, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
return 0;
}
for (;;) {
if (data[5])
target_reset();
sleep_core_us(50000);
}
}
static void startExitThread() {
pthread_t pid;
pthread_create(&pid, NULL, exitThread, NULL);
pthread_detach(pid);
}
void sendUsb(uint16_t code, const char *data, int len) {
while (len > 0) {
int sz = len;
@ -489,14 +515,22 @@ void runLMS() {
}
void stopMotors() {
uint8_t cmd[2] = { 0xA3, 0x0F };
uint8_t cmd[3] = {opOutputStop, 0x0F, 0};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 2);
write(fd, cmd, 3);
close(fd);
}
void stopProgram() {
uint8_t cmd[1] = {opOutputProgramStop};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 1);
close(fd);
}
extern "C" void target_reset() {
stopMotors();
stopProgram();
if (lmsPid)
runLMS();
else
@ -510,6 +544,7 @@ void initRuntime() {
DMESG("runtime starting...");
stopLMS();
startUsb();
startExitThread();
pthread_t disp;
pthread_create(&disp, NULL, evtDispatcher, NULL);
pthread_detach(disp);

View File

@ -36,6 +36,13 @@ enum MoveUnit {
MilliSeconds
}
enum MovePhase {
//% block="acceleration"
Acceleration,
//% block="deceleration"
Deceleration
}
namespace motors {
let pwmMM: MMap
let motorMM: MMap
@ -116,7 +123,8 @@ namespace motors {
//% help=motors/stop-all
export function stopAll() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
writePWM(b)
writePWM(b);
pause(1);
}
/**
@ -125,6 +133,13 @@ namespace motors {
//% group="Move"
export function resetAllMotors() {
reset(Output.ALL)
pause(1);
}
interface MoveSchedule {
speed: number;
useSteps: boolean;
steps: number[];
}
//% fixedInstances
@ -132,26 +147,32 @@ namespace motors {
protected _port: Output;
protected _portName: string;
protected _brake: boolean;
protected _regulated: boolean;
private _pauseOnRun: boolean;
private _initialized: boolean;
private _brakeSettleTime: number;
private _init: () => void;
private _run: (speed: number) => void;
private _move: (steps: boolean, stepsOrTime: number, speed: number) => void;
private _accelerationSteps: number;
private _accelerationTime: number;
private _decelerationSteps: number;
private _decelerationTime: number;
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();
this._port = port;
this._portName = outputToName(this._port);
this._brake = false;
this._regulated = true;
this._pauseOnRun = true;
this._initialized = false;
this._brakeSettleTime = 10;
this._init = init;
this._run = run;
this._move = move;
this._accelerationSteps = 0;
this._accelerationTime = 0;
this._decelerationSteps = 0;
this._decelerationTime = 0;
}
/**
@ -214,6 +235,7 @@ namespace motors {
//% weight=1 blockGap=8
//% group="Properties"
//% millis.defl=200 millis.min=0 millis.max=500
//% help=motors/motor/set-brake-settle-time
setBrakeSettleTime(millis: number) {
this.init();
// ensure in [0,500]
@ -239,6 +261,8 @@ namespace motors {
// allow 500ms for robot to settle
if (this._brake && this._brakeSettleTime > 0)
pause(this._brakeSettleTime);
else
pause(1); // give a tiny breather
}
protected pauseOnRun(stepsOrTime: number) {
@ -247,6 +271,9 @@ namespace motors {
this.pauseUntilReady();
// allow robot to settle
this.settle();
} else {
// give a breather to the event system in tight loops
pause(1);
}
}
@ -263,6 +290,34 @@ namespace motors {
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;
break;
case MoveUnit.Degrees:
r.useSteps = true;
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.
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
@ -277,41 +332,156 @@ namespace motors {
//% help=motors/motor/run
run(speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
const schedule = this.normalizeSchedule(speed, 0, value, 0, unit);
// stop if speed is 0
if (!speed) {
if (!schedule.speed) {
this.stop();
return;
}
// special: 0 is infinity
if (value == 0) {
this._run(speed);
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
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;
}
// timed motor moves
let useSteps: boolean;
let stepsOrTime: number;
const steps = schedule.steps;
// 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) {
case MoveUnit.Rotations:
stepsOrTime = (value * 360) >> 0;
useSteps = true;
temp = Math.max(0, (value * 360) | 0);
if (phase == MovePhase.Acceleration)
this._accelerationSteps = temp;
else
this._decelerationSteps = temp;
break;
case MoveUnit.Degrees:
stepsOrTime = value >> 0;
useSteps = true;
temp = Math.max(0, value | 0);
if (phase == MovePhase.Acceleration)
this._accelerationSteps = temp;
else
this._decelerationSteps = temp;
break;
case MoveUnit.Seconds:
stepsOrTime = (value * 1000) >> 0;
useSteps = false;
temp = Math.max(0, (value * 1000) | 0);
if (phase == MovePhase.Acceleration)
this._accelerationTime = temp;
else
this._decelerationTime = temp;
break;
default:
stepsOrTime = value;
useSteps = false;
case MoveUnit.MilliSeconds:
temp = Math.max(0, value | 0);
if (phase == MovePhase.Acceleration)
this._accelerationTime = temp;
else
this._decelerationTime = temp;
break;
}
}
this._move(useSteps, stepsOrTime, speed);
this.pauseOnRun(stepsOrTime);
private _run(speed: number) {
// 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;
}
/**
@ -338,8 +508,12 @@ namespace motors {
pauseUntil(() => this.isReady(), timeOut);
}
setRunSmoothness(accelerationPercent: number, decelerationPercent: number) {
}
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)) {
// (0x07: Large motor, Medium motor = 0x08)
MotorBase.output_types[i] = large ? 0x07 : 0x08;
@ -364,12 +538,10 @@ namespace motors {
//% fixedInstances
export class Motor extends MotorBase {
private _large: boolean;
private _regulated: 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._regulated = true;
this.markUsed();
}
@ -381,44 +553,6 @@ namespace motors {
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.
* @param motor the port which connects to the motor
@ -506,7 +640,7 @@ namespace motors {
export class SynchedMotorPair extends MotorBase {
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();
}
@ -518,24 +652,6 @@ namespace motors {
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.
* Use the Move Tank block for robot vehicles that have two Large Motors,
@ -561,10 +677,12 @@ namespace motors {
speedRight = Math.clamp(-100, 100, speedRight >> 0);
const speed = Math.abs(speedLeft) > Math.abs(speedRight) ? speedLeft : speedRight;
const turnRatio = speedLeft == speed
? (100 - speedRight / speedLeft * 100)
: (speedLeft / speedRight * 100 - 100);
let turnRatio = speedLeft == speed
? speedLeft == 0 ? 0 : (100 - speedRight / speedLeft * 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);
}
@ -587,7 +705,7 @@ namespace motors {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
stop(this._port, this._brake);
this.stop();
return;
}
@ -743,26 +861,15 @@ namespace motors {
return
}
speed = Math.clamp(-100, 100, speed)
control.dmesg('speed: ' + speed)
let b = mkCmd(out, op, 15)
control.dmesg('STEP 5')
b.setNumber(NumberFormat.Int8LE, 2, speed)
// note that b[3] is padding
control.dmesg('STEP 1')
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 0, opts.step1)
control.dmesg('STEP 2')
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 1, opts.step2)
control.dmesg('STEP 3')
b.setNumber(NumberFormat.Int32LE, 4 + 4 * 2, opts.step3)
control.dmesg('STEP 4')
control.dmesg('br ' + opts.useBrake);
const br = !!opts.useBrake ? 1 : 0;
control.dmesg('Step 4.5 ' + br)
b.setNumber(NumberFormat.Int8LE, 4 + 4 * 3, br)
control.dmesg('STEP 5')
writePWM(b)
control.dmesg('end step')
}
}

View File

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

View File

@ -1,5 +1,7 @@
// 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
brick.setStatusLight(StatusLight.GreenPulse);
// We pause for 100ms to give time to read sensor values, so they work in on_start block

View File

@ -3,6 +3,7 @@
```cards
sensors.gyro1.angle();
sensors.gyro1.rate();
sensors.gyro1.calibrate();
sensors.gyro1.reset();
```
@ -10,4 +11,6 @@ sensors.gyro1.reset();
[angle](/reference/sensors/gyro/angle),
[rate](/reference/sensors/gyro/rate),
[reset](/reference/sensors/gyro/calibrate),
[reset](/reference/sensors/gyro/reset)

View File

@ -12,7 +12,7 @@ When the brick changes its position, it's moved in the direction of one of the a
## 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
@ -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_.
### 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.
@ -42,7 +42,14 @@ Turn the brick and press ENTER to see the current rotation angle of `gyro 2`.
```blocks
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)
})
```

View File

@ -0,0 +1,51 @@
# calibrate
Reset the zero reference for the gyro to current position of the brick.
```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)

View File

@ -13,12 +13,39 @@ When the brick is in motion, it moves in the direction of one of axes used to me
* a [number](/types/number) that is the current rate of rotation in degrees per second.
## ~hint
## Accuracy and calibration
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
If you want to turn the tank or robot you built to the left by 45 degrees, you might run the motor on the right side until the gyro reported that you turned by 45 degrees. What if the gyro was getting an error of 4 degrees every time it measured an angle? You may have actually turned 49 degrees when you expected to turn 45 degrees. Well, that might not be too bad if you use the gyro's angle value only once. It's fine if you just wanted to turn and stop or drive a short distance in only that direction.
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_.
### 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.
Are you using a gyro sensor in your project and need accuracy for your angle values? You should reset the gyro sensor at a regular intervals to improve precision in the values reported to your program.
## ~
## Example
Flash the status light to red if the roll rate of `gyro 2` is more that `30` degrees per second.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.reset()
})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.calibrate()
})
forever(function () {
brick.showNumber(sensors.gyro2.rate(), 2)
if (sensors.gyro2.rate() > 30) {
brick.setStatusLight(StatusLight.RedFlash)
} else {

View File

@ -1,6 +1,6 @@
# reset
Reset the zero reference for the gyro to current position of the brick.
Reset the gyro sensor.
```sig
sensors.gyro2.reset()
@ -8,6 +8,8 @@ sensors.gyro2.reset()
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 only resets the sensor; if you wish to have progress indication and a more robust calibration sequence, use [calibrate](/reference/gyro-sensor/calibrate).
## ~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).
@ -18,27 +20,18 @@ The current position is considered to be the [_horizon_](https://en.wikipedia.or
**Important**
To properly reset the gyro, the brick must remain still (undistrurbed) while the reset operation takes place.
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 slighly. Reset it again while it's still tilted. Lay the brick down flat again and display the angle measurement.
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.reset()
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
forever(function() {
brick.showNumber(sensors.gyro2.angle(), 1)
})
```

View File

@ -9,12 +9,12 @@ namespace sensors {
export class GyroSensor extends internal.UartSensor {
private calibrating: boolean;
private _drift: number;
private _drifting: boolean;
private _driftCorrection: boolean;
constructor(port: number) {
super(port)
this.calibrating = false;
this._drift = 0;
this._drifting = true;
this._driftCorrection = false;
this.setMode(GyroSensorMode.Rate);
}
@ -70,14 +70,78 @@ namespace sensors {
this.setMode(GyroSensorMode.Rate);
let curr = this._query();
if (Math.abs(curr) < 20) {
const p = 0.0005;
if (Math.abs(curr) < 4 && this._driftCorrection) {
const p = 0.01;
this._drift = (1 - p) * this._drift + p * curr;
curr -= this._drift;
curr = Math.round(curr - this._drift);
}
return curr;
}
/**
* Forces a calibration of the with light progress indicators.
* Must be called when the sensor is completely still.
*/
//% help=sensors/gyro/calibrate
//% block="calibrate **gyro** %this|"
//% blockId=gyroCalibrate
//% parts="gyroscope"
//% blockNamespace=sensors
//% this.fieldEditor="ports"
//% weight=51 blockGap=8
//% group="Gyro Sensor"
calibrate(): void {
if (this.calibrating) return; // already in calibration mode
const statusLight = brick.statusLight(); // save current status light
brick.setStatusLight(StatusLight.Orange);
this.calibrating = true;
// may be triggered by a button click,
// give time for robot to settle
pause(700);
// calibrating
brick.setStatusLight(StatusLight.OrangePulse);
// send a reset command
super.reset();
// wait till sensor is live
pauseUntil(() => this.isActive(), 7000);
// mode toggling
this.setMode(GyroSensorMode.Rate);
this.setMode(GyroSensorMode.Angle);
// switch back to the desired mode
this.setMode(this.mode);
// check sensor is ready
if (!this.isActive()) {
brick.setStatusLight(StatusLight.RedFlash); // didn't work
pause(2000);
brick.setStatusLight(statusLight); // restore previous light
this.calibrating = false;
return;
}
// compute drift
this._drift = 0;
if (this._driftCorrection && this.mode == GyroSensorMode.Rate) {
const n = 100;
for (let i = 0; i < n; ++i) {
this._drift += this._query();
pause(4);
}
this._drift /= n;
}
brick.setStatusLight(StatusLight.Green); // success
pause(1000);
brick.setStatusLight(statusLight); // resture previous light
// and we're done
this.calibrating = false;
}
/**
* Forces a calibration of the gyro. Must be called when the sensor is completely still.
*/
@ -92,49 +156,10 @@ namespace sensors {
reset(): void {
if (this.calibrating) return; // already in calibration mode
const statusLight = brick.statusLight(); // save current status light
brick.setStatusLight(StatusLight.Orange);
this.calibrating = true;
// may be triggered by a button click,
// give time for robot to settle
pause(700);
// send a reset command
super.reset();
// switch back to the desired mode
this.setMode(this.mode);
// wait till sensor is live
pauseUntil(() => this.isActive(), 5000);
// check sensor is ready
if (!this.isActive()) {
brick.setStatusLight(StatusLight.RedFlash); // didn't work
pause(2000);
brick.setStatusLight(statusLight); // restore previous light
return;
}
// give it a bit of time to init
pause(1000)
// calibrating
brick.setStatusLight(StatusLight.OrangePulse);
// compute drift
this._drift = 0;
if (this.mode == GyroSensorMode.Rate) {
for (let i = 0; i < 200; ++i) {
this._drift += this._query();
pause(4);
}
this._drift /= 200;
}
brick.setStatusLight(StatusLight.Green); // success
pause(1000);
brick.setStatusLight(statusLight); // resture previous light
// and we're done
// and done
this.calibrating = false;
}
@ -152,7 +177,8 @@ namespace sensors {
*/
//%
setDriftCorrection(enabled: boolean) {
this._drifting = enabled;
this._driftCorrection = enabled;
this._drift = 0;
}
}

View File

@ -1,6 +1,6 @@
{
"name": "storage",
"description": "USB Pen-drive support and flash storage",
"description": "USB Pen-drive support and flash storage - beta",
"files": [
"storage.cpp",
"storage-core.ts",

View File

@ -199,4 +199,10 @@ namespace storage {
return '/' + filename;
}
}
/**
* Permanent storage on the brick, must be deleted with code.
*/
//% whenUsed fixedInstance block="permanent"
export const permanent: Storage = new PermanentStorage();
}

View File

@ -3,7 +3,6 @@ namespace storage {
storage.temporary.remove("console.txt");
console.addListener(function(line) {
const fn = "console.txt";
const mxs = 65536;
const t = control.millis();
storage.temporary.appendLine(fn, `${t}> ${line}`);
storage.temporary.limit(fn, 65536);

View File

@ -1,8 +1,8 @@
{
"name": "pxt-ev3",
"version": "1.1.2",
"version": "1.2.3",
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
"private": true,
"private": false,
"keywords": [
"JavaScript",
"education",
@ -39,7 +39,7 @@
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.23.56",
"pxt-common-packages": "0.23.61",
"pxt-core": "4.0.9"
},
"scripts": {

View File

@ -16,7 +16,8 @@
"libs/infrared-sensor",
"libs/gyro-sensor",
"libs/screen",
"libs/ev3"
"libs/ev3",
"libs/storage"
],
"simulator": {
"autoRun": true,
@ -100,6 +101,7 @@
"copyrightText": "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.",
"crowdinProject": "kindscript",
"selectLanguage": true,
"greenScreen": true,
"availableLocales": [
"en",
"de",
@ -112,7 +114,7 @@
"docMenu": [
{
"name": "Support",
"path": "https://www.lego.com/service/"
"path": "https://forum.makecode.com/"
},
{
"name": "Troubleshoot",
@ -129,6 +131,10 @@
{
"name": "Reference",
"path": "/reference"
},
{
"name": "FIRST LEGO League",
"path": "/fll"
}
],
"print": true,

View File

@ -27,6 +27,7 @@ namespace pxsim.sensors {
export function __sensorUsed(port: number, type: number) {
//console.log("SENSOR INIT " + port + ", type: " + type);
if (type == DAL.DEVICE_TYPE_IIC_UNKNOWN) return; // Ignore IIC
if (!ev3board().hasSensor(port)) {
const sensor = ev3board().getSensor(port, type);
runtime.queueDisplayUpdate();

View File

@ -1,4 +1,5 @@
namespace pxsim {
const MIN_RAMP_SPEED = 3;
export class MotorNode extends BaseNode {
isOutput = true;
@ -30,11 +31,11 @@ namespace pxsim {
}
getSpeed() {
return this.speed * (!this._synchedMotor && this.polarity == 0 ? -1 : 1);
return Math.round(this.speed * (!this._synchedMotor && this.polarity == 0 ? -1 : 1));
}
getAngle() {
return this.angle;
return Math.round(this.angle);
}
// returns the slave motor if any
@ -63,6 +64,12 @@ namespace pxsim {
delete this.speedCmd;
delete this.speedCmdValues;
delete this._synchedMotor;
this.setChangedState();
}
clearSyncCmd() {
if (this._synchedMotor)
this.clearSpeedCmd();
}
setLarge(large: boolean) {
@ -154,13 +161,19 @@ namespace pxsim {
const dstep = isTimeCommand
? pxsim.U.now() - this.speedCmdTime
: this.tacho - this.speedCmdTacho;
if (dstep < step1) // rampup
if (step1 && dstep < step1) { // rampup
this.speed = speed * dstep / step1;
// ensure non-zero speed
this.speed = Math.max(MIN_RAMP_SPEED, Math.ceil(Math.abs(this.speed))) * Math.sign(speed);
}
else if (dstep < step1 + step2) // run
this.speed = speed;
else if (dstep < step1 + step2 + step3)
this.speed = speed * (step1 + step2 + step3 - dstep) / (step1 + step2 + step3);
else {
else if (step2 && dstep < step1 + step2 + step3) {
this.speed = speed * (step1 + step2 + step3 - dstep)
/ (step1 + step2 + step3) + 5;
// ensure non-zero speed
this.speed = Math.max(MIN_RAMP_SPEED, Math.ceil(Math.abs(this.speed))) * Math.sign(speed);
} else {
if (brake) this.speed = 0;
if (!isTimeCommand) {
// we need to patch the actual position of the motor when
@ -179,11 +192,13 @@ namespace pxsim {
case DAL.opOutputStepSync:
case DAL.opOutputTimeSync: {
const otherMotor = this._synchedMotor;
if (otherMotor.port < this.port) // handled in other motor code
break;
const speed = this.speedCmdValues[0];
const turnRatio = this.speedCmdValues[1];
// if turnratio is negative, right motor at power level
// right motor -> this.port > otherMotor.port
if (Math.sign(this.port - otherMotor.port)
== Math.sign(turnRatio))
break; // handled in other motor code
const stepsOrTime = this.speedCmdValues[2];
const brake = this.speedCmdValues[3];
const dstep = this.speedCmd == DAL.opOutputTimeSync
@ -199,12 +214,7 @@ namespace pxsim {
// turn ratio is a bit weird to interpret
// see https://communities.theiet.org/blogs/698/1706
if (turnRatio < 0) {
otherMotor.speed = speed;
this.speed *= (100 + turnRatio) / 100;
} else {
otherMotor.speed = this.speed * (100 - turnRatio) / 100;
}
otherMotor.speed = this.speed * (100 - Math.abs(turnRatio)) / 100;
// clamp
this.speed = Math.max(-100, Math.min(100, this.speed >> 0));
@ -224,11 +234,10 @@ namespace pxsim {
this.angle = this.manualReferenceAngle + this.manualAngle;
this.setChangedState();
}
this.speed = Math.round(this.speed); // integer only
// don't round speed
// compute delta angle
const rotations = this.getSpeed() / 100 * this.rotationsPerMilliSecond * elapsed;
const deltaAngle = Math.round(rotations * 360);
const rotations = this.speed / 100 * this.rotationsPerMilliSecond * elapsed;
const deltaAngle = rotations * 360;
if (deltaAngle) {
this.angle += deltaAngle;
this.tacho += Math.abs(deltaAngle);

View File

@ -80,6 +80,12 @@ namespace pxsim {
const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 12);
const motors = ev3board().getMotor(port);
// cancel any other sync command
for(const motor of ev3board().getMotors().filter(motor => motors.indexOf(motor) < 0)) {
motor.clearSyncCmd()
}
// apply commands to all motors
for (const motor of motors) {
const otherMotor = motors.filter(m => m.port != motor.port)[0];
motor.setSyncCmd(

View File

@ -9,13 +9,19 @@
},
"galleries": {
"Getting Started": "getting-started",
"Tutorials": "tutorials",
"Brick Tutorials": "tutorials/brick",
"Motor Tutorials": "tutorials/motors",
"Touch Sensor Tutorials": "tutorials/touch-sensor",
"Color Sensor Tutorials": "tutorials/color-sensor",
"Ultrasonic Sensor Tutorials": "tutorials/ultrasonic-sensor",
"Infrared Sensor Tutorials": "tutorials/infrared-sensor",
"FLL / City Shaper / Crane Mission": "tutorials/city-shaper/crane-mission",
"Design Engineering": "design-engineering",
"Coding": "coding",
"Maker": "maker",
"Videos": "videos"
},
"electronManifest": {
"latest": "v1.0.11"
"latest": "v1.1.20"
}
}