2.1.28, initiation update to PXT v5.28.24 (#54)
This commit is contained in:
		
				
					committed by
					
						
						Peli de Halleux
					
				
			
			
				
	
			
			
			
						parent
						
							38a964516e
						
					
				
				
					commit
					5c114a0c57
				
			@@ -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
									
								
							
							
						
						
									
										201
									
								
								sim/state/buttonpairsim.ts
									
									
									
									
									
										Normal 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);
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										89
									
								
								sim/state/edgeconnectorsim.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								sim/state/edgeconnectorsim.ts
									
									
									
									
									
										Normal 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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
									
								
							
							
						
						
									
										83
									
								
								sim/state/microservo.ts
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										6
									
								
								sim/state/midi.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
namespace pxsim.control {
 | 
			
		||||
    export function __midiSend(data: RefBuffer) {
 | 
			
		||||
        const b = board();
 | 
			
		||||
        pxsim.AudioContextManager.sendMidiMessage(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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) { }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user