Compare commits

...

37 Commits

Author SHA1 Message Date
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
312729142f 1.1.2 2019-08-27 16:42:36 -07:00
Max
5bd4aed0e1 Multiple sensors 2 (#877)
* Fixed -1 mode in libs

* Deleted todo
2019-08-27 16:34:55 -07:00
cfaa4ae3ef migrate build to travis 2019-08-27 16:29:18 -07:00
faa839d59f 1.1.1 2019-08-27 16:26:37 -07:00
630687bfce bump to 1.1 2019-08-27 16:26:19 -07:00
2b300a4094 Maximmasterr set settle (#882)
* Added set settle time

* updated blocks
2019-08-27 14:57:28 -07:00
5fb8c0de6e delete package-lock.json 2019-08-27 14:45:25 -07:00
1f65cd59a8 Fixes to "Line Following in Loop" Sample Solutions (#871)
Sample Solutions were wrong for both Methods of Line Following in a Loop
2019-08-27 14:27:20 -07:00
32 changed files with 726 additions and 4928 deletions

9
.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: node_js
node_js:
- "8.9.0"
script:
- "node node_modules/pxt-core/built/pxt.js travis"
sudo: false
cache:
directories:
- node_modules

View File

@ -1,10 +1,8 @@
# LEGO® MINDSTORMS® Education EV3 for Microsoft MakeCode # 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)
[![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/)
This repo contains the editor target hosted at https://makecode.mindstorms.com 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. 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` * install Docker; make sure `docker` command is in your `PATH`
* (optional) install [Visual Studio Code](https://code.visualstudio.com/) * (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 * clone https://github.com/Microsoft/pxt-ev3 to ``pxt-ev3`` folder
* go to ``pxt`` and run * go to ``pxt`` and run
@ -23,6 +17,18 @@ In a common folder,
npm install 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 * go to ``pxt-common-packages`` and run
``` ```
@ -54,9 +60,6 @@ cd libs/core
pxt deploy pxt deploy
``` ```
### Jenkins build
https://ci2.dot.net/job/Private/job/pxt_project_rainbow/job/master/
## License ## License
MIT MIT

View File

@ -190,12 +190,10 @@ if (true) {
```blocks ```blocks
forever(function () { forever(function () {
while (true) { while (sensors.color3.color() == ColorSensorColor.Black) {
sensors.color3.pauseUntilColorDetected(ColorSensorColor.Black)
motors.largeBC.steer(-30, 50) motors.largeBC.steer(-30, 50)
} }
while (true) { while (sensors.color3.color() == ColorSensorColor.White) {
sensors.color3.pauseUntilColorDetected(ColorSensorColor.White)
motors.largeBC.steer(30, 50) motors.largeBC.steer(30, 50)
} }
}) })
@ -209,11 +207,9 @@ Else the Color Sensor detects the color white, start motors ``B`` and ``C`` (dri
```blocks ```blocks
forever(function () { forever(function () {
if (true) { if (sensors.color3.color() == ColorSensorColor.Black) {
sensors.color3.pauseUntilColorDetected(ColorSensorColor.Black)
motors.largeBC.steer(-30, 50) motors.largeBC.steer(-30, 50)
} else { } else {
sensors.color3.pauseUntilColorDetected(ColorSensorColor.White)
motors.largeBC.steer(30, 50) motors.largeBC.steer(30, 50)
} }
}) })

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

View File

@ -6,6 +6,10 @@ For teams participating in the Open Software Platform Pilot utilizing MakeCode,
## FAQ ## FAQ
### I found a bug what do I do?
If you found a bug, please try if it hasn't been fixed yet! Go to https://makecode.mindstorms.com/beta and try if the bug is corrected. Otherwise, please open an issue at https://github.com/microsoft/pxt-ev3/issues.
### How do I use MakeCode with my EV3? ### How do I use MakeCode with my EV3?
* You will need to install the latest EV3 firmware on your brick. Instructions on how to do that are located here: https://makecode.mindstorms.com/troubleshoot. * You will need to install the latest EV3 firmware on your brick. Instructions on how to do that are located here: https://makecode.mindstorms.com/troubleshoot.

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

@ -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 ## Examples
### Make a slight right ### Make a slight right
@ -79,6 +86,51 @@ for (let i = 0; i < 4; i++) {
motors.stopAll() motors.stopAll()
``` ```
### Steer tester
This program lets you change the values of speed and turn ratio with the buttons.
```typescript
let speed = 0;
let turnRatio = 0;
brick.showString(`steer tester`, 1)
brick.showString(`connect motors BC`, 7)
brick.showString(`up/down for speed`, 8)
brick.showString(`left/right for turn ratio`, 9)
forever(function () {
brick.showString(`motor B speed ${motors.largeB.speed()}%`, 4)
brick.showString(`motor C speed ${motors.largeC.speed()}%`, 5)
pause(100)
})
function updateSteer() {
motors.largeBC.steer(turnRatio, speed);
brick.showString(`speed ${speed}%`, 2)
brick.showString(`turnRatio ${turnRatio}`, 3)
}
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
speed += 10
updateSteer()
})
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
speed -= 10
updateSteer()
})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
turnRatio -= 10
updateSteer()
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
turnRatio += 10
updateSteer()
})
updateSteer()
```
## See also ## See also
[tank](/reference/motors/synced/tank), [run](/reference/motors/motor/run) [tank](/reference/motors/synced/tank), [run](/reference/motors/motor/run)

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 ## Examples
### Tank forward and backward ### Tank forward and backward
@ -76,6 +82,51 @@ pause(5000)
motors.stopAll() motors.stopAll()
``` ```
### Tank tester
This program lets you change the tank values using the brick buttons
```typescript
let tankB = 0;
let tankC = 0;
brick.showString(`tank tester`, 1)
brick.showString(`connect motors BC`, 7)
brick.showString(`up/down for tank B`, 8)
brick.showString(`left/right for tank C`, 9)
forever(function () {
brick.showString(`motor B speed ${motors.largeB.speed()}%`, 4)
brick.showString(`motor C speed ${motors.largeC.speed()}%`, 5)
pause(100)
})
function updateTank() {
brick.showString(`tank A: ${tankB}%`, 2)
brick.showString(`tank B: ${tankC}%`, 3)
motors.largeBC.tank(tankB, tankC);
}
brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
tankB += 10
updateTank();
})
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
tankB -= 10
updateTank();
})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
tankC += 10
updateTank();
})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
tankC -= 10
updateTank();
})
updateTank();
```
## See also ## See also
[steer](/reference/motors/synced/steer), [run](/reference/motors/motor/run) [steer](/reference/motors/synced/steer), [run](/reference/motors/motor/run)

View File

@ -1,41 +0,0 @@
import jobs.generation.Utilities;
import jobs.generation.InternalUtilities;
def project = GithubProject
def projectName = "pxt-ev3"
[true, false].each { isPR ->
def newJobName = projectName
if (isPR) {
newJobName += "_PR"
} else {
newJobName += "_Push"
}
def newJob = job(newJobName) {
steps {
shell("chmod +x ./jenkins.sh")
shell("./jenkins.sh ${isPR}")
}
if (!isPR) {
wrappers {
credentialsBinding {
string("PXT_ACCESS_TOKEN", "pxt_access_token")
string("PXT_RELEASE_REPO", "pxt_release_repo_ev3")
string("CROWDIN_KEY", "pxt_crowdin_key")
}
}
}
}
Utilities.setMachineAffinity(newJob, "Ubuntu", "20161020")
InternalUtilities.standardJobSetup(newJob, project, isPR, "*/*")
if (isPR) {
Utilities.addGithubPRTrigger(newJob, "Default Testing")
} else {
Utilities.addGithubPushTrigger(newJob)
}
}

View File

@ -1,56 +0,0 @@
#!/usr/bin/env bash
# Set up NVM
export NVM_DIR="/home/dotnet-bot/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm install 8
# Set up build environment variables
echo ---------- Setting build environment variables
echo Git branch: $GIT_BRANCH
echo isPR: $1
originRegex="^origin/.*"
branchRegex="^origin/\K.*(?=$)"
releaseBranchRegex="^(master|v\d+)$"
if [[ "$GIT_BRANCH" =~ $originRegex ]]; then
branchName=$(echo ${GIT_BRANCH} | grep -oP $branchRegex)
echo Setting TRAVIS_BRANCH to ${branchName}
export TRAVIS_BRANCH=${branchName}
else
echo Setting TRAVIS_BRANCH to $GIT_BRANCH
export TRAVIS_BRANCH=$GIT_BRANCH
fi
if [ "$1" == "false" ]; then
echo Setting TRAVIS_PULL_REQUEST to false
export TRAVIS_PULL_REQUEST=false
if [[ "$TRAVIS_BRANCH" =~ $releaseBranchRegex ]]; then
if [[ -z $PXT_RELEASE_REPO ]]; then
echo Cannot find release repo\; skipping tag checks
else
gitTag=$(git describe --tags --exact-match 2> /dev/null)
builtTag=$(git ls-remote --tags $PXT_RELEASE_REPO | grep -o "refs/tags/$gitTag$")
echo Current tag: $gitTag
echo Built tag: $builtTag
if [[ ! -z $gitTag && -z $builtTag ]]; then
echo Built tag not found\; building tag
echo Setting TRAVIS_BRANCH to $gitTag
export TRAVIS_BRANCH=$gitTag
echo Setting TRAVIS_TAG to $gitTag
export TRAVIS_TAG=$gitTag
else
echo Not a tag build
fi
fi
fi
fi
# Perform build
npm install
npm test

View File

@ -1,5 +1,5 @@
const enum ColorSensorMode { const enum ColorSensorMode {
None = -1, None = 0,
//% block="reflected light intensity" //% block="reflected light intensity"
ReflectedLightIntensity = 0, ReflectedLightIntensity = 0,
//% block="ambient light intensity" //% block="ambient light intensity"
@ -15,7 +15,9 @@ enum LightIntensityMode {
//% block="reflected light" //% block="reflected light"
Reflected = ColorSensorMode.ReflectedLightIntensity, Reflected = ColorSensorMode.ReflectedLightIntensity,
//% block="ambient light" //% block="ambient light"
Ambient = ColorSensorMode.AmbientLightIntensity Ambient = ColorSensorMode.AmbientLightIntensity,
//% block="reflected light (raw)"
ReflectedRaw = ColorSensorMode.RefRaw
} }
const enum ColorSensorColor { const enum ColorSensorColor {
@ -93,6 +95,8 @@ namespace sensors {
|| this.mode == ColorSensorMode.AmbientLightIntensity || this.mode == ColorSensorMode.AmbientLightIntensity
|| this.mode == ColorSensorMode.ReflectedLightIntensity) || this.mode == ColorSensorMode.ReflectedLightIntensity)
return this.getNumber(NumberFormat.UInt8LE, 0) return this.getNumber(NumberFormat.UInt8LE, 0)
if (this.mode == ColorSensorMode.RefRaw || this.mode == ColorSensorMode.RgbRaw)
return this.getNumber(NumberFormat.UInt16LE, 0)
return 0 return 0
} }
@ -114,7 +118,7 @@ namespace sensors {
_update(prev: number, curr: number) { _update(prev: number, curr: number) {
if (this.calibrating) return; // simply ignore data updates while calibrating 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)); control.raiseEvent(this._id, this._colorEventValue(curr));
else else
this.thresholdDetector.setLevel(curr); this.thresholdDetector.setLevel(curr);
@ -179,6 +183,23 @@ namespace sensors {
return this.getNumber(NumberFormat.UInt8LE, 0) 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. * Registers code to run when the ambient light changes.
* @param condition the light condition * @param condition the light condition
@ -225,12 +246,17 @@ namespace sensors {
//% parts="colorsensor" //% parts="colorsensor"
//% blockNamespace=sensors //% blockNamespace=sensors
//% this.fieldEditor="ports" //% this.fieldEditor="ports"
//% weight=87 //% weight=87 blockGap=8
//% group="Color Sensor" //% group="Color Sensor"
light(mode: LightIntensityMode) { light(mode: LightIntensityMode) {
this.setMode(<ColorSensorMode><number>mode) this.setMode(<ColorSensorMode><number>mode)
switch(mode) {
case LightIntensityMode.ReflectedRaw:
return this.reflectedLightRaw();
default:
return this.getNumber(NumberFormat.UInt8LE, 0) return this.getNumber(NumberFormat.UInt8LE, 0)
} }
}
/** /**
* Gets the ambient light * Gets the ambient light
@ -248,6 +274,15 @@ namespace sensors {
return this.light(LightIntensityMode.Reflected); 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 * Set a threshold value
* @param condition the dark or bright light condition * @param condition the dark or bright light condition

View File

@ -159,12 +159,6 @@ namespace brick {
if (sl[i]) if (sl[i])
ret |= 1 << 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 return ret
} }

View File

@ -25,8 +25,17 @@ 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 analogMM: MMap
let uartMM: MMap let uartMM: MMap
let IICMM: MMap
let devcon: Buffer let devcon: Buffer
let sensorInfos: SensorInfo[] let sensorInfos: SensorInfo[]
@ -36,11 +45,13 @@ namespace sensors.internal {
sensors: Sensor[] sensors: Sensor[]
connType: number connType: number
devType: number devType: number
iicid: string
constructor(p: number) { constructor(p: number) {
this.port = p this.port = p
this.connType = DAL.CONN_NONE this.connType = DAL.CONN_NONE
this.devType = DAL.DEVICE_TYPE_NONE this.devType = DAL.DEVICE_TYPE_NONE
this.iicid = ''
this.sensors = [] this.sensors = []
} }
} }
@ -57,20 +68,21 @@ namespace sensors.internal {
uartMM = control.mmap("/dev/lms_uart", UartOff.Size, 0) uartMM = control.mmap("/dev/lms_uart", UartOff.Size, 0)
if (!uartMM) control.fail("no uart sensor") if (!uartMM) control.fail("no uart sensor")
forever(() => { IICMM = control.mmap("/dev/lms_iic", IICOff.Size, 0)
detectDevices() if (!IICMM) control.fail("no iic sensor")
pause(500)
})
for (let info_ of sensorInfos) { unsafePollForChanges(500,
let info = info_ () => { return hashDevices(); },
(prev, curr) => { detectDevices();
});
sensorInfos.forEach(info => {
unsafePollForChanges(50, () => { unsafePollForChanges(50, () => {
if (info.sensor) return info.sensor._query() if (info.sensor) return info.sensor._query()
return 0 return 0
}, (prev, curr) => { }, (prev, curr) => {
if (info.sensor) info.sensor._update(prev, curr) if (info.sensor) info.sensor._update(prev, curr)
}) })
} })
} }
@ -89,6 +101,15 @@ namespace sensors.internal {
//serial.writeLine("UART " + port + " / " + mode + " - " + info) //serial.writeLine("UART " + port + " / " + mode + " - " + info)
} }
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;
}
export function getBatteryInfo(): { temp: number; current: number } { export function getBatteryInfo(): { temp: number; current: number } {
init(); init();
return { return {
@ -97,52 +118,87 @@ namespace sensors.internal {
} }
} }
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() { function detectDevices() {
let conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS) //control.dmesg(`detect devices (${nonActivated} na)`)
let numChanged = 0 const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let numChanged = 0;
const uartSensors: SensorInfo[] = [];
for (let info of sensorInfos) { for (const sensorInfo of sensorInfos) {
let newConn = conns[info.port] const newConn = conns[sensorInfo.port]
if (newConn == info.connType) if (newConn == sensorInfo.connType) {
continue // control.dmesg(`connection unchanged ${newConn} at ${sensorInfo.port}`)
continue;
}
numChanged++ numChanged++
info.connType = newConn sensorInfo.connType = newConn
info.devType = DAL.DEVICE_TYPE_NONE sensorInfo.devType = DAL.DEVICE_TYPE_NONE
if (newConn == DAL.CONN_INPUT_UART) { if (newConn == DAL.CONN_INPUT_UART) {
control.dmesg(`new UART connection at ${info.port}`) control.dmesg(`new UART connection at ${sensorInfo.port}`)
setUartMode(info.port, 0) updateUartMode(sensorInfo.port, 0);
let uinfo = readUartInfo(info.port, 0) uartSensors.push(sensorInfo);
info.devType = uinfo[TypesOff.Type] } else if (newConn == DAL.CONN_NXT_IIC) {
control.dmesg(`UART type ${info.devType}`) 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) { } 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 // 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) { } else if (newConn == DAL.CONN_NONE || newConn == 0) {
control.dmesg(`disconnect at ${info.port}`) control.dmesg(`disconnect at port ${sensorInfo.port}`)
} else { } 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 return
for (let si of sensorInfos) { control.dmesg(`updating sensor status`)
if (si.sensor && si.sensor._deviceType() != si.devType) { nonActivated = 0;
si.sensor = null for (const sensorInfo of sensorInfos) {
} if (sensorInfo.devType == DAL.DEVICE_TYPE_IIC_UNKNOWN) {
if (si.devType != DAL.DEVICE_TYPE_NONE) { sensorInfo.sensor = sensorInfo.sensors.filter(s => s._IICId() == sensorInfo.iicid)[0]
// TODO figure out compiler problem when '|| null' is added here! if (!sensorInfo.sensor) {
si.sensor = si.sensors.filter(s => s._deviceType() == si.devType)[0] control.dmesg(`sensor not found for iicid=${sensorInfo.iicid} at ${sensorInfo.port}`)
if (si.sensor == null) { nonActivated++;
control.dmesg(`sensor not found for type=${si.devType} at ${si.port}`)
} else { } else {
control.dmesg(`sensor connected type=${si.devType} at ${si.port}`) control.dmesg(`sensor connected iicid=${sensorInfo.iicid} at ${sensorInfo.port}`)
si.sensor._activated() 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 { export class Sensor extends control.Component {
@ -187,6 +243,10 @@ namespace sensors.internal {
_deviceType() { _deviceType() {
return 0 return 0
} }
_IICId() {
return ''
}
} }
export class AnalogSensor extends Sensor { export class AnalogSensor extends Sensor {
@ -212,7 +272,6 @@ namespace sensors.internal {
_activated() { _activated() {
this.realmode = 0 this.realmode = 0
// uartReset(this.port) // TODO is it ever needed?
this._setMode(this.mode) this._setMode(this.mode)
} }
@ -243,6 +302,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) { function uartReset(port: number) {
if (port < 0) return if (port < 0) return
control.dmesg(`UART reset at ${port}`) control.dmesg(`UART reset at ${port}`)
@ -267,6 +375,7 @@ namespace sensors.internal {
} }
function uartClearChange(port: number) { function uartClearChange(port: number) {
control.dmesg(`UART clear change`);
const UART_DATA_READY = 8 const UART_DATA_READY = 8
const UART_PORT_CHANGED = 1 const UART_PORT_CHANGED = 1
while (true) { while (true) {
@ -288,20 +397,43 @@ namespace sensors.internal {
} }
} }
function setUartMode(port: number, mode: number) { function setUartModes() {
const UART_PORT_CHANGED = 1 control.dmesg(`UART set modes`)
while (true) { uartMM.ioctl(IO.UART_SET_CONN, devcon)
if (port < 0) return 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}`) 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.Connection + port, DAL.CONN_INPUT_UART)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 33) devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 33)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode) 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
updateUartMode(port, mode);
uartMM.ioctl(IO.UART_SET_CONN, devcon) uartMM.ioctl(IO.UART_SET_CONN, devcon)
let status = waitNonZeroUartStatus(port) let status = waitNonZeroUartStatus(port)
if (status & UART_PORT_CHANGED) { if (status & UART_PORT_CHANGED) {
control.dmesg(`UART clear changed at ${port}`)
uartClearChange(port) uartClearChange(port)
} else { } else {
break control.dmesg(`UART status ${status}`);
break;
} }
pause(10) pause(10)
} }
@ -322,6 +454,48 @@ namespace sensors.internal {
UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index + off) 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 { const enum NxtColOff {
Calibration = 0, // uint32[4][3] Calibration = 0, // uint32[4][3]
@ -405,6 +579,52 @@ namespace sensors.internal {
Size = 58 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 { const enum IO {
UART_SET_CONN = 0xc00c7500, UART_SET_CONN = 0xc00c7500,
UART_READ_MODE_INFO = 0xc03c7501, UART_READ_MODE_INFO = 0xc03c7501,

View File

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

View File

@ -134,6 +134,7 @@ namespace motors {
protected _brake: boolean; protected _brake: boolean;
private _pauseOnRun: boolean; private _pauseOnRun: boolean;
private _initialized: boolean; private _initialized: boolean;
private _brakeSettleTime: number;
private _init: () => void; private _init: () => void;
private _run: (speed: number) => void; private _run: (speed: number) => void;
private _move: (steps: boolean, stepsOrTime: number, speed: number) => void; private _move: (steps: boolean, stepsOrTime: number, speed: number) => void;
@ -147,6 +148,7 @@ namespace motors {
this._brake = false; this._brake = false;
this._pauseOnRun = true; this._pauseOnRun = true;
this._initialized = false; this._initialized = false;
this._brakeSettleTime = 10;
this._init = init; this._init = init;
this._run = run; this._run = run;
this._move = move; this._move = move;
@ -204,6 +206,20 @@ namespace motors {
writePWM(b) writePWM(b)
} }
/**
* Set the settle time after braking in milliseconds (default is 10ms).
*/
//% blockId=motorSetBrakeSettleTime block="set %motor|brake settle time %millis|ms"
//% motor.fieldEditor="motors"
//% weight=1 blockGap=8
//% group="Properties"
//% millis.defl=200 millis.min=0 millis.max=500
setBrakeSettleTime(millis: number) {
this.init();
// ensure in [0,500]
this._brakeSettleTime = Math.max(0, Math.min(500, millis | 0))
}
/** /**
* Stops the motor(s). * Stops the motor(s).
*/ */
@ -221,8 +237,8 @@ namespace motors {
protected settle() { protected settle() {
// if we've recently completed a motor command with brake // if we've recently completed a motor command with brake
// allow 500ms for robot to settle // allow 500ms for robot to settle
if (this._brake) if (this._brake && this._brakeSettleTime > 0)
pause(500); pause(this._brakeSettleTime);
} }
protected pauseOnRun(stepsOrTime: number) { protected pauseOnRun(stepsOrTime: number) {
@ -396,7 +412,7 @@ namespace motors {
*/ */
//% blockId=outputMotorSetRegulated block="set %motor|regulated %value=toggleOnOff" //% blockId=outputMotorSetRegulated block="set %motor|regulated %value=toggleOnOff"
//% motor.fieldEditor="motors" //% motor.fieldEditor="motors"
//% weight=58 //% weight=58 blockGap=8
//% group="Properties" //% group="Properties"
//% help=motors/motor/set-regulated //% help=motors/motor/set-regulated
setRegulated(value: boolean) { setRegulated(value: boolean) {
@ -545,10 +561,12 @@ namespace motors {
speedRight = Math.clamp(-100, 100, speedRight >> 0); speedRight = Math.clamp(-100, 100, speedRight >> 0);
const speed = Math.abs(speedLeft) > Math.abs(speedRight) ? speedLeft : speedRight; const speed = Math.abs(speedLeft) > Math.abs(speedRight) ? speedLeft : speedRight;
const turnRatio = speedLeft == speed let turnRatio = speedLeft == speed
? (100 - speedRight / speedLeft * 100) ? speedLeft == 0 ? 0 : (100 - speedRight / speedLeft * 100)
: (speedLeft / speedRight * 100 - 100); : speedRight == 0 ? 0 : (speedLeft / speedRight * 100 - 100);
turnRatio = Math.floor(turnRatio);
//control.dmesg(`tank ${speedLeft} ${speedRight} => ${turnRatio} ${speed}`)
this.steer(turnRatio, speed, value, unit); this.steer(turnRatio, speed, value, unit);
} }

View File

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

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 ## Accuracy and calibration
Gyro sensors aren't perfectly accurate. Sometimes, because of temperature and changes in the way elecricity behaves in the sensor, the gyro returns a small error in it's measurement. This causes the gyro sensor to return an incorrect value for the amount of angle it detected. This might make your robot drive off course and not go to where you want it to. Gyro sensors aren't perfectly accurate. Sometimes, because of temperature and changes in the way electricity behaves in the sensor, the gyro returns a small error in it's measurement. This causes the gyro sensor to return an incorrect value for the amount of angle it detected. This might make your robot drive off course and not go to where you want it to.
### Drift ### Drift
@ -20,7 +20,7 @@ If you want to turn the tank or robot you built to the left by 45 degrees, you m
The problem is that when you need to read the angle measurement frequently, the amount of error in the angle measurement may continue to increase. If the sensor thought it moved by 45 degrees the first time instead of really 49 degrees, your second turn will put you at 98 degrees when the sensor said 90 degrees. If you want a robot to turn right 90 degrees and drive for 5 meters, it might actually turn 98 degrees and drive 0.7 meters off course before it stops. This error in the sensor's measurement is called _drift_. The problem is that when you need to read the angle measurement frequently, the amount of error in the angle measurement may continue to increase. If the sensor thought it moved by 45 degrees the first time instead of really 49 degrees, your second turn will put you at 98 degrees when the sensor said 90 degrees. If you want a robot to turn right 90 degrees and drive for 5 meters, it might actually turn 98 degrees and drive 0.7 meters off course before it stops. This error in the sensor's measurement is called _drift_.
### Time to reset ### Calibration
If errors in the angle values returned by the gyro sensor are making your project not work right, then it's time to **[reset](/reference/sensors/gyro/reset)**. A reset will return the gyro sensor's current angle value back to `0` and _calibrate_ for drift. Calibration is the process of finding out how much error there is in a sensor's measurement and then removing the error from the value returned to your program. If errors in the angle values returned by the gyro sensor are making your project not work right, then it's time to **[reset](/reference/sensors/gyro/reset)**. A reset will return the gyro sensor's current angle value back to `0` and _calibrate_ for drift. Calibration is the process of finding out how much error there is in a sensor's measurement and then removing the error from the value returned to your program.
@ -42,7 +42,14 @@ Turn the brick and press ENTER to see the current rotation angle of `gyro 2`.
```blocks ```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () { brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
brick.showNumber(sensors.gyro2.angle(), 1) sensors.gyro2.reset()
})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.calibrate()
})
forever(function () {
brick.showNumber(control.millis(), 1)
brick.showNumber(sensors.gyro2.angle(), 2)
}) })
``` ```

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. * 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 ## Example
Flash the status light to red if the roll rate of `gyro 2` is more that `30` degrees per second. Flash the status light to red if the roll rate of `gyro 2` is more that `30` degrees per second.
```blocks ```blocks
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.reset()
})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.calibrate()
})
forever(function () { forever(function () {
brick.showNumber(sensors.gyro2.rate(), 2)
if (sensors.gyro2.rate() > 30) { if (sensors.gyro2.rate() > 30) {
brick.setStatusLight(StatusLight.RedFlash) brick.setStatusLight(StatusLight.RedFlash)
} else { } else {

View File

@ -1,6 +1,6 @@
# reset # reset
Reset the zero reference for the gyro to current position of the brick. Reset the gyro sensor.
```sig ```sig
sensors.gyro2.reset() 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. 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 ## ~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). 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** **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 ## 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 ```blocks
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () { brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
sensors.gyro2.reset() sensors.gyro2.reset()
}) })
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () { forever(function() {
brick.showNumber(sensors.gyro2.angle(), 1) brick.showNumber(sensors.gyro2.angle(), 1)
}) })
``` ```

View File

@ -1,5 +1,5 @@
const enum GyroSensorMode { const enum GyroSensorMode {
None = -1, None = 0,
Angle = 0, Angle = 0,
Rate = 1, Rate = 1,
} }
@ -9,12 +9,12 @@ namespace sensors {
export class GyroSensor extends internal.UartSensor { export class GyroSensor extends internal.UartSensor {
private calibrating: boolean; private calibrating: boolean;
private _drift: number; private _drift: number;
private _drifting: boolean; private _driftCorrection: boolean;
constructor(port: number) { constructor(port: number) {
super(port) super(port)
this.calibrating = false; this.calibrating = false;
this._drift = 0; this._drift = 0;
this._drifting = true; this._driftCorrection = false;
this.setMode(GyroSensorMode.Rate); this.setMode(GyroSensorMode.Rate);
} }
@ -70,14 +70,78 @@ namespace sensors {
this.setMode(GyroSensorMode.Rate); this.setMode(GyroSensorMode.Rate);
let curr = this._query(); let curr = this._query();
if (Math.abs(curr) < 20) { if (Math.abs(curr) < 4 && this._driftCorrection) {
const p = 0.0005; const p = 0.01;
this._drift = (1 - p) * this._drift + p * curr; this._drift = (1 - p) * this._drift + p * curr;
curr -= this._drift; curr = Math.round(curr - this._drift);
} }
return curr; 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. * Forces a calibration of the gyro. Must be called when the sensor is completely still.
*/ */
@ -92,49 +156,10 @@ namespace sensors {
reset(): void { reset(): void {
if (this.calibrating) return; // already in calibration mode if (this.calibrating) return; // already in calibration mode
const statusLight = brick.statusLight(); // save current status light
brick.setStatusLight(StatusLight.Orange);
this.calibrating = true; this.calibrating = true;
// may be triggered by a button click,
// give time for robot to settle
pause(700);
// send a reset command // send a reset command
super.reset(); super.reset();
// switch back to the desired mode // and done
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
this.calibrating = false; this.calibrating = false;
} }
@ -152,7 +177,8 @@ namespace sensors {
*/ */
//% //%
setDriftCorrection(enabled: boolean) { setDriftCorrection(enabled: boolean) {
this._drifting = enabled; this._driftCorrection = enabled;
this._drift = 0;
} }
} }

View File

@ -1,5 +1,5 @@
const enum InfraredSensorMode { const enum InfraredSensorMode {
None = -1, None = 0,
Proximity = 0, Proximity = 0,
Seek = 1, Seek = 1,
RemoteControl = 2, RemoteControl = 2,

View File

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

4670
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -16,7 +16,8 @@
"libs/infrared-sensor", "libs/infrared-sensor",
"libs/gyro-sensor", "libs/gyro-sensor",
"libs/screen", "libs/screen",
"libs/ev3" "libs/ev3",
"libs/storage"
], ],
"simulator": { "simulator": {
"autoRun": true, "autoRun": true,

View File

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

View File

@ -63,6 +63,12 @@ namespace pxsim {
delete this.speedCmd; delete this.speedCmd;
delete this.speedCmdValues; delete this.speedCmdValues;
delete this._synchedMotor; delete this._synchedMotor;
this.setChangedState();
}
clearSyncCmd() {
if (this._synchedMotor)
this.clearSpeedCmd();
} }
setLarge(large: boolean) { setLarge(large: boolean) {
@ -179,11 +185,13 @@ namespace pxsim {
case DAL.opOutputStepSync: case DAL.opOutputStepSync:
case DAL.opOutputTimeSync: { case DAL.opOutputTimeSync: {
const otherMotor = this._synchedMotor; const otherMotor = this._synchedMotor;
if (otherMotor.port < this.port) // handled in other motor code
break;
const speed = this.speedCmdValues[0]; const speed = this.speedCmdValues[0];
const turnRatio = this.speedCmdValues[1]; 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 stepsOrTime = this.speedCmdValues[2];
const brake = this.speedCmdValues[3]; const brake = this.speedCmdValues[3];
const dstep = this.speedCmd == DAL.opOutputTimeSync const dstep = this.speedCmd == DAL.opOutputTimeSync
@ -199,12 +207,7 @@ namespace pxsim {
// turn ratio is a bit weird to interpret // turn ratio is a bit weird to interpret
// see https://communities.theiet.org/blogs/698/1706 // see https://communities.theiet.org/blogs/698/1706
if (turnRatio < 0) { otherMotor.speed = this.speed * (100 - Math.abs(turnRatio)) / 100;
otherMotor.speed = speed;
this.speed *= (100 + turnRatio) / 100;
} else {
otherMotor.speed = this.speed * (100 - turnRatio) / 100;
}
// clamp // clamp
this.speed = Math.max(-100, Math.min(100, this.speed >> 0)); this.speed = Math.max(-100, Math.min(100, this.speed >> 0));

View File

@ -80,6 +80,12 @@ namespace pxsim {
const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 12); const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 12);
const motors = ev3board().getMotor(port); 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) { for (const motor of motors) {
const otherMotor = motors.filter(m => m.port != motor.port)[0]; const otherMotor = motors.filter(m => m.port != motor.port)[0];
motor.setSyncCmd( motor.setSyncCmd(