Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
89ceeefc32 | |||
91235d2377 | |||
a805d7b8a8 | |||
89fdbcdc4f | |||
92d1d2ffc3 | |||
82c9af836f | |||
da90401fe6 | |||
315a12dd8b | |||
72ec2d617e | |||
4d9ae63831 | |||
3eab21cff1 | |||
5a37385d14 | |||
9fa412ff6f | |||
3f7fbb70ba | |||
20189a709b | |||
111b321722 | |||
b8fe8f9294 | |||
7b478c4c38 | |||
a68e1a611d | |||
b3b2944b46 | |||
c809ee4fdf | |||
8e67e69602 | |||
0b508e37b1 | |||
e1a3aca9f8 | |||
bbe93ae9a2 | |||
6fd18b4f4c | |||
b511537348 | |||
749ba6b984 | |||
e571bec213 | |||
1ad90eda81 | |||
0c604498d3 | |||
a8ec408f96 | |||
40fe1b4616 | |||
e52da19925 | |||
d558f70118 | |||
8fca50e907 | |||
8c4abe979b | |||
d4fa9dab1b | |||
955b67b6b8 | |||
739be09ac9 | |||
5877adc595 | |||
0380f4f0d0 | |||
13a4b03e9a | |||
4b21d51769 | |||
e7aed1d162 | |||
60e71e3e35 | |||
b003d89061 | |||
a5eb93d3e1 | |||
224cb446e4 | |||
62b3d7504f | |||
c937cba17e | |||
79c89b832a | |||
3ccc8b7db3 | |||
7c8bae3cf0 | |||
000f784011 | |||
d85446d34d |
1
.gitignore
vendored
@ -16,6 +16,7 @@ clients/win10/*.opendb
|
||||
clients/**/bin/**
|
||||
clients/**/obj/**
|
||||
clients/electron/projects
|
||||
hexcache
|
||||
|
||||
*.user
|
||||
*.sw?
|
||||
|
BIN
docs/favicon.ico
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -1,3 +1,3 @@
|
||||
{
|
||||
"appref": "v0.5.31"
|
||||
"appref": "v0.5.51"
|
||||
}
|
||||
|
BIN
docs/static/icons/favicon.ico
vendored
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
docs/static/mb/device/usb-generic.jpg
vendored
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 68 KiB |
BIN
docs/static/mb/device/usb-mac.jpg
vendored
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 159 KiB |
BIN
docs/static/mb/device/usb-osx-dnd.png
vendored
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 50 KiB |
BIN
docs/static/mb/device/usb-thin.jpg
vendored
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 67 KiB |
@ -1,54 +1,173 @@
|
||||
/*basic.showString("RGB")
|
||||
basic.setLedColor(Colors.Blue)
|
||||
basic.pause(500)
|
||||
basic.setLedColor(Colors.Red)
|
||||
basic.pause(500)
|
||||
basic.setLedColor(Colors.Green)
|
||||
basic.pause(500)
|
||||
basic.setLedColor(Colors.Violet)
|
||||
basic.pause(500)
|
||||
basic.showString("RGB");
|
||||
basic.setLedColor(Colors.Blue);
|
||||
basic.pause(500);
|
||||
basic.setLedColor(Colors.Red);
|
||||
basic.pause(500);
|
||||
basic.setLedColor(Colors.Green);
|
||||
basic.pause(500);
|
||||
basic.setLedColor(Colors.Violet);
|
||||
basic.pause(500);
|
||||
basic.setLedColor(0);
|
||||
basic.showString("Gesten")
|
||||
basic.showString("Gesten");
|
||||
input.onGesture(Gesture.Shake, () => {
|
||||
basic.showString("S")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.LogoUp, () => {
|
||||
basic.showString("U")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.LogoDown, () => {
|
||||
basic.showString("D")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.ScreenUp, () => {
|
||||
basic.showString("+")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.TiltRight, () => {
|
||||
basic.showString("R")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.FreeFall, () => {
|
||||
basic.showString("F")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.ScreenDown, () => {
|
||||
basic.showString("-")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.TiltLeft, () => {
|
||||
basic.showString("L")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.ThreeG, () => {
|
||||
basic.showString("3")
|
||||
})
|
||||
});
|
||||
input.onGesture(Gesture.SixG, () => {
|
||||
basic.showString("6")
|
||||
})
|
||||
*/
|
||||
});
|
||||
input.onPinPressed(TouchPin.P0, () => {
|
||||
basic.showNumber(0)
|
||||
})
|
||||
});
|
||||
input.onPinPressed(TouchPin.P1, () => {
|
||||
basic.showNumber(1)
|
||||
})
|
||||
});
|
||||
input.onPinPressed(TouchPin.P2, () => {
|
||||
basic.showNumber(2)
|
||||
})
|
||||
});
|
||||
input.onPinPressed(TouchPin.P3, () => {
|
||||
basic.showNumber(3)
|
||||
})
|
||||
});
|
||||
|
||||
basic.showString("Sound");
|
||||
music.setTempo(150);
|
||||
let whole = music.beat(BeatFraction.Whole);
|
||||
function note(n: Note, l: BeatFraction): number[] {
|
||||
return [music.noteFrequency(n), music.beat(l)];
|
||||
}
|
||||
|
||||
function getNoteName(frequency: number): string {
|
||||
switch (frequency) {
|
||||
case 262:
|
||||
return "C";
|
||||
case 277:
|
||||
return "CSharp";
|
||||
case 294:
|
||||
return "D";
|
||||
case 311:
|
||||
return "Eb";
|
||||
case 330:
|
||||
return "E";
|
||||
case 349:
|
||||
return "F";
|
||||
case 370:
|
||||
return "FSharp";
|
||||
case 392:
|
||||
return "G";
|
||||
case 415:
|
||||
return "GSharp";
|
||||
case 440:
|
||||
return "A";
|
||||
case 466:
|
||||
return "Bb";
|
||||
case 494:
|
||||
return "B";
|
||||
case 131:
|
||||
return "C3";
|
||||
case 139:
|
||||
return "CSharp3";
|
||||
case 147:
|
||||
return "D3";
|
||||
case 156:
|
||||
return "Eb3";
|
||||
case 165:
|
||||
return "E3";
|
||||
case 175:
|
||||
return "F3";
|
||||
case 185:
|
||||
return "FSharp3";
|
||||
case 196:
|
||||
return "G3";
|
||||
case 208:
|
||||
return "GSharp3";
|
||||
case 220:
|
||||
return "A3";
|
||||
case 233:
|
||||
return "Bb3";
|
||||
case 247:
|
||||
return "B3";
|
||||
case 523:
|
||||
return "C5";
|
||||
case 555:
|
||||
return "CSharp5";
|
||||
case 587:
|
||||
return "D5";
|
||||
case 622:
|
||||
return "Eb5";
|
||||
case 659:
|
||||
return "E5";
|
||||
case 698:
|
||||
return "F5";
|
||||
case 740:
|
||||
return "FSharp5";
|
||||
case 784:
|
||||
return "G5";
|
||||
case 831:
|
||||
return "GSharp5";
|
||||
case 880:
|
||||
return "A5";
|
||||
case 932:
|
||||
return "Bb5";
|
||||
case 989:
|
||||
return "B5";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
var notes = [
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.E, BeatFraction.Quarter), note(Note.F, BeatFraction.Quarter),
|
||||
note(Note.G, BeatFraction.Quarter), note(Note.G, BeatFraction.Quarter), note(Note.F, BeatFraction.Quarter),
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter), note(Note.C, BeatFraction.Quarter),
|
||||
note(Note.C, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter), note(Note.E, BeatFraction.Quarter),
|
||||
note(Note.E, BeatFraction.Quarter + BeatFraction.Eighth),
|
||||
note(Note.D, BeatFraction.Eighth), note(Note.D, BeatFraction.Half),
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.E, BeatFraction.Quarter), note(Note.F, BeatFraction.Quarter),
|
||||
note(Note.G, BeatFraction.Quarter), note(Note.G, BeatFraction.Quarter), note(Note.F, BeatFraction.Quarter),
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter), note(Note.C, BeatFraction.Quarter),
|
||||
note(Note.C, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter), note(Note.E, BeatFraction.Quarter),
|
||||
note(Note.D, BeatFraction.Quarter + BeatFraction.Eighth),
|
||||
note(Note.C, BeatFraction.Eighth), note(Note.C, BeatFraction.Half),
|
||||
note(Note.D, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter), note(Note.E, BeatFraction.Quarter),
|
||||
note(Note.C, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter),
|
||||
note(Note.E, BeatFraction.Eighth), note(Note.F, BeatFraction.Eighth),
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.C, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter),
|
||||
note(Note.E, BeatFraction.Eighth), note(Note.F, BeatFraction.Eighth),
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter), note(Note.C, BeatFraction.Quarter),
|
||||
note(Note.D, BeatFraction.Quarter), note(Note.G3, BeatFraction.Quarter), note(Note.E, BeatFraction.Half),
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.F, BeatFraction.Quarter), note(Note.G, BeatFraction.Quarter),
|
||||
note(Note.G, BeatFraction.Quarter), note(Note.F, BeatFraction.Quarter), note(Note.E, BeatFraction.Quarter),
|
||||
note(Note.E, BeatFraction.Eighth), note(Note.F, BeatFraction.Eighth),
|
||||
note(Note.C, BeatFraction.Quarter), note(Note.C, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter),
|
||||
note(Note.E, BeatFraction.Quarter), note(Note.D, BeatFraction.Quarter + BeatFraction.Eighth),
|
||||
note(Note.C, BeatFraction.Eighth), note(Note.C, BeatFraction.Half)
|
||||
];
|
||||
|
||||
for (var t = 0; t < notes.length; t++) {
|
||||
music.playTone(notes[t][0], notes[t][1]);
|
||||
basic.showString(getNoteName(notes[t][0]));
|
||||
music.rest(whole - notes[t][1]);
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
/**
|
||||
* Provides access to basic micro:bit functionality.
|
||||
*/
|
||||
//% color=#0078D7 weight=100
|
||||
//% color=#54C9C9 weight=100
|
||||
namespace basic {
|
||||
/**
|
||||
* Sets the color on the build-in LED. Set to 0 to turn off.
|
||||
|
@ -34,7 +34,7 @@ enum Colors {
|
||||
/**
|
||||
* Provides access to basic micro:bit functionality.
|
||||
*/
|
||||
//% color=#0078D7 weight=100
|
||||
//% color=#54C9C9 weight=100
|
||||
namespace basic {
|
||||
/**
|
||||
* Converts the color name to a number
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Runtime and event utilities.
|
||||
*/
|
||||
//% weight=1 color="#333333"
|
||||
//% weight=1 color="#42495F"
|
||||
//% advanced=true
|
||||
namespace control {
|
||||
|
||||
|
@ -108,7 +108,7 @@ enum class Gesture {
|
||||
SixG = MICROBIT_ACCELEROMETER_EVT_6G
|
||||
};
|
||||
|
||||
//% color=300 weight=99
|
||||
//% color=#C90072 weight=99
|
||||
namespace input {
|
||||
/**
|
||||
* Do something when a button (``A``, ``B`` or both ``A+B``) is pressed
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Events and data from sensors
|
||||
*/
|
||||
//% color=#B4009E weight=99
|
||||
//% color=#C90072 weight=99
|
||||
namespace input {
|
||||
/**
|
||||
* Attaches code to run when the screen is facing up.
|
||||
|
@ -8,7 +8,7 @@ enum class DisplayMode_ {
|
||||
// TODO DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE
|
||||
};
|
||||
|
||||
//% color=3 weight=35
|
||||
//% color=#8169E6 weight=35
|
||||
namespace led {
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Control of the LED screen.
|
||||
*/
|
||||
//% color=#5C2D91 weight=97
|
||||
//% color=#8169E6 weight=97
|
||||
namespace led {
|
||||
|
||||
// what's the current high value
|
||||
|
@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Blocks to control the onboard motors
|
||||
*/
|
||||
//% weight=30
|
||||
//% color=#008272 weight=30
|
||||
namespace motors {
|
||||
/**
|
||||
* Controls the power sent to a single motor
|
||||
|
28
libs/core/music.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "ksbit.h"
|
||||
|
||||
namespace music {
|
||||
/**
|
||||
* Plays a tone through ``speaker`` for the given duration.
|
||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
* @param ms tone duration in milliseconds (ms)
|
||||
*/
|
||||
//% help=music/play-tone weight=90
|
||||
//% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" icon="\uf025" blockGap=8
|
||||
//% parts="speaker" async
|
||||
void playTone(int freqency, int ms) {
|
||||
uBit.soundmotor.Sound_On(freqency);
|
||||
if(ms > 0) uBit.sleep(ms);
|
||||
uBit.soundmotor.Sound_Off();
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a tone through ``speaker``.
|
||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
*/
|
||||
//% help=music/ring-tone weight=80
|
||||
//% blockId=device_ring block="ring tone (Hz)|%note=device_note" icon="\uf025" blockGap=8
|
||||
//% parts="speaker"
|
||||
void ringTone(int frequency) {
|
||||
playTone(frequency, 0);
|
||||
}
|
||||
}
|
@ -125,34 +125,34 @@ enum BeatFraction {
|
||||
/**
|
||||
* Generation of music tones through pin ``P0``.
|
||||
*/
|
||||
//% color=#D83B01 weight=98
|
||||
//% color=#DF4600 weight=98
|
||||
namespace music {
|
||||
let beatsPerMinute: number = 120;
|
||||
|
||||
/**
|
||||
* Plays a tone through pin ``P0`` for the given duration.
|
||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
* @param ms tone duration in milliseconds (ms)
|
||||
*/
|
||||
//% help=music/play-tone weight=90
|
||||
//% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" icon="\uf025" blockGap=8
|
||||
//% parts="headphone"
|
||||
export function playTone(frequency: number, ms: number): void {
|
||||
pins.analogSetPitchPin(AnalogPin.P0);
|
||||
pins.analogPitch(frequency, ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a tone through pin ``P0``.
|
||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
*/
|
||||
//% help=music/ring-tone weight=80
|
||||
//% blockId=device_ring block="ring tone (Hz)|%note=device_note" icon="\uf025" blockGap=8
|
||||
//% parts="headphone"
|
||||
export function ringTone(frequency: number): void {
|
||||
pins.analogSetPitchPin(AnalogPin.P0);
|
||||
pins.analogPitch(frequency, 0);
|
||||
}
|
||||
// /**
|
||||
// * Plays a tone through pin ``P0`` for the given duration.
|
||||
// * @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
// * @param ms tone duration in milliseconds (ms)
|
||||
// */
|
||||
// //% help=music/play-tone weight=90
|
||||
// //% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" icon="\uf025" blockGap=8
|
||||
// //% parts="speaker"
|
||||
// export function playTone(frequency: number, ms: number): void {
|
||||
// pins.analogSetPitchPin(AnalogPin.P0);
|
||||
// pins.analogPitch(frequency, ms);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Plays a tone through pin ``P0``.
|
||||
// * @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
// */
|
||||
// //% help=music/ring-tone weight=80
|
||||
// //% blockId=device_ring block="ring tone (Hz)|%note=device_note" icon="\uf025" blockGap=8
|
||||
// //% parts="speaker"
|
||||
// export function ringTone(frequency: number): void {
|
||||
// pins.analogSetPitchPin(AnalogPin.P0);
|
||||
// pins.analogPitch(frequency, 0);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Rests (plays nothing) for a specified time through pin ``P0``.
|
||||
@ -160,7 +160,7 @@ namespace music {
|
||||
*/
|
||||
//% help=music/rest weight=79
|
||||
//% blockId=device_rest block="rest(ms)|%duration=device_beat"
|
||||
//% parts="headphone"
|
||||
//% parts="speaker"
|
||||
export function rest(ms: number): void {
|
||||
playTone(0, ms);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
"led.cpp",
|
||||
"led.ts",
|
||||
"motors.cpp",
|
||||
"music.cpp",
|
||||
"music.ts",
|
||||
"pins.cpp",
|
||||
"pins.ts",
|
||||
|
29
libs/core/shims.d.ts
vendored
@ -126,7 +126,7 @@ declare interface Image {
|
||||
/**
|
||||
* Provides access to basic micro:bit functionality.
|
||||
*/
|
||||
//% color=#0078D7 weight=100
|
||||
//% color=#54C9C9 weight=100
|
||||
declare namespace basic {
|
||||
|
||||
/**
|
||||
@ -218,7 +218,7 @@ declare namespace basic {
|
||||
|
||||
|
||||
|
||||
//% color=300 weight=99
|
||||
//% color=#C90072 weight=99
|
||||
declare namespace input {
|
||||
|
||||
/**
|
||||
@ -432,7 +432,7 @@ declare namespace control {
|
||||
|
||||
|
||||
|
||||
//% color=3 weight=35
|
||||
//% color=#8169E6 weight=35
|
||||
declare namespace led {
|
||||
|
||||
/**
|
||||
@ -513,7 +513,7 @@ declare namespace led {
|
||||
/**
|
||||
* Blocks to control the onboard motors
|
||||
*/
|
||||
//% weight=30
|
||||
//% color=#008272 weight=30
|
||||
declare namespace motors {
|
||||
|
||||
/**
|
||||
@ -524,6 +524,27 @@ declare namespace motors {
|
||||
//% parts=dcmotor shim=motors::motorOn
|
||||
function motorOn(power: number): void;
|
||||
}
|
||||
declare namespace music {
|
||||
|
||||
/**
|
||||
* Plays a tone through ``speaker`` for the given duration.
|
||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
* @param ms tone duration in milliseconds (ms)
|
||||
*/
|
||||
//% help=music/play-tone weight=90
|
||||
//% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" icon="\uf025" blockGap=8
|
||||
//% parts="speaker" async shim=music::playTone
|
||||
function playTone(freqency: number, ms: number): void;
|
||||
|
||||
/**
|
||||
* Plays a tone through ``speaker``.
|
||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||
*/
|
||||
//% help=music/ring-tone weight=80
|
||||
//% blockId=device_ring block="ring tone (Hz)|%note=device_note" icon="\uf025" blockGap=8
|
||||
//% parts="speaker" shim=music::ringTone
|
||||
function ringTone(frequency: number): void;
|
||||
}
|
||||
declare namespace pins {
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,26 @@
|
||||
using namespace pxt;
|
||||
|
||||
#define MAX_FIELD_NAME_LENGTH 12
|
||||
#define MAX_PAYLOAD_LENGTH 20
|
||||
#define PACKET_PREFIX_LENGTH 9
|
||||
#define VALUE_PACKET_NAME_LEN_OFFSET 13
|
||||
|
||||
|
||||
// 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)
|
||||
#define PACKET_TYPE_NUMBER 0
|
||||
|
||||
// payload: number (9 ... 12), name length (13), name (14 ... 26)
|
||||
#define PACKET_TYPE_VALUE 1
|
||||
|
||||
// payload: string length (9), string (10 ... 28)
|
||||
#define PACKET_TYPE_STRING 2
|
||||
|
||||
//% color=270 weight=34
|
||||
namespace radio {
|
||||
@ -15,6 +35,12 @@ namespace radio {
|
||||
|
||||
PacketBuffer packet;
|
||||
|
||||
uint8_t type;
|
||||
uint32_t time;
|
||||
uint32_t serial;
|
||||
int value;
|
||||
StringData* msg;
|
||||
|
||||
int radioEnable() {
|
||||
int r = uBit.radio.enable();
|
||||
if (r != MICROBIT_OK) {
|
||||
@ -38,6 +64,105 @@ namespace radio {
|
||||
registerWithDal(MES_BROADCAST_GENERAL_ID, message, f);
|
||||
}
|
||||
|
||||
void setPacketPrefix(uint8_t* buf, int type) {
|
||||
// prefix: type (0), time (1..4), serial (5..8)
|
||||
uint32_t t = system_timer_current_time();
|
||||
uint32_t sn = transmitSerialNumber ? microbit_serial_number() : 0;
|
||||
|
||||
buf[0] = (uint8_t) type;
|
||||
memcpy(buf + 1, &t, 4);
|
||||
memcpy(buf + 5, &sn, 4);
|
||||
}
|
||||
|
||||
uint8_t copyStringValue(uint8_t* buf, StringData* data, uint8_t maxLength) {
|
||||
ManagedString s(data);
|
||||
uint8_t len = min(maxLength, s.length());
|
||||
|
||||
// One byte for length of the string
|
||||
buf[0] = len;
|
||||
|
||||
if (len > 0) {
|
||||
memcpy(buf + 1, s.toCharArray(), len);
|
||||
}
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
StringData* getStringValue(uint8_t* buf, uint8_t maxLength) {
|
||||
// First byte is the string length
|
||||
uint8_t len = min(maxLength, buf[0]);
|
||||
|
||||
if (len) {
|
||||
char name[maxLength + 1];
|
||||
memcpy(name, buf + 1, len);
|
||||
name[len] = 0;
|
||||
return ManagedString(name).leakData();
|
||||
}
|
||||
return ManagedString().leakData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a packet from the micro:bit radio queue.
|
||||
* @param writeToSerial if true, write the received packet to serial without updating the global packet;
|
||||
if false, update the global packet instead
|
||||
*/
|
||||
void receivePacket(bool writeToSerial) {
|
||||
PacketBuffer p = uBit.radio.datagram.recv();
|
||||
|
||||
uint8_t* buf = p.getBytes();
|
||||
uint8_t tp;
|
||||
int t;
|
||||
int s;
|
||||
int v;
|
||||
StringData* m;
|
||||
|
||||
|
||||
memcpy(&tp, buf, 1);
|
||||
memcpy(&t, buf + 1, 4);
|
||||
memcpy(&s, buf + 5, 4);
|
||||
|
||||
if (tp == PACKET_TYPE_STRING) {
|
||||
v = 0;
|
||||
m = getStringValue(buf + PACKET_PREFIX_LENGTH, MAX_PAYLOAD_LENGTH - 1);
|
||||
}
|
||||
else {
|
||||
memcpy(&v, buf + 9, 4);
|
||||
if (tp == PACKET_TYPE_VALUE) {
|
||||
m = getStringValue(buf + VALUE_PACKET_NAME_LEN_OFFSET, MAX_FIELD_NAME_LENGTH);
|
||||
}
|
||||
else {
|
||||
m = ManagedString().leakData();
|
||||
}
|
||||
}
|
||||
|
||||
if (!writeToSerial) {
|
||||
// Refresh global packet
|
||||
packet = p;
|
||||
type = tp;
|
||||
time = t;
|
||||
serial = s;
|
||||
value = v;
|
||||
msg = m;
|
||||
}
|
||||
else {
|
||||
// Convert the packet to JSON and send over serial
|
||||
uBit.serial.send("{");
|
||||
uBit.serial.send("\"t\":");
|
||||
uBit.serial.send(t);
|
||||
uBit.serial.send(",\"s\":");
|
||||
uBit.serial.send(s);
|
||||
if (tp == PACKET_TYPE_STRING || tp == PACKET_TYPE_VALUE) {
|
||||
uBit.serial.send(",\"n\":\"");
|
||||
uBit.serial.send(m);
|
||||
uBit.serial.send("\"");
|
||||
}
|
||||
if (tp == PACKET_TYPE_NUMBER || tp == PACKET_TYPE_VALUE) {
|
||||
uBit.serial.send(",\"v\":");
|
||||
uBit.serial.send(v);
|
||||
}
|
||||
uBit.serial.send("}\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a number over radio to any connected micro:bit in the group.
|
||||
*/
|
||||
@ -46,10 +171,14 @@ namespace radio {
|
||||
//% blockId=radio_datagram_send block="radio send number %value" blockGap=8
|
||||
void sendNumber(int value) {
|
||||
if (radioEnable() != MICROBIT_OK) return;
|
||||
uint32_t t = system_timer_current_time();
|
||||
uint32_t sn = transmitSerialNumber ? microbit_serial_number() : 0;
|
||||
uint32_t buf[] = { (uint32_t)value, t, sn };
|
||||
uBit.radio.datagram.send((uint8_t*)buf, 3*sizeof(uint32_t));
|
||||
uint8_t length = PACKET_PREFIX_LENGTH + sizeof(uint32_t);
|
||||
uint8_t buf[length];
|
||||
memset(buf, 0, length);
|
||||
|
||||
setPacketPrefix(buf, PACKET_TYPE_NUMBER);
|
||||
memcpy(buf + PACKET_PREFIX_LENGTH, &value, 4);
|
||||
|
||||
uBit.radio.datagram.send(buf, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,24 +194,20 @@ namespace radio {
|
||||
if (radioEnable() != MICROBIT_OK) return;
|
||||
|
||||
ManagedString n(name);
|
||||
uint32_t t = system_timer_current_time();
|
||||
uint32_t sn = transmitSerialNumber ? microbit_serial_number() : 0;
|
||||
uint8_t buf[32];
|
||||
uint32_t* buf32 = (uint32_t*)buf;
|
||||
memset(buf, 0, 32);
|
||||
buf32[0] = value; // 4 bytes: value
|
||||
buf32[1] = t; // 4 bytes: running time
|
||||
buf32[2] = sn; // 4 bytes: serial number
|
||||
uint8_t len = min(MAX_FIELD_NAME_LENGTH, n.length()); // 1 byte: string length
|
||||
if (len > 0) {
|
||||
buf[12] = len; //
|
||||
memcpy(buf + 13, n.toCharArray(), len); // 13-25: field name
|
||||
}
|
||||
uBit.radio.datagram.send(buf, 13 + len);
|
||||
|
||||
setPacketPrefix(buf, PACKET_TYPE_VALUE);
|
||||
memcpy(buf + PACKET_PREFIX_LENGTH, &value, 4);
|
||||
|
||||
int stringLen = copyStringValue(buf + VALUE_PACKET_NAME_LEN_OFFSET, name, MAX_FIELD_NAME_LENGTH);
|
||||
|
||||
uBit.radio.datagram.send(buf, VALUE_PACKET_NAME_LEN_OFFSET + stringLen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a number over radio to any connected micro:bit in the group.
|
||||
* 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
|
||||
@ -90,16 +215,18 @@ namespace radio {
|
||||
void sendString(StringData* msg) {
|
||||
if (radioEnable() != MICROBIT_OK) return;
|
||||
|
||||
ManagedString s(msg);
|
||||
if (s.length() > MICROBIT_RADIO_MAX_PACKET_SIZE)
|
||||
s = s.substring(0, MICROBIT_RADIO_MAX_PACKET_SIZE);
|
||||
uint8_t buf[32];
|
||||
memset(buf, 0, 32);
|
||||
|
||||
uBit.radio.datagram.send(s);
|
||||
setPacketPrefix(buf, PACKET_TYPE_STRING);
|
||||
int stringLen = copyStringValue(buf + PACKET_PREFIX_LENGTH, msg, MAX_PAYLOAD_LENGTH - 1);
|
||||
|
||||
uBit.radio.datagram.send(buf, PACKET_PREFIX_LENGTH + stringLen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a value sent with `stream value` and writes it
|
||||
* to the serial stream as JSON
|
||||
* Reads the next packet from the radio queue and and writes it to serial
|
||||
* as JSON.
|
||||
*/
|
||||
//% help=radio/write-value-to-serial
|
||||
//% weight=3
|
||||
@ -107,63 +234,22 @@ namespace radio {
|
||||
//% advanced=true
|
||||
void writeValueToSerial() {
|
||||
if (radioEnable() != MICROBIT_OK) return;
|
||||
PacketBuffer p = uBit.radio.datagram.recv();
|
||||
int length = p.length();
|
||||
uint8_t* bytes = p.getBytes();
|
||||
int value;
|
||||
|
||||
uBit.serial.send("{");
|
||||
if (length >= 4) {
|
||||
memcpy(&value, bytes, 4);
|
||||
uBit.serial.send("\"v\":"); uBit.serial.send(value);
|
||||
if(length >= 8) {
|
||||
memcpy(&value, bytes + 4, 4);
|
||||
uBit.serial.send(",\"t\":"); uBit.serial.send(value);
|
||||
if (length >= 12) {
|
||||
memcpy(&value, bytes + 8, 4);
|
||||
uBit.serial.send(",\"s\":"); uBit.serial.send(value);
|
||||
if (length >= 13) {
|
||||
char name[MAX_FIELD_NAME_LENGTH+1];
|
||||
uint8_t len = min(MAX_FIELD_NAME_LENGTH, bytes[12]);
|
||||
memcpy(name, bytes + 13, len);
|
||||
name[len] = 0;
|
||||
uBit.serial.send(",\"n\":\""); uBit.serial.send(name); uBit.serial.send("\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uBit.serial.send("}\r\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a number at a given index, between ``0`` and ``3``, from the packet received by ``receive number``. Not supported in simulator.
|
||||
* @param index index of the number to read from 0 to 3. 1 eg
|
||||
*/
|
||||
//% help=radio/received-number-at
|
||||
//% weight=45 debug=true
|
||||
int receivedNumberAt(int index) {
|
||||
if (radioEnable() != MICROBIT_OK) return 0;
|
||||
if (0 <= index && index < packet.length() / 4) {
|
||||
// packet.getBytes() is not aligned
|
||||
int r;
|
||||
memcpy(&r, packet.getBytes() + index * 4, 4);
|
||||
return r;
|
||||
}
|
||||
return 0;
|
||||
receivePacket(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next packet as a number from the radio queue.
|
||||
* Reads the next packet from the radio queue and returns the packet's number
|
||||
* payload or 0 if the packet did not contain a number.
|
||||
*/
|
||||
//% help=radio/receive-number
|
||||
//% weight=46
|
||||
//% blockId=radio_datagram_receive block="radio receive number" blockGap=8
|
||||
//% advanced=true
|
||||
int receiveNumber()
|
||||
{
|
||||
if (radioEnable() != MICROBIT_OK) return 0;
|
||||
packet = uBit.radio.datagram.recv();
|
||||
return receivedNumberAt(0);
|
||||
receivePacket(false);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,28 +258,32 @@ namespace radio {
|
||||
//% help=radio/on-data-received
|
||||
//% weight=50
|
||||
//% blockId=radio_datagram_received_event block="radio on data received" blockGap=8
|
||||
//% advanced=true
|
||||
void onDataReceived(Action body) {
|
||||
if (radioEnable() != MICROBIT_OK) return;
|
||||
registerWithDal(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM, body);
|
||||
// make the the receive buffer has a free spot
|
||||
// make sure the receive buffer has a free spot
|
||||
receiveNumber();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the next packet as a string and returns it.
|
||||
*/
|
||||
* Reads the next packet from the radio queue and returns the packet's string
|
||||
* payload or the empty string if the packet did not contain a string.
|
||||
*/
|
||||
//% blockId=radio_datagram_receive_string block="radio receive string" blockGap=8
|
||||
//% weight=44
|
||||
//% help=radio/receive-string
|
||||
//% advanced=true
|
||||
StringData* receiveString() {
|
||||
if (radioEnable() != MICROBIT_OK) return ManagedString().leakData();
|
||||
packet = uBit.radio.datagram.recv();
|
||||
return ManagedString(packet).leakData();
|
||||
receivePacket(false);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the received signal strength indicator (RSSI) from the packet received by ``receive number``. Not supported in simulator.
|
||||
* Gets the received signal strength indicator (RSSI) from the last packet taken
|
||||
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.
|
||||
* namespace=radio
|
||||
*/
|
||||
//% help=radio/received-signal-strength
|
||||
@ -242,4 +332,48 @@ namespace radio {
|
||||
if (radioEnable() != MICROBIT_OK) return;
|
||||
transmitSerialNumber = transmit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number payload from the last packet taken from the radio queue
|
||||
* (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not
|
||||
* contain a number.
|
||||
*/
|
||||
//% help=radio/received-number
|
||||
int receivedNumber() {
|
||||
if (radioEnable() != MICROBIT_OK) return 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the serial number of the sender micro:bit from the last packet taken
|
||||
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc) or 0 if
|
||||
* that packet did not send a serial number.
|
||||
*/
|
||||
//% help=radio/received-serial
|
||||
uint32_t receivedSerial() {
|
||||
if (radioEnable() != MICROBIT_OK) return 0;
|
||||
return serial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string payload from the last packet taken from the radio queue
|
||||
* (via ``receiveNumber``, ``receiveString``, etc) or the empty string if that
|
||||
* packet did not contain a string.
|
||||
*/
|
||||
//% help=radio/received-string
|
||||
StringData* receivedString() {
|
||||
if (radioEnable() != MICROBIT_OK) return ManagedString().leakData();
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the system time of the sender micro:bit at the moment when it sent the
|
||||
* last packet taken from the radio queue (via ``receiveNumber``,
|
||||
* ``receiveString``, etc).
|
||||
*/
|
||||
//% help=radio/received-time
|
||||
uint32_t receivedTime() {
|
||||
if (radioEnable() != MICROBIT_OK) return 0;
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,49 @@
|
||||
*/
|
||||
//% color=#E3008C weight=34
|
||||
namespace radio {
|
||||
export class Packet {
|
||||
/**
|
||||
* The number payload if a number was sent in this packet (via ``sendNumber()`` or ``sendValue()``)
|
||||
* or 0 if this packet did not contain a number.
|
||||
*/
|
||||
public receivedNumber: number;
|
||||
/**
|
||||
* The string payload if a string was sent in this packet (via ``sendString()`` or ``sendValue()``)
|
||||
* or the empty string if this packet did not contain a string.
|
||||
*/
|
||||
public text: string;
|
||||
/**
|
||||
* The system time of the sender of the packet at the time the packet was sent.
|
||||
*/
|
||||
public time: number;
|
||||
/**
|
||||
* The serial number of the sender of the packet or 0 if the sender did not sent their serial number.
|
||||
*/
|
||||
public serial: number;
|
||||
/**
|
||||
* The received signal strength indicator (RSSI) of the packet.
|
||||
*/
|
||||
public signal: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers code to run when the radio receives a packet. Also takes the
|
||||
* received packet from the radio queue.
|
||||
*/
|
||||
//% mutate=true
|
||||
//% mutateText=Packet
|
||||
//% mutateDefaults="receivedNumber;text,receivedNumber;text"
|
||||
//% blockId=radio_on_packet block="on radio received" blockGap=8
|
||||
export function onDataPacketReceived(cb: (packet: Packet) => void) {
|
||||
onDataReceived(() => {
|
||||
receiveNumber();
|
||||
const packet = new Packet();
|
||||
packet.receivedNumber = receivedNumber();
|
||||
packet.time = receivedTime();
|
||||
packet.serial = receivedSerial();
|
||||
packet.text = receivedString();
|
||||
packet.signal = receivedSignalStrength();
|
||||
cb(packet)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
65
libs/radio/shims.d.ts
vendored
@ -25,7 +25,8 @@ declare namespace radio {
|
||||
function sendValue(name: string, value: number): void;
|
||||
|
||||
/**
|
||||
* Broadcasts a number over radio to any connected micro:bit in the group.
|
||||
* 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
|
||||
@ -33,8 +34,8 @@ declare namespace radio {
|
||||
function sendString(msg: string): void;
|
||||
|
||||
/**
|
||||
* Reads a value sent with `stream value` and writes it
|
||||
* to the serial stream as JSON
|
||||
* Reads the next packet from the radio queue and and writes it to serial
|
||||
* as JSON.
|
||||
*/
|
||||
//% help=radio/write-value-to-serial
|
||||
//% weight=3
|
||||
@ -43,19 +44,13 @@ declare namespace radio {
|
||||
function writeValueToSerial(): void;
|
||||
|
||||
/**
|
||||
* Reads a number at a given index, between ``0`` and ``3``, from the packet received by ``receive number``. Not supported in simulator.
|
||||
* @param index index of the number to read from 0 to 3. 1 eg
|
||||
*/
|
||||
//% help=radio/received-number-at
|
||||
//% weight=45 debug=true shim=radio::receivedNumberAt
|
||||
function receivedNumberAt(index: number): number;
|
||||
|
||||
/**
|
||||
* Reads the next packet as a number from the radio queue.
|
||||
* Reads the next packet from the radio queue and returns the packet's number
|
||||
* payload or 0 if the packet did not contain a number.
|
||||
*/
|
||||
//% help=radio/receive-number
|
||||
//% weight=46
|
||||
//% blockId=radio_datagram_receive block="radio receive number" blockGap=8 shim=radio::receiveNumber
|
||||
//% blockId=radio_datagram_receive block="radio receive number" blockGap=8
|
||||
//% advanced=true shim=radio::receiveNumber
|
||||
function receiveNumber(): number;
|
||||
|
||||
/**
|
||||
@ -63,19 +58,23 @@ declare namespace radio {
|
||||
*/
|
||||
//% help=radio/on-data-received
|
||||
//% weight=50
|
||||
//% blockId=radio_datagram_received_event block="radio on data received" blockGap=8 shim=radio::onDataReceived
|
||||
//% blockId=radio_datagram_received_event block="radio on data received" blockGap=8
|
||||
//% advanced=true shim=radio::onDataReceived
|
||||
function onDataReceived(body: () => void): void;
|
||||
|
||||
/**
|
||||
* Reads the next packet as a string and returns it.
|
||||
* Reads the next packet from the radio queue and returns the packet's string
|
||||
* payload or the empty string if the packet did not contain a string.
|
||||
*/
|
||||
//% blockId=radio_datagram_receive_string block="radio receive string" blockGap=8
|
||||
//% weight=44
|
||||
//% help=radio/receive-string shim=radio::receiveString
|
||||
//% help=radio/receive-string
|
||||
//% advanced=true shim=radio::receiveString
|
||||
function receiveString(): string;
|
||||
|
||||
/**
|
||||
* Gets the received signal strength indicator (RSSI) from the packet received by ``receive number``. Not supported in simulator.
|
||||
* Gets the received signal strength indicator (RSSI) from the last packet taken
|
||||
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.
|
||||
* namespace=radio
|
||||
*/
|
||||
//% help=radio/received-signal-strength
|
||||
@ -112,6 +111,38 @@ declare namespace radio {
|
||||
//% blockId=radio_set_transmit_serial_number block="radio set transmit serial number %transmit"
|
||||
//% advanced=true shim=radio::setTransmitSerialNumber
|
||||
function setTransmitSerialNumber(transmit: boolean): void;
|
||||
|
||||
/**
|
||||
* Returns the number payload from the last packet taken from the radio queue
|
||||
* (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not
|
||||
* contain a number.
|
||||
*/
|
||||
//% help=radio/received-number shim=radio::receivedNumber
|
||||
function receivedNumber(): number;
|
||||
|
||||
/**
|
||||
* Returns the serial number of the sender micro:bit from the last packet taken
|
||||
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc) or 0 if
|
||||
* that packet did not send a serial number.
|
||||
*/
|
||||
//% help=radio/received-serial shim=radio::receivedSerial
|
||||
function receivedSerial(): number;
|
||||
|
||||
/**
|
||||
* Returns the string payload from the last packet taken from the radio queue
|
||||
* (via ``receiveNumber``, ``receiveString``, etc) or the empty string if that
|
||||
* packet did not contain a string.
|
||||
*/
|
||||
//% help=radio/received-string shim=radio::receivedString
|
||||
function receivedString(): string;
|
||||
|
||||
/**
|
||||
* Returns the system time of the sender micro:bit at the moment when it sent the
|
||||
* last packet taken from the radio queue (via ``receiveNumber``,
|
||||
* ``receiveString``, etc).
|
||||
*/
|
||||
//% help=radio/received-time shim=radio::receivedTime
|
||||
function receivedTime(): number;
|
||||
}
|
||||
|
||||
// Auto-generated. Do not edit. Really.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pxt-calliope",
|
||||
"version": "0.5.33",
|
||||
"version": "0.5.52",
|
||||
"description": "calliope target for PXT",
|
||||
"keywords": [
|
||||
"JavaScript",
|
||||
@ -23,13 +23,14 @@
|
||||
"sim/public",
|
||||
"docs/*.md",
|
||||
"docs/*/*.md",
|
||||
"docs/*/*/*.md"
|
||||
"docs/*/*/*.md",
|
||||
"hexcache/*.hex"
|
||||
],
|
||||
"devDependencies": {
|
||||
"typescript": "^1.8.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"pxt-core": "0.5.3",
|
||||
"pxt-core": "0.5.29",
|
||||
"less": "^2.6.0",
|
||||
"semantic-ui-less": "^2.2.4"
|
||||
}
|
||||
|
@ -106,54 +106,54 @@
|
||||
"visual": "calliope",
|
||||
"gpioPinBlocks": [
|
||||
[
|
||||
"P0"
|
||||
"EDGE_P0"
|
||||
],
|
||||
[
|
||||
"P1"
|
||||
"EDGE_P1"
|
||||
],
|
||||
[
|
||||
"P2"
|
||||
"EDGE_P2"
|
||||
],
|
||||
[
|
||||
"P3"
|
||||
"EDGE_P3"
|
||||
],
|
||||
[
|
||||
"P4",
|
||||
"P5",
|
||||
"P6",
|
||||
"P7"
|
||||
"C_P4",
|
||||
"C_P5",
|
||||
"C_P6",
|
||||
"C_P7"
|
||||
],
|
||||
[
|
||||
"P8",
|
||||
"P9",
|
||||
"P10",
|
||||
"P11",
|
||||
"P12"
|
||||
"C_P8",
|
||||
"C_P9",
|
||||
"C_P10",
|
||||
"C_P11",
|
||||
"C_P12"
|
||||
],
|
||||
[
|
||||
"P16"
|
||||
"C_P16"
|
||||
]
|
||||
],
|
||||
"gpioPinMap": {
|
||||
"P0": "P0",
|
||||
"P1": "P1",
|
||||
"P2": "P2",
|
||||
"P3": "P3",
|
||||
"P4": "P4",
|
||||
"P5": "P5",
|
||||
"P6": "P6",
|
||||
"P7": "P7",
|
||||
"P8": "P8",
|
||||
"P9": "P9",
|
||||
"P10": "P10",
|
||||
"P11": "P11",
|
||||
"P12": "P12",
|
||||
"P13": "P13",
|
||||
"P14": "P14",
|
||||
"P15": "P15",
|
||||
"P16": "P16",
|
||||
"P19": "P19",
|
||||
"P20": "P20",
|
||||
"P0": "EDGE_P0",
|
||||
"P1": "EDGE_P1",
|
||||
"P2": "EDGE_P2",
|
||||
"P3": "EDGE_P3",
|
||||
"P4": "C_P4",
|
||||
"P5": "C_P5",
|
||||
"P6": "C_P6",
|
||||
"P7": "C_P7",
|
||||
"P8": "C_P8",
|
||||
"P9": "C_P9",
|
||||
"P10": "C_P10",
|
||||
"P11": "C_P11",
|
||||
"P12": "C_P12",
|
||||
"P13": "C_P13",
|
||||
"P14": "C_P14",
|
||||
"P15": "C_P15",
|
||||
"P16": "C_P16",
|
||||
"P19": "C_P19",
|
||||
"P20": "C_P20",
|
||||
"EXT_PWR":"EXT_PWR",
|
||||
"SPKR":"EXT_PWR",
|
||||
"BTN_A": "BTN_A",
|
||||
@ -178,10 +178,10 @@
|
||||
"P10"
|
||||
],
|
||||
"groundPins": [
|
||||
"GND"
|
||||
"EDGE_GND"
|
||||
],
|
||||
"threeVoltPins": [
|
||||
"+3v3"
|
||||
"EDGE_VCC"
|
||||
],
|
||||
"attachPowerOnRight": true,
|
||||
"onboardComponents": [
|
||||
@ -219,8 +219,6 @@
|
||||
},
|
||||
"appTheme": {
|
||||
"accentColor": "#249899",
|
||||
"downloadClass": "orange",
|
||||
"invertedMenu": true,
|
||||
"defaultLocale": "de",
|
||||
"logoUrl": "https://calliope.cc/about",
|
||||
"logo": "./static/Calliopeminieditor.svg",
|
||||
@ -350,6 +348,9 @@
|
||||
"browser": "*",
|
||||
"path": "/static/mb/device/usb-windows-sendto.jpg"
|
||||
}
|
||||
]
|
||||
],
|
||||
"invertedMenu": true,
|
||||
"invertedToolbox": true,
|
||||
"blocklyOptions": { }
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace pxsim {
|
||||
radioState: RadioState;
|
||||
neopixelState: NeoPixelState;
|
||||
rgbLedState: number;
|
||||
speakerState: SpeakerState;
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
@ -59,6 +60,7 @@ namespace pxsim {
|
||||
this.builtinParts["lightsensor"] = this.lightSensorState = new LightSensorState();
|
||||
this.builtinParts["compass"] = this.compassState = new CompassState();
|
||||
this.builtinParts["neopixel"] = this.neopixelState = new NeoPixelState();
|
||||
this.builtinParts["speaker"] = this.speakerState = new SpeakerState();
|
||||
|
||||
this.builtinVisuals["buttonpair"] = () => new visuals.ButtonPairView();
|
||||
this.builtinVisuals["ledmatrix"] = () => new visuals.LedMatrixView();
|
||||
|
29
sim/state/music.ts
Normal file
@ -0,0 +1,29 @@
|
||||
namespace pxsim {
|
||||
export class SpeakerState {
|
||||
frequency: number;
|
||||
ms: number;
|
||||
}
|
||||
|
||||
}
|
||||
namespace pxsim.music {
|
||||
|
||||
export function playTone(frequency: number, ms: number) {
|
||||
const b = board();
|
||||
b.speakerState.frequency = frequency;
|
||||
b.speakerState.ms = ms;
|
||||
|
||||
runtime.queueDisplayUpdate();
|
||||
let cb = getResume();
|
||||
AudioContextManager.tone(frequency, 1);
|
||||
if (ms <= 0) cb();
|
||||
else {
|
||||
setTimeout(() => {
|
||||
AudioContextManager.stop();
|
||||
b.speakerState.frequency = 0;
|
||||
b.speakerState.ms = 0;
|
||||
runtime.queueDisplayUpdate();
|
||||
cb()
|
||||
}, ms);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,14 @@
|
||||
namespace pxsim {
|
||||
export interface PacketBuffer {
|
||||
data: number[] | string;
|
||||
rssi?: number;
|
||||
payload: SimulatorRadioPacketPayload;
|
||||
rssi: number;
|
||||
serial: number;
|
||||
time: number;
|
||||
}
|
||||
|
||||
export class RadioDatagram {
|
||||
datagram: PacketBuffer[] = [];
|
||||
lastReceived: PacketBuffer = {
|
||||
data: [0, 0, 0, 0],
|
||||
rssi: -1
|
||||
};
|
||||
lastReceived: PacketBuffer = RadioDatagram.defaultPacket();
|
||||
|
||||
constructor(private runtime: Runtime) {
|
||||
}
|
||||
@ -21,35 +20,43 @@ namespace pxsim {
|
||||
(<DalBoard>runtime.board).bus.queue(DAL.MICROBIT_ID_RADIO, DAL.MICROBIT_RADIO_EVT_DATAGRAM);
|
||||
}
|
||||
|
||||
send(buffer: number[] | string) {
|
||||
if (buffer instanceof String) buffer = buffer.slice(0, 32);
|
||||
else buffer = buffer.slice(0, 8);
|
||||
|
||||
send(payload: SimulatorRadioPacketPayload) {
|
||||
Runtime.postMessage(<SimulatorRadioPacketMessage>{
|
||||
type: "radiopacket",
|
||||
data: buffer
|
||||
rssi: 0, // Not yet supported
|
||||
serial: board().radioState.bus.transmitSerialNumber ? board().radioState.bus.serial : 0,
|
||||
time: 0, // Not yet supported
|
||||
payload
|
||||
})
|
||||
}
|
||||
|
||||
recv(): PacketBuffer {
|
||||
let r = this.datagram.shift();
|
||||
if (!r) r = {
|
||||
data: [0, 0, 0, 0],
|
||||
rssi: -1
|
||||
};
|
||||
if (!r) r = RadioDatagram.defaultPacket();
|
||||
return this.lastReceived = r;
|
||||
}
|
||||
|
||||
private static defaultPacket(): PacketBuffer {
|
||||
return {
|
||||
rssi: -1,
|
||||
serial: 0,
|
||||
time: 0,
|
||||
payload: { type: -1 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class RadioBus {
|
||||
// uint8_t radioDefaultGroup = MICROBIT_RADIO_DEFAULT_GROUP;
|
||||
groupId = 0; // todo
|
||||
power = 0;
|
||||
serial = 0;
|
||||
transmitSerialNumber = false;
|
||||
datagram: RadioDatagram;
|
||||
|
||||
constructor(private runtime: Runtime) {
|
||||
this.datagram = new RadioDatagram(runtime);
|
||||
this.serial = Math.floor(Math.random() * Math.pow(2, 32)) - Math.pow(2, 31); // 32 bit signed integer
|
||||
}
|
||||
|
||||
setGroup(id: number) {
|
||||
@ -83,12 +90,18 @@ namespace pxsim {
|
||||
}
|
||||
|
||||
public recievePacket(packet: SimulatorRadioPacketMessage) {
|
||||
this.bus.datagram.queue({ data: packet.data, rssi: packet.rssi || 0 })
|
||||
this.bus.datagram.queue(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.radio {
|
||||
enum PacketPayloadType {
|
||||
NUMBER = 0,
|
||||
VALUE = 1,
|
||||
STRING = 2
|
||||
}
|
||||
|
||||
export function broadcastMessage(msg: number): void {
|
||||
board().radioState.bus.broadcast(msg);
|
||||
}
|
||||
@ -110,41 +123,57 @@ namespace pxsim.radio {
|
||||
}
|
||||
|
||||
export function sendNumber(value: number): void {
|
||||
board().radioState.bus.datagram.send([value]);
|
||||
board().radioState.bus.datagram.send({
|
||||
type: PacketPayloadType.NUMBER,
|
||||
numberData: value
|
||||
});
|
||||
}
|
||||
|
||||
export function sendString(msg: string): void {
|
||||
board().radioState.bus.datagram.send(msg);
|
||||
msg = msg.substr(0, 19);
|
||||
board().radioState.bus.datagram.send({
|
||||
type: PacketPayloadType.STRING,
|
||||
stringData: msg
|
||||
});
|
||||
}
|
||||
|
||||
export function writeValueToSerial(): void {
|
||||
let b = board();
|
||||
let v = b.radioState.bus.datagram.recv().data[0];
|
||||
b.writeSerial(`{v:${v}}`);
|
||||
let p = b.radioState.bus.datagram.recv();
|
||||
|
||||
switch(p.payload.type) {
|
||||
case PacketPayloadType.NUMBER:
|
||||
b.writeSerial(`{"t":${p.time},"s":${p.serial},"v":${p.payload.numberData}}`)
|
||||
break;
|
||||
case PacketPayloadType.VALUE:
|
||||
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}","v":${p.payload.numberData}}`)
|
||||
break;
|
||||
case PacketPayloadType.STRING:
|
||||
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}"}`)
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
export function sendValue(name: string, value: number) {
|
||||
board().radioState.bus.datagram.send([value]);
|
||||
name = name.substr(0, 12);
|
||||
const msg: number[] = [];
|
||||
msg.push()
|
||||
board().radioState.bus.datagram.send({
|
||||
type: PacketPayloadType.VALUE,
|
||||
stringData: name,
|
||||
numberData: value
|
||||
});
|
||||
}
|
||||
|
||||
export function receiveNumber(): number {
|
||||
let buffer = board().radioState.bus.datagram.recv().data;
|
||||
if (buffer instanceof Array) return buffer[0];
|
||||
|
||||
return 0;
|
||||
const packet = board().radioState.bus.datagram.recv();
|
||||
return receivedNumber();
|
||||
}
|
||||
|
||||
export function receiveString(): string {
|
||||
let buffer = board().radioState.bus.datagram.recv().data;
|
||||
if (typeof buffer === "string") return <string>buffer;
|
||||
return "";
|
||||
}
|
||||
|
||||
export function receivedNumberAt(index: number): number {
|
||||
let buffer = board().radioState.bus.datagram.recv().data;
|
||||
if (buffer instanceof Array) return buffer[index] || 0;
|
||||
|
||||
return 0;
|
||||
const packet = board().radioState.bus.datagram.recv();
|
||||
return receivedString();
|
||||
}
|
||||
|
||||
export function receivedSignalStrength(): number {
|
||||
@ -155,4 +184,20 @@ namespace pxsim.radio {
|
||||
pxtcore.registerWithDal(DAL.MICROBIT_ID_RADIO, DAL.MICROBIT_RADIO_EVT_DATAGRAM, handler);
|
||||
radio.receiveNumber();
|
||||
}
|
||||
|
||||
export function receivedNumber(): number {
|
||||
return board().radioState.bus.datagram.lastReceived.payload.numberData || 0;
|
||||
}
|
||||
|
||||
export function receivedSerial(): number {
|
||||
return board().radioState.bus.datagram.lastReceived.serial;
|
||||
}
|
||||
|
||||
export function receivedString(): string {
|
||||
return board().radioState.bus.datagram.lastReceived.payload.stringData || "";
|
||||
}
|
||||
|
||||
export function receivedTime(): number {
|
||||
return board().radioState.bus.datagram.lastReceived.time;
|
||||
}
|
||||
}
|
@ -1077,6 +1077,7 @@ namespace pxsim.visuals {
|
||||
204.92835235595703,
|
||||
56.631452560424805
|
||||
],
|
||||
// rings
|
||||
"EDGE_P0": [
|
||||
56.002254486083984,
|
||||
95.43130111694336
|
||||
@ -1128,7 +1129,7 @@ namespace pxsim.visuals {
|
||||
"C_P0": [
|
||||
119.33667373657227,
|
||||
159.83989715576172
|
||||
],
|
||||
],
|
||||
"C_P2": [
|
||||
125.52401733398438,
|
||||
159.83989715576172
|
||||
@ -1348,6 +1349,7 @@ namespace pxsim.visuals {
|
||||
this.updateButtonAB();
|
||||
this.updateGestures();
|
||||
this.updateRgbLed();
|
||||
this.updateSpeaker();
|
||||
|
||||
if (!runtime || runtime.dead) svg.addClass(this.element, "grayscale");
|
||||
else svg.removeClass(this.element, "grayscale");
|
||||
@ -1370,6 +1372,15 @@ namespace pxsim.visuals {
|
||||
}
|
||||
}
|
||||
|
||||
private updateSpeaker() {
|
||||
let state = this.board;
|
||||
if (state.speakerState.frequency) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private updateGestures() {
|
||||
let state = this.board;
|
||||
if (state.accelerometerState.useShake && !this.shakeButton) {
|
||||
|
@ -1,3 +1,13 @@
|
||||
/*******************************
|
||||
User Variable Overrides
|
||||
*******************************/
|
||||
|
||||
@invertedBackground: #525A67;
|
||||
@dropdownMenuDistance: 0px;
|
||||
|
||||
/*******************************
|
||||
PXT Theme Overrides
|
||||
*******************************/
|
||||
|
||||
@mainMenuHeight: 5rem;
|
||||
@mainMenuMinHeight: (@itemVerticalPadding * 2) + 2em;
|
@ -1,3 +1,15 @@
|
||||
/*******************************
|
||||
Site Overrides
|
||||
*******************************/
|
||||
|
||||
.ui.inverted.input input {
|
||||
background:#424852;
|
||||
border-radius: 20px;
|
||||
border: 0px !important;
|
||||
padding: 0.2em !important;
|
||||
padding-left: 0.6em !important;
|
||||
}
|
||||
|
||||
.rtl .ui.inverted.input input {
|
||||
padding-right: 0.6em !important;
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
/*******************************
|
||||
Site Overrides
|
||||
*******************************/
|
||||
|
||||
@importGoogleFonts: false;
|
||||
|
@ -1,19 +1,38 @@
|
||||
/*******************************
|
||||
User Global Variables
|
||||
*******************************/
|
||||
@importGoogleFonts: false;
|
||||
|
||||
@importGoogleFonts: true;
|
||||
|
||||
@fontName : 'Roboto Mono';
|
||||
|
||||
@emSize : 14px;
|
||||
@fontSize : 13px;
|
||||
|
||||
/*-------------------
|
||||
Site Colors
|
||||
--------------------*/
|
||||
|
||||
@primaryColor: #3CB5B5;
|
||||
@secondaryColor: #43C9C9;
|
||||
|
||||
@red:#E81123;
|
||||
@orange:#D78C01;
|
||||
@yellow:#EEDA47;
|
||||
@orange:#DF4600;
|
||||
@yellow:#F4C918;
|
||||
@green:#3FC863;
|
||||
@teal:#008272;
|
||||
@blue:#0078D7;
|
||||
@violet:#CF0071;
|
||||
@purple:#8073E5;
|
||||
@blue:#54C9C9;
|
||||
@violet:#C90072;
|
||||
@purple:#8169E6;
|
||||
@pink:#E2008C;
|
||||
@brown:#6B5B4C;
|
||||
@grey:#515B67;
|
||||
@grey:#42495F;
|
||||
|
||||
@pageBackground : #FDFDFA;
|
||||
|
||||
|
||||
/*******************************
|
||||
PXT Overrides
|
||||
*******************************/
|
||||
|
||||
@blocklyToolboxColor: #F6F4E6;
|
||||
|
101
theme/style.less
@ -1,2 +1,103 @@
|
||||
/* Import all components */
|
||||
@import 'semantic';
|
||||
@import 'themes/default/globals/site.variables';
|
||||
@import 'themes/pxt/globals/site.variables';
|
||||
@import 'site/globals/site.variables';
|
||||
|
||||
/* Reference import */
|
||||
@import (reference) "semantic.less";
|
||||
|
||||
.openproject {
|
||||
background: #4ECC60 !important;
|
||||
}
|
||||
|
||||
.blocks-menuitem, .javascript-menuitem {
|
||||
background: #738791 !important;
|
||||
}
|
||||
|
||||
.help-dropdown-menuitem, .more-dropdown-menuitem {
|
||||
background: #424955 !important;
|
||||
margin-right:0px !important;
|
||||
}
|
||||
|
||||
.play-button {
|
||||
&:extend(.ui all);
|
||||
&:extend(.button all);
|
||||
&:extend(.blue all);
|
||||
}
|
||||
|
||||
.download-button {
|
||||
&:extend(.ui all);
|
||||
&:extend(.button all);
|
||||
&:extend(.yellow all);
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Blockly
|
||||
*******************************/
|
||||
|
||||
.blocklyTreeRow {
|
||||
border-radius:5px;
|
||||
}
|
||||
|
||||
/* This removes any padding at the top of the toolbox */
|
||||
.blocklyTreeRoot {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
/* Blockly Text */
|
||||
.blocklyTreeLabel {
|
||||
font-family: @pageFont !important;
|
||||
}
|
||||
|
||||
.blocklyTreeLabel {
|
||||
font-size:1rem !important;
|
||||
}
|
||||
|
||||
.blocklyFlyoutBackground {
|
||||
fill: @grey;
|
||||
}
|
||||
|
||||
.blocklyToolboxDiv {
|
||||
/*left:30px !important;*/
|
||||
padding:7px;
|
||||
}
|
||||
|
||||
|
||||
/* Mobile */
|
||||
@media only screen and (max-width: @largestMobileScreen) {
|
||||
.blocklyTreeLabel {
|
||||
font-size: 0.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet */
|
||||
@media only screen and (min-width: @tabletBreakpoint) and (max-width: @largestTabletScreen) {
|
||||
|
||||
}
|
||||
|
||||
/* Small Monitor */
|
||||
@media only screen and (min-width: @computerBreakpoint) and (max-width: @largestSmallMonitor) {
|
||||
.blocklyTreeRow {
|
||||
width: 230px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Large Monitor */
|
||||
@media only screen and (min-width: @largeMonitorBreakpoint) {
|
||||
.blocklyTreeRow {
|
||||
width: 230px;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Menu Bar
|
||||
*******************************/
|
||||
|
||||
#menubar {
|
||||
height: 5rem;
|
||||
}
|
||||
|
||||
#fileNameInput {
|
||||
width: 350px;
|
||||
}
|
@ -18,31 +18,31 @@
|
||||
*/
|
||||
|
||||
/* Global */
|
||||
@site : 'default';
|
||||
@site : 'pxt';
|
||||
@reset : 'default';
|
||||
|
||||
/* Elements */
|
||||
@button : 'default';
|
||||
@container : 'default';
|
||||
@divider : 'default';
|
||||
@flag : 'default';
|
||||
@header : 'default';
|
||||
@button : 'pxt';
|
||||
@container : 'pxt';
|
||||
@divider : 'pxt';
|
||||
@flag : 'pxt';
|
||||
@header : 'pxt';
|
||||
@icon : 'default';
|
||||
@image : 'default';
|
||||
@input : 'default';
|
||||
@label : 'default';
|
||||
@list : 'default';
|
||||
@loader : 'default';
|
||||
@rail : 'default';
|
||||
@reveal : 'default';
|
||||
@segment : 'default';
|
||||
@step : 'default';
|
||||
@image : 'pxt';
|
||||
@input : 'pxt';
|
||||
@label : 'pxt';
|
||||
@list : 'pxt';
|
||||
@loader : 'pxt';
|
||||
@rail : 'pxt';
|
||||
@reveal : 'pxt';
|
||||
@segment : 'pxt';
|
||||
@step : 'pxt';
|
||||
|
||||
/* Collections */
|
||||
@breadcrumb : 'default';
|
||||
@form : 'default';
|
||||
@grid : 'default';
|
||||
@menu : 'default';
|
||||
@menu : 'pxt';
|
||||
@message : 'default';
|
||||
@table : 'default';
|
||||
|
||||
@ -91,7 +91,4 @@
|
||||
|
||||
@fontPath : 'fonts';
|
||||
|
||||
@headerFont : 'Segoe UI', 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
@pageFont : 'Segoe UI', 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
|
||||
/* End Config */
|
||||
|