2.1.28, initiation update to PXT v5.28.24 (#54)

This commit is contained in:
Amerlander
2019-12-02 05:58:26 +01:00
committed by Peli de Halleux
parent 38a964516e
commit 5c114a0c57
1261 changed files with 50692 additions and 21604 deletions

View File

@ -1,37 +1,53 @@
namespace pxsim.input {
export function onGesture(gesture: number, handler: RefAction) {
function accForGesture(gesture: number) {
let b = board().accelerometerState;
b.accelerometer.activate();
if (gesture == 11 && !b.useShake) { // SAKE
if (gesture == 11 && !b.useShake) { // SHAKE
b.useShake = true;
runtime.queueDisplayUpdate();
}
return b;
}
export function onGesture(gesture: number, handler: RefAction) {
const b = accForGesture(gesture);
pxtcore.registerWithDal(DAL.MICROBIT_ID_GESTURE, gesture, handler);
}
export function isGesture(gesture: number): boolean {
const b = accForGesture(gesture);
return b.accelerometer.getGesture() == gesture;
}
export function acceleration(dimension: number): number {
let b = board().accelerometerState;
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()));
case 0:
acc.activate(AccelerometerFlag.X);
return acc.getX();
case 1:
acc.activate(AccelerometerFlag.Y);
return acc.getY();
case 2:
acc.activate(AccelerometerFlag.Z);
return acc.getZ();
default:
acc.activate();
return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared()));
}
}
export function rotation(kind: number): number {
let b = board().accelerometerState;
let acc = b.accelerometer;
const b = board().accelerometerState;
const acc = b.accelerometer;
acc.activate();
let x = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let y = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let z = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const x = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const y = acc.getY(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const z = acc.getZ(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let roll = Math.atan2(y, z);
let pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
const roll = Math.atan2(y, z);
const pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
let r = 0;
switch (kind) {
@ -99,6 +115,12 @@ namespace pxsim {
NORTH_EAST_DOWN
}
export enum AccelerometerFlag {
X = 1,
Y = 2,
Z = 4
}
export class Accelerometer {
private sigma: number = 0; // the number of ticks that the instantaneous gesture has been stable.
private lastGesture: number = 0; // the last, stable gesture recorded.
@ -110,6 +132,7 @@ namespace pxsim {
private id: number;
public isActive = false;
public sampleRange = 2;
public flags: AccelerometerFlag = 0;
constructor(public runtime: Runtime) {
this.id = DAL.MICROBIT_ID_ACCELEROMETER;
@ -120,11 +143,13 @@ namespace pxsim {
this.sampleRange = Math.max(1, Math.min(8, range));
}
public activate() {
public activate(flags?: AccelerometerFlag) {
if (!this.isActive) {
this.isActive = true;
this.runtime.queueDisplayUpdate();
}
if (flags)
this.flags |= flags;
}
/**
@ -199,9 +224,8 @@ namespace pxsim {
if (force < sq(DAL.MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_FREEFALL;
// TODO: fix this
//if (force > sq(DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE))
// return DAL.MICROBIT_ACCELEROMETER_EVT_3G;
if (force > sq(DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_3G;
if (force > sq(DAL.MICROBIT_ACCELEROMETER_6G_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_6G;
@ -223,10 +247,10 @@ namespace pxsim {
return DAL.MICROBIT_ACCELEROMETER_EVT_TILT_UP;
if (this.getZ() < (-1000 + DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN;
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP;
if (this.getZ() > (1000 - DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP;
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN;
return 0;
}
@ -236,22 +260,28 @@ namespace pxsim {
let g = this.instantaneousPosture();
// Perform some low pass filtering to reduce jitter from any detected effects
if (g == this.currentGesture) {
if (this.sigma < DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
this.sigma++;
}
else {
if (g != this.currentGesture) {
this.currentGesture = g;
this.sigma = 0;
} else if (this.sigma < DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
++this.sigma;
}
// If we've reached threshold, update our record and raise the relevant event...
if (this.currentGesture != this.lastGesture && this.sigma >= DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
this.lastGesture = this.currentGesture;
board().bus.queue(DAL.MICROBIT_ID_GESTURE, this.lastGesture);
if (this.currentGesture !== this.lastGesture && this.sigma >= DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
this.enqueueCurrentGesture();
}
}
forceGesture(gesture: number) {
this.currentGesture = gesture;
this.enqueueCurrentGesture();
}
private enqueueCurrentGesture() {
this.lastGesture = this.currentGesture;
board().bus.queue(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.
@ -363,6 +393,10 @@ namespace pxsim {
return this.roll;
}
getGesture(): number {
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
@ -384,7 +418,11 @@ namespace pxsim {
useShake = false;
constructor(runtime: Runtime) {
this.accelerometer = new Accelerometer(runtime);
this.accelerometer = new Accelerometer(runtime);
}
shake() {
this.accelerometer.forceGesture(DAL.MICROBIT_ACCELEROMETER_EVT_SHAKE); // SHAKE == 11
}
}
}

201
sim/state/buttonpairsim.ts Normal file
View File

@ -0,0 +1,201 @@
namespace pxsim.visuals {
export function mkBtnSvg(xy: Coord): SVGAndSize<SVGGElement> {
let [innerCls, outerCls] = ["sim-button", "sim-button-outer"];
const tabSize = PIN_DIST / 2.5;
const pegR = PIN_DIST / 5;
const btnR = PIN_DIST * .8;
const pegMargin = PIN_DIST / 8;
const plateR = PIN_DIST / 12;
const pegOffset = pegMargin + pegR;
let [x, y] = xy;
const left = x - tabSize / 2;
const top = y - tabSize / 2;
const plateH = 3 * PIN_DIST - tabSize;
const plateW = 2 * PIN_DIST + tabSize;
const plateL = left;
const plateT = top + tabSize;
const btnCX = plateL + plateW / 2;
const btnCY = plateT + plateH / 2;
let btng = <SVGGElement>svg.elt("g");
//tabs
const mkTab = (x: number, y: number) => {
svg.child(btng, "rect", { class: "sim-button-tab", x: x, y: y, width: tabSize, height: tabSize})
}
mkTab(left, top);
mkTab(left + 2 * PIN_DIST, top);
mkTab(left, top + 3 * PIN_DIST);
mkTab(left + 2 * PIN_DIST, top + 3 * PIN_DIST);
//plate
svg.child(btng, "rect", { class: outerCls, x: plateL, y: plateT, rx: plateR, ry: plateR, width: plateW, height: plateH });
//pegs
const mkPeg = (x: number, y: number) => {
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 = 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 };
}
export const 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: ${PIN_DIST / 5}px;
}
.sim-button-nut {
fill:#000;
pointer-events:none;
}
.sim-button-nut:hover {
stroke:${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: ${PIN_DIST / 5}px;
}
.sim-button-virtual:hover {
stroke: rgba(128, 128, 128, 1);
}
.sim-text-virtual {
fill: #000;
pointer-events:none;
}
`;
export class ButtonPairView implements IBoardPart<ButtonPairState> {
public element: SVGElement;
public defs: SVGElement[];
public style = BUTTON_PAIR_STYLE;
private state: ButtonPairState;
private bus: EventBus;
private aBtn: SVGGElement;
private bBtn: SVGGElement;
private abBtn: SVGGElement;
public init(bus: EventBus, state: ButtonPairState) {
this.state = state;
this.bus = bus;
this.defs = [];
this.element = this.mkBtns();
this.updateState();
this.attachEvents();
}
public moveToCoord(xy: Coord) {
let btnWidth = PIN_DIST * 3;
let [x, y] = xy;
translateEl(this.aBtn, [x, y])
translateEl(this.bBtn, [x + btnWidth, y])
translateEl(this.abBtn, [x + PIN_DIST * 1.5, y + PIN_DIST * 4])
}
public 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";
}
}
public updateTheme() {}
private mkBtns() {
this.aBtn = mkBtnSvg([0, 0]).el;
this.bBtn = mkBtnSvg([0, 0]).el;
const mkVirtualBtn = () => {
const numPins = 2;
const w = PIN_DIST * 2.8;
const offset = (w - (numPins * PIN_DIST)) / 2;
const corner = PIN_DIST / 2;
const cx = 0 - offset + w / 2;
const cy = cx;
const txtSize = PIN_DIST * 1.3;
const x = -offset;
const y = -offset;
const txtXOff = PIN_DIST / 7;
const txtYOff = PIN_DIST / 10;
let btng = <SVGGElement>svg.elt("g");
let btn = svg.child(btng, "rect", { class: "sim-button-virtual", x: x, y: y, rx: corner, ry: corner, width: w, height: w});
let btnTxt = mkTxt(cx + txtXOff, cy + txtYOff, txtSize, 0, "A+B");
U.addClass(btnTxt, "sim-text")
U.addClass(btnTxt, "sim-text-virtual");
btng.appendChild(btnTxt);
return btng;
}
this.abBtn = mkVirtualBtn();
this.abBtn.style.visibility = "hidden";
let el = svg.elt("g");
U.addClass(el, "sim-buttonpair")
el.appendChild(this.aBtn);
el.appendChild(this.bBtn);
el.appendChild(this.abBtn);
return el;
}
private 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(pointerEvents.leave, ev => {
btnStates[index].pressed = false;
})
btn.addEventListener(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: boolean) => {
btnStates.forEach(b => b.pressed = s)
};
pxsim.pointerEvents.down.forEach(evid => this.abBtn.addEventListener(evid, ev => {
updateBtns(true);
}));
this.abBtn.addEventListener(pointerEvents.leave, ev => {
updateBtns(false);
})
this.abBtn.addEventListener(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);
})
}
}
}

View File

@ -3,6 +3,7 @@ namespace pxsim.input {
let pin = getPin(pinId);
if (!pin) return;
pin.isTouched();
runtime.queueDisplayUpdate();
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_CLICK, handler);
}
@ -10,6 +11,7 @@ namespace pxsim.input {
let pin = getPin(pinId);
if (!pin) return;
pin.isTouched();
runtime.queueDisplayUpdate();
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_UP, handler);
}
@ -29,7 +31,7 @@ namespace pxsim {
namespace pxsim.pins {
export function digitalReadPin(pinId: number): number {
let pin = getPin(pinId);
if (!pin) return;
if (!pin) return -1;
pin.mode = PinFlags.Digital | PinFlags.Input;
return pin.value > 100 ? 1 : 0;
}
@ -50,7 +52,7 @@ namespace pxsim.pins {
export function analogReadPin(pinId: number): number {
let pin = getPin(pinId);
if (!pin) return;
if (!pin) return -1;
pin.mode = PinFlags.Analog | PinFlags.Input;
return pin.value || 0;
}
@ -76,7 +78,7 @@ namespace pxsim.pins {
if (!pin) return;
analogSetPeriod(pinId, 20000);
pin.servoAngle = Math.max(0, Math.min(180, value));
pin.servoAngle = value;
}
export function servoSetPulse(pinId: number, micros: number) {

View File

@ -0,0 +1,89 @@
namespace pxsim {
export enum PinFlags {
Unused = 0,
Digital = 0x0001,
Analog = 0x0002,
Input = 0x0004,
Output = 0x0008,
Touch = 0x0010
}
export class Pin {
constructor(public id: number) { }
touched = false;
value = 0;
period = 0;
servoAngle = 0;
mode = PinFlags.Unused;
pitch = false;
pull = 0; // PullDown
digitalReadPin(): number {
this.mode = PinFlags.Digital | PinFlags.Input;
return this.value > 100 ? 1 : 0;
}
digitalWritePin(value: number) {
this.mode = PinFlags.Digital | PinFlags.Output;
this.value = value > 0 ? 200 : 0;
runtime.queueDisplayUpdate();
}
setPull(pull: number) {
this.pull = pull;
}
analogReadPin(): number {
this.mode = PinFlags.Analog | PinFlags.Input;
return this.value || 0;
}
analogWritePin(value: number) {
value = value >> 0;
this.mode = PinFlags.Analog | PinFlags.Output;
this.value = Math.max(0, Math.min(1023, value));
runtime.queueDisplayUpdate();
}
analogSetPeriod(micros: number) {
micros = micros >> 0;
this.mode = PinFlags.Analog | PinFlags.Output;
this.period = micros;
runtime.queueDisplayUpdate();
}
servoWritePin(value: number) {
value = value >> 0;
this.analogSetPeriod(20000);
this.servoAngle = Math.max(0, Math.min(180, value));
runtime.queueDisplayUpdate();
}
servoSetPulse(pinId: number, micros: number) {
// TODO
}
isTouched(): boolean {
this.mode = PinFlags.Touch | PinFlags.Analog | PinFlags.Input;
return this.touched;
}
}
export interface EdgeConnectorProps {
pins: number[];
servos?: { [name: string]: number; }
}
export class EdgeConnectorState {
pins: Pin[];
constructor(public props: EdgeConnectorProps) {
this.pins = props.pins.map(id => id != undefined ? new Pin(id) : null);
}
public getPin(id: number) {
return this.pins.filter(p => p && p.id == id)[0] || null
}
}
}

View File

@ -15,4 +15,10 @@ namespace pxsim.files {
const b = board();
b.fileSystem.remove(filename);
}
export function readToSerial(filename: string) {
const b = board();
let f = b.fileSystem.files[filename];
if (f)
b.serialState.writeSerial(f);
}
}

View File

@ -28,17 +28,24 @@ namespace pxsim {
this.data = data;
}
public print() {
// console.debug(`Image id:${this.id} refs:${this.refcnt} size:${this.width}x${Image.height}`)
console.debug(`Image id:${this.id} size:${this.width}x${Image.height}`)
}
public get(x: number, y: number): number {
x = x >> 0;
y = y >> 0;
if (x < 0 || x >= this.width || y < 0 || y >= 5) return 0;
return this.data[y * this.width + x];
}
public set(x: number, y: number, v: number) {
x = x >> 0;
y = y >> 0;
if (x < 0 || x >= this.width || y < 0 || y >= 5) return;
this.data[y * this.width + x] = Math.max(0, Math.min(255, v));
}
public copyTo(xSrcIndex: number, length: number, target: Image, xTargetIndex: number): void {
xSrcIndex = xSrcIndex >> 0;
length = length >> 0;
xTargetIndex = xTargetIndex >> 0;
for (let x = 0; x < length; x++) {
for (let y = 0; y < 5; y++) {
let value = this.get(xSrcIndex + x, y);
@ -47,12 +54,14 @@ namespace pxsim {
}
}
public shiftLeft(cols: number) {
cols = cols >> 0;
for (let x = 0; x < this.width; ++x)
for (let y = 0; y < 5; ++y)
this.set(x, y, x < this.width - cols ? this.get(x + cols, y) : 0);
}
public shiftRight(cols: number) {
cols = cols >> 0;
for (let x = this.width - 1; x >= 0; --x)
for (let y = 0; y < 5; ++y)
this.set(x, y, x >= cols ? this.get(x - cols, y) : 0);
@ -65,12 +74,13 @@ namespace pxsim {
}
export function createInternalImage(width: number): Image {
width = width >> 0;
let img = createImage(width)
pxsim.noLeakTracking(img)
return img
}
export function createImage(width: number): Image {
width = width >> 0;
return new Image(width, new Array(width * 5));
}
@ -131,9 +141,12 @@ namespace pxsim.images {
namespace pxsim.ImageMethods {
export function showImage(leds: Image, offset: number, interval: number) {
pxtrt.nullCheck(leds)
offset = offset >> 0;
interval = interval >> 0;
let cb = getResume();
let first = true;
leds = clampPixelBrightness(leds);
board().ledMatrixState.animationQ.enqueue({
interval,
frame: () => {
@ -150,7 +163,9 @@ namespace pxsim.ImageMethods {
export function plotImage(leds: Image, offset: number): void {
pxtrt.nullCheck(leds)
offset = offset >> 0;
leds = clampPixelBrightness(leds);
board().ledMatrixState.animationQ.enqueue({
interval: 0,
frame: () => {
@ -205,12 +220,15 @@ namespace pxsim.ImageMethods {
export function scrollImage(leds: Image, stride: number, interval: number): void {
pxtrt.nullCheck(leds)
stride = stride >> 0;
interval = interval >> 0;
if (stride == 0) stride = 1;
let cb = getResume();
let off = stride > 0 ? 0 : leds.width - 1;
let display = board().ledMatrixState.image;
leds = clampPixelBrightness(leds);
board().ledMatrixState.animationQ.enqueue({
interval: interval,
frame: () => {
@ -230,10 +248,27 @@ namespace pxsim.ImageMethods {
whenDone: cb
})
}
function clampPixelBrightness(img: Image): Image {
let res = img;
if (led.displayMode() === DisplayMode.greyscale && led.brightness() < 0xff) {
res = new Image(img.width, img.data);
const b = led.brightness();
for (let x = 0; x < res.width; ++x) {
for (let y = 0; y < 5; ++y) {
if (pixelBrightness(res, x, y) > b) {
setPixelBrightness(res, x, y, b);
}
}
}
}
return res;
}
}
namespace pxsim.basic {
export function showNumber(x: number, interval: number) {
interval = interval >> 0;
if (interval <= 0)
interval = 1;
let leds = createImageFromString(x.toString());
@ -242,19 +277,21 @@ namespace pxsim.basic {
}
export function showString(s: string, interval: number) {
interval = interval >> 0;
if (interval <= 0)
interval = 1;
if (s.length == 0) {
clearScreen();
pause(interval * 5);
} else if (s.length > 1) {
ImageMethods.scrollImage(createImageFromString(s + " "), 1, interval);
} else {
if (s.length == 1) showLeds(createImageFromString(s), 0);
else ImageMethods.scrollImage(createImageFromString(s + " "), 1, interval);
showLeds(createImageFromString(s), interval * 5);
}
}
export function showLeds(leds: Image, delay: number): void {
showAnimation(leds, delay);
export function showLeds(leds: Image, interval: number): void {
showAnimation(leds, interval);
}
export function clearScreen() {
@ -279,9 +316,10 @@ namespace pxsim.led {
export function plotBrightness(x: number, y: number, brightness: number) {
const state = board().ledMatrixState;
brightness = Math.max(0, Math.min(0xff, brightness));
brightness = brightness >> 0;
brightness = Math.max(0, Math.min(led.brightness(), brightness));
if (brightness != 0 && brightness != 0xff && state.displayMode != DisplayMode.greyscale)
state.displayMode = DisplayMode.greyscale;
state.displayMode = DisplayMode.greyscale;
state.image.set(x, y, brightness);
runtime.queueDisplayUpdate()
}
@ -300,6 +338,7 @@ namespace pxsim.led {
}
export function setBrightness(value: number): void {
value = value >> 0;
board().ledMatrixState.brigthness = Math.max(0, Math.min(255, value));
runtime.queueDisplayUpdate()
}

83
sim/state/microservo.ts Normal file
View File

@ -0,0 +1,83 @@
namespace pxsim.visuals {
function createMicroServoElement() {
return svg.parseString(`
<svg xmlns="http://www.w3.org/2000/svg" id="svg2" width="112.188" height="299.674">
<g id="layer1" stroke-linecap="round" stroke-linejoin="round" transform="scale(0.8)">
<path id="path8212" fill="#0061ff" stroke-width="6.6" d="M.378 44.61v255.064h112.188V44.61H.378z"/>
<path id="crankbase" fill="#00f" stroke-width="6.6" d="M56.57 88.047C25.328 88.047 0 113.373 0 144.615c.02 22.352 11.807 42.596 32.238 51.66.03 3.318.095 5.24.088 7.938 0 13.947 11.307 25.254 25.254 25.254 13.947 0 25.254-11.307 25.254-25.254-.006-2.986-.415-5.442-.32-8.746 19.487-9.45 30.606-29.195 30.625-50.852 0-31.24-25.33-56.568-56.57-56.568z"/>
<path id="lowertip" fill="#00a2ff" stroke-width="2" d="M.476 260.78v38.894h53.82v-10.486a6.82 6.566 0 0 1-4.545-6.182 6.82 6.566 0 0 1 6.82-6.566 6.82 6.566 0 0 1 6.82 6.566 6.82 6.566 0 0 1-4.545 6.182v10.486h53.82V260.78H.475z"/>
<path id="uppertip" fill="#00a2ff" stroke-width="2" d="M112.566 83.503V44.61h-53.82v10.487a6.82 6.566 0 0 1 4.544 6.18 6.82 6.566 0 0 1-6.818 6.568 6.82 6.566 0 0 1-6.82-6.567 6.82 6.566 0 0 1 4.546-6.18V44.61H.378v38.893h112.188z"/>
<path id="VCC" fill="red" stroke-width="2" d="M53.72 21.93h5.504v22.627H53.72z"/>
<path id="LOGIC" fill="#fc0" stroke-width="2" d="M47.3 21.93h5.503v22.627H47.3z"/>
<path id="GND" fill="#a02c2c" stroke-width="2" d="M60.14 21.93h5.505v22.627H60.14z"/>
<path id="connector" fill="#111" stroke-width="2" d="M45.064 0a1.488 1.488 0 0 0-1.488 1.488v24.5a1.488 1.488 0 0 0 1.488 1.487h22.71a1.488 1.488 0 0 0 1.49-1.488v-24.5A1.488 1.488 0 0 0 67.774 0h-22.71z"/>
<g id="crank" transform="translate(0 -752.688)">
<path id="arm" fill="#ececec" stroke="#000" stroke-width="1.372" d="M47.767 880.88c-4.447 1.162-8.412 8.278-8.412 18.492s3.77 18.312 8.412 18.494c8.024.314 78.496 5.06 78.51-16.952.012-22.013-74.377-21.117-78.51-20.035z"/>
<circle id="path8216" cx="56.661" cy="899.475" r="8.972" fill="gray" stroke-width="2"/>
</g>
</g>
</svg>
`).firstElementChild as SVGGElement;
}
export function mkMicroServoPart(xy: Coord = [0, 0]): SVGElAndSize {
return { el: createMicroServoElement(), x: xy[0], y: xy[1], w: 112.188, h: 299.674 };
}
export class MicroServoView implements IBoardPart<EdgeConnectorState> {
public style: string = "";
public overElement: SVGElement = undefined;
public element: SVGElement;
public defs: SVGElement[] = [];
public state: EdgeConnectorState;
public bus: EventBus;
private currentAngle = 0;
private targetAngle = 0;
private lastAngleTime = 0;
private pin: number;
private crankEl: SVGGElement;
private crankTransform: string;
public init(bus: EventBus, state: EdgeConnectorState, svgEl: SVGSVGElement, otherParams: Map<string>) {
this.state = state;
this.pin = this.state.props.servos[
pxsim.readPin(otherParams["name"] || otherParams["pin"])
];
this.bus = bus;
this.defs = [];
this.initDom();
this.updateState();
}
initDom() {
this.element = createMicroServoElement();
this.crankEl = this.element.querySelector("#crank") as SVGGElement;
this.crankTransform = this.crankEl.getAttribute("transform");
}
moveToCoord(xy: visuals.Coord): void {
let [x, y] = xy;
translateEl(this.element, [x, y])
}
updateState(): void {
this.targetAngle = 180.0 - this.state.getPin(this.pin).servoAngle;
if (this.targetAngle != this.currentAngle) {
const now = U.now();
const cx = 56.661;
const cy = 899.475;
const speed = 300; // 0.1s/60 degree
const dt = Math.min(now - this.lastAngleTime, 50) / 1000;
const delta = this.targetAngle - this.currentAngle;
this.currentAngle += Math.min(Math.abs(delta), speed * dt) * (delta > 0 ? 1 : -1);
this.crankEl.setAttribute("transform", this.crankTransform
+ ` rotate(${this.currentAngle}, ${cx}, ${cy})`)
this.lastAngleTime = now;
setTimeout(() => runtime.updateDisplay(), 20);
}
}
updateTheme(): void {
}
}
}

6
sim/state/midi.ts Normal file
View File

@ -0,0 +1,6 @@
namespace pxsim.control {
export function __midiSend(data: RefBuffer) {
const b = board();
pxsim.AudioContextManager.sendMidiMessage(data);
}
}

View File

@ -35,8 +35,13 @@ namespace pxsim.basic {
namespace pxsim.control {
export var inBackground = thread.runInBackground;
export function createBuffer(sz: number) {
return pxsim.BufferMethods.createBuffer(sz)
}
export function reset() {
U.userError("reset not implemented in simulator yet")
const cb = getResume();
pxsim.runtime.restart();
}
export function waitMicros(micros: number) {
@ -58,6 +63,13 @@ namespace pxsim.control {
}
export function onEvent(id: number, evid: number, handler: RefAction) {
if (id == DAL.MICROBIT_ID_BUTTON_AB) {
const b = board().buttonPairState;
if (!b.usesButtonAB) {
b.usesButtonAB = true;
runtime.queueDisplayUpdate();
}
}
pxtcore.registerWithDal(id, evid, handler)
}
@ -65,6 +77,14 @@ namespace pxsim.control {
// TODO mode?
board().bus.queue(id, evid)
}
export function eventTimestamp() {
return board().bus.getLastEventTime()
}
export function eventValue() {
return board().bus.getLastEventValue()
}
}
namespace pxsim.pxtcore {
@ -78,7 +98,12 @@ namespace pxsim.input {
return runtime.runningTime();
}
export function calibrate() {
export function runningTimeMicros(): number {
return runtime.runningTimeUs();
}
export function calibrateCompass() {
// device calibrates...
}
}
@ -106,6 +131,18 @@ namespace pxsim.pins {
return 0;
}
export function spiFrequency(f: number): void {
// TODO
}
export function spiFormat(bits: number, mode: number): void {
// TODO
}
export function spiPins(mosi: number, miso: number, sck: number) {
// TODO
}
export function i2cReadBuffer(address: number, size: number, repeat?: boolean): RefBuffer {
// fake reading zeros
return createBuffer(size)
@ -137,7 +174,7 @@ namespace pxsim.devices {
export function onSignalStrengthChanged(action: number) {
// TODO
}
export function signalStrength() : number {
export function signalStrength(): number {
// TODO
return 0;
}
@ -168,25 +205,34 @@ namespace pxsim.bluetooth {
export function startUartService(): void {
// TODO
}
export function uartWrite(s : string): void {
export function uartWriteString(s: string): void {
serial.writeString(s)
}
export function uartWriteBuffer(b: RefBuffer): void {
serial.writeBuffer(b);
}
export function uartReadBuffer(): RefBuffer {
return pins.createBuffer(0);
}
export function uartReadUntil(del: string): string {
return serial.readUntil(del);
}
export function onDataReceived(delimiters: string, handler: RefAction) {
export function onUartDataReceived(delimiters: string, handler: RefAction) {
let b = board();
b.bus.listen(DAL.MICROBIT_ID_BLE_UART, DAL.MICROBIT_UART_S_EVT_DELIM_MATCH, handler);
}
export function onBluetoothConnected(a : RefAction) {
export function onBluetoothConnected(a: RefAction) {
// TODO
}
export function onBluetoothDisconnected(a : RefAction) {
export function onBluetoothDisconnected(a: RefAction) {
// TODO
}
export function advertiseUrl(url: string, power: number, connectable: boolean) { }
export function advertiseUidBuffer(nsAndInstance: Buffer, power: number, connectable: boolean) { }
export function advertiseUidBuffer(nsAndInstance: RefBuffer, power: number, connectable: boolean) { }
export function stopAdvertising() { }
export function setTransmitPower(power: number) {}
export function setTransmitPower(power: number) { }
}

View File

@ -1,11 +1,11 @@
namespace pxsim {
export function sendBufferAsm(buffer: Buffer, pin: DigitalPin) {
export function sendBufferAsm(buffer: RefBuffer, pin: DigitalPin) {
let b = board();
if (b) {
let np = b.neopixelState;
if (np) {
let buf = <Uint8Array>(<any>buffer).data;
np.updateBuffer(buf, pin);
let buf = buffer.data;
np.updateBuffer(buf as any, pin); // TODO this is wrong
runtime.queueDisplayUpdate();
}
}

View File

@ -6,6 +6,11 @@ namespace pxsim {
time: number;
}
// Extends interface in pxt-core
export interface SimulatorRadioPacketPayload {
bufferData?: Uint8Array;
}
export class RadioDatagram {
datagram: PacketBuffer[] = [];
lastReceived: PacketBuffer = RadioDatagram.defaultPacket();
@ -24,8 +29,9 @@ namespace pxsim {
const b = board();
Runtime.postMessage(<SimulatorRadioPacketMessage>{
type: "radiopacket",
rssi: 70, // Not yet supported
serial: b.radioState.bus.transmitSerialNumber ? pxsim.control.deviceSerialNumber() : 0,
broadcast: true,
rssi: -42, // -42 is the strongest signal
serial: b.radioState.transmitSerialNumber ? pxsim.control.deviceSerialNumber() : 0,
time: new Date().getTime(),
payload
})
@ -42,22 +48,31 @@ namespace pxsim {
rssi: -1,
serial: 0,
time: 0,
payload: { type: -1, groupId: 0 }
payload: { type: -1, groupId: 0, bufferData: new Uint8Array(0) }
};
}
}
export class RadioBus {
// uint8_t radioDefaultGroup = MICROBIT_RADIO_DEFAULT_GROUP;
export class RadioState {
power = 0;
transmitSerialNumber = false;
datagram: RadioDatagram;
groupId: number;
band: number;
constructor(private runtime: Runtime) {
constructor(runtime: Runtime) {
this.datagram = new RadioDatagram(runtime);
this.power = 6; // default value
this.groupId = 0;
this.band = 7; // https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitConfig.h#L320
}
public setGroup(id: number) {
this.groupId = id & 0xff; // byte only
}
setTransmitPower(power: number) {
power = power | 0;
this.power = Math.max(0, Math.min(7, power));
}
@ -65,54 +80,33 @@ namespace pxsim {
this.transmitSerialNumber = !!sn;
}
broadcast(msg: number, groupId: number) {
setFrequencyBand(band: number) {
band = band | 0;
if (band < 0 || band > 83) return;
this.band = band;
}
raiseEvent(id: number, eventid: number) {
Runtime.postMessage(<SimulatorEventBusMessage>{
type: "eventbus",
id: DAL.MES_BROADCAST_GENERAL_ID,
eventid: msg,
broadcast: true,
id,
eventid,
power: this.power,
group: groupId
group: this.groupId
})
}
}
export class RadioState {
bus: RadioBus;
groupId: number;
constructor(runtime: Runtime) {
this.bus = new RadioBus(runtime);
this.groupId = 0;
}
public setGroup(id: number) {
this.groupId = id & 0xff; // byte only
}
public broadcast(msg: number) {
this.bus.broadcast(msg, this.groupId)
}
public receivePacket(packet: SimulatorRadioPacketMessage) {
receivePacket(packet: SimulatorRadioPacketMessage) {
if (this.groupId == packet.payload.groupId)
this.bus.datagram.queue(packet)
this.datagram.queue(packet)
}
}
}
namespace pxsim.radio {
enum PacketPayloadType {
NUMBER = 0,
VALUE = 1,
STRING = 2
}
export function broadcastMessage(msg: number): void {
board().radioState.broadcast(msg);
}
export function onBroadcastMessageReceived(msg: number, handler: RefAction): void {
pxtcore.registerWithDal(DAL.MES_BROADCAST_GENERAL_ID, msg, handler);
export function raiseEvent(id: number, eventid: number): void {
board().radioState.raiseEvent(id, eventid);
}
export function setGroup(id: number): void {
@ -120,99 +114,34 @@ namespace pxsim.radio {
}
export function setTransmitPower(power: number): void {
board().radioState.bus.setTransmitPower(power);
board().radioState.setTransmitPower(power);
}
export function setTransmitSerialNumber(transmit: boolean): void {
board().radioState.bus.setTransmitSerialNumber(transmit);
export function setFrequencyBand(band: number) {
board().radioState.setFrequencyBand(band);
}
export function sendNumber(value: number): void {
board().radioState.bus.datagram.send({
type: PacketPayloadType.NUMBER,
export function sendRawPacket(buf: RefBuffer) {
let cb = getResume();
board().radioState.datagram.send({
type: 0,
groupId: board().radioState.groupId,
numberData: value,
bufferData: buf.data
});
setTimeout(cb, 1);
}
export function sendString(msg: string): void {
msg = msg.substr(0, 19);
board().radioState.bus.datagram.send({
type: PacketPayloadType.STRING,
groupId: board().radioState.groupId,
stringData: msg,
});
}
export function writeValueToSerial(): void {
const b = board();
writePacketToSerial(b, b.radioState.bus.datagram.recv())
}
export function writeReceivedPacketToSerial(): void {
const b = board();
writePacketToSerial(b, b.radioState.bus.datagram.lastReceived);
}
export function sendValue(name: string, value: number) {
name = name.substr(0, 12);
const msg: number[] = [];
msg.push()
board().radioState.bus.datagram.send({
type: PacketPayloadType.VALUE,
groupId: board().radioState.groupId,
stringData: name,
numberData: value
});
}
export function receiveNumber(): number {
const packet = board().radioState.bus.datagram.recv();
return receivedNumber();
}
export function receiveString(): string {
const packet = board().radioState.bus.datagram.recv();
return receivedString();
export function readRawPacket() {
const packet = board().radioState.datagram.recv();
return new RefBuffer(packet.payload.bufferData)
}
export function receivedSignalStrength(): number {
return board().radioState.bus.datagram.lastReceived.rssi;
return board().radioState.datagram.lastReceived.rssi;
}
export function onDataReceived(handler: RefAction): void {
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 initString(board().radioState.bus.datagram.lastReceived.payload.stringData || "");
}
export function receivedTime(): number {
return board().radioState.bus.datagram.lastReceived.time;
}
function writePacketToSerial(b: DalBoard, p: PacketBuffer) {
switch(p.payload.type) {
case PacketPayloadType.NUMBER:
b.writeSerial(`{"t":${p.time},"s":${p.serial},"v":${p.payload.numberData}}\r\n`)
break;
case PacketPayloadType.VALUE:
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}","v":${p.payload.numberData}}\r\n`)
break;
case PacketPayloadType.STRING:
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}"}\r\n`)
break;
default:
}
readRawPacket();
}
}

View File

@ -28,11 +28,21 @@ namespace pxsim {
}
}
namespace pxsim.control {
export function __log(s: string) {
board().writeSerial(s + "\r\n");
}
}
namespace pxsim.serial {
export function writeString(s: string) {
board().writeSerial(s);
}
export function writeBuffer(buf: RefBuffer) {
// TODO
}
export function readUntil(del: string): string {
return readString();
}
@ -53,4 +63,18 @@ namespace pxsim.serial {
export function redirectToUSB() {
// TODO
}
export function setRxBufferSize(size: number) {
// TODO
}
export function setTxBufferSize(size: number) {
// TODO
}
export function readBuffer(length: number) {
if (length <= 0)
length = 64;
return pins.createBuffer(length);
}
}