pxt-maker
Version:
Microsoft MakeCode Maker Boards
1,257 lines (1,256 loc) • 219 kB
JavaScript
var pxsim;
(function (pxsim) {
var input;
(function (input) {
function onGesture(gesture, handler) {
let b = pxsim.accelerometer();
b.accelerometer.activate();
if (gesture == 11 /* DAL.ACCELEROMETER_EVT_SHAKE */ && !b.useShake) {
b.useShake = true;
pxsim.runtime.queueDisplayUpdate();
}
pxsim.pxtcore.registerWithDal(13 /* DAL.DEVICE_ID_GESTURE */, gesture, handler);
}
input.onGesture = onGesture;
function rotation(kind) {
let b = pxsim.accelerometer();
let acc = b.accelerometer;
acc.activate();
let x = acc.getX(pxsim.MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let y = acc.getY(pxsim.MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let z = acc.getZ(pxsim.MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let roll = Math.atan2(y, z);
let pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
let r = 0;
switch (kind) {
case 0:
r = pitch;
break;
case 1:
r = roll;
break;
}
return Math.floor(r / Math.PI * 180);
}
input.rotation = rotation;
function setAccelerometerRange(range) {
let b = pxsim.accelerometer();
b.accelerometer.setSampleRange(range);
}
input.setAccelerometerRange = setAccelerometerRange;
function acceleration(dimension) {
let b = pxsim.accelerometer();
let acc = b.accelerometer;
acc.activate();
switch (dimension) {
case 0: return acc.getX();
case 1: return acc.getY();
case 2: return acc.getZ();
default: return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared()));
}
}
input.acceleration = acceleration;
})(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 = {}));
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.id = 5 /* DAL.DEVICE_ID_ACCELEROMETER */;
}
setSampleRange(range) {
this.activate();
this.sampleRange = Math.max(1, Math.min(8, range));
}
activate() {
if (!this.isActive) {
this.isActive = true;
this.runtime.queueDisplayUpdate();
}
}
/**
* 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.ACCELEROMETER_EVT_DATA_UPDATE */);
}
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.ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.x) || (this.getX() > 400 /* DAL.ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.x)) {
shakeDetected = true;
this.shake.x = !this.shake.x;
}
if ((this.getY() < -400 /* DAL.ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.y) || (this.getY() > 400 /* DAL.ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.y)) {
shakeDetected = true;
this.shake.y = !this.shake.y;
}
if ((this.getZ() < -400 /* DAL.ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.z) || (this.getZ() > 400 /* DAL.ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.z)) {
shakeDetected = true;
this.shake.z = !this.shake.z;
}
if (shakeDetected && this.shake.count < 4 /* DAL.ACCELEROMETER_SHAKE_COUNT_THRESHOLD */ && ++this.shake.count == 4 /* DAL.ACCELEROMETER_SHAKE_COUNT_THRESHOLD */)
this.shake.shaken = 1;
if (++this.shake.timer >= 10 /* DAL.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.ACCELEROMETER_EVT_SHAKE */;
let sq = (n) => n * n;
if (force < sq(400 /* DAL.ACCELEROMETER_FREEFALL_TOLERANCE */))
return 7 /* DAL.ACCELEROMETER_EVT_FREEFALL */;
if (force > sq(3072 /* DAL.ACCELEROMETER_3G_TOLERANCE */))
return 8 /* DAL.ACCELEROMETER_EVT_3G */;
if (force > sq(6144 /* DAL.ACCELEROMETER_6G_TOLERANCE */))
return 9 /* DAL.ACCELEROMETER_EVT_6G */;
if (force > sq(8192 /* DAL.ACCELEROMETER_8G_TOLERANCE */))
return 10 /* DAL.ACCELEROMETER_EVT_8G */;
// Determine our posture.
if (this.getX() < (-1000 + 200 /* DAL.ACCELEROMETER_TILT_TOLERANCE */))
return 3 /* DAL.ACCELEROMETER_EVT_TILT_LEFT */;
if (this.getX() > (1000 - 200 /* DAL.ACCELEROMETER_TILT_TOLERANCE */))
return 4 /* DAL.ACCELEROMETER_EVT_TILT_RIGHT */;
if (this.getY() < (-1000 + 200 /* DAL.ACCELEROMETER_TILT_TOLERANCE */))
return 1 /* DAL.ACCELEROMETER_EVT_TILT_UP */;
if (this.getY() > (1000 - 200 /* DAL.ACCELEROMETER_TILT_TOLERANCE */))
return 2 /* DAL.ACCELEROMETER_EVT_TILT_DOWN */;
if (this.getZ() < (-1000 + 200 /* DAL.ACCELEROMETER_TILT_TOLERANCE */))
return 5 /* DAL.ACCELEROMETER_EVT_FACE_UP */;
if (this.getZ() > (1000 - 200 /* DAL.ACCELEROMETER_TILT_TOLERANCE */))
return 6 /* DAL.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) {
if (this.sigma < 5 /* DAL.ACCELEROMETER_GESTURE_DAMPING */)
this.sigma++;
}
else {
this.currentGesture = g;
this.sigma = 0;
}
// If we've reached threshold, update our record and raise the relevant event...
if (this.currentGesture != this.lastGesture && this.sigma >= 5 /* DAL.ACCELEROMETER_GESTURE_DAMPING */) {
this.lastGesture = this.currentGesture;
pxsim.board().bus.queue(13 /* DAL.DEVICE_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) {
this.activate();
let val;
switch (system) {
case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
val = -this.sample.x;
case MicroBitCoordinateSystem.NORTH_EAST_DOWN:
val = this.sample.y;
//case MicroBitCoordinateSystem.SIMPLE_CARTESIAN.RAW:
default:
val = this.sample.x;
}
return pxsim.board().invertAccelerometerXAxis ? val * -1 : val;
}
/**
* 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) {
this.activate();
let val;
switch (system) {
case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
val = -this.sample.y;
case MicroBitCoordinateSystem.NORTH_EAST_DOWN:
val = -this.sample.x;
//case RAW:
default:
val = this.sample.y;
}
return pxsim.board().invertAccelerometerYAxis ? val * -1 : val;
}
/**
* 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) {
this.activate();
let val;
switch (system) {
case MicroBitCoordinateSystem.NORTH_EAST_DOWN:
val = -this.sample.z;
//case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
//case MicroBitCoordinateSystem.RAW:
default:
val = this.sample.z;
}
return pxsim.board().invertAccelerometerZAxis ? val * -1 : val;
}
/**
* 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() {
this.activate();
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() {
this.activate();
return Math.floor((360 * this.getRollRadians()) / (2 * Math.PI));
}
getRollRadians() {
this.recalculatePitchRoll();
return this.roll;
}
/**
* 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.tiltDecayer = 0;
this.accelerometer = new Accelerometer(runtime);
}
attachEvents(element) {
this.element = element;
this.tiltDecayer = 0;
this.element.addEventListener(pxsim.pointerEvents.move, (ev) => {
if (!this.accelerometer.isActive)
return;
if (this.tiltDecayer) {
clearInterval(this.tiltDecayer);
this.tiltDecayer = 0;
}
let bbox = element.getBoundingClientRect();
let ax = (ev.clientX - bbox.width / 2) / (bbox.width / 3);
let ay = (ev.clientY - bbox.height / 2) / (bbox.height / 3);
let x = -Math.max(-1023, Math.min(1023, Math.floor(ax * 1023)));
let y = Math.max(-1023, Math.min(1023, Math.floor(ay * 1023)));
let z2 = 1023 * 1023 - x * x - y * y;
let z = Math.floor((z2 > 0 ? -1 : 1) * Math.sqrt(Math.abs(z2)));
this.accelerometer.update(-x, y, z);
this.updateTilt();
}, false);
this.element.addEventListener(pxsim.pointerEvents.leave, (ev) => {
if (!this.accelerometer.isActive)
return;
if (!this.tiltDecayer) {
this.tiltDecayer = setInterval(() => {
let accx = this.accelerometer.getX();
accx = Math.floor(Math.abs(accx) * 0.85) * (accx > 0 ? 1 : -1);
let accy = this.accelerometer.getY();
accy = Math.floor(Math.abs(accy) * 0.85) * (accy > 0 ? 1 : -1);
let accz = -Math.sqrt(Math.max(0, 1023 * 1023 - accx * accx - accy * accy));
if (Math.abs(accx) <= 24 && Math.abs(accy) <= 24) {
clearInterval(this.tiltDecayer);
this.tiltDecayer = 0;
accx = 0;
accy = 0;
accz = -1023;
}
this.accelerometer.update(accx, accy, accz);
this.updateTilt();
}, 50);
}
}, false);
}
updateTilt() {
if (!this.accelerometer.isActive || !this.element)
return;
const x = this.accelerometer.getX();
const y = this.accelerometer.getY();
const af = 8 / 1023;
const s = 1 - Math.min(0.1, Math.pow(Math.max(Math.abs(x), Math.abs(y)) / 1023, 2) / 35);
this.element.style.transform = `perspective(30em) rotateX(${y * af}deg) rotateY(${x * af}deg) scale(${s}, ${s})`;
this.element.style.perspectiveOrigin = "50% 50% 50%";
this.element.style.perspective = "30em";
}
}
pxsim.AccelerometerState = AccelerometerState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
function accelerometer() {
return pxsim.board().accelerometerState;
}
pxsim.accelerometer = accelerometer;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var pxtcore;
(function (pxtcore) {
function getPin(id) {
const b = pxsim.board();
if (b && b.edgeConnectorState)
return b.edgeConnectorState.getPin(id);
return undefined;
}
pxtcore.getPin = getPin;
function lookupPinCfg(key) {
return getPinCfg(key);
}
pxtcore.lookupPinCfg = lookupPinCfg;
function getPinCfg(key) {
return getPin(pxtcore.getConfig(key, -1));
}
pxtcore.getPinCfg = getPinCfg;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var pxtcore;
(function (pxtcore) {
// TODO: add in support for mode, as in CODAL
function registerWithDal(id, evid, handler, mode = 0) {
pxsim.board().bus.listen(id, evid, handler);
}
pxtcore.registerWithDal = registerWithDal;
function deepSleep() {
// TODO?
console.log("deep sleep requested");
}
pxtcore.deepSleep = deepSleep;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
var BufferMethods;
(function (BufferMethods) {
function fnv1(data) {
let h = 0x811c9dc5;
for (let i = 0; i < data.length; ++i) {
h = Math.imul(h, 0x1000193) ^ data[i];
}
return h;
}
function hash(buf, bits) {
bits |= 0;
if (bits < 1)
return 0;
const h = fnv1(buf.data);
if (bits >= 32)
return h >>> 0;
else
return ((h ^ (h >>> bits)) & ((1 << bits) - 1)) >>> 0;
}
BufferMethods.hash = hash;
})(BufferMethods = pxsim.BufferMethods || (pxsim.BufferMethods = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
var control;
(function (control) {
control.runInParallel = pxsim.thread.runInBackground;
control.delay = pxsim.thread.pause;
function reset() {
pxsim.Runtime.postMessage({
type: "simulator",
command: "restart",
controlReset: true
});
const cb = pxsim.getResume();
}
control.reset = reset;
function waitMicros(micros) {
pxsim.thread.pause(micros / 1000); // it prempts not much we can do here.
}
control.waitMicros = waitMicros;
function deviceName() {
let b = pxsim.board();
return b && b.id
? b.id.slice(0, 4)
: "abcd";
}
control.deviceName = deviceName;
function _ramSize() {
return 32 * 1024 * 1024;
}
control._ramSize = _ramSize;
function deviceSerialNumber() {
let b = pxsim.board();
if (!b)
return 42;
let n = 0;
if (b.id) {
n = parseInt(b.id.slice(1));
if (isNaN(n)) {
n = 0;
for (let i = 0; i < b.id.length; ++i) {
n = ((n << 5) - n) + b.id.charCodeAt(i);
n |= 0;
}
n = Math.abs(n);
}
}
if (!n)
n = 42;
return n;
}
control.deviceSerialNumber = deviceSerialNumber;
function deviceLongSerialNumber() {
let b = control.createBuffer(8);
pxsim.BufferMethods.setNumber(b, pxsim.BufferMethods.NumberFormat.UInt32LE, 0, deviceSerialNumber());
return b;
}
control.deviceLongSerialNumber = deviceLongSerialNumber;
function deviceDalVersion() {
return "sim";
}
control.deviceDalVersion = deviceDalVersion;
function internalOnEvent(id, evid, handler) {
pxsim.pxtcore.registerWithDal(id, evid, handler);
}
control.internalOnEvent = internalOnEvent;
function waitForEvent(id, evid) {
const cb = pxsim.getResume();
pxsim.board().bus.wait(id, evid, cb);
}
control.waitForEvent = waitForEvent;
function allocateNotifyEvent() {
let b = pxsim.board();
return b.bus.nextNotifyEvent++;
}
control.allocateNotifyEvent = allocateNotifyEvent;
function raiseEvent(id, evid, mode) {
// TODO mode?
pxsim.board().bus.queue(id, evid);
}
control.raiseEvent = raiseEvent;
function millis() {
return pxsim.runtime.runningTime();
}
control.millis = millis;
function micros() {
return pxsim.runtime.runningTimeUs() & 0x3fffffff;
}
control.micros = micros;
function delayMicroseconds(us) {
control.delay(us / 0.001);
}
control.delayMicroseconds = delayMicroseconds;
function createBuffer(size) {
return pxsim.BufferMethods.createBuffer(size);
}
control.createBuffer = createBuffer;
function dmesg(msg) {
console.log(`DMESG: ${msg}`);
}
control.dmesg = dmesg;
function setDebugFlags(flags) {
console.log(`debug flags: ${flags}`);
}
control.setDebugFlags = setDebugFlags;
function heapSnapshot() {
console.log(pxsim.runtime.traceObjects());
}
control.heapSnapshot = heapSnapshot;
function toStr(v) {
if (v instanceof pxsim.RefRecord) {
return `${v.vtable.name}@${v.id}`;
}
if (v instanceof pxsim.RefCollection) {
let r = "[";
for (let e of v.toArray()) {
if (r.length > 200) {
r += "...";
break;
}
r += toStr(e) + ", ";
}
r += "]";
return r;
}
if (typeof v == "function") {
return (v + "").slice(0, 60) + "...";
}
return v + "";
}
function dmesgPtr(msg, ptr) {
console.log(`DMESG: ${msg} ${toStr(ptr)}`);
}
control.dmesgPtr = dmesgPtr;
function dmesgValue(ptr) {
console.log(`DMESG: ${toStr(ptr)}`);
}
control.dmesgValue = dmesgValue;
function gc() { }
control.gc = gc;
function profilingEnabled() {
return !!pxsim.runtime.perfCounters;
}
control.profilingEnabled = profilingEnabled;
function __log(priority, str) {
switch (priority) {
case 0:
console.debug("d>" + str);
break;
case 1:
console.log("l>" + str);
break;
case 2:
console.warn("w>" + str);
break;
case 3:
console.error("e>" + str);
break;
}
pxsim.runtime.board.writeSerial(str);
}
control.__log = __log;
function heapDump() {
// TODO something better
}
control.heapDump = heapDump;
function isUSBInitialized() {
return false;
}
control.isUSBInitialized = isUSBInitialized;
})(control = pxsim.control || (pxsim.control = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var pxtcore;
(function (pxtcore) {
// general purpose message sending mechanism
function sendMessage(channel, message, parentOnly) {
if (!channel)
return;
pxsim.Runtime.postMessage({
type: "messagepacket",
broadcast: !parentOnly,
channel: channel,
data: message && message.data
});
}
pxtcore.sendMessage = sendMessage;
function peekMessageChannel() {
const state = pxsim.getControlMessageState();
const msg = state && state.peek();
return msg && msg.channel;
}
pxtcore.peekMessageChannel = peekMessageChannel;
function readMessageData() {
const state = pxsim.getControlMessageState();
const msg = state && state.read();
return msg && new pxsim.RefBuffer(msg.data);
}
pxtcore.readMessageData = readMessageData;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
// keep in sync with ts
pxsim.CONTROL_MESSAGE_EVT_ID = 2999;
pxsim.CONTROL_MESSAGE_RECEIVED = 1;
class ControlMessageState {
constructor(board) {
this.board = board;
this.messages = [];
this.enabled = false;
this.board.addMessageListener(msg => this.messageHandler(msg));
}
messageHandler(msg) {
if (msg.type == "messagepacket") {
let packet = msg;
this.enqueue(packet);
}
}
enqueue(message) {
this.messages.push(message);
this.board.bus.queue(pxsim.CONTROL_MESSAGE_EVT_ID, pxsim.CONTROL_MESSAGE_RECEIVED);
}
peek() {
return this.messages[0];
}
read() {
return this.messages.shift();
}
}
pxsim.ControlMessageState = ControlMessageState;
function getControlMessageState() {
return pxsim.board().controlMessageState;
}
pxsim.getControlMessageState = getControlMessageState;
})(pxsim || (pxsim = {}));
/// <reference path="../../../node_modules/pxt-core/built/pxtsim.d.ts" />
var pxsim;
(function (pxsim) {
function board() {
return pxsim.runtime && pxsim.runtime.board;
}
pxsim.board = board;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var loops;
(function (loops) {
loops.pause = pxsim.thread.pause;
loops.forever = pxsim.thread.forever;
})(loops = pxsim.loops || (pxsim.loops = {}));
})(pxsim || (pxsim = {}));
/// <reference path="../../core/dal.d.ts"/>
var pxsim;
(function (pxsim) {
const DOUBLE_CLICK_TIME = 500;
class CommonButton extends pxsim.Button {
constructor() {
super(...arguments);
this._pressedTime = -1;
this._clickedTime = -1;
}
setPressed(p) {
if (this.pressed === p) {
return;
}
this.pressed = p;
if (p) {
this._wasPressed = true;
pxsim.board().bus.queue(this.id, 1 /* DAL.DEVICE_BUTTON_EVT_DOWN */);
this._pressedTime = pxsim.runtime.runningTime();
}
else if (this._pressedTime !== -1) {
pxsim.board().bus.queue(this.id, 2 /* DAL.DEVICE_BUTTON_EVT_UP */);
const current = pxsim.runtime.runningTime();
if (current - this._pressedTime >= 1000 /* DAL.DEVICE_BUTTON_LONG_CLICK_TIME */) {
pxsim.board().bus.queue(this.id, 4 /* DAL.DEVICE_BUTTON_EVT_LONG_CLICK */);
}
else {
pxsim.board().bus.queue(this.id, 3 /* DAL.DEVICE_BUTTON_EVT_CLICK */);
}
if (this._clickedTime !== -1) {
if (current - this._clickedTime <= DOUBLE_CLICK_TIME) {
pxsim.board().bus.queue(this.id, 6 /* DAL.DEVICE_BUTTON_EVT_DOUBLE_CLICK */);
}
}
this._clickedTime = current;
}
}
wasPressed() {
const temp = this._wasPressed;
this._wasPressed = false;
return temp;
}
pressureLevel() {
// digital for now
return this.isPressed() ? 512 : 0;
}
isPressed() {
return this.pressed;
}
}
pxsim.CommonButton = CommonButton;
class CommonButtonState {
constructor(buttons) {
this.usesButtonAB = false;
this.buttonsByPin = {};
this.buttons = buttons || [
new CommonButton(1 /* DAL.DEVICE_ID_BUTTON_A */),
new CommonButton(2 /* DAL.DEVICE_ID_BUTTON_B */),
new CommonButton(3 /* DAL.DEVICE_ID_BUTTON_AB */)
];
this.buttons.forEach(btn => this.buttonsByPin[btn.id] = btn);
}
}
pxsim.CommonButtonState = CommonButtonState;
})(pxsim || (pxsim = {}));
(function (pxsim) {
var pxtcore;
(function (pxtcore) {
function getButtonByPin(pinId) {
let m = pxsim.board().buttonState.buttonsByPin;
let b = m[pinId + ""];
if (!b) {
b = m[pinId + ""] = new pxsim.CommonButton(pinId);
}
return b;
}
pxtcore.getButtonByPin = getButtonByPin;
function getButtonByPinCfg(key) {
return getButtonByPin(pxtcore.getConfig(key, -1));
}
pxtcore.getButtonByPinCfg = getButtonByPinCfg;
function getButton(buttonId) {
const buttons = pxsim.board().buttonState.buttons;
if (buttonId === 2) {
pxsim.board().buttonState.usesButtonAB = true;
pxsim.runtime.queueDisplayUpdate();
}
if (buttonId < buttons.length && buttonId >= 0) {
return buttons[buttonId];
}
// panic
return undefined;
}
pxtcore.getButton = getButton;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
var ButtonMethods;
(function (ButtonMethods) {
function onEvent(button, ev, body) {
pxsim.pxtcore.registerWithDal(button.id, ev, body);
}
ButtonMethods.onEvent = onEvent;
function isPressed(button) {
return button.pressed;
}
ButtonMethods.isPressed = isPressed;
function pressureLevel(button) {
return button.pressureLevel();
}
ButtonMethods.pressureLevel = pressureLevel;
function wasPressed(button) {
return button.wasPressed();
}
ButtonMethods.wasPressed = wasPressed;
function id(button) {
return button.id;
}
ButtonMethods.id = id;
})(ButtonMethods = pxsim.ButtonMethods || (pxsim.ButtonMethods = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
var DigitalInOutPinMethods;
(function (DigitalInOutPinMethods) {
function pushButton(pin) {
return pxsim.pxtcore.getButtonByPin(pin.id);
}
DigitalInOutPinMethods.pushButton = pushButton;
})(DigitalInOutPinMethods = pxsim.DigitalInOutPinMethods || (pxsim.DigitalInOutPinMethods = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var network;
(function (network) {
function cableSendPacket(buf) {
const state = pxsim.getCableState();
state.send(buf);
}
network.cableSendPacket = cableSendPacket;
function cablePacket() {
const state = pxsim.getCableState();
return (state.packet);
}
network.cablePacket = cablePacket;
function onCablePacket(body) {
const state = pxsim.getCableState();
state.listen(body);
}
network.onCablePacket = onCablePacket;
function onCableError(body) {
const state = pxsim.getCableState();
state.listenError(body);
}
network.onCableError = onCableError;
})(network = pxsim.network || (pxsim.network = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
class CableState {
constructor() {
// notify view that a packet was received
this.packetReceived = false;
// PULSE_IR_COMPONENT_ID = 0x2042;
this.PULSE_CABLE_COMPONENT_ID = 0x2043;
this.PULSE_PACKET_EVENT = 0x2;
this.PULSE_PACKET_ERROR_EVENT = 0x3;
}
send(buf) {
pxsim.Runtime.postMessage({
type: "irpacket",
packet: buf.data
});
}
listen(body) {
pxsim.pxtcore.registerWithDal(this.PULSE_CABLE_COMPONENT_ID, this.PULSE_PACKET_EVENT, body);
}
listenError(body) {
pxsim.pxtcore.registerWithDal(this.PULSE_CABLE_COMPONENT_ID, this.PULSE_PACKET_ERROR_EVENT, body);
}
receive(buf) {
this.packet = buf;
this.packetReceived = true;
pxsim.board().bus.queue(this.PULSE_CABLE_COMPONENT_ID, this.PULSE_PACKET_EVENT);
}
}
pxsim.CableState = CableState;
function getCableState() {
return pxsim.board().cableState;
}
pxsim.getCableState = getCableState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
let ThresholdState;
(function (ThresholdState) {
ThresholdState[ThresholdState["High"] = 0] = "High";
ThresholdState[ThresholdState["Low"] = 1] = "Low";
ThresholdState[ThresholdState["Normal"] = 2] = "Normal";
})(ThresholdState || (ThresholdState = {}));
class AnalogSensorState {
constructor(id, min = 0, max = 255, lowThreshold = 64, highThreshold = 192) {
this.id = id;
this.min = min;
this.max = max;
this.lowThreshold = lowThreshold;
this.highThreshold = highThreshold;
this.sensorUsed = false;
this.state = ThresholdState.Normal;
this.level = Math.ceil((max - min) / 2);
}
setUsed() {
if (!this.sensorUsed) {
this.sensorUsed = true;
pxsim.runtime.queueDisplayUpdate();
}
}
setLevel(level) {
this.level = this.clampValue(level);
if (this.level >= this.highThreshold) {
this.setState(ThresholdState.High);
}
else if (this.level <= this.lowThreshold) {
this.setState(ThresholdState.Low);
}
else {
this.setState(ThresholdState.Normal);
}
}
getLevel() {
return this.level;
}
setLowThreshold(value) {
this.lowThreshold = this.clampValue(value);
this.highThreshold = Math.max(this.lowThreshold + 1, this.highThreshold);
}
setHighThreshold(value) {
this.highThreshold = this.clampValue(value);
this.lowThreshold = Math.min(this.highThreshold - 1, this.lowThreshold);
}
clampValue(value) {
if (value < this.min) {
return this.min;
}
else if (value > this.max) {
return this.max;
}
return value;
}
setState(state) {
if (this.state === state) {
return;
}
this.state = state;
switch (state) {
case ThresholdState.High:
pxsim.board().bus.queue(this.id, 2 /* DAL.SENSOR_THRESHOLD_HIGH */);
break;
case ThresholdState.Low:
pxsim.board().bus.queue(this.id, 1 /* DAL.SENSOR_THRESHOLD_LOW */);
break;
case ThresholdState.Normal:
break;
}
}
}
pxsim.AnalogSensorState = AnalogSensorState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
function mkBtnSvg(xy) {
let [innerCls, outerCls] = ["sim-button", "sim-button-outer"];
const tabSize = visuals.PIN_DIST / 2.5;
const pegR = visuals.PIN_DIST / 5;
const btnR = visuals.PIN_DIST * .8;
const pegMargin = visuals.PIN_DIST / 8;
const plateR = visuals.PIN_DIST / 12;
const pegOffset = pegMargin + pegR;
let [x, y] = xy;
const left = x - tabSize / 2;
const top = y - tabSize / 2;
const plateH = 3 * visuals.PIN_DIST - tabSize;
const plateW = 2 * visuals.PIN_DIST + tabSize;
const plateL = left;
const plateT = top + tabSize;
const btnCX = plateL + plateW / 2;
const btnCY = plateT + plateH / 2;
let btng = pxsim.svg.elt("g");
//tabs
const mkTab = (x, y) => {
pxsim.svg.child(btng, "rect", { class: "sim-button-tab", x: x, y: y, width: tabSize, height: tabSize });
};
mkTab(left, top);
mkTab(left + 2 * visuals.PIN_DIST, top);
mkTab(left, top + 3 * visuals.PIN_DIST);
mkTab(left + 2 * visuals.PIN_DIST, top + 3 * visuals.PIN_DIST);
//plate
pxsim.svg.child(btng, "rect", { class: outerCls, x: plateL, y: plateT, rx: plateR, ry: plateR, width: plateW, height: plateH });
//pegs
const mkPeg = (x, y) => {
pxsim.svg.child(btng, "circle", { class: "sim-button-nut", cx: x, cy: y, r: pegR });
};
mkPeg(plateL + pegOffset, plateT + pegOffset);
mkPeg(plateL + plateW - pegOffset, plateT + pegOffset);
mkPeg(plateL + pegOffset, plateT + plateH - pegOffset);
mkPeg(plateL + plateW - pegOffset, plateT + plateH - pegOffset);
//inner btn
let innerBtn = pxsim.svg.child(btng, "circle", { class: innerCls, cx: btnCX, cy: btnCY, r: btnR });
//return
return { el: btng, y: top, x: left, w: plateW, h: plateH + 2 * tabSize };
}
visuals.mkBtnSvg = mkBtnSvg;
visuals.BUTTON_PAIR_STYLE = `
.sim-button {
pointer-events: none;
fill: #000;
}
.sim-button-outer:active ~ .sim-button,
.sim-button-virtual:active {
fill: #FFA500;
}
.sim-button-outer {
cursor: pointer;
fill: #979797;
}
.sim-button-outer:hover {
stroke:gray;
stroke-width: ${visuals.PIN_DIST / 5}px;
}
.sim-button-nut {
fill:#000;
pointer-events:none;
}
.sim-button-nut:hover {
stroke:${visuals.PIN_DIST / 15}px solid #704A4A;
}
.sim-button-tab {
fill:#FFF;
pointer-events:none;
}
.sim-button-virtual {
cursor: pointer;
fill: rgba(255, 255, 255, 0.6);
stroke: rgba(255, 255, 255, 1);
stroke-width: ${visuals.PIN_DIST / 5}px;
}
.sim-button-virtual:hover {
stroke: rgba(128, 128, 128, 1);
}
.sim-text-virtual {
fill: #000;
pointer-events:none;
}
`;
class ButtonPairView {
constructor() {
this.style = visuals.BUTTON_PAIR_STYLE;
}
init(bus, state) {
this.state = state;
this.bus = bus;
this.defs = [];
this.element = this.mkBtns();
this.updateState();
this.attachEvents();
}
moveToCoord(xy) {
let btnWidth = visuals.PIN_DIST * 3;
let [x, y] = xy;
visuals.translateEl(this.aBtn, [x, y]);
visuals.translateEl(this.bBtn, [x + btnWidth, y]);
visuals.translateEl(this.abBtn, [x + visuals.PIN_DIST * 1.5, y + visuals.PIN_DIST * 4]);
}
updateState() {
let stateBtns = [this.state.aBtn, this.state.bBtn, this.state.abBtn];
let svgBtns = [this.aBtn, this.bBtn, this.abBtn];
if (this.state.usesButtonAB && this.abBtn.style.visibility != "visible") {
this.abBtn.style.visibility = "visible";
}
}
updateTheme() { }
mkBtns() {
this.aBtn = mkBtnSvg([0, 0]).el;
this.bBtn = mkBtnSvg([0, 0]).el;
const mkVirtualBtn = () => {
const numPins = 2;
const w = visuals.PIN_DIST * 2.8;
const offset = (w - (numPins * visuals.PIN_DIST)) / 2;
const corner = visuals.PIN_DIST / 2;
const cx = 0 - offset + w / 2;
const cy = cx;
const txtSize = visuals.PIN_DIST * 1.3;
const x = -offset;
const y = -offset;
const txtXOff = visuals.PIN_DIST / 7;
const txtYOff = visuals.PIN_DIST / 10;
let btng = pxsim.svg.elt("g");
let btn = pxsim.svg.child(btng, "rect", { class: "sim-button-virtual", x: x, y: y, rx: corner, ry: corner, width: w, height: w });
let btnTxt = visuals.mkTxt(cx + txtXOff, cy + txtYOff, txtSize, 0, "A+B");
pxsim.U.addClass(btnTxt, "sim-text");
pxsim.U.addClass(btnTxt, "sim-text-virtual");
btng.appendChild(btnTxt);
return btng;
};
this.abBtn = mkVirtualBtn();
this.abBtn.style.visibility = "hidden";
let el = pxsim.svg.elt("g");
pxsim.U.addClass(el, "sim-buttonpair");
el.appendChild(this.aBtn);
el.appendChild(this.bBtn);
el.appendChild(this.abBtn);
return el;
}
attachEvents() {
let btnStates = [this.state.aBtn, this.state.bBtn];
let btnSvgs = [this.aBtn, this.bBtn];
btnSvgs.forEach((btn, index) => {
pxsim.pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
btnStates[index].pressed = true;
}));
btn.addEventListener(pxsim.pointerEvents.leave, ev => {
btnStates[index].pressed = false;
});
btn.addEventListener(pxsim.pointerEvents.up, ev => {
btnStates[index].pressed = false;
this.bus.queue(btnStates[index].id, this.state.props.BUTTON_EVT_UP);
this.bus.queue(btnStates[index].id, this.state.props.BUTTON_EVT_CLICK);
});
});
let updateBtns = (s) => {
btnStates.forEach(b => b.pressed = s);
};
pxsim.pointerEvents.down.forEach(evid => this.abBtn.addEventListener(evid, ev => {
updateBtns(true);
}));
this.abBtn.addEventListener(pxsim.pointerEvents.leave, ev => {
updateBtns(false);
});
this.abBtn.addEventListener(pxsim.pointerEvents.up, ev => {
updateBtns(false);
this.bus.queue(this.state.abBtn.id, this.state.props.BUTTON_EVT_UP);
this.bus.queue(this.state.abBtn.id, this.state.props.BUTTON_EVT_CLICK);
});
}
}
visuals.ButtonPairView = ButtonPairView;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
let PinFlags;
(function (PinFlags) {
PinFlags[PinFlags["Unused"] = 0] = "Unused";
PinFlags[PinFlags["Digital"] = 1] = "Digital";
PinFlags[PinFlags["Analog"] = 2] = "Analog";
PinFlags[PinFlags["Input"] = 4] = "Input";
PinFlags[PinFlags["Output"] = 8] = "Output";
PinFlags[PinFlags["Touch"] = 16] = "Touch";
})(PinFlags = pxsim.PinFlags || (pxsim.PinFlags = {}));
class Pin {
constructor(id) {
this.id = id;
this.touched = false;
this.value = 0;
this.period = 0;
this.servoAngle = 0;
this.mode = PinFlags.Unused;
this.pitch = false;
this.pull = 0; // PullDown
this.eventMode = 0;
this.used = false;
}
setValue(value) {
// value set from the simulator
const old = this.value;
this.value = value;
const b = pxsim.board();
if (b && this.eventMode == 2 /* DAL.DEVICE_PIN_EVENT_ON_EDGE */ && old != this.value)
b.bus.queue(this.id, this.value > 0 ? 2 /* DAL.DEVICE_PIN_EVT_RISE */ : 3 /* DAL.DEVICE_PIN_EVT_FALL */);
}
digitalReadPin() {
this.mode = PinFlags.Digital | PinFlags.Input;
return this.value > 100 ? 1 : 0;
}
digitalWritePin(value) {
const b = pxsim.board();
this.mode = PinFlags.Digital | PinFlags.Output;
const v = this.value;
this.value = value > 0 ? 1023 : 0;
pxsim.runtime.queueDisplayUpdate();
}
setPull(pull) {
this.pull = pull;
switch (pull) {
case 2 /*PinPullMode.PullDown*/:
this.value = 0;
break;
case 1 /*PinPullMode.PullUp*/:
this.value = 1023;
break;
default:
this.value = pxsim.Math_.randomRange(0, 1023);
break;
}
}
analogReadPin() {
this.mode = PinFlags.Analog | PinFlags.Input;
return this.value || 0;
}
analogWritePin(value) {
const b = pxsim.board();
this.mode = PinFlags.Analog | PinFlags.Output;
const v = this.value;
this.value = Math.max(0, Math.min(1023, value));
pxsim.runtime.queueDisplayUpdate();
}
analogSetPeriod(micros) {
this.mode = PinFlags.Analog | PinFlags.Output;
this.period = micros;
pxsim.runtime.queueDisplayUpdate();
}
servoWritePin(value) {
this.analogSetPeriod(20000);
this.servoAngle = Math.max(0, Math.min(180, value));
pxsim.runtime.queueDisplayUpdate();
}
servoSetContinuous(continuous) {
this.servoContinuous = continuous;
}
servoSetPulse(pinId, micros) {
// TODO
}
isTouched() {
this.mode = PinFlags.Touch | PinFlags.Analog | PinFlags.Input;
return this.touched;
}
onEvent(ev, handler) {
const b = pxsim.board();
switch (ev) {
case 4 /* DAL.DEVICE_PIN_EVT_PULSE_HI */:
case 5 /* DAL.DEVICE_PIN_EVT_PULSE_LO */:
this.eventMode = 3 /* DAL.DEVICE_PIN_EVENT_ON_PULSE */;