Merge branch 'master_ms'

This commit is contained in:
Amerlander 2020-05-29 13:33:07 +02:00
commit 1450feb0d7
45 changed files with 2049 additions and 2096 deletions

8
.github/lock.yml vendored
View File

@ -30,13 +30,7 @@ lockLabel: false
# Comment to post before locking. Set to `false` to disable # Comment to post before locking. Set to `false` to disable
lockComment: > lockComment: false
This thread has been automatically locked since there has not been
any recent activity after it was closed. Please open a new issue for
related bugs.

1
.gitignore vendored
View File

@ -34,3 +34,4 @@ crowdinstats.csv
.vscode/.BROWSE.VC.DB-shm .vscode/.BROWSE.VC.DB-shm
.vscode/.BROWSE.VC.DB-wal .vscode/.BROWSE.VC.DB-wal
package-lock.json package-lock.json
.DS_Store

View File

@ -4,6 +4,6 @@ Generate a random coordinate and display it on the LED screen.
```blocks ```blocks
basic.forever(() => { basic.forever(() => {
led.toggle(Math.randomRange(0, 5), Math.randomRange(0, 5)) led.toggle(Math.randomRange(0, 4), Math.randomRange(0, 4))
}) })
``` ```

View File

@ -3,7 +3,6 @@
## Introduction @unplugged ## Introduction @unplugged
Make a love meter, how sweet! The @boardname@ is feeling the love, then sometimes not so much! Make a love meter, how sweet! The @boardname@ is feeling the love, then sometimes not so much!
Tell everyone who you are. Show you name on the LEDs.
![Love meter banner message](/calliope/tutorials/05_love_meter_animation.gif) ![Love meter banner message](/calliope/tutorials/05_love_meter_animation.gif)

View File

