pxt-microbit
Version:
micro:bit target for Microsoft MakeCode (PXT)
1,257 lines • 287 kB
JavaScript
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
/// <reference path="../libs/core/dal.d.ts"/>
/// <reference path="../libs/core/enums.d.ts"/>
var pxsim;
(function (pxsim) {
class DalBoard extends pxsim.CoreBoard {
constructor() {
super();
this.speakerEnabled = true;
// board hardware version
this.hardwareVersion = 1;
// components
this.lightState = {};
this.fileSystem = new pxsim.FileSystemState();
this.controlMessageState = new pxsim.ControlMessageState(this);
this.builtinParts["ledmatrix"] = this.ledMatrixState = new pxsim.LedMatrixState(pxsim.runtime);
this.builtinParts["buttonpair"] = this.buttonPairState = new pxsim.ButtonPairState({
ID_BUTTON_A: 1 /* DAL.MICROBIT_ID_BUTTON_A */,
ID_BUTTON_B: 2 /* DAL.MICROBIT_ID_BUTTON_B */,
ID_BUTTON_AB: 3 /* DAL.MICROBIT_ID_BUTTON_AB */,
BUTTON_EVT_UP: 2 /* DAL.MICROBIT_BUTTON_EVT_UP */,
BUTTON_EVT_CLICK: 3 /* DAL.MICROBIT_BUTTON_EVT_CLICK */
});
this.builtinParts["edgeconnector"] = this.edgeConnectorState = new pxsim.EdgeConnectorState({
pins: [
100 /* DAL.MICROBIT_ID_IO_P0 */,
101 /* DAL.MICROBIT_ID_IO_P1 */,
102 /* DAL.MICROBIT_ID_IO_P2 */,
103 /* DAL.MICROBIT_ID_IO_P3 */,
104 /* DAL.MICROBIT_ID_IO_P4 */,
105 /* DAL.MICROBIT_ID_IO_P5 */,
106 /* DAL.MICROBIT_ID_IO_P6 */,
107 /* DAL.MICROBIT_ID_IO_P7 */,
108 /* DAL.MICROBIT_ID_IO_P8 */,
109 /* DAL.MICROBIT_ID_IO_P9 */,
110 /* DAL.MICROBIT_ID_IO_P10 */,
111 /* DAL.MICROBIT_ID_IO_P11 */,
112 /* DAL.MICROBIT_ID_IO_P12 */,
113 /* DAL.MICROBIT_ID_IO_P13 */,
114 /* DAL.MICROBIT_ID_IO_P14 */,
115 /* DAL.MICROBIT_ID_IO_P15 */,
116 /* DAL.MICROBIT_ID_IO_P16 */,
0,
0,
119 /* DAL.MICROBIT_ID_IO_P19 */,
120 /* DAL.MICROBIT_ID_IO_P20 */
],
servos: {
"P0": 100 /* DAL.MICROBIT_ID_IO_P0 */,
"P1": 101 /* DAL.MICROBIT_ID_IO_P1 */,
"P2": 102 /* DAL.MICROBIT_ID_IO_P2 */,
"P3": 103 /* DAL.MICROBIT_ID_IO_P3 */,
"P4": 104 /* DAL.MICROBIT_ID_IO_P4 */,
"P5": 105 /* DAL.MICROBIT_ID_IO_P5 */,
"P6": 106 /* DAL.MICROBIT_ID_IO_P6 */,
"P7": 107 /* DAL.MICROBIT_ID_IO_P7 */,
"P8": 108 /* DAL.MICROBIT_ID_IO_P8 */,
"P9": 109 /* DAL.MICROBIT_ID_IO_P9 */,
"P10": 110 /* DAL.MICROBIT_ID_IO_P10 */,
"P11": 111 /* DAL.MICROBIT_ID_IO_P11 */,
"P12": 112 /* DAL.MICROBIT_ID_IO_P12 */,
"P13": 113 /* DAL.MICROBIT_ID_IO_P13 */,
"P14": 114 /* DAL.MICROBIT_ID_IO_P14 */,
"P15": 115 /* DAL.MICROBIT_ID_IO_P15 */,
"P16": 116 /* DAL.MICROBIT_ID_IO_P16 */,
"P19": 119 /* DAL.MICROBIT_ID_IO_P19 */
}
});
this.builtinParts["radio"] = this.radioState = new pxsim.RadioState(pxsim.runtime, this, {
ID_RADIO: 9 /* DAL.MICROBIT_ID_RADIO */,
RADIO_EVT_DATAGRAM: 1 /* DAL.MICROBIT_RADIO_EVT_DATAGRAM */
});
this.builtinParts["microphone"] = this.microphoneState = new pxsim.MicrophoneState(3001 /* DAL.DEVICE_ID_MICROPHONE */, 0, 255, 86, 165);
this.builtinParts["recording"] = this.recordingState = new pxsim.RecordingState();
this.builtinParts["accelerometer"] = this.accelerometerState = new pxsim.AccelerometerState(pxsim.runtime);
this.builtinParts["serial"] = this.serialState = new pxsim.SerialState(pxsim.runtime, this);
this.builtinParts["thermometer"] = this.thermometerState = new pxsim.ThermometerState();
this.builtinParts["lightsensor"] = this.lightSensorState = new pxsim.LightSensorState();
this.builtinParts["compass"] = this.compassState = new pxsim.CompassState();
this.builtinParts["microservo"] = this.edgeConnectorState;
this.builtinParts["logotouch"] = this.logoTouch = new pxsim.Button(121 /* DAL.MICROBIT_ID_LOGO */);
this.builtinVisuals["buttonpair"] = () => new pxsim.visuals.ButtonPairView();
this.builtinVisuals["ledmatrix"] = () => new pxsim.visuals.LedMatrixView();
this.builtinVisuals["microservo"] = () => new pxsim.visuals.MicroServoView();
this.builtinParts["neopixel"] = (pin) => { return this.neopixelState(pin.id); };
this.builtinVisuals["neopixel"] = () => new pxsim.visuals.NeoPixelView(pxsim.parsePinString);
this.builtinPartVisuals["neopixel"] = (xy) => pxsim.visuals.mkNeoPixelPart(xy);
this.builtinPartVisuals["buttonpair"] = (xy) => pxsim.visuals.mkBtnSvg(xy);
this.builtinPartVisuals["ledmatrix"] = (xy) => pxsim.visuals.mkLedMatrixSvg(xy, 8, 8);
this.builtinPartVisuals["microservo"] = (xy) => pxsim.visuals.mkMicroServoPart(xy);
this.samplesState = new pxsim.samples.SamplesState();
}
ensureHardwareVersion(version) {
if (version > this.hardwareVersion) {
this.hardwareVersion = version;
this.updateView();
}
}
initAsync(msg) {
super.initAsync(msg);
const boardDef = msg.boardDefinition;
const cmpsList = msg.parts;
const cmpDefs = msg.partDefinitions || {};
const fnArgs = msg.fnArgs;
const v2Parts = {
"microphone": true,
"logotouch": true,
"builtinspeaker": true,
"flashlog": true,
"v2": true
};
if (msg.builtinParts) {
const v2PartsUsed = msg.builtinParts.filter(k => v2Parts[k]);
if (v2PartsUsed.length) {
console.log(`detected v2 feature`, v2PartsUsed);
cmpsList.push(...v2PartsUsed);
this.hardwareVersion = 2;
}
}
const opts = {
state: this,
boardDef: boardDef,
partsList: cmpsList,
partDefs: cmpDefs,
fnArgs: fnArgs,
maxWidth: "100%",
maxHeight: "100%",
highContrast: msg.highContrast
};
this.viewHost = new pxsim.visuals.BoardHost(pxsim.visuals.mkBoardView({
visual: boardDef.visual,
boardDef: boardDef,
highContrast: msg.highContrast
}), opts);
document.body.innerHTML = ""; // clear children
if (pxsim.shouldShowMute()) {
document.body.appendChild(pxsim.createMuteButton());
pxsim.AudioContextManager.mute(true);
pxsim.setParentMuteState("disabled");
}
document.body.appendChild(this.view = this.viewHost.getView());
if (msg.theme === "mbcodal") {
this.ensureHardwareVersion(2);
}
return Promise.resolve();
}
tryGetNeopixelState(pinId) {
return this.lightState[pinId];
}
neopixelState(pinId) {
if (pinId === undefined) {
pinId = 100 /* DAL.MICROBIT_ID_IO_P0 */;
}
let state = this.lightState[pinId];
if (!state)
state = this.lightState[pinId] = new pxsim.CommonNeoPixelState();
return state;
}
screenshotAsync(width) {
return this.viewHost.screenshotAsync(width);
}
kill() {
super.kill();
this.viewHost.removeEventListeners();
}
}
pxsim.DalBoard = DalBoard;
function initRuntimeWithDalBoard() {
pxsim.U.assert(!pxsim.runtime.board);
let b = new DalBoard();
pxsim.runtime.board = b;
pxsim.runtime.postError = (e) => {
pxsim.led.setBrightness(255);
let img = board().ledMatrixState.image;
img.clear();
img.set(0, 4, 255);
img.set(1, 3, 255);
img.set(2, 3, 255);
img.set(3, 3, 255);
img.set(4, 4, 255);
img.set(0, 0, 255);
img.set(1, 0, 255);
img.set(0, 1, 255);
img.set(1, 1, 255);
img.set(3, 0, 255);
img.set(4, 0, 255);
img.set(3, 1, 255);
img.set(4, 1, 255);
pxsim.runtime.updateDisplay();
};
}
pxsim.initRuntimeWithDalBoard = initRuntimeWithDalBoard;
if (!pxsim.initCurrentRuntime) {
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
}
function board() {
return pxsim.runtime.board;
}
pxsim.board = board;
function parsePinString(gpioPin) {
if (gpioPin == "*")
return board().edgeConnectorState.getPin(100 /* DAL.MICROBIT_ID_IO_P0 */);
const m = /^(Analog|Digital)Pin\.P(\d)+/.exec(gpioPin);
if (!m)
return undefined;
const pinNum = parseInt(m[2]);
return board().edgeConnectorState.pins[pinNum];
}
pxsim.parsePinString = parsePinString;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var input;
(function (input) {
function accForGesture(gesture) {
let b = pxsim.board().accelerometerState;
b.accelerometer.activate();
if (gesture == 11 && !b.useShake) { // SHAKE
b.useShake = true;
pxsim.runtime.queueDisplayUpdate();
}
return b;
}
function onGesture(gesture, handler) {
const b = accForGesture(gesture);
pxsim.pxtcore.registerWithDal(13 /* DAL.MICROBIT_ID_GESTURE */, gesture, handler);
}
input.onGesture = onGesture;
function isGesture(gesture) {
const b = accForGesture(gesture);
b.accelerometer.activate();
return b.accelerometer.getGesture() == gesture;
}
input.isGesture = isGesture;
function acceleration(dimension) {
let b = pxsim.board().accelerometerState;
let acc = b.accelerometer;
switch (dimension) {
case 0:
acc.activate(pxsim.AccelerometerFlag.X);
return acc.getX();
case 1:
acc.activate(pxsim.AccelerometerFlag.Y);
return acc.getY();
case 2:
acc.activate(pxsim.AccelerometerFlag.Z);
return acc.getZ();
default:
acc.activate(pxsim.AccelerometerFlag.Strength);
return acc.getStrength();
}
}
input.acceleration = acceleration;
function rotation(kind) {
const b = pxsim.board().accelerometerState;
const acc = b.accelerometer;
switch (kind) {
case 0: {
acc.activate(pxsim.AccelerometerFlag.Pitch);
return acc.getPitch();
}
case 1: {
acc.activate(pxsim.AccelerometerFlag.Roll);
return acc.getRoll();
}
default: return 0;
}
}
input.rotation = rotation;
function setAccelerometerRange(range) {
let b = pxsim.board().accelerometerState;
b.accelerometer.setSampleRange(range);
}
input.setAccelerometerRange = setAccelerometerRange;
})(input = pxsim.input || (pxsim.input = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
/**
* Co-ordinate systems that can be used.
* RAW: Unaltered data. Data will be returned directly from the accelerometer.
*
* SIMPLE_CARTESIAN: Data will be returned based on an easy to understand alignment, consistent with the cartesian system taught in schools.
* When held upright, facing the user:
*
* /
* +--------------------+ z
* | |
* | ..... |
* | * ..... * |
* ^ | ..... |
* | | |
* y +--------------------+ x-->
*
*
* NORTH_EAST_DOWN: Data will be returned based on the industry convention of the North East Down (NED) system.
* When held upright, facing the user:
*
* z
* +--------------------+ /
* | |
* | ..... |
* | * ..... * |
* ^ | ..... |
* | | |
* x +--------------------+ y-->
*
*/
let MicroBitCoordinateSystem;
(function (MicroBitCoordinateSystem) {
MicroBitCoordinateSystem[MicroBitCoordinateSystem["RAW"] = 0] = "RAW";
MicroBitCoordinateSystem[MicroBitCoordinateSystem["SIMPLE_CARTESIAN"] = 1] = "SIMPLE_CARTESIAN";
MicroBitCoordinateSystem[MicroBitCoordinateSystem["NORTH_EAST_DOWN"] = 2] = "NORTH_EAST_DOWN";
})(MicroBitCoordinateSystem = pxsim.MicroBitCoordinateSystem || (pxsim.MicroBitCoordinateSystem = {}));
let AccelerometerFlag;
(function (AccelerometerFlag) {
AccelerometerFlag[AccelerometerFlag["X"] = 1] = "X";
AccelerometerFlag[AccelerometerFlag["Y"] = 2] = "Y";
AccelerometerFlag[AccelerometerFlag["Z"] = 4] = "Z";
AccelerometerFlag[AccelerometerFlag["Strength"] = 8] = "Strength";
AccelerometerFlag[AccelerometerFlag["Pitch"] = 16] = "Pitch";
AccelerometerFlag[AccelerometerFlag["Roll"] = 32] = "Roll";
})(AccelerometerFlag = pxsim.AccelerometerFlag || (pxsim.AccelerometerFlag = {}));
class Accelerometer {
constructor(runtime) {
this.runtime = runtime;
this.sigma = 0; // the number of ticks that the instantaneous gesture has been stable.
this.lastGesture = 0; // the last, stable gesture recorded.
this.currentGesture = 0; // the instantaneous, unfiltered gesture detected.
this.sample = { x: 0, y: 0, z: -1023 };
this.shake = { x: false, y: false, z: false, count: 0, shaken: 0, timer: 0 }; // State information needed to detect shake events.
this.isActive = false;
this.sampleRange = 2;
this.flags = 0;
this.id = 5 /* DAL.MICROBIT_ID_ACCELEROMETER */;
}
setSampleRange(range) {
this.activate();
this.sampleRange = Math.max(1, Math.min(8, range));
}
activate(flags) {
if (!this.isActive) {
this.isActive = true;
this.runtime.queueDisplayUpdate();
}
if (!!flags)
this.flags |= flags;
}
/**
* Reads the acceleration data from the accelerometer, and stores it in our buffer.
* This is called by the tick() member function, if the interrupt is set!
*/
update(x, y, z) {
// read MSB values...
this.sample.x = Math.floor(x);
this.sample.y = Math.floor(y);
this.sample.z = Math.floor(z);
// Update gesture tracking
this.updateGesture();
// Indicate that a new sample is available
pxsim.board().bus.queue(this.id, 1 /* DAL.MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE */);
}
getStrength() {
return Math.floor(Math.sqrt(this.instantaneousAccelerationSquared()));
}
updateEnvironmentGlobals() {
// update debugger
if (this.isActive) {
if (this.flags & AccelerometerFlag.X)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.x")] = this.sample.x;
if (this.flags & AccelerometerFlag.Y)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.y")] = this.sample.y;
if (this.flags & AccelerometerFlag.Z)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.z")] = this.sample.z;
if (this.flags & AccelerometerFlag.Strength)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.strength")] = Math.sqrt(this.instantaneousAccelerationSquared());
if (this.flags & AccelerometerFlag.Pitch)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.pitch")] = this.getPitch();
if (this.flags & AccelerometerFlag.Roll)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.roll")] = this.getRoll();
}
}
instantaneousAccelerationSquared() {
// Use pythagoras theorem to determine the combined force acting on the device.
return this.sample.x * this.sample.x + this.sample.y * this.sample.y + this.sample.z * this.sample.z;
}
/**
* Service function. Determines the best guess posture of the device based on instantaneous data.
* This makes no use of historic data (except for shake), and forms this input to the filter implemented in updateGesture().
*
* @return A best guess of the current posture of the device, based on instantaneous data.
*/
instantaneousPosture() {
let force = this.instantaneousAccelerationSquared();
let shakeDetected = false;
// Test for shake events.
// We detect a shake by measuring zero crossings in each axis. In other words, if we see a strong acceleration to the left followed by
// a string acceleration to the right, then we can infer a shake. Similarly, we can do this for each acxis (left/right, up/down, in/out).
//
// If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device
// has been shaken.
if ((this.getX() < -400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.x) || (this.getX() > 400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.x)) {
shakeDetected = true;
this.shake.x = !this.shake.x;
}
if ((this.getY() < -400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.y) || (this.getY() > 400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.y)) {
shakeDetected = true;
this.shake.y = !this.shake.y;
}
if ((this.getZ() < -400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.z) || (this.getZ() > 400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.z)) {
shakeDetected = true;
this.shake.z = !this.shake.z;
}
if (shakeDetected && this.shake.count < 4 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD */ && ++this.shake.count == 4 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD */)
this.shake.shaken = 1;
if (++this.shake.timer >= 10 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_DAMPING */) {
this.shake.timer = 0;
if (this.shake.count > 0) {
if (--this.shake.count == 0)
this.shake.shaken = 0;
}
}
if (this.shake.shaken)
return 11 /* DAL.MICROBIT_ACCELEROMETER_EVT_SHAKE */;
let sq = (n) => n * n;
if (force < sq(400 /* DAL.MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE */))
return 7 /* DAL.MICROBIT_ACCELEROMETER_EVT_FREEFALL */;
if (force > sq(3072 /* DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE */))
return 8 /* DAL.MICROBIT_ACCELEROMETER_EVT_3G */;
if (force > sq(6144 /* DAL.MICROBIT_ACCELEROMETER_6G_TOLERANCE */))
return 9 /* DAL.MICROBIT_ACCELEROMETER_EVT_6G */;
if (force > sq(8192 /* DAL.MICROBIT_ACCELEROMETER_8G_TOLERANCE */))
return 10 /* DAL.MICROBIT_ACCELEROMETER_EVT_8G */;
// Determine our posture.
if (this.getX() < (-1000 + 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */))
return 3 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_LEFT */;
if (this.getX() > (1000 - 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */))
return 4 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT */;
if (this.getY() < (-1000 + 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */))
return 2 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_DOWN */;
if (this.getY() > (1000 - 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */))
return 1 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_UP */;
if (this.getZ() < (-1000 + 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */))
return 5 /* DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP */;
if (this.getZ() > (1000 - 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */))
return 6 /* DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN */;
return 0;
}
updateGesture() {
// Determine what it looks like we're doing based on the latest sample...
let g = this.instantaneousPosture();
// Perform some low pass filtering to reduce jitter from any detected effects
if (g != this.currentGesture) {
this.currentGesture = g;
this.sigma = 0;
}
else if (this.sigma < 5 /* DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING */) {
++this.sigma;
}
if (this.currentGesture !== this.lastGesture && this.sigma >= 5 /* DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING */) {
this.enqueueCurrentGesture();
}
}
forceGesture(gesture) {
this.currentGesture = gesture;
this.enqueueCurrentGesture();
}
enqueueCurrentGesture() {
this.lastGesture = this.currentGesture;
pxsim.board().bus.queue(13 /* DAL.MICROBIT_ID_GESTURE */, this.lastGesture);
}
/**
* Reads the X axis value of the latest update from the accelerometer.
* @param system The coordinate system to use. By default, a simple cartesian system is provided.
* @return The force measured in the X axis, in milli-g.
*
* Example:
* @code
* uBit.accelerometer.getX();
* uBit.accelerometer.getX(RAW);
* @endcode
*/
getX(system = MicroBitCoordinateSystem.SIMPLE_CARTESIAN) {
switch (system) {
case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
return -this.sample.x;
case MicroBitCoordinateSystem.NORTH_EAST_DOWN:
return this.sample.y;
//case MicroBitCoordinateSystem.SIMPLE_CARTESIAN.RAW:
default:
return this.sample.x;
}
}
/**
* Reads the Y axis value of the latest update from the accelerometer.
* @param system The coordinate system to use. By default, a simple cartesian system is provided.
* @return The force measured in the Y axis, in milli-g.
*
* Example:
* @code
* uBit.accelerometer.getY();
* uBit.accelerometer.getY(RAW);
* @endcode
*/
getY(system = MicroBitCoordinateSystem.SIMPLE_CARTESIAN) {
switch (system) {
case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
return -this.sample.y;
case MicroBitCoordinateSystem.NORTH_EAST_DOWN:
return -this.sample.x;
//case RAW:
default:
return this.sample.y;
}
}
/**
* Reads the Z axis value of the latest update from the accelerometer.
* @param system The coordinate system to use. By default, a simple cartesian system is provided.
* @return The force measured in the Z axis, in milli-g.
*
* Example:
* @code
* uBit.accelerometer.getZ();
* uBit.accelerometer.getZ(RAW);
* @endcode
*/
getZ(system = MicroBitCoordinateSystem.SIMPLE_CARTESIAN) {
switch (system) {
case MicroBitCoordinateSystem.NORTH_EAST_DOWN:
return -this.sample.z;
//case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
//case MicroBitCoordinateSystem.RAW:
default:
return this.sample.z;
}
}
/**
* Provides a rotation compensated pitch of the device, based on the latest update from the accelerometer.
* @return The pitch of the device, in degrees.
*
* Example:
* @code
* uBit.accelerometer.getPitch();
* @endcode
*/
getPitch() {
return Math.floor((360 * this.getPitchRadians()) / (2 * Math.PI));
}
getPitchRadians() {
this.recalculatePitchRoll();
return this.pitch;
}
/**
* Provides a rotation compensated roll of the device, based on the latest update from the accelerometer.
* @return The roll of the device, in degrees.
*
* Example:
* @code
* uBit.accelerometer.getRoll();
* @endcode
*/
getRoll() {
return Math.floor((360 * this.getRollRadians()) / (2 * Math.PI));
}
getRollRadians() {
this.recalculatePitchRoll();
return this.roll;
}
getGesture() {
return this.lastGesture;
}
/**
* Recalculate roll and pitch values for the current sample.
* We only do this at most once per sample, as the necessary trigonemteric functions are rather
* heavyweight for a CPU without a floating point unit...
*/
recalculatePitchRoll() {
let x = this.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let y = this.getY(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let z = this.getZ(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
this.roll = Math.atan2(y, z);
this.pitch = Math.atan(-x / (y * Math.sin(this.roll) + z * Math.cos(this.roll)));
}
}
pxsim.Accelerometer = Accelerometer;
class AccelerometerState {
constructor(runtime) {
this.useShake = false;
this.accelerometer = new Accelerometer(runtime);
}
shake() {
this.accelerometer.forceGesture(11 /* DAL.MICROBIT_ACCELEROMETER_EVT_SHAKE */); // SHAKE == 11
}
}
pxsim.AccelerometerState = AccelerometerState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var pxtcore;
(function (pxtcore) {
function updateScreen(img) {
}
pxtcore.updateScreen = updateScreen;
function updateStats(s) {
}
pxtcore.updateStats = updateStats;
function setPalette(b) {
}
pxtcore.setPalette = setPalette;
function setScreenBrightness(b) {
}
pxtcore.setScreenBrightness = setScreenBrightness;
function displayHeight() {
return 120;
}
pxtcore.displayHeight = displayHeight;
function displayWidth() {
return 160;
}
pxtcore.displayWidth = displayWidth;
function displayPresent() {
return true;
}
pxtcore.displayPresent = displayPresent;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var samples;
(function (samples) {
class SampleChannel {
constructor(id) {
this.id = id;
this.sampleRate = 11000;
}
playSampleAsync(sample) {
if (this.playing) {
this.playing.cancel();
}
this.playing = pxsim.AudioContextManager.startSamplePlayback(sample, pxsim.BufferMethods.NumberFormat.UInt8LE, 255, this.sampleRate, pxsim.music.volume() / 0xff);
this.playing.promise.then(() => {
this.playing = undefined;
});
}
}
class SamplesState {
constructor() {
this.channels = [];
this.enabled = false;
this.channels = [
new SampleChannel(0),
new SampleChannel(1),
new SampleChannel(2),
new SampleChannel(3),
];
}
setEnabled(enabled) {
this.enabled = enabled;
}
setSampleRate(channelId, sampleRate) {
channelId |= 0;
if (channelId < 0 || channelId >= this.channels.length) {
return;
}
this.channels[channelId].sampleRate = sampleRate;
}
playSampleAsync(channelId, sample) {
if (!this.enabled) {
return;
}
channelId |= 0;
if (channelId < 0 || channelId >= this.channels.length) {
return;
}
this.channels[channelId].playSampleAsync(sample);
}
}
samples.SamplesState = SamplesState;
function enable() {
pxsim.board().samplesState.setEnabled(true);
}
samples.enable = enable;
function disable() {
pxsim.board().samplesState.setEnabled(false);
}
samples.disable = disable;
function setSampleRate(src, sampleRate) {
pxsim.board().samplesState.setSampleRate(src, sampleRate);
}
samples.setSampleRate = setSampleRate;
function playAsync(src, buf) {
pxsim.board().samplesState.playSampleAsync(src, buf);
}
samples.playAsync = playAsync;
})(samples = pxsim.samples || (pxsim.samples = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
class RefImage extends pxsim.RefObject {
constructor(w, h, bpp) {
super();
this.isStatic = true;
this.revision = 0;
this.data = new Uint8Array(w * h);
this._width = w;
this._height = h;
this._bpp = bpp;
}
scan(mark) { }
gcKey() { return "Image"; }
gcSize() { return 4 + (this.data.length + 3 >> 3); }
gcIsStatic() { return this.isStatic; }
pix(x, y) {
return (x | 0) + (y | 0) * this._width;
}
inRange(x, y) {
return 0 <= (x | 0) && (x | 0) < this._width &&
0 <= (y | 0) && (y | 0) < this._height;
}
color(c) {
return c & 0xff;
}
clamp(x, y) {
x |= 0;
y |= 0;
if (x < 0)
x = 0;
else if (x >= this._width)
x = this._width - 1;
if (y < 0)
y = 0;
else if (y >= this._height)
y = this._height - 1;
return [x, y];
}
makeWritable() {
this.revision++;
this.isStatic = false;
}
toDebugString() {
return this._width + "x" + this._height;
}
}
pxsim.RefImage = RefImage;
})(pxsim || (pxsim = {}));
(function (pxsim) {
var BitmapMethods;
(function (BitmapMethods) {
function XX(x) { return (x << 16) >> 16; }
BitmapMethods.XX = XX;
function YY(x) { return x >> 16; }
BitmapMethods.YY = YY;
function __buffer(img) {
return new pxsim.RefBuffer(img.data); // no clone for now
}
BitmapMethods.__buffer = __buffer;
function width(img) { return img._width; }
BitmapMethods.width = width;
function height(img) { return img._height; }
BitmapMethods.height = height;
function isMono(img) { return img._bpp == 1; }
BitmapMethods.isMono = isMono;
function isStatic(img) { return img.gcIsStatic(); }
BitmapMethods.isStatic = isStatic;
function revision(img) { return img.revision; }
BitmapMethods.revision = revision;
function setPixel(img, x, y, c) {
img.makeWritable();
if (img.inRange(x, y))
img.data[img.pix(x, y)] = img.color(c);
}
BitmapMethods.setPixel = setPixel;
function getPixel(img, x, y) {
if (img.inRange(x, y))
return img.data[img.pix(x, y)];
return 0;
}
BitmapMethods.getPixel = getPixel;
function fill(img, c) {
img.makeWritable();
img.data.fill(img.color(c));
}
BitmapMethods.fill = fill;
function fillRect(img, x, y, w, h, c) {
if (w == 0 || h == 0 || x >= img._width || y >= img._height || x + w - 1 < 0 || y + h - 1 < 0)
return;
img.makeWritable();
let [x2, y2] = img.clamp(x + w - 1, y + h - 1);
[x, y] = img.clamp(x, y);
let p = img.pix(x, y);
w = x2 - x + 1;
h = y2 - y + 1;
let d = img._width - w;
c = img.color(c);
while (h-- > 0) {
for (let i = 0; i < w; ++i)
img.data[p++] = c;
p += d;
}
}
BitmapMethods.fillRect = fillRect;
function _fillRect(img, xy, wh, c) {
fillRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c);
}
BitmapMethods._fillRect = _fillRect;
function mapRect(img, x, y, w, h, c) {
if (c.data.length < 16)
return;
img.makeWritable();
let [x2, y2] = img.clamp(x + w - 1, y + h - 1);
[x, y] = img.clamp(x, y);
let p = img.pix(x, y);
w = x2 - x + 1;
h = y2 - y + 1;
let d = img._width - w;
while (h-- > 0) {
for (let i = 0; i < w; ++i) {
img.data[p] = c.data[img.data[p]];
p++;
}
p += d;
}
}
BitmapMethods.mapRect = mapRect;
function _mapRect(img, xy, wh, c) {
mapRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c);
}
BitmapMethods._mapRect = _mapRect;
function equals(img, other) {
if (!other || img._bpp != other._bpp || img._width != other._width || img._height != other._height) {
return false;
}
let imgData = img.data;
let otherData = other.data;
let len = imgData.length;
for (let i = 0; i < len; i++) {
if (imgData[i] != otherData[i]) {
return false;
}
}
return true;
}
BitmapMethods.equals = equals;
function getRows(img, x, dst) {
x |= 0;
if (!img.inRange(x, 0))
return;
let dp = 0;
let len = Math.min(dst.data.length, (img._width - x) * img._height);
let sp = x;
let hh = 0;
while (len--) {
if (hh++ >= img._height) {
hh = 1;
sp = ++x;
}
dst.data[dp++] = img.data[sp];
sp += img._width;
}
}
BitmapMethods.getRows = getRows;
function setRows(img, x, src) {
x |= 0;
if (!img.inRange(x, 0))
return;
let sp = 0;
let len = Math.min(src.data.length, (img._width - x) * img._height);
let dp = x;
let hh = 0;
while (len--) {
if (hh++ >= img._height) {
hh = 1;
dp = ++x;
}
img.data[dp] = src.data[sp++];
dp += img._width;
}
}
BitmapMethods.setRows = setRows;
function clone(img) {
let r = new pxsim.RefImage(img._width, img._height, img._bpp);
r.data.set(img.data);
return r;
}
BitmapMethods.clone = clone;
function flipX(img) {
img.makeWritable();
const w = img._width;
const h = img._height;
for (let i = 0; i < h; ++i) {
img.data.subarray(i * w, (i + 1) * w).reverse();
}
}
BitmapMethods.flipX = flipX;
function flipY(img) {
img.makeWritable();
const w = img._width;
const h = img._height;
const d = img.data;
for (let i = 0; i < w; ++i) {
let top = i;
let bot = i + (h - 1) * w;
while (top < bot) {
let c = d[top];
d[top] = d[bot];
d[bot] = c;
top += w;
bot -= w;
}
}
}
BitmapMethods.flipY = flipY;
function transposed(img) {
const w = img._width;
const h = img._height;
const d = img.data;
const r = new pxsim.RefImage(h, w, img._bpp);
const n = r.data;
let src = 0;
for (let i = 0; i < h; ++i) {
let dst = i;
for (let j = 0; j < w; ++j) {
n[dst] = d[src++];
dst += w;
}
}
return r;
}
BitmapMethods.transposed = transposed;
function copyFrom(img, from) {
if (img._width != from._width || img._height != from._height ||
img._bpp != from._bpp)
return;
img.data.set(from.data);
}
BitmapMethods.copyFrom = copyFrom;
function scroll(img, dx, dy) {
img.makeWritable();
dx |= 0;
dy |= 0;
if (dx != 0) {
const img2 = clone(img);
img.data.fill(0);
drawTransparentBitmap(img, img2, dx, dy);
}
else if (dy < 0) {
dy = -dy;
if (dy < img._height)
img.data.copyWithin(0, dy * img._width);
else
dy = img._height;
img.data.fill(0, (img._height - dy) * img._width);
}
else if (dy > 0) {
if (dy < img._height)
img.data.copyWithin(dy * img._width, 0);
else
dy = img._height;
img.data.fill(0, 0, dy * img._width);
}
// TODO implement dx
}
BitmapMethods.scroll = scroll;
function replace(img, from, to) {
to &= 0xf;
const d = img.data;
for (let i = 0; i < d.length; ++i)
if (d[i] == from)
d[i] = to;
}
BitmapMethods.replace = replace;
function doubledX(img) {
const w = img._width;
const h = img._height;
const d = img.data;
const r = new pxsim.RefImage(w * 2, h, img._bpp);
const n = r.data;
let dst = 0;
for (let src = 0; src < d.length; ++src) {
let c = d[src];
n[dst++] = c;
n[dst++] = c;
}
return r;
}
BitmapMethods.doubledX = doubledX;
function doubledY(img) {
const w = img._width;
const h = img._height;
const d = img.data;
const r = new pxsim.RefImage(w, h * 2, img._bpp);
const n = r.data;
let src = 0;
let dst0 = 0;
let dst1 = w;
for (let i = 0; i < h; ++i) {
for (let j = 0; j < w; ++j) {
let c = d[src++];
n[dst0++] = c;
n[dst1++] = c;
}
dst0 += w;
dst1 += w;
}
return r;
}
BitmapMethods.doubledY = doubledY;
function doubled(img) {
return doubledX(doubledY(img));
}
BitmapMethods.doubled = doubled;
function drawImageCore(img, from, x, y, clear, check) {
x |= 0;
y |= 0;
const w = from._width;
let h = from._height;
const sh = img._height;
const sw = img._width;
if (x + w <= 0)
return false;
if (x >= sw)
return false;
if (y + h <= 0)
return false;
if (y >= sh)
return false;
if (clear)
fillRect(img, x, y, from._width, from._height, 0);
else if (!check)
img.makeWritable();
const len = x < 0 ? Math.min(sw, w + x) : Math.min(sw - x, w);
const fdata = from.data;
const tdata = img.data;
for (let p = 0; h--; y++, p += w) {
if (0 <= y && y < sh) {
let dst = y * sw;
let src = p;
if (x < 0)
src += -x;
else
dst += x;
for (let i = 0; i < len; ++i) {
const v = fdata[src++];
if (v) {
if (check) {
if (tdata[dst])
return true;
}
else {
tdata[dst] = v;
}
}
dst++;
}
}
}
return false;
}
function drawBitmap(img, from, x, y) {
drawImageCore(img, from, x, y, true, false);
}
BitmapMethods.drawBitmap = drawBitmap;
function drawTransparentBitmap(img, from, x, y) {
drawImageCore(img, from, x, y, false, false);
}
BitmapMethods.drawTransparentBitmap = drawTransparentBitmap;
function overlapsWith(img, other, x, y) {
return drawImageCore(img, other, x, y, false, true);
}
BitmapMethods.overlapsWith = overlapsWith;
function drawLineLow(img, x0, y0, x1, y1, c) {
let dx = x1 - x0;
let dy = y1 - y0;
let yi = img._width;
if (dy < 0) {
yi = -yi;
dy = -dy;
}
let D = 2 * dy - dx;
dx <<= 1;
dy <<= 1;
c = img.color(c);
let ptr = img.pix(x0, y0);
for (let x = x0; x <= x1; ++x) {
img.data[ptr] = c;
if (D > 0) {
ptr += yi;
D -= dx;
}
D += dy;
ptr++;
}
}
function drawLineHigh(img, x0, y0, x1, y1, c) {
let dx = x1 - x0;
let dy = y1 - y0;
let xi = 1;
if (dx < 0) {
xi = -1;
dx = -dx;
}
let D = 2 * dx - dy;
dx <<= 1;
dy <<= 1;
c = img.color(c);
let ptr = img.pix(x0, y0);
for (let y = y0; y <= y1; ++y) {
img.data[ptr] = c;
if (D > 0) {
ptr += xi;
D -= dy;
}
D += dx;
ptr += img._width;
}
}
function _drawLine(img, xy, wh, c) {
drawLine(img, XX(xy), YY(xy), XX(wh), YY(wh), c);
}
BitmapMethods._drawLine = _drawLine;
function drawLine(img, x0, y0, x1, y1, c) {
x0 |= 0;
y0 |= 0;
x1 |= 0;
y1 |= 0;
if (x1 < x0) {
drawLine(img, x1, y1, x0, y0, c);
return;
}
let w = x1 - x0;
let h = y1 - y0;
if (h == 0) {
if (w == 0)
setPixel(img, x0, y0, c);
else
fillRect(img, x0, y0, w + 1, 1, c);
return;
}
if (w == 0) {
if (h > 0)
fillRect(img, x0, y0, 1, h + 1, c);
else
fillRect(img, x0, y1, 1, -h + 1, c);
return;
}
if (x1 < 0 || x0 >= img._width)
return;
if (x0 < 0) {
y0 -= (h * x0 / w) | 0;
x0 = 0;
}
if (x1 >= img._width) {
let d = (img._width - 1) - x1;
y1 += (h * d / w) | 0;
x1 = img._width - 1;
}
if (y0 < y1) {
if (y0 >= img._height || y1 < 0)
return;
if (y0 < 0) {
x0 -= (w * y0 / h) | 0;
y0 = 0;
}
if (y1 >= img._height) {
let d = (img._height - 1) - y1;
x1 += (w * d / h) | 0;
y1 = img._height;
}
}
else {
if (y1 >= img._height || y0 < 0)
return;
if (y1 < 0) {
x1 -= (w * y1 / h) | 0;
y1 = 0;
}
if (y0 >= img._height) {
let d = (img._height - 1) - y0;
x0 += (w * d / h) | 0;
y0 = img._height;
}
}
img.makeWritable();
if (h < 0) {
h = -h;
if (h < w)
drawLineLow(img, x0, y0, x1, y1, c);
else
drawLineHigh(img, x1, y1, x0, y0, c);
}
else {
if (h < w)
drawLineLow(img, x0, y0, x1, y1, c);
else
drawLineHigh(img, x0, y0, x1, y1, c);
}
}
BitmapMethods.drawLine = drawLine;
function drawIcon(img, icon, x, y, color) {
const src = icon.data;
if (!pxsim.bitmaps.isValidImage(icon))
return;
if (src[1] != 1)
return; // only mono
let width = pxsim.bitmaps.bufW(src);
let height = pxsim.bitmaps.bufH(src);
let byteH = pxsim.bitmaps.byteHeight(height, 1);
x |= 0;
y |= 0;
const destHeight = img._height;
const destWidth = img._width;
if (x + width <= 0)
return;
if (x >= destWidth)
return;
if (y + height <= 0)
return;
if (y >= destHeight)
return;
img.makeWritable();
let srcPointer = 8;
color = img.color(color);
const screen = img.data;
for (let i = 0; i < width; ++i) {
let destX = x + i;
if (0 <= destX && destX < destWidth) {
let destIndex = destX + y * destWidth;
let srcIndex = srcPointer;
let destY = y;
let destEnd = Math.min(destHeight, height + y);
if (y < 0) {
srcIndex += ((-y) >> 3);
destY += ((-y) >> 3) * 8;
destIndex += (destY - y) * destWidth;
}
let mask = 0x01;
let srcByte = src[srcIndex++];
while (destY < destEnd) {
if (destY >= 0 && (srcByte & mask)) {
screen[destIndex] = color;
}
mask <<= 1;
if (mask == 0x100) {
mask = 0x01;
srcByte = src[srcIndex++];
}
destIndex += destWidth;
destY++;
}
}
srcPointer += byteH;
}
}
BitmapMethods.drawIcon = drawIcon;
function _drawIcon(img, icon, xy, color) {
drawIcon(img, icon, XX(xy), YY(xy), color);
}
BitmapMethods._drawIcon = _drawIco