Merge branch 'master_ms'
8
.github/lock.yml
vendored
@ -30,13 +30,7 @@ lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
|
||||
lockComment: >
|
||||
|
||||
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.
|
||||
lockComment: false
|
||||
|
||||
|
||||
|
||||
|
1
.gitignore
vendored
@ -34,3 +34,4 @@ crowdinstats.csv
|
||||
.vscode/.BROWSE.VC.DB-shm
|
||||
.vscode/.BROWSE.VC.DB-wal
|
||||
package-lock.json
|
||||
.DS_Store
|
@ -4,6 +4,6 @@ Generate a random coordinate and display it on the LED screen.
|
||||
|
||||
```blocks
|
||||
basic.forever(() => {
|
||||
led.toggle(Math.randomRange(0, 5), Math.randomRange(0, 5))
|
||||
led.toggle(Math.randomRange(0, 4), Math.randomRange(0, 4))
|
||||
})
|
||||
```
|
@ -3,7 +3,6 @@
|
||||
## Introduction @unplugged
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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.
|
||||
|
||||
## 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
|
||||
let sprite: game.LedSprite = null
|
||||
sprite = game.createSprite(2, 2)
|
||||
let sprite = 0
|
||||
```
|
||||
## 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
|
||||
@ -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.
|
||||
|
||||
```blocks
|
||||
let sprite: game.LedSprite = null
|
||||
sprite = game.createSprite(2, 2)
|
||||
let sprite = game.createSprite(2, 2)
|
||||
basic.forever(function () {
|
||||
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.
|
||||
|
||||
```blocks
|
||||
let sprite: game.LedSprite = null
|
||||
sprite = game.createSprite(2, 2)
|
||||
let sprite = game.createSprite(2, 2)
|
||||
basic.forever(function () {
|
||||
sprite.move(1)
|
||||
sprite.ifOnEdgeBounce()
|
||||
@ -60,7 +64,6 @@ input.onButtonEvent(Button.A, ButtonEvent.Click, function () {
|
||||
} else {
|
||||
}
|
||||
})
|
||||
sprite = game.createSprite(2, 2)
|
||||
basic.forever(function () {
|
||||
sprite.move(1)
|
||||
basic.pause(100)
|
||||
@ -81,7 +84,6 @@ input.onButtonEvent(Button.A, ButtonEvent.Click, function () {
|
||||
game.gameOver()
|
||||
}
|
||||
})
|
||||
sprite = game.createSprite(2, 2)
|
||||
basic.forever(function () {
|
||||
sprite.move(1)
|
||||
basic.pause(100)
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
Show a bar graph of the [acceleration](/reference/input/acceleration)
|
||||
|
BIN
docs/static/configurations/chrome-version.png
vendored
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/static/configurations/edge-version.png
vendored
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
docs/static/configurations/firefox-version.png
vendored
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
docs/static/configurations/ie-version.png
vendored
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/static/configurations/osx-version.png
vendored
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
docs/static/configurations/safari-version.png
vendored
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
docs/static/configurations/windows-version.png
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
@ -19,7 +19,6 @@ export class FieldGestures extends pxtblockly.FieldImages implements Blockly.Fie
|
||||
|
||||
this.setText = Blockly.FieldDropdown.prototype.setText;
|
||||
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
|
||||
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
|
||||
}
|
||||
|
||||
trimOptions_() {
|
||||
|
@ -89,8 +89,8 @@
|
||||
"Gesture.FreeFall": "Raised when the board is falling!",
|
||||
"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.ScreenDown": "Raised when the screen is pointing up and the board is horizontal",
|
||||
"Gesture.ScreenUp": "Raised when the screen is pointing down 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 up and the board is horizontal",
|
||||
"Gesture.Shake": "Raised when shaken",
|
||||
"Gesture.SixG": "Raised when a 6G 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.eventValueId": "Returns the value of a C++ runtime constant",
|
||||
"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.panic": "Display specified error code and stop the program.",
|
||||
"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.reset": "Resets the BBC micro:bit.",
|
||||
"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|param|micros": "number of micro-seconds to wait. eg: 4",
|
||||
"convertToText": "Convert any value to text",
|
||||
@ -475,9 +478,13 @@
|
||||
"music.builtInMelody": "Gets the melody array of a built-in melody.",
|
||||
"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.melodyEditor": "Create a melody with the melody editor.",
|
||||
"music.noteFrequency": "Get the frequency of a note.",
|
||||
"music.noteFrequency|param|name": "the note name, eg: Note.C",
|
||||
"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|param|frequency": "pitch of the tone to play in Hertz (Hz), eg: Note.C",
|
||||
"music.playTone|param|ms": "tone duration in milliseconds (ms)",
|
||||
|
@ -83,9 +83,9 @@
|
||||
"Gesture.LogoDown|block": "logo down",
|
||||
"Gesture.LogoUp": "Raised when the logo is upward and the screen is vertical",
|
||||
"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.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.Shake": "Raised when shaken",
|
||||
"Gesture.Shake|block": "shake",
|
||||
@ -266,9 +266,11 @@
|
||||
"control.eventValueId|block": "%id",
|
||||
"control.eventValue|block": "event value",
|
||||
"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.raiseEvent|block": "raise event|from source %src=control_event_source_id|with value %value=control_event_value_id",
|
||||
"control.reset|block": "reset",
|
||||
"control.waitForEvent|block": "wait for event|from %src|with value %value",
|
||||
"control.waitMicros|block": "wait (µs)%micros",
|
||||
"control|block": "control",
|
||||
"convertToText|block": "convert $value=math_number to text",
|
||||
@ -336,17 +338,19 @@
|
||||
"led.toggle|block": "toggle|x %x|y %y",
|
||||
"led.unplot|block": "unplot|x %x|y %y",
|
||||
"led|block": "led",
|
||||
"motors.dualMotorPower|block": "motor %motor|at %percent",
|
||||
"motors.dualMotorPower|block": "motor %motor|at %percent \\%",
|
||||
"motors.motorCommand|block": "motor %command",
|
||||
"motors.motorPower|block": "motor on at %percent",
|
||||
"motors.motorPower|block": "motor on at %percent \\%",
|
||||
"motors|block": "motors",
|
||||
"msgpack|block": "msgpack",
|
||||
"music.beat|block": "%fraction|beat",
|
||||
"music.beginMelody|block": "start melody %melody=device_builtin_melody| repeating %options",
|
||||
"music.builtInMelody|block": "%melody",
|
||||
"music.changeTempoBy|block": "change tempo by (bpm)|%value",
|
||||
"music.melodyEditor|block": "$melody",
|
||||
"music.noteFrequency|block": "%name",
|
||||
"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.rest|block": "rest(ms)|%duration=device_beat",
|
||||
"music.ringTone|block": "ring tone (Hz)|%note=device_note",
|
||||
|
@ -223,6 +223,23 @@ namespace control {
|
||||
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.
|
||||
*/
|
||||
@ -232,6 +249,15 @@ namespace control {
|
||||
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.
|
||||
*/
|
||||
|
4
libs/core/enums.d.ts
vendored
@ -127,13 +127,13 @@ declare namespace basic {
|
||||
//% jres=gestures.tiltbackwards
|
||||
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"
|
||||
//% jres=gestures.frontsideup
|
||||
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"
|
||||
//% jres=gestures.backsideup
|
||||
|
@ -90,13 +90,13 @@ enum class Gesture {
|
||||
//% jres=gestures.tiltbackwards
|
||||
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"
|
||||
//% jres=gestures.frontsideup
|
||||
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"
|
||||
//% jres=gestures.backsideup
|
||||
@ -412,26 +412,6 @@ namespace input {
|
||||
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.
|
||||
*/
|
||||
|
@ -64,4 +64,25 @@ namespace input {
|
||||
export function calibrate() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace motors {
|
||||
* 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
|
||||
*/
|
||||
//% blockId=motor_on block="motor on at %percent"
|
||||
//% blockId=motor_on block="motor on at %percent \\%"
|
||||
//% parts=dcmotor weight=90 blockGap=8
|
||||
//% percent.shadow="speedPicker"
|
||||
void motorPower(int power) {
|
||||
@ -48,7 +48,7 @@ namespace motors {
|
||||
/**
|
||||
* 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"
|
||||
//% weight=80
|
||||
void dualMotorPower(Motor motor, int duty_percent) {
|
||||
|
@ -174,6 +174,8 @@ enum MusicEvent {
|
||||
*/
|
||||
//% color=#DF4600 weight=98 icon="\uf025"
|
||||
namespace music {
|
||||
const INTERNAL_MELODY_ENDED = 5;
|
||||
|
||||
let beatsPerMinute: number = 120;
|
||||
//% whenUsed
|
||||
const freqs = hex`
|
||||
@ -358,14 +360,69 @@ namespace music {
|
||||
currentBackgroundMelody = null;
|
||||
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.MelodyEnded);
|
||||
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);
|
||||
if (!currentMelody.background)
|
||||
control.raiseEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED);
|
||||
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
|
||||
* @param options which melody to stop
|
||||
|
40
libs/core/shims.d.ts
vendored
@ -368,22 +368,6 @@ declare namespace input {
|
||||
//% advanced=true shim=input::magneticForce
|
||||
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.
|
||||
*/
|
||||
@ -410,6 +394,19 @@ declare namespace input {
|
||||
//% advanced=true
|
||||
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.
|
||||
*/
|
||||
@ -417,6 +414,13 @@ declare namespace control {
|
||||
//% blockId="control_in_background" block="run in background" blockGap=8 shim=control::inBackground
|
||||
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.
|
||||
*/
|
||||
@ -620,7 +624,7 @@ declare namespace motors {
|
||||
* 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
|
||||
*/
|
||||
//% blockId=motor_on block="motor on at %percent"
|
||||
//% blockId=motor_on block="motor on at %percent \\%"
|
||||
//% parts=dcmotor weight=90 blockGap=8
|
||||
//% percent.shadow="speedPicker" shim=motors::motorPower
|
||||
function motorPower(power: int32): void;
|
||||
@ -635,7 +639,7 @@ declare namespace motors {
|
||||
/**
|
||||
* 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"
|
||||
//% weight=80 shim=motors::dualMotorPower
|
||||
function dualMotorPower(motor: Motor, duty_percent: int32): void;
|
||||
|
@ -1,12 +1,3 @@
|
||||
{
|
||||
"name": "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"
|
||||
}
|
||||
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/radio-broadcast"
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
# radio
|
||||
|
||||
The radio library.
|
||||
|
@ -9,7 +9,7 @@
|
||||
"radio._packetProperty": "Gets a packet property.",
|
||||
"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.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.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.",
|
||||
|
@ -1,19 +1,5 @@
|
||||
{
|
||||
"name": "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"
|
||||
},
|
||||
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/radio",
|
||||
"yotta": {
|
||||
"config": {
|
||||
"microbit-dal": {
|
||||
@ -22,6 +8,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"installedVersion": "rlfgis"
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
6
libs/radio/shims.d.ts
vendored
@ -28,12 +28,12 @@ declare namespace radio {
|
||||
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
|
||||
//% weight=50
|
||||
//% weight=0
|
||||
//% 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;
|
||||
|
||||
/**
|
||||
|
@ -97,19 +97,6 @@ namespace radio {
|
||||
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
|
||||
* (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not
|
||||
@ -185,4 +172,67 @@ namespace radio {
|
||||
lastPacket = RadioPacket.getPacket(readRawPacket());
|
||||
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");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
2554
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pxt-calliope",
|
||||
"version": "2.1.55",
|
||||
"version": "2.1.57",
|
||||
"description": "micro:bit target for Microsoft MakeCode (PXT)",
|
||||
"keywords": [
|
||||
"JavaScript",
|
||||
@ -45,7 +45,7 @@
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"pxt-common-packages": "6.21.9",
|
||||
"pxt-core": "5.35.24"
|
||||
"pxt-common-packages": "6.21.10",
|
||||
"pxt-core": "5.36.9"
|
||||
}
|
||||
}
|
||||
|
@ -302,14 +302,11 @@
|
||||
"experiments": [
|
||||
"allowPackageExtensions",
|
||||
"instructions",
|
||||
"debugger",
|
||||
"debugExtensionCode",
|
||||
"bluetoothUartConsole",
|
||||
"bluetoothPartialFlashing",
|
||||
"simScreenshot",
|
||||
"simGif",
|
||||
"qrCode",
|
||||
"githubBlocksDiff"
|
||||
"simGif"
|
||||
],
|
||||
"bluetoothUartFilters": [
|
||||
{
|
||||
@ -325,14 +322,6 @@
|
||||
"name": "Reference",
|
||||
"path": "/reference"
|
||||
},
|
||||
{
|
||||
"name": "Blocks",
|
||||
"path": "/blocks"
|
||||
},
|
||||
{
|
||||
"name": "JavaScript",
|
||||
"path": "/javascript"
|
||||
},
|
||||
{
|
||||
"name": "Hardware",
|
||||
"path": "/device"
|
||||
@ -347,6 +336,12 @@
|
||||
"coloredToolbox": true,
|
||||
"monacoToolbox": true,
|
||||
"hasAudio": true,
|
||||
"socialOptions": {
|
||||
"orgTwitterHandle": "MSMakeCode",
|
||||
"hashtags": "MakeCode",
|
||||
"discourse": "https://forum.makecode.com/",
|
||||
"discourseCategory": "micro:bit"
|
||||
},
|
||||
"blocklyOptions": {
|
||||
"grid": {
|
||||
"spacing": 45,
|
||||
@ -373,6 +368,7 @@
|
||||
},
|
||||
"highContrast": true,
|
||||
"greenScreen": true,
|
||||
"accessibleBlocks": true,
|
||||
"print": true,
|
||||
"selectLanguage": true,
|
||||
"availableLocales": [
|
||||
@ -391,7 +387,7 @@
|
||||
"ja",
|
||||
"ko",
|
||||
"nl",
|
||||
"no",
|
||||
"nb",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
@ -416,12 +412,13 @@
|
||||
},
|
||||
"showProjectSettings": true,
|
||||
"scriptManager": true,
|
||||
"debugger": true,
|
||||
"simGifTransparent": "rgba(0,0,0,0)",
|
||||
"simGifMaxFrames": 44,
|
||||
"simScreenshotMaxUriLength": 300000,
|
||||
"importExtensionFiles": true,
|
||||
"githubEditor": true,
|
||||
"tutorialBlocksDiff": true,
|
||||
"tutorialBlocksDiff": false,
|
||||
"python": true,
|
||||
"pythonToolbox": true,
|
||||
"chooseLanguageRestrictionOnNewProject": true,
|
||||
|
@ -1,7 +1,8 @@
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
namespace pxsim {
|
||||
export class DalBoard extends CoreBoard {
|
||||
export class DalBoard extends CoreBoard
|
||||
implements RadioBoard {
|
||||
// state & update logic for component services
|
||||
ledMatrixState: LedMatrixState;
|
||||
edgeConnectorState: EdgeConnectorState;
|
||||
@ -67,7 +68,10 @@ namespace pxsim {
|
||||
"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["serial"] = this.serialState = new SerialState();
|
||||
this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState();
|
||||
@ -173,7 +177,7 @@ namespace pxsim {
|
||||
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
|
||||
}
|
||||
|
||||
export function board() {
|
||||
export function board(): DalBoard {
|
||||
return runtime.board as DalBoard;
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ namespace pxsim {
|
||||
|
||||
setPull(pull: number) {
|
||||
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 {
|
||||
|
@ -47,6 +47,19 @@ namespace pxsim.control {
|
||||
export function waitMicros(micros: number) {
|
||||
// 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 {
|
||||
let b = board();
|
||||
@ -94,14 +107,6 @@ namespace pxsim.pxtcore {
|
||||
}
|
||||
|
||||
namespace pxsim.input {
|
||||
export function runningTime(): number {
|
||||
return runtime.runningTime();
|
||||
}
|
||||
|
||||
export function runningTimeMicros(): number {
|
||||
return runtime.runningTimeUs();
|
||||
}
|
||||
|
||||
export function calibrateCompass() {
|
||||
// device calibrates...
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -11,5 +11,11 @@
|
||||
"lib": ["dom", "dom.iterable", "scripthost", "es6"],
|
||||
"types": ["bluebird"],
|
||||
"typeRoots": ["../node_modules/@types"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"state/*.ts",
|
||||
"visuals/*.ts",
|
||||
"../node_modules/pxt-common-packages/libs/radio/sim/*.ts"
|
||||
]
|
||||
}
|
||||
|
@ -1106,10 +1106,10 @@ namespace pxsim.visuals {
|
||||
}
|
||||
else if (pin.mode & PinFlags.Touch) {
|
||||
v = pin.touched ? "0%" : "100%";
|
||||
if (text) text.textContent = "";
|
||||
if (text) text.textContent = "TOUCHED";
|
||||
} else {
|
||||
v = "100%";
|
||||
if (text) text.textContent = "";
|
||||
if (text) text.textContent = "unused";
|
||||
}
|
||||
if (v) svg.setGradientValue(this.pinGradients[index], v);
|
||||
}
|
||||
|
@ -185,7 +185,18 @@
|
||||
"EBOTICS/pxt-eboticsMIBO",
|
||||
"KitronikLtd/pxt-kitronik-halohd",
|
||||
"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": [
|
||||
"Microsoft/pxt-neopixel",
|
||||
@ -199,33 +210,6 @@
|
||||
"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": {
|
||||
"First Steps": "calliope/firststeps",
|
||||
"Tutorials": "calliope/tutorials",
|
||||
|