@ -8,13 +8,19 @@ Snap the dot is a game of skill where the player has to press **A** exactly when
This tutorial shows how to use the game engine. This tutorial shows how to use the game engine.
## Create a sprite @fullscreen ## Make a sprite variable @fullscreen
Drag a ``||game:create sprite||`` block onto the workspace. A sprite is a single pixel that can move on the screen. It has an ``x`` and ``y`` position along with a direction of motion. Create a new variable called `sprite`. Drag a ``||variables:set sprite to||`` into the ``||basic:on start||`` on the workspace.
```blocks ```blocks
let sprite: game.LedSprite = null let sprite = 0
sprite = game.createSprite(2, 2) ```
## Create a sprite @fullscreen
Pull out a ``||game:create sprite||`` block and put it in ``||variables:set sprite to||`` replacing the `0`. A sprite is a single pixel that can move on the screen. It has an ``x`` and ``y`` position along with a direction of motion.
```blocks
let sprite = game.createSprite(2, 2)
``` ```
## Move the dot @fullscreen ## Move the dot @fullscreen
@ -22,8 +28,7 @@ sprite = game.createSprite(2, 2)
The sprite starts in the center facing right. Put a ``||game:move||`` block into the ``||basic:forever||`` to make it move. Notice how it moves to the right but does not bounce back. The sprite starts in the center facing right. Put a ``||game:move||`` block into the ``||basic:forever||`` to make it move. Notice how it moves to the right but does not bounce back.
```blocks ```blocks
let sprite: game.LedSprite = null let sprite = game.createSprite(2, 2)
sprite = game.createSprite(2, 2)
basic.forever(function () { basic.forever(function () {
sprite.move(1) sprite.move(1)
}) })
@ -34,8 +39,7 @@ basic.forever(function () {
Grab a ``||game:if on edge, bounce||`` block to make the sprite bounce on the side of the screen. Also, add a ``||basic:pause||`` block to slow down the sprite. Grab a ``||game:if on edge, bounce||`` block to make the sprite bounce on the side of the screen. Also, add a ``||basic:pause||`` block to slow down the sprite.
```blocks ```blocks
let sprite: game.LedSprite = null let sprite = game.createSprite(2, 2)
sprite = game.createSprite(2, 2)
basic.forever(function () { basic.forever(function () {
sprite.move(1) sprite.move(1)
sprite.ifOnEdgeBounce() sprite.ifOnEdgeBounce()
@ -60,7 +64,6 @@ input.onButtonEvent(Button.A, ButtonEvent.Click, function () {
} else { } else {
} }
}) })
sprite = game.createSprite(2, 2)
basic.forever(function () { basic.forever(function () {
sprite.move(1) sprite.move(1)
basic.pause(100) basic.pause(100)
@ -81,7 +84,6 @@ input.onButtonEvent(Button.A, ButtonEvent.Click, function () {
game.gameOver() game.gameOver()
} }
}) })
sprite = game.createSprite(2, 2)
basic.forever(function () { basic.forever(function () {
sprite.move(1) sprite.move(1)
basic.pause(100) basic.pause(100)

View File

@ -17,6 +17,15 @@ A bar graph is a kind of chart that shows numbers as lines with different length
* **high**: a [number](/types/number) that is the highest * **high**: a [number](/types/number) that is the highest
possible number (maximum) that the **value** parameter can be. The lines in the bar graph will reach their highest point when **value** reaches this number. If **high** is `0`, then the largest value recently plotted is used as the maximum. possible number (maximum) that the **value** parameter can be. The lines in the bar graph will reach their highest point when **value** reaches this number. If **high** is `0`, then the largest value recently plotted is used as the maximum.
### ~hint
#### Serial Output
The ``||led:plot bar graph||`` block also writes the number from **value** to the [serial](/reference/serial) port as a way to help you record
values.
### ~
## Example: chart acceleration ## Example: chart acceleration
Show a bar graph of the [acceleration](/reference/input/acceleration) Show a bar graph of the [acceleration](/reference/input/acceleration)

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -19,7 +19,6 @@ export class FieldGestures extends pxtblockly.FieldImages implements Blockly.Fie
this.setText = Blockly.FieldDropdown.prototype.setText; this.setText = Blockly.FieldDropdown.prototype.setText;
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_; this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
} }
trimOptions_() { trimOptions_() {

View File

@ -89,8 +89,8 @@
"Gesture.FreeFall": "Raised when the board is falling!", "Gesture.FreeFall": "Raised when the board is falling!",
"Gesture.LogoDown": "Raised when the logo is downward and the screen is vertical", "Gesture.LogoDown": "Raised when the logo is downward and the screen is vertical",
"Gesture.LogoUp": "Raised when the logo is upward and the screen is vertical", "Gesture.LogoUp": "Raised when the logo is upward and the screen is vertical",
"Gesture.ScreenDown": "Raised when the screen is pointing up and the board is horizontal", "Gesture.ScreenDown": "Raised when the screen is pointing down and the board is horizontal",
"Gesture.ScreenUp": "Raised when the screen is pointing down and the board is horizontal", "Gesture.ScreenUp": "Raised when the screen is pointing up and the board is horizontal",
"Gesture.Shake": "Raised when shaken", "Gesture.Shake": "Raised when shaken",
"Gesture.SixG": "Raised when a 6G shock is detected", "Gesture.SixG": "Raised when a 6G shock is detected",
"Gesture.ThreeG": "Raised when a 3G shock is detected", "Gesture.ThreeG": "Raised when a 3G shock is detected",
@ -273,6 +273,8 @@
"control.eventValue": "Gets the value of the last event executed on the bus", "control.eventValue": "Gets the value of the last event executed on the bus",
"control.eventValueId": "Returns the value of a C++ runtime constant", "control.eventValueId": "Returns the value of a C++ runtime constant",
"control.inBackground": "Schedules code that run in the background.", "control.inBackground": "Schedules code that run in the background.",
"control.micros": "Gets current time in microseconds. Overflows every ~18 minutes.",
"control.millis": "Gets the number of milliseconds elapsed since power on.",
"control.onEvent": "Registers an event handler.", "control.onEvent": "Registers an event handler.",
"control.panic": "Display specified error code and stop the program.", "control.panic": "Display specified error code and stop the program.",
"control.raiseEvent": "Raises an event in the event bus.", "control.raiseEvent": "Raises an event in the event bus.",
@ -281,6 +283,7 @@
"control.raiseEvent|param|value": "Component specific code indicating the cause of the event.", "control.raiseEvent|param|value": "Component specific code indicating the cause of the event.",
"control.reset": "Resets the BBC micro:bit.", "control.reset": "Resets the BBC micro:bit.",
"control.runtimeWarning": "Display warning in the simulator.", "control.runtimeWarning": "Display warning in the simulator.",
"control.waitForEvent": "Blocks the calling thread until the specified event is raised.",
"control.waitMicros": "Blocks the current fiber for the given microseconds", "control.waitMicros": "Blocks the current fiber for the given microseconds",
"control.waitMicros|param|micros": "number of micro-seconds to wait. eg: 4", "control.waitMicros|param|micros": "number of micro-seconds to wait. eg: 4",
"convertToText": "Convert any value to text", "convertToText": "Convert any value to text",
@ -475,9 +478,13 @@
"music.builtInMelody": "Gets the melody array of a built-in melody.", "music.builtInMelody": "Gets the melody array of a built-in melody.",
"music.changeTempoBy": "Change the tempo by the specified amount", "music.changeTempoBy": "Change the tempo by the specified amount",
"music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20", "music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20",
"music.melodyEditor": "Create a melody with the melody editor.",
"music.noteFrequency": "Get the frequency of a note.", "music.noteFrequency": "Get the frequency of a note.",
"music.noteFrequency|param|name": "the note name, eg: Note.C", "music.noteFrequency|param|name": "the note name, eg: Note.C",
"music.onEvent": "Registers code to run on various melody events", "music.onEvent": "Registers code to run on various melody events",
"music.playMelody": "Play a melody from the melody editor.",
"music.playMelody|param|melody": "- string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: \"E D G F B A C5 B \"",
"music.playMelody|param|tempo": "- number in beats per minute (bpm), dictating how long each note will play for",
"music.playTone": "Plays a tone through pin ``P0`` for the given duration.", "music.playTone": "Plays a tone through pin ``P0`` for the given duration.",
"music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz), eg: Note.C", "music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz), eg: Note.C",
"music.playTone|param|ms": "tone duration in milliseconds (ms)", "music.playTone|param|ms": "tone duration in milliseconds (ms)",

View File

@ -83,9 +83,9 @@
"Gesture.LogoDown|block": "logo down", "Gesture.LogoDown|block": "logo down",
"Gesture.LogoUp": "Raised when the logo is upward and the screen is vertical", "Gesture.LogoUp": "Raised when the logo is upward and the screen is vertical",
"Gesture.LogoUp|block": "logo up", "Gesture.LogoUp|block": "logo up",
"Gesture.ScreenDown": "Raised when the screen is pointing up and the board is horizontal", "Gesture.ScreenDown": "Raised when the screen is pointing down and the board is horizontal",
"Gesture.ScreenDown|block": "screen down", "Gesture.ScreenDown|block": "screen down",
"Gesture.ScreenUp": "Raised when the screen is pointing down and the board is horizontal", "Gesture.ScreenUp": "Raised when the screen is pointing up and the board is horizontal",
"Gesture.ScreenUp|block": "screen up", "Gesture.ScreenUp|block": "screen up",
"Gesture.Shake": "Raised when shaken", "Gesture.Shake": "Raised when shaken",
"Gesture.Shake|block": "shake", "Gesture.Shake|block": "shake",
@ -266,9 +266,11 @@
"control.eventValueId|block": "%id", "control.eventValueId|block": "%id",
"control.eventValue|block": "event value", "control.eventValue|block": "event value",
"control.inBackground|block": "run in background", "control.inBackground|block": "run in background",
"control.millis|block": "millis (ms)",
"control.onEvent|block": "on event|from %src=control_event_source_id|with value %value=control_event_value_id", "control.onEvent|block": "on event|from %src=control_event_source_id|with value %value=control_event_value_id",
"control.raiseEvent|block": "raise event|from source %src=control_event_source_id|with value %value=control_event_value_id", "control.raiseEvent|block": "raise event|from source %src=control_event_source_id|with value %value=control_event_value_id",
"control.reset|block": "reset", "control.reset|block": "reset",
"control.waitForEvent|block": "wait for event|from %src|with value %value",
"control.waitMicros|block": "wait (µs)%micros", "control.waitMicros|block": "wait (µs)%micros",
"control|block": "control", "control|block": "control",
"convertToText|block": "convert $value=math_number to text", "convertToText|block": "convert $value=math_number to text",
@ -336,17 +338,19 @@
"led.toggle|block": "toggle|x %x|y %y", "led.toggle|block": "toggle|x %x|y %y",
"led.unplot|block": "unplot|x %x|y %y", "led.unplot|block": "unplot|x %x|y %y",
"led|block": "led", "led|block": "led",
"motors.dualMotorPower|block": "motor %motor|at %percent", "motors.dualMotorPower|block": "motor %motor|at %percent \\%",
"motors.motorCommand|block": "motor %command", "motors.motorCommand|block": "motor %command",
"motors.motorPower|block": "motor on at %percent", "motors.motorPower|block": "motor on at %percent \\%",
"motors|block": "motors", "motors|block": "motors",
"msgpack|block": "msgpack", "msgpack|block": "msgpack",
"music.beat|block": "%fraction|beat", "music.beat|block": "%fraction|beat",
"music.beginMelody|block": "start melody %melody=device_builtin_melody| repeating %options", "music.beginMelody|block": "start melody %melody=device_builtin_melody| repeating %options",
"music.builtInMelody|block": "%melody", "music.builtInMelody|block": "%melody",
"music.changeTempoBy|block": "change tempo by (bpm)|%value", "music.changeTempoBy|block": "change tempo by (bpm)|%value",
"music.melodyEditor|block": "$melody",
"music.noteFrequency|block": "%name", "music.noteFrequency|block": "%name",
"music.onEvent|block": "music on %value", "music.onEvent|block": "music on %value",
"music.playMelody|block": "play melody $melody at tempo $tempo|(bpm)",
"music.playTone|block": "play|tone %note=device_note|for %duration=device_beat", "music.playTone|block": "play|tone %note=device_note|for %duration=device_beat",
"music.rest|block": "rest(ms)|%duration=device_beat", "music.rest|block": "rest(ms)|%duration=device_beat",
"music.ringTone|block": "ring tone (Hz)|%note=device_note", "music.ringTone|block": "ring tone (Hz)|%note=device_note",

View File

@ -223,6 +223,23 @@ namespace control {
release_fiber(); release_fiber();
} }
/**
* Gets the number of milliseconds elapsed since power on.
*/
//% help=control/millis weight=50
//% blockId=control_running_time block="millis (ms)"
int millis() {
return system_timer_current_time();
}
/**
* Gets current time in microseconds. Overflows every ~18 minutes.
*/
//%
int micros() {
return system_timer_current_time_us() & 0x3fffffff;
}
/** /**
* Schedules code that run in the background. * Schedules code that run in the background.
*/ */
@ -232,6 +249,15 @@ namespace control {
runInParallel(a); runInParallel(a);
} }
/**
* Blocks the calling thread until the specified event is raised.
*/
//% help=control/wait-for-event async
//% blockId=control_wait_for_event block="wait for event|from %src|with value %value"
void waitForEvent(int src, int value) {
pxt::waitForEvent(src, value);
}
/** /**
* Resets the BBC micro:bit. * Resets the BBC micro:bit.
*/ */

View File

@ -127,13 +127,13 @@ declare namespace basic {
//% jres=gestures.tiltbackwards //% jres=gestures.tiltbackwards
LogoDown = 2, // MICROBIT_ACCELEROMETER_EVT_TILT_DOWN LogoDown = 2, // MICROBIT_ACCELEROMETER_EVT_TILT_DOWN
/** /**
* Raised when the screen is pointing down and the board is horizontal * Raised when the screen is pointing up and the board is horizontal
*/ */
//% block="screen up" //% block="screen up"
//% jres=gestures.frontsideup //% jres=gestures.frontsideup
ScreenUp = 5, // MICROBIT_ACCELEROMETER_EVT_FACE_UP ScreenUp = 5, // MICROBIT_ACCELEROMETER_EVT_FACE_UP
/** /**
* Raised when the screen is pointing up and the board is horizontal * Raised when the screen is pointing down and the board is horizontal
*/ */
//% block="screen down" //% block="screen down"
//% jres=gestures.backsideup //% jres=gestures.backsideup

View File

@ -90,13 +90,13 @@ enum class Gesture {
//% jres=gestures.tiltbackwards //% jres=gestures.tiltbackwards
LogoDown = MICROBIT_ACCELEROMETER_EVT_TILT_DOWN, LogoDown = MICROBIT_ACCELEROMETER_EVT_TILT_DOWN,
/** /**
* Raised when the screen is pointing down and the board is horizontal * Raised when the screen is pointing up and the board is horizontal
*/ */
//% block="screen up" //% block="screen up"
//% jres=gestures.frontsideup //% jres=gestures.frontsideup
ScreenUp = MICROBIT_ACCELEROMETER_EVT_FACE_UP, ScreenUp = MICROBIT_ACCELEROMETER_EVT_FACE_UP,
/** /**
* Raised when the screen is pointing up and the board is horizontal * Raised when the screen is pointing down and the board is horizontal
*/ */
//% block="screen down" //% block="screen down"
//% jres=gestures.backsideup //% jres=gestures.backsideup
@ -412,26 +412,6 @@ namespace input {
return 0; return 0;
} }
/**
* Gets the number of milliseconds elapsed since power on.
*/
//% help=input/running-time weight=50 blockGap=8
//% blockId=device_get_running_time block="running time (ms)"
//% advanced=true
int runningTime() {
return system_timer_current_time();
}
/**
* Gets the number of microseconds elapsed since power on.
*/
//% help=input/running-time-micros weight=49
//% blockId=device_get_running_time_micros block="running time (micros)"
//% advanced=true
int runningTimeMicros() {
return system_timer_current_time_us();
}
/** /**
* Obsolete, compass calibration is automatic. * Obsolete, compass calibration is automatic.
*/ */

View File

@ -64,4 +64,25 @@ namespace input {
export function calibrate() { export function calibrate() {
input.calibrateCompass(); input.calibrateCompass();
} }
/**
* Gets the number of milliseconds elapsed since power on.
*/
//% help=input/running-time weight=50 blockGap=8
//% blockId=device_get_running_time block="running time (ms)"
//% advanced=true
export function runningTime() {
return control.millis();
}
/**
* Gets the number of microseconds elapsed since power on.
*/
//% help=input/running-time-micros weight=49
//% blockId=device_get_running_time_micros block="running time (micros)"
//% advanced=true
export function runningTimeMicros() {
return control.micros();
}
} }

View File

@ -25,7 +25,7 @@ namespace motors {
* Turns on the motor at a certain percent of power. Switches to single motor mode! * Turns on the motor at a certain percent of power. Switches to single motor mode!
* @param power %percent of power sent to the motor. Negative power goes backward. eg: 50 * @param power %percent of power sent to the motor. Negative power goes backward. eg: 50
*/ */
//% blockId=motor_on block="motor on at %percent" //% blockId=motor_on block="motor on at %percent \\%"
//% parts=dcmotor weight=90 blockGap=8 //% parts=dcmotor weight=90 blockGap=8
//% percent.shadow="speedPicker" //% percent.shadow="speedPicker"
void motorPower(int power) { void motorPower(int power) {
@ -48,7 +48,7 @@ namespace motors {
/** /**
* Controls two motors attached to the board. Switches to dual-motor mode! * Controls two motors attached to the board. Switches to dual-motor mode!
*/ */
//% blockId=block_dual_motor block="motor %motor|at %percent" //% blockId=block_dual_motor block="motor %motor|at %percent \\%"
//% percent.shadow="speedPicker" //% percent.shadow="speedPicker"
//% weight=80 //% weight=80
void dualMotorPower(Motor motor, int duty_percent) { void dualMotorPower(Motor motor, int duty_percent) {

View File

@ -174,6 +174,8 @@ enum MusicEvent {
*/ */
//% color=#DF4600 weight=98 icon="\uf025" //% color=#DF4600 weight=98 icon="\uf025"
namespace music { namespace music {
const INTERNAL_MELODY_ENDED = 5;
let beatsPerMinute: number = 120; let beatsPerMinute: number = 120;
//% whenUsed //% whenUsed
const freqs = hex` const freqs = hex`
@ -358,14 +360,69 @@ namespace music {
currentBackgroundMelody = null; currentBackgroundMelody = null;
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.MelodyEnded); control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.MelodyEnded);
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyResumed); control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyResumed);
control.raiseEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED);
} }
} }
control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyEnded : MusicEvent.MelodyEnded); control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyEnded : MusicEvent.MelodyEnded);
if (!currentMelody.background)
control.raiseEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED);
currentMelody = null; currentMelody = null;
}) })
} }
} }
/**
* Play a melody from the melody editor.
* @param melody - string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: "E D G F B A C5 B "
* @param tempo - number in beats per minute (bpm), dictating how long each note will play for
*/
//% block="play melody $melody at tempo $tempo|(bpm)" blockId=playMelody
//% weight=85 blockGap=8 help=music/play-melody
//% melody.shadow="melody_editor"
//% tempo.min=40 tempo.max=500
//% tempo.defl=120
//% parts=headphone
export function playMelody(melody: string, tempo: number) {
melody = melody || "";
setTempo(tempo);
let notes: string[] = melody.split(" ").filter(n => !!n);
let newOctave = false;
// build melody string, replace '-' with 'R' and add tempo
// creates format like "C5-174 B4 A G F E D C "
for (let i = 0; i < notes.length; i++) {
if (notes[i] === "-") {
notes[i] = "R";
} else if (notes[i] === "C5") {
newOctave = true;
} else if (newOctave) { // change the octave if necesary
notes[i] += "4";
newOctave = false;
}
}
music.beginMelody(notes, MelodyOptions.Once)
control.waitForEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED);
}
/**
* Create a melody with the melody editor.
* @param melody
*/
//% block="$melody" blockId=melody_editor
//% blockHidden = true
//% weight=85 blockGap=8
//% duplicateShadowOnDrag
//% melody.fieldEditor="melody"
//% melody.fieldOptions.decompileLiterals=true
//% melody.fieldOptions.decompileIndirectFixedInstances="true"
//% melody.fieldOptions.onParentBlock="true"
//% shim=TD_ID
export function melodyEditor(melody: string): string {
return melody;
}
/** /**
* Stops the melodies * Stops the melodies
* @param options which melody to stop * @param options which melody to stop

40
libs/core/shims.d.ts vendored
View File

@ -368,22 +368,6 @@ declare namespace input {
//% advanced=true shim=input::magneticForce //% advanced=true shim=input::magneticForce
function magneticForce(dimension: Dimension): int32; function magneticForce(dimension: Dimension): int32;
/**
* Gets the number of milliseconds elapsed since power on.
*/
//% help=input/running-time weight=50 blockGap=8
//% blockId=device_get_running_time block="running time (ms)"
//% advanced=true shim=input::runningTime
function runningTime(): int32;
/**
* Gets the number of microseconds elapsed since power on.
*/
//% help=input/running-time-micros weight=49
//% blockId=device_get_running_time_micros block="running time (micros)"
//% advanced=true shim=input::runningTimeMicros
function runningTimeMicros(): int32;
/** /**
* Obsolete, compass calibration is automatic. * Obsolete, compass calibration is automatic.
*/ */
@ -410,6 +394,19 @@ declare namespace input {
//% advanced=true //% advanced=true
declare namespace control { declare namespace control {
/**
* Gets the number of milliseconds elapsed since power on.
*/
//% help=control/millis weight=50
//% blockId=control_running_time block="millis (ms)" shim=control::millis
function millis(): int32;
/**
* Gets current time in microseconds. Overflows every ~18 minutes.
*/
//% shim=control::micros
function micros(): int32;
/** /**
* Schedules code that run in the background. * Schedules code that run in the background.
*/ */
@ -417,6 +414,13 @@ declare namespace control {
//% blockId="control_in_background" block="run in background" blockGap=8 shim=control::inBackground //% blockId="control_in_background" block="run in background" blockGap=8 shim=control::inBackground
function inBackground(a: () => void): void; function inBackground(a: () => void): void;
/**
* Blocks the calling thread until the specified event is raised.
*/
//% help=control/wait-for-event async
//% blockId=control_wait_for_event block="wait for event|from %src|with value %value" shim=control::waitForEvent
function waitForEvent(src: int32, value: int32): void;
/** /**
* Resets the BBC micro:bit. * Resets the BBC micro:bit.
*/ */
@ -620,7 +624,7 @@ declare namespace motors {
* Turns on the motor at a certain percent of power. Switches to single motor mode! * Turns on the motor at a certain percent of power. Switches to single motor mode!
* @param power %percent of power sent to the motor. Negative power goes backward. eg: 50 * @param power %percent of power sent to the motor. Negative power goes backward. eg: 50
*/ */
//% blockId=motor_on block="motor on at %percent" //% blockId=motor_on block="motor on at %percent \\%"
//% parts=dcmotor weight=90 blockGap=8 //% parts=dcmotor weight=90 blockGap=8
//% percent.shadow="speedPicker" shim=motors::motorPower //% percent.shadow="speedPicker" shim=motors::motorPower
function motorPower(power: int32): void; function motorPower(power: int32): void;
@ -635,7 +639,7 @@ declare namespace motors {
/** /**
* Controls two motors attached to the board. Switches to dual-motor mode! * Controls two motors attached to the board. Switches to dual-motor mode!
*/ */
//% blockId=block_dual_motor block="motor %motor|at %percent" //% blockId=block_dual_motor block="motor %motor|at %percent \\%"
//% percent.shadow="speedPicker" //% percent.shadow="speedPicker"
//% weight=80 shim=motors::dualMotorPower //% weight=80 shim=motors::dualMotorPower
function dualMotorPower(motor: Motor, duty_percent: int32): void; function dualMotorPower(motor: Motor, duty_percent: int32): void;

View File

@ -1,12 +1,3 @@
{ {
"name": "radio-broadcast", "additionalFilePath": "../../node_modules/pxt-common-packages/libs/radio-broadcast"
"description": "Adds new blocks for message communication in the radio category",
"files": [
"pxt.json",
"radio-broadcast.ts"
],
"dependencies": {
"core": "file:../core",
"radio": "file:../radio"
}
} }

View File

@ -1,39 +0,0 @@
namespace radio {
/**
* Gets the message code
*/
//% blockHidden=1 shim=ENUM_GET
//% blockId=radioMessageCode block="$msg" enumInitialMembers="message1"
//% enumName=RadioMessage enumMemberName=msg enumPromptHint="e.g. Start, Stop, Jump..."
//% enumIsHash=1
export function __message(msg: number): number {
return msg;
}
/**
* Broadcasts a message over radio
* @param msg
*/
//% blockId=radioBroadcastMessage block="radio send $msg"
//% msg.shadow=radioMessageCode draggableParameters
//% weight=200
//% blockGap=8
//% help=radio/send-message
export function sendMessage(msg: number): void {
// 0 is MICROBIT_EVT_ANY, shifting by 1
radio.raiseEvent(DAL.MES_BROADCAST_GENERAL_ID, msg + 1);
}
/**
* Registers code to run for a particular message
* @param msg
* @param handler
*/
//% blockId=radioOnMessageReceived block="on radio $msg received"
//% msg.shadow=radioMessageCode draggableParameters
//% weight=199
//% help=radio/on-received-message
export function onReceivedMessage(msg: number, handler: () => void) {
control.onEvent(DAL.MES_BROADCAST_GENERAL_ID, msg + 1, handler);
}
}

View File

@ -1,4 +0,0 @@
# radio
The radio library.

View File

@ -9,7 +9,7 @@
"radio._packetProperty": "Gets a packet property.", "radio._packetProperty": "Gets a packet property.",
"radio._packetProperty|param|type": "the packet property type, eg: PacketProperty.time", "radio._packetProperty|param|type": "the packet property type, eg: PacketProperty.time",
"radio.onDataPacketReceived": "Deprecated. Use onDataReceived() instead\nRegisters code to run when the radio receives a packet. Also takes the\nreceived packet from the radio queue.", "radio.onDataPacketReceived": "Deprecated. Use onDataReceived() instead\nRegisters code to run when the radio receives a packet. Also takes the\nreceived packet from the radio queue.",
"radio.onDataReceived": "Registers code to run when a packet is received over radio.", "radio.onDataReceived": "Used internally by the library.",
"radio.onReceivedBuffer": "Registers code to run when the radio receives a buffer.", "radio.onReceivedBuffer": "Registers code to run when the radio receives a buffer.",
"radio.onReceivedBufferDeprecated": "Registers code to run when the radio receives a buffer. Deprecated, use\nonReceivedBuffer instead.", "radio.onReceivedBufferDeprecated": "Registers code to run when the radio receives a buffer. Deprecated, use\nonReceivedBuffer instead.",
"radio.onReceivedNumber": "Registers code to run when the radio receives a number.", "radio.onReceivedNumber": "Registers code to run when the radio receives a number.",

View File

@ -1,19 +1,5 @@
{ {
"name": "radio", "additionalFilePath": "../../node_modules/pxt-common-packages/libs/radio",
"description": "The radio services",
"files": [
"README.md",
"shims.d.ts",
"enums.d.ts",
"radio.cpp",
"radio.ts",
"deprecated.ts"
],
"icon": "./static/packages/radio/icon.png",
"public": true,
"dependencies": {
"core": "file:../core"
},
"yotta": { "yotta": {
"config": { "config": {
"microbit-dal": { "microbit-dal": {
@ -22,6 +8,5 @@
} }
} }
} }
}, }
"installedVersion": "rlfgis"
} }

View File

@ -1,126 +0,0 @@
#include "pxt.h"
using namespace pxt;
//% color=#E3008C weight=96 icon="\uf012"
namespace radio {
bool radioEnabled = false;
int radioEnable() {
int r = uBit.radio.enable();
if (r != MICROBIT_OK) {
uBit.panic(43);
return r;
}
if (!radioEnabled) {
uBit.radio.setGroup(pxt::programHash());
uBit.radio.setTransmitPower(6); // start with high power by default
radioEnabled = true;
}
return r;
}
/**
* Sends an event over radio to neigboring devices
*/
//% blockId=radioRaiseEvent block="radio raise event|from source %src=control_event_source_id|with value %value=control_event_value_id"
//% blockExternalInputs=1
//% advanced=true
//% weight=1
//% help=radio/raise-event
void raiseEvent(int src, int value) {
if (radioEnable() != MICROBIT_OK) return;
uBit.radio.event.eventReceived(MicroBitEvent(src, value, CREATE_ONLY));
}
/**
* Internal use only. Takes the next packet from the radio queue and returns its contents + RSSI in a Buffer
*/
//%
Buffer readRawPacket() {
if (radioEnable() != MICROBIT_OK) return mkBuffer(NULL, 0);
PacketBuffer p = uBit.radio.datagram.recv();
if (p == PacketBuffer::EmptyPacket)
return mkBuffer(NULL, 0);
int rssi = p.getRSSI();
uint8_t buf[MICROBIT_RADIO_MAX_PACKET_SIZE + sizeof(int)]; // packet length + rssi
memset(buf, 0, sizeof(buf));
memcpy(buf, p.getBytes(), p.length()); // data
memcpy(buf + MICROBIT_RADIO_MAX_PACKET_SIZE, &rssi, sizeof(int)); // RSSi - assumes Int32LE layout
return mkBuffer(buf, sizeof(buf));
}
/**
* Internal use only. Sends a raw packet through the radio (assumes RSSI appened to packet)
*/
//% async
void sendRawPacket(Buffer msg) {
if (radioEnable() != MICROBIT_OK || NULL == msg) return;
// don't send RSSI data; and make sure no buffer underflow
int len = msg->length - sizeof(int);
if (len > 0)
uBit.radio.datagram.send(msg->data, len);
}
/**
* Registers code to run when a packet is received over radio.
*/
//% help=radio/on-data-received
//% weight=50
//% blockId=radio_datagram_received_event block="radio on data received" blockGap=8
//% deprecated=true
void onDataReceived(Action body) {
if (radioEnable() != MICROBIT_OK) return;
registerWithDal(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM, body);
uBit.radio.datagram.recv(); // wake up read code
}
/**
* Sets the group id for radio communications. A micro:bit can only listen to one group ID at any time.
* @param id the group id between ``0`` and ``255``, eg: 1
*/
//% help=radio/set-group
//% weight=100
//% blockId=radio_set_group block="radio set group %ID"
//% id.min=0 id.max=255
void setGroup(int id) {
if (radioEnable() != MICROBIT_OK) return;
uBit.radio.setGroup(id);
}
/**
* Change the output power level of the transmitter to the given value.
* @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest. eg: 7
*/
//% help=radio/set-transmit-power
//% weight=9 blockGap=8
//% blockId=radio_set_transmit_power block="radio set transmit power %power"
//% power.min=0 power.max=7
//% advanced=true
void setTransmitPower(int power) {
if (radioEnable() != MICROBIT_OK) return;
uBit.radio.setTransmitPower(power);
}
/**
* Change the transmission and reception band of the radio to the given channel
* @param band a frequency band in the range 0 - 83. Each step is 1MHz wide, based at 2400MHz.
**/
//% help=radio/set-frequency-band
//% weight=8 blockGap=8
//% blockId=radio_set_frequency_band block="radio set frequency band %band"
//% band.min=0 band.max=83
//% advanced=true
void setFrequencyBand(int band) {
if (radioEnable() != MICROBIT_OK) return;
uBit.radio.setFrequencyBand(band);
}
}

View File

@ -1,444 +0,0 @@
enum RadioPacketProperty {
//% blockIdentity=radio._packetProperty
//% block="signal strength"
SignalStrength = 2,
//% blockIdentity=radio._packetProperty
//% block="time"
Time = 0,
//% block="serial number"
//% blockIdentity=radio._packetProperty
SerialNumber = 1
}
/**
* Communicate data using radio packets
*/
//% color=#E3008C weight=96 icon="\uf012"
namespace radio {
const MAX_FIELD_DOUBLE_NAME_LENGTH = 8;
const MAX_PAYLOAD_LENGTH = 20;
const PACKET_PREFIX_LENGTH = 9;
const VALUE_PACKET_NAME_LEN_OFFSET = 13;
const DOUBLE_VALUE_PACKET_NAME_LEN_OFFSET = 17;
// Packet Spec:
// | 0 | 1 ... 4 | 5 ... 8 | 9 ... 28
// ----------------------------------------------------------------
// | packet type | system time | serial number | payload
//
// Serial number defaults to 0 unless enabled by user
// payload: number (9 ... 12)
const PACKET_TYPE_NUMBER = 0;
// payload: number (9 ... 12), name length (13), name (14 ... 26)
const PACKET_TYPE_VALUE = 1;
// payload: string length (9), string (10 ... 28)
const PACKET_TYPE_STRING = 2;
// payload: buffer length (9), buffer (10 ... 28)
const PACKET_TYPE_BUFFER = 3;
// payload: number (9 ... 16)
const PACKET_TYPE_DOUBLE = 4;
// payload: number (9 ... 16), name length (17), name (18 ... 26)
const PACKET_TYPE_DOUBLE_VALUE = 5;
let transmittingSerial: boolean;
let initialized = false;
export let lastPacket: RadioPacket;
let onReceivedNumberHandler: (receivedNumber: number) => void;
let onReceivedValueHandler: (name: string, value: number) => void;
let onReceivedStringHandler: (receivedString: string) => void;
let onReceivedBufferHandler: (receivedBuffer: Buffer) => void;
function init() {
if (initialized) return;
initialized = true;
onDataReceived(handleDataReceived);
}
function handleDataReceived() {
let buffer: Buffer = readRawPacket();
while (buffer && buffer.length) {
lastPacket = RadioPacket.getPacket(buffer);
switch (lastPacket.packetType) {
case PACKET_TYPE_NUMBER:
case PACKET_TYPE_DOUBLE:
if (onReceivedNumberHandler)
onReceivedNumberHandler(lastPacket.numberPayload);
break;
case PACKET_TYPE_VALUE:
case PACKET_TYPE_DOUBLE_VALUE:
if (onReceivedValueHandler)
onReceivedValueHandler(lastPacket.stringPayload, lastPacket.numberPayload);
break;
case PACKET_TYPE_BUFFER:
if (onReceivedBufferHandler)
onReceivedBufferHandler(lastPacket.bufferPayload);
break;
case PACKET_TYPE_STRING:
if (onReceivedStringHandler)
onReceivedStringHandler(lastPacket.stringPayload);
break;
}
// read next packet if any
buffer = readRawPacket();
}
}
/**
* Registers code to run when the radio receives a number.
*/
//% help=radio/on-received-number
//% blockId=radio_on_number_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedNumber(cb: (receivedNumber: number) => void) {
init();
onReceivedNumberHandler = cb;
}
/**
* Registers code to run when the radio receives a key value pair.
*/
//% help=radio/on-received-value
//% blockId=radio_on_value_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedValue(cb: (name: string, value: number) => void) {
init();
onReceivedValueHandler = cb;
}
/**
* Registers code to run when the radio receives a string.
*/
//% help=radio/on-received-string
//% blockId=radio_on_string_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedString(cb: (receivedString: string) => void) {
init();
onReceivedStringHandler = cb;
}
/**
* Registers code to run when the radio receives a buffer.
*/
//% help=radio/on-received-buffer blockHidden=1
//% blockId=radio_on_buffer_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedBuffer(cb: (receivedBuffer: Buffer) => void) {
init();
onReceivedBufferHandler = cb;
}
/**
* Returns properties of the last radio packet received.
* @param type the type of property to retrieve from the last packet
*/
//% help=radio/received-packet
//% weight=11 blockGap=8
//% blockId=radio_received_packet block="received packet %type=radio_packet_property" blockGap=16
export function receivedPacket(type: number) {
if (lastPacket) {
switch (type) {
case RadioPacketProperty.Time: return lastPacket.time;
case RadioPacketProperty.SerialNumber: return lastPacket.serial;
case RadioPacketProperty.SignalStrength: return lastPacket.signal;
}
}
return 0;
}
/**
* Gets a packet property.
* @param type the packet property type, eg: PacketProperty.time
*/
//% blockId=radio_packet_property block="%note"
//% shim=TD_ID blockHidden=1
export function _packetProperty(type: RadioPacketProperty): number {
return type;
}
export class RadioPacket {
public static getPacket(data: Buffer) {
// last 4 bytes is RSSi
return new RadioPacket(data);
}
public static mkPacket(packetType: number) {
const res = new RadioPacket();
res.data[0] = packetType;
return res;
}
private constructor(public readonly data?: Buffer) {
if (!data) this.data = control.createBuffer(DAL.MICROBIT_RADIO_MAX_PACKET_SIZE + 4);
}
get signal() {
return this.data.getNumber(NumberFormat.Int32LE, this.data.length - 4);
}
get packetType() {
return this.data[0];
}
get time() {
return this.data.getNumber(NumberFormat.Int32LE, 1);
}
set time(val: number) {
this.data.setNumber(NumberFormat.Int32LE, 1, val);
}
get serial() {
return this.data.getNumber(NumberFormat.Int32LE, 5);
}
set serial(val: number) {
this.data.setNumber(NumberFormat.Int32LE, 5, val);
}
get stringPayload() {
const offset = getStringOffset(this.packetType) as number;
return offset ? this.data.slice(offset + 1, this.data[offset]).toString() : undefined;
}
set stringPayload(val: string) {
const offset = getStringOffset(this.packetType) as number;
if (offset) {
const buf = control.createBufferFromUTF8(truncateString(val, getMaxStringLength(this.packetType)));
this.data[offset] = buf.length;
this.data.write(offset + 1, buf);
}
}
get numberPayload() {
switch (this.packetType) {
case PACKET_TYPE_NUMBER:
case PACKET_TYPE_VALUE:
return this.data.getNumber(NumberFormat.Int32LE, PACKET_PREFIX_LENGTH);
case PACKET_TYPE_DOUBLE:
case PACKET_TYPE_DOUBLE_VALUE:
return this.data.getNumber(NumberFormat.Float64LE, PACKET_PREFIX_LENGTH);
}
return undefined;
}
set numberPayload(val: number) {
switch (this.packetType) {
case PACKET_TYPE_NUMBER:
case PACKET_TYPE_VALUE:
this.data.setNumber(NumberFormat.Int32LE, PACKET_PREFIX_LENGTH, val);
break;
case PACKET_TYPE_DOUBLE:
case PACKET_TYPE_DOUBLE_VALUE:
this.data.setNumber(NumberFormat.Float64LE, PACKET_PREFIX_LENGTH, val);
break;
}
}
get bufferPayload() {
const len = this.data[PACKET_PREFIX_LENGTH];
return this.data.slice(PACKET_PREFIX_LENGTH + 1, len);
}
set bufferPayload(b: Buffer) {
const len = Math.min(b.length, MAX_PAYLOAD_LENGTH - 1);
this.data[PACKET_PREFIX_LENGTH] = len;
this.data.write(PACKET_PREFIX_LENGTH + 1, b.slice(0, len));
}
hasString() {
return this.packetType === PACKET_TYPE_STRING ||
this.packetType === PACKET_TYPE_VALUE ||
this.packetType === PACKET_TYPE_DOUBLE_VALUE;
}
hasNumber() {
return this.packetType === PACKET_TYPE_NUMBER ||
this.packetType === PACKET_TYPE_DOUBLE ||
this.packetType === PACKET_TYPE_VALUE ||
this.packetType === PACKET_TYPE_DOUBLE_VALUE;
}
}
/**
* Broadcasts a number over radio to any connected micro:bit in the group.
*/
//% help=radio/send-number
//% weight=60
//% blockId=radio_datagram_send block="radio send number %value" blockGap=8
export function sendNumber(value: number) {
let packet: RadioPacket;
if (value === (value | 0)) {
packet = RadioPacket.mkPacket(PACKET_TYPE_NUMBER);
}
else {
packet = RadioPacket.mkPacket(PACKET_TYPE_DOUBLE);
}
packet.numberPayload = value;
sendPacket(packet);
}
/**
* Broadcasts a name / value pair along with the device serial number
* and running time to any connected micro:bit in the group. The name can
* include no more than 8 characters.
* @param name the field name (max 8 characters), eg: "name"
* @param value the numeric value
*/
//% help=radio/send-value
//% weight=59
//% blockId=radio_datagram_send_value block="radio send|value %name|= %value" blockGap=8
export function sendValue(name: string, value: number) {
let packet: RadioPacket;
if (value === (value | 0)) {
packet = RadioPacket.mkPacket(PACKET_TYPE_VALUE);
}
else {
packet = RadioPacket.mkPacket(PACKET_TYPE_DOUBLE_VALUE);
}
packet.numberPayload = value;
packet.stringPayload = name;
sendPacket(packet);
}
/**
* Broadcasts a string along with the device serial number
* and running time to any connected micro:bit in the group.
*/
//% help=radio/send-string
//% weight=58
//% blockId=radio_datagram_send_string block="radio send string %msg"
//% msg.shadowOptions.toString=true
export function sendString(value: string) {
const packet = RadioPacket.mkPacket(PACKET_TYPE_STRING);
packet.stringPayload = value;
sendPacket(packet);
}
/**
* Broadcasts a buffer (up to 19 bytes long) along with the device serial number
* and running time to any connected micro:bit in the group.
*/
//% help=radio/send-buffer
//% weight=57
//% advanced=true
export function sendBuffer(msg: Buffer) {
const packet = RadioPacket.mkPacket(PACKET_TYPE_BUFFER);
packet.bufferPayload = msg;
sendPacket(packet);
}
/**
* Writes the last received packet to serial as JSON. This should be called
* within an ``onDataPacketReceived`` callback.
*/
//% help=radio/write-received-packet-to-serial
//% weight=3
//% blockId=radio_write_packet_serial block="radio write received packet to serial"
//% advanced=true
export function writeReceivedPacketToSerial() {
if (lastPacket) writeToSerial(lastPacket)
}
/**
* Set the radio to transmit the serial number in each message.
* @param transmit value indicating if the serial number is transmitted, eg: true
*/
//% help=radio/set-transmit-serial-number
//% weight=8 blockGap=8
//% blockId=radio_set_transmit_serial_number block="radio set transmit serial number %transmit"
//% advanced=true
export function setTransmitSerialNumber(transmit: boolean) {
transmittingSerial = transmit;
}
/**
* Gets the received signal strength indicator (RSSI) from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.
*/
//% help=radio/received-signal-strength
//% weight=40
//% blockId=radio_datagram_rssi block="radio received signal strength"
//% deprecated=true blockHidden=true
export function receivedSignalStrength(): number {
return lastPacket ? lastPacket.signal : 0;
}
export function writeToSerial(packet: RadioPacket) {
serial.writeString("{");
serial.writeString("\"t\":");
serial.writeString("" + packet.time);
serial.writeString(",\"s\":");
serial.writeString("" + packet.serial);
if (packet.hasString()) {
serial.writeString(",\"n\":\"");
serial.writeString(packet.stringPayload);
serial.writeString("\"");
}
if (packet.packetType == PACKET_TYPE_BUFFER) {
serial.writeString(",\"b\":\"");
// TODO: proper base64 encoding
serial.writeString(packet.bufferPayload.toString());
serial.writeString("\"");
}
if (packet.hasNumber()) {
serial.writeString(",\"v\":");
serial.writeString("" + packet.numberPayload);
}
serial.writeString("}\r\n");
}
function sendPacket(packet: RadioPacket) {
packet.time = input.runningTime();
packet.serial = transmittingSerial ? control.deviceSerialNumber() : 0;
radio.sendRawPacket(packet.data);
}
function truncateString(str: string, bytes: number) {
str = str.substr(0, bytes);
let buff = control.createBufferFromUTF8(str);
while (buff.length > bytes) {
str = str.substr(0, str.length - 1);
buff = control.createBufferFromUTF8(str);
}
return str;
}
function getStringOffset(packetType: number) {
switch (packetType) {
case PACKET_TYPE_STRING:
return PACKET_PREFIX_LENGTH;
case PACKET_TYPE_VALUE:
return VALUE_PACKET_NAME_LEN_OFFSET;
case PACKET_TYPE_DOUBLE_VALUE:
return DOUBLE_VALUE_PACKET_NAME_LEN_OFFSET;
default:
return undefined;
}
}
function getMaxStringLength(packetType: number) {
switch (packetType) {
case PACKET_TYPE_STRING:
return MAX_PAYLOAD_LENGTH - 2;
case PACKET_TYPE_VALUE:
case PACKET_TYPE_DOUBLE_VALUE:
return MAX_FIELD_DOUBLE_NAME_LENGTH;
default:
return undefined;
}
}
}

View File

@ -28,12 +28,12 @@ declare namespace radio {
function sendRawPacket(msg: Buffer): void; function sendRawPacket(msg: Buffer): void;
/** /**
* Registers code to run when a packet is received over radio. * Used internally by the library.
*/ */
//% help=radio/on-data-received //% help=radio/on-data-received
//% weight=50 //% weight=0
//% blockId=radio_datagram_received_event block="radio on data received" blockGap=8 //% blockId=radio_datagram_received_event block="radio on data received" blockGap=8
//% deprecated=true shim=radio::onDataReceived //% deprecated=true blockHidden=1 shim=radio::onDataReceived
function onDataReceived(body: () => void): void; function onDataReceived(body: () => void): void;
/** /**

View File

@ -97,19 +97,6 @@ namespace radio {
onReceivedBuffer(cb); onReceivedBuffer(cb);
} }
/**
* Reads the next packet from the radio queue and and writes it to serial
* as JSON.
*/
//% help=radio/write-value-to-serial
//% weight=3
//% blockId=radio_write_value_serial block="radio write value to serial"
//% deprecated=true
export function writeValueToSerial() {
const p = RadioPacket.getPacket(radio.readRawPacket());
writeToSerial(p);
}
/** /**
* Returns the number payload from the last packet taken from the radio queue * Returns the number payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not * (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not
@ -185,4 +172,67 @@ namespace radio {
lastPacket = RadioPacket.getPacket(readRawPacket()); lastPacket = RadioPacket.getPacket(readRawPacket());
return receivedString(); return receivedString();
} }
/**
* Gets the received signal strength indicator (RSSI) from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.
*/
//% help=radio/received-signal-strength
//% weight=40
//% blockId=radio_datagram_rssi block="radio received signal strength"
//% deprecated=true blockHidden=true
export function receivedSignalStrength(): number {
return lastPacket ? lastPacket.signal : 0;
}
/**
* Reads the next packet from the radio queue and and writes it to serial
* as JSON.
*/
//% help=radio/write-value-to-serial
//% weight=3
//% blockId=radio_write_value_serial block="radio write value to serial"
//% deprecated=true
export function writeValueToSerial() {
const p = RadioPacket.getPacket(radio.readRawPacket());
writeToSerial(p);
}
/**
* Writes the last received packet to serial as JSON. This should be called
* within an ``onDataPacketReceived`` callback.
*/
//% help=radio/write-received-packet-to-serial
//% weight=3
//% blockId=radio_write_packet_serial block="radio write received packet to serial"
//% advanced=true deprecated=true
export function writeReceivedPacketToSerial() {
if (lastPacket) writeToSerial(lastPacket)
}
function writeToSerial(packet: RadioPacket) {
serial.writeString("{");
serial.writeString("\"t\":");
serial.writeString("" + packet.time);
serial.writeString(",\"s\":");
serial.writeString("" + packet.serial);
if (packet.hasString()) {
serial.writeString(",\"n\":\"");
serial.writeString(packet.stringPayload);
serial.writeString("\"");
}
if (packet.packetType == PACKET_TYPE_BUFFER) {
serial.writeString(",\"b\":\"");
// TODO: proper base64 encoding
serial.writeString(packet.bufferPayload.toString());
serial.writeString("\"");
}
if (packet.hasNumber()) {
serial.writeString(",\"v\":");
serial.writeString("" + packet.numberPayload);
}
serial.writeString("}\r\n");
}
} }

View File

@ -1,349 +0,0 @@
/**
* Tests for the radio. Press A on mbit 1 and B on mbit 2 to run the tests.
* Sends random ints, doubles, strings, and buffers and checks them on
* the other side
*/
class FastRandom {
private lfsr: number;
public seed: number;
constructor(seed?: number) {
if (seed === undefined) seed = Math.randomRange(0x0001, 0xFFFF);
this.seed = seed;
this.lfsr = seed;
}
next(): number {
return this.lfsr = (this.lfsr >> 1) ^ ((-(this.lfsr & 1)) & 0xb400);
}
randomRange(min: number, max: number): number {
return min + (max > min ? this.next() % (max - min + 1) : 0);
}
reset() {
this.lfsr = this.seed;
}
}
enum TestStage {
Integer,
String,
Double,
IntValue,
DblValue,
Buffer
}
const TEST_COUNT = 30;
radio.setGroup(78)
const rand = new FastRandom(1234);
let stage = TestStage.Integer;
function initSender() {
let lastReceived: number;
let lastString: string;
let testIndex = 0;
let lastBuf: Buffer;
let lastAck = -1;
rand.reset();
basic.clearScreen();
// Send loop
control.inBackground(function () {
while (true) {
for (let i = 0; i < TEST_COUNT; i++) {
toggle(testIndex);
if (stage === TestStage.Integer) {
lastReceived = getNextInt();
}
else if (stage === TestStage.Double) {
lastReceived = getNextDouble();
}
else if (stage === TestStage.IntValue) {
lastString = getNextName();
console.log(truncateString(lastString, 8))
lastReceived = getNextInt();
}
else if (stage === TestStage.DblValue) {
lastString = getNextName();
lastReceived = getNextDouble();
}
else if (stage === TestStage.String) {
lastString = getNextString();
console.log(truncateString(lastString, 19))
}
else if (stage === TestStage.Buffer) {
lastBuf = getNextBuffer();
}
while (lastAck !== testIndex) {
if (stage === TestStage.Integer || stage === TestStage.Double) {
radio.sendNumber(lastReceived)
}
else if (stage === TestStage.IntValue || stage === TestStage.DblValue) {
radio.sendValue(lastString, lastReceived)
}
else if (stage === TestStage.String) {
radio.sendString(lastString);
}
else if (stage === TestStage.Buffer) {
radio.sendBuffer(lastBuf);
}
basic.pause(10);
}
testIndex++;
}
stage++;
if (stage > TestStage.Buffer) {
basic.showIcon(IconNames.Yes);
return;
}
}
})
radio.onReceivedNumber(function (receivedNumber: number) {
if (receivedNumber > lastAck) {
lastAck = receivedNumber;
}
});
}
let lastReceived: number;
let lastString: string;
let testIndex = -1;
let running = true;
let lastBuf: Buffer;
let lastPacket = new radio.Packet();
let currentPacket = new radio.Packet();
function truncateString(str: string, bytes: number) {
str = str.substr(0, bytes);
let buff = control.createBufferFromUTF8(str);
while (buff.length > bytes) {
str = str.substr(0, str.length - 1);
buff = control.createBufferFromUTF8(str);
}
return str;
}
function initReceiver() {
rand.reset();
basic.clearScreen();
radio.onDataReceived(function () {
radio.receiveNumber();
currentPacket.receivedNumber = radio.receivedNumber();
currentPacket.receivedString = radio.receivedString();
currentPacket.receivedBuffer = radio.receivedBuffer();
if (currentPacket.receivedNumber === lastPacket.receivedNumber &&
currentPacket.receivedString === lastPacket.receivedString &&
checkBufferEqual(currentPacket.receivedBuffer, lastPacket.receivedBuffer)) {
return;
}
lastPacket.receivedNumber = currentPacket.receivedNumber
lastPacket.receivedString = currentPacket.receivedString
lastPacket.receivedBuffer = currentPacket.receivedBuffer
switch (stage) {
case TestStage.Integer:
verifyInt(radio.receivedNumber());
break;
case TestStage.Double:
verifyDouble(radio.receivedNumber());
break;
case TestStage.IntValue:
verifyIntValue(radio.receivedString(), radio.receivedNumber());
break;
case TestStage.DblValue:
verifyDblValue(radio.receivedString(), radio.receivedNumber());
break;
case TestStage.String:
verifyString(radio.receivedString());
break;
case TestStage.Buffer:
verifyBuffer(radio.receivedBuffer());
break;
}
})
control.inBackground(function () {
while (running) {
radio.sendNumber(testIndex);
basic.pause(10)
}
})
}
function nextTest() {
testIndex++;
toggle(testIndex);
console.log(`test ${testIndex}`)
if (((testIndex + 1) % TEST_COUNT) === 0) {
stage++;
if (stage > TestStage.Buffer) {
basic.showIcon(IconNames.Yes)
running = false;
}
}
}
function verifyInt(int: number) {
if (int === lastReceived) return;
lastReceived = int;
if (lastReceived != getNextInt()) fail();
nextTest()
}
function verifyDouble(dbl: number) {
if (dbl === lastReceived) return;
lastReceived = dbl;
if (lastReceived != getNextDouble()) fail();
nextTest()
}
function verifyIntValue(name: string, val: number) {
if (val === lastReceived) return;
lastReceived = val;
if (name != truncateString(getNextName(), 8) || lastReceived != getNextInt()) fail();
nextTest()
}
function verifyDblValue(name: string, val: number) {
if (val === lastReceived) return;
lastReceived = val;
if (name != truncateString(getNextName(), 8) || lastReceived != getNextDouble()) fail();
nextTest()
}
function verifyString(str: string) {
if (!str || str === lastString) return;
lastString = str;
let next = truncateString(getNextString(), 19);
if (lastString !== next) {
console.log(`got ${control.createBufferFromUTF8(lastString).toHex()} expected ${control.createBufferFromUTF8(next).toHex()}`)
}
nextTest()
}
function verifyBuffer(buf: Buffer) {
if (checkBufferEqual(lastBuf, buf)) return;
lastBuf = buf;
if (!checkBufferEqual(lastBuf, getNextBuffer())) {
fail();
}
nextTest()
}
function fail() {
control.panic(testIndex);
}
let _lastInt: number;
let _lastDbl: number;
let _lastStr: string;
let _lastBuf: Buffer;
let _lastNam: string;
function getNextInt(): number {
let res = rand.next();
if (!res || res === _lastInt) return getNextInt();
_lastInt = res;
return res;
}
function getNextDouble(): number {
let res = rand.next() / rand.next();
if (res === _lastDbl) return getNextDouble();
_lastDbl = res;
return res;
}
function getNextString(): string {
let len = rand.randomRange(1, 19);
let res = "";
for (let i = 0; i < len; i++) {
res += String.fromCharCode(rand.next() & 0xbfff);
}
if (res === _lastStr) return getNextString();
_lastStr = res;
return res;
}
function getNextName(): string {
let len = rand.randomRange(1, 8);
let res = "";
for (let i = 0; i < len; i++) {
res += String.fromCharCode(rand.next() & 0xbfff);
}
if (res === _lastNam) return getNextName();
_lastNam = res;
return res;
}
function getNextBuffer(): Buffer {
let len = rand.randomRange(0, 8);
let res = control.createBuffer(len);
for (let i = 0; i < len; i++) {
res[i] = rand.next() & 0xff;
}
if (checkBufferEqual(_lastBuf, res)) return getNextBuffer();
_lastBuf = res;
return res;
}
function checkBufferEqual(a: Buffer, b: Buffer) {
if (a === b) return true;
if ((!a && b) || (a && !b)) return false;
if (a.length != b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
input.onButtonEvent(Button.A, ButtonEvent.Click, function () {
basic.showString("S");
initSender();
})
input.onButtonEvent(Button.B, ButtonEvent.Click, function () {
basic.showString("R");
initReceiver();
})
function toggle(index: number) {
const x = index % 5;
const y = Math.idiv(index, 5) % 5;
led.toggle(x, y);
}

2912
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "pxt-calliope", "name": "pxt-calliope",
"version": "2.1.55", "version": "2.1.57",
"description": "micro:bit target for Microsoft MakeCode (PXT)", "description": "micro:bit target for Microsoft MakeCode (PXT)",
"keywords": [ "keywords": [
"JavaScript", "JavaScript",
@ -45,7 +45,7 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"dependencies": { "dependencies": {
"pxt-common-packages": "6.21.9", "pxt-common-packages": "6.21.10",
"pxt-core": "5.35.24" "pxt-core": "5.36.9"
} }
} }

View File

@ -302,14 +302,11 @@
"experiments": [ "experiments": [
"allowPackageExtensions", "allowPackageExtensions",
"instructions", "instructions",
"debugger",
"debugExtensionCode", "debugExtensionCode",
"bluetoothUartConsole", "bluetoothUartConsole",
"bluetoothPartialFlashing", "bluetoothPartialFlashing",
"simScreenshot", "simScreenshot",
"simGif", "simGif"
"qrCode",
"githubBlocksDiff"
], ],
"bluetoothUartFilters": [ "bluetoothUartFilters": [
{ {
@ -325,14 +322,6 @@
"name": "Reference", "name": "Reference",
"path": "/reference" "path": "/reference"
}, },
{
"name": "Blocks",
"path": "/blocks"
},
{
"name": "JavaScript",
"path": "/javascript"
},
{ {
"name": "Hardware", "name": "Hardware",
"path": "/device" "path": "/device"
@ -347,6 +336,12 @@
"coloredToolbox": true, "coloredToolbox": true,
"monacoToolbox": true, "monacoToolbox": true,
"hasAudio": true, "hasAudio": true,
"socialOptions": {
"orgTwitterHandle": "MSMakeCode",
"hashtags": "MakeCode",
"discourse": "https://forum.makecode.com/",
"discourseCategory": "micro:bit"
},
"blocklyOptions": { "blocklyOptions": {
"grid": { "grid": {
"spacing": 45, "spacing": 45,
@ -373,6 +368,7 @@
}, },
"highContrast": true, "highContrast": true,
"greenScreen": true, "greenScreen": true,
"accessibleBlocks": true,
"print": true, "print": true,
"selectLanguage": true, "selectLanguage": true,
"availableLocales": [ "availableLocales": [
@ -391,7 +387,7 @@
"ja", "ja",
"ko", "ko",
"nl", "nl",
"no", "nb",
"pl", "pl",
"pt-BR", "pt-BR",
"pt-PT", "pt-PT",
@ -416,12 +412,13 @@
}, },
"showProjectSettings": true, "showProjectSettings": true,
"scriptManager": true, "scriptManager": true,
"debugger": true,
"simGifTransparent": "rgba(0,0,0,0)", "simGifTransparent": "rgba(0,0,0,0)",
"simGifMaxFrames": 44, "simGifMaxFrames": 44,
"simScreenshotMaxUriLength": 300000, "simScreenshotMaxUriLength": 300000,
"importExtensionFiles": true, "importExtensionFiles": true,
"githubEditor": true, "githubEditor": true,
"tutorialBlocksDiff": true, "tutorialBlocksDiff": false,
"python": true, "python": true,
"pythonToolbox": true, "pythonToolbox": true,
"chooseLanguageRestrictionOnNewProject": true, "chooseLanguageRestrictionOnNewProject": true,

View File

@ -1,7 +1,8 @@
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/> /// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
namespace pxsim { namespace pxsim {
export class DalBoard extends CoreBoard { export class DalBoard extends CoreBoard
implements RadioBoard {
// state & update logic for component services // state & update logic for component services
ledMatrixState: LedMatrixState; ledMatrixState: LedMatrixState;
edgeConnectorState: EdgeConnectorState; edgeConnectorState: EdgeConnectorState;
@ -67,7 +68,10 @@ namespace pxsim {
"P3": DAL.MICROBIT_ID_IO_P16 "P3": DAL.MICROBIT_ID_IO_P16
} }
}); });
this.builtinParts["radio"] = this.radioState = new RadioState(runtime); this.builtinParts["radio"] = this.radioState = new RadioState(runtime, {
ID_RADIO: DAL.MICROBIT_ID_RADIO,
RADIO_EVT_DATAGRAM: DAL.MICROBIT_RADIO_EVT_DATAGRAM
});
this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime); this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
this.builtinParts["serial"] = this.serialState = new SerialState(); this.builtinParts["serial"] = this.serialState = new SerialState();
this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState(); this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState();
@ -173,7 +177,7 @@ namespace pxsim {
pxsim.initCurrentRuntime = initRuntimeWithDalBoard; pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
} }
export function board() { export function board(): DalBoard {
return runtime.board as DalBoard; return runtime.board as DalBoard;
} }
} }

View File

@ -32,6 +32,11 @@ namespace pxsim {
setPull(pull: number) { setPull(pull: number) {
this.pull = pull; this.pull = pull;
switch(pull) {
case PinPullMode.PullDown: this.value = 0; break;
case PinPullMode.PullUp: this.value = 1023; break;
default: this.value = Math_.randomRange(0, 1023); break;
}
} }
analogReadPin(): number { analogReadPin(): number {

View File

@ -47,6 +47,19 @@ namespace pxsim.control {
export function waitMicros(micros: number) { export function waitMicros(micros: number) {
// TODO // TODO
} }
export function waitForEvent(id: number, evid: number) {
const cb = getResume();
board().bus.wait(id, evid, cb);
}
export function millis(): number {
return runtime.runningTime();
}
export function micros(): number {
return runtime.runningTimeUs();
}
export function deviceName(): string { export function deviceName(): string {
let b = board(); let b = board();
@ -94,14 +107,6 @@ namespace pxsim.pxtcore {
} }
namespace pxsim.input { namespace pxsim.input {
export function runningTime(): number {
return runtime.runningTime();
}
export function runningTimeMicros(): number {
return runtime.runningTimeUs();
}
export function calibrateCompass() { export function calibrateCompass() {
// device calibrates... // device calibrates...
} }

View File

@ -1,147 +0,0 @@
namespace pxsim {
export interface PacketBuffer {
payload: SimulatorRadioPacketPayload;
rssi: number;
serial: number;
time: number;
}
// Extends interface in pxt-core
export interface SimulatorRadioPacketPayload {
bufferData?: Uint8Array;
}
export class RadioDatagram {
datagram: PacketBuffer[] = [];
lastReceived: PacketBuffer = RadioDatagram.defaultPacket();
constructor(private runtime: Runtime) {
}
queue(packet: PacketBuffer) {
if (this.datagram.length < 4) {
this.datagram.push(packet);
}
(<DalBoard>runtime.board).bus.queue(DAL.MICROBIT_ID_RADIO, DAL.MICROBIT_RADIO_EVT_DATAGRAM);
}
send(payload: SimulatorRadioPacketPayload) {
const b = board();
Runtime.postMessage(<SimulatorRadioPacketMessage>{
type: "radiopacket",
broadcast: true,
rssi: -42, // -42 is the strongest signal
serial: b.radioState.transmitSerialNumber ? pxsim.control.deviceSerialNumber() : 0,
time: new Date().getTime(),
payload
})
}
recv(): PacketBuffer {
let r = this.datagram.shift();
if (!r) r = RadioDatagram.defaultPacket();
return this.lastReceived = r;
}
private static defaultPacket(): PacketBuffer {
return {
rssi: -1,
serial: 0,
time: 0,
payload: { type: -1, groupId: 0, bufferData: new Uint8Array(0) }
};
}
}
export class RadioState {
power = 0;
transmitSerialNumber = false;
datagram: RadioDatagram;
groupId: number;
band: number;
constructor(runtime: Runtime) {
this.datagram = new RadioDatagram(runtime);
this.power = 6; // default value
this.groupId = 0;
this.band = 7; // https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitConfig.h#L320
}
public setGroup(id: number) {
this.groupId = id & 0xff; // byte only
}
setTransmitPower(power: number) {
power = power | 0;
this.power = Math.max(0, Math.min(7, power));
}
setTransmitSerialNumber(sn: boolean) {
this.transmitSerialNumber = !!sn;
}
setFrequencyBand(band: number) {
band = band | 0;
if (band < 0 || band > 83) return;
this.band = band;
}
raiseEvent(id: number, eventid: number) {
Runtime.postMessage(<SimulatorEventBusMessage>{
type: "eventbus",
broadcast: true,
id,
eventid,
power: this.power,
group: this.groupId
})
}
receivePacket(packet: SimulatorRadioPacketMessage) {
if (this.groupId == packet.payload.groupId)
this.datagram.queue(packet)
}
}
}
namespace pxsim.radio {
export function raiseEvent(id: number, eventid: number): void {
board().radioState.raiseEvent(id, eventid);
}
export function setGroup(id: number): void {
board().radioState.setGroup(id);
}
export function setTransmitPower(power: number): void {
board().radioState.setTransmitPower(power);
}
export function setFrequencyBand(band: number) {
board().radioState.setFrequencyBand(band);
}
export function sendRawPacket(buf: RefBuffer) {
let cb = getResume();
board().radioState.datagram.send({
type: 0,
groupId: board().radioState.groupId,
bufferData: buf.data
});
setTimeout(cb, 1);
}
export function readRawPacket() {
const packet = board().radioState.datagram.recv();
return new RefBuffer(packet.payload.bufferData)
}
export function receivedSignalStrength(): number {
return board().radioState.datagram.lastReceived.rssi;
}
export function onDataReceived(handler: RefAction): void {
pxtcore.registerWithDal(DAL.MICROBIT_ID_RADIO, DAL.MICROBIT_RADIO_EVT_DATAGRAM, handler);
readRawPacket();
}
}

View File

@ -11,5 +11,11 @@
"lib": ["dom", "dom.iterable", "scripthost", "es6"], "lib": ["dom", "dom.iterable", "scripthost", "es6"],
"types": ["bluebird"], "types": ["bluebird"],
"typeRoots": ["../node_modules/@types"] "typeRoots": ["../node_modules/@types"]
} },
"include": [
"*.ts",
"state/*.ts",
"visuals/*.ts",
"../node_modules/pxt-common-packages/libs/radio/sim/*.ts"
]
} }

View File

@ -1106,10 +1106,10 @@ namespace pxsim.visuals {
} }
else if (pin.mode & PinFlags.Touch) { else if (pin.mode & PinFlags.Touch) {
v = pin.touched ? "0%" : "100%"; v = pin.touched ? "0%" : "100%";
if (text) text.textContent = ""; if (text) text.textContent = "TOUCHED";
} else { } else {
v = "100%"; v = "100%";
if (text) text.textContent = ""; if (text) text.textContent = "unused";
} }
if (v) svg.setGradientValue(this.pinGradients[index], v); if (v) svg.setGradientValue(this.pinGradients[index], v);
} }

View File

@ -185,7 +185,18 @@
"EBOTICS/pxt-eboticsMIBO", "EBOTICS/pxt-eboticsMIBO",
"KitronikLtd/pxt-kitronik-halohd", "KitronikLtd/pxt-kitronik-halohd",
"dugbraden/pxt-climate-action-kit", "dugbraden/pxt-climate-action-kit",
"alsrobot-microbit-makecode-packages/MiniCruise" "alsrobot-microbit-makecode-packages/MiniCruise",
"4tronix/ServoBit",
"DFRobot/pxt-maqueen",
"DFRobot/pxt-DFRobot-microIoT",
"mu-opensource/pxt-muvision",
"KitronikLtd/pxt-kitronik-clip-detector",
"DFRobot/pxt-DFRobot-NaturalScience",
"strawbees/pxt-robotic-inventions",
"daferdur/pxt-myHX711",
"CytronTechnologies/pxt-edubit",
"MakeAndLearn/pxt-microshield",
"elecfreaks/pxt-TPBot"
], ],
"preferredRepos": [ "preferredRepos": [
"Microsoft/pxt-neopixel", "Microsoft/pxt-neopixel",
@ -199,33 +210,6 @@
"MKleinSB/pxt-foldio" "MKleinSB/pxt-foldio"
] ]
}, },
"languages": [
"en",
"ar",
"cs",
"da",
"de",
"el",
"es-ES",
"fi",
"fr",
"hu",
"it",
"ja",
"ko",
"nl",
"no",
"pt-BR",
"pt-PT",
"ru",
"si-LK",
"sk",
"sv-SE",
"tr",
"uk",
"zh-CN",
"zh-TW"
],
"galleries": { "galleries": {
"First Steps": "calliope/firststeps", "First Steps": "calliope/firststeps",
"Tutorials": "calliope/tutorials", "Tutorials": "calliope/tutorials",

0
theme/site/globals/site.variables Normal file → Executable file
View File