diff --git a/libs/base/_locales/base-jsdoc-strings.json b/libs/base/_locales/base-jsdoc-strings.json
new file mode 100644
index 00000000..e865a90d
--- /dev/null
+++ b/libs/base/_locales/base-jsdoc-strings.json
@@ -0,0 +1,99 @@
+{
+ "Math.abs": "Returns the absolute value of a number (the value without regard to whether it is positive or negative).\nFor example, the absolute value of -5 is the same as the absolute value of 5.",
+ "Math.abs|param|x": "A numeric expression for which the absolute value is needed.",
+ "Math.acos": "Returns the arccosine (in radians) of a number",
+ "Math.acos|param|x": "A number",
+ "Math.asin": "Returns the arcsine (in radians) of a number",
+ "Math.asin|param|x": "A number",
+ "Math.atan": "Returns the arctangent (in radians) of a number",
+ "Math.atan2": "Returns the arctangent of the quotient of its arguments.",
+ "Math.atan2|param|x": "A number",
+ "Math.atan2|param|y": "A number",
+ "Math.atan|param|x": "A number",
+ "Math.ceil": "Returns the smallest number greater than or equal to its numeric argument.",
+ "Math.ceil|param|x": "A numeric expression.",
+ "Math.constrain": "Constrains a number to be within a range",
+ "Math.cos": "Returns the cosine of a number.",
+ "Math.cos|param|x": "An angle in radians",
+ "Math.exp": "Returns returns ``e^x``.",
+ "Math.exp|param|x": "A number",
+ "Math.floor": "Returns the greatest number less than or equal to its numeric argument.",
+ "Math.floor|param|x": "A numeric expression.",
+ "Math.icos": "Returns the cosine of an input angle. This is an 8-bit approximation.",
+ "Math.icos|param|theta": "input angle from 0-255",
+ "Math.idiv": "Returns the value of integer signed 32 bit division of two numbers.",
+ "Math.idiv|param|x": "The first number",
+ "Math.idiv|param|y": "The second number",
+ "Math.imul": "Returns the value of integer signed 32 bit multiplication of two numbers.",
+ "Math.imul|param|x": "The first number",
+ "Math.imul|param|y": "The second number",
+ "Math.isin": "Returns the sine of an input angle. This is an 8-bit approximation.",
+ "Math.isin|param|theta": "input angle from 0-255",
+ "Math.log": "Returns the natural logarithm (base e) of a number.",
+ "Math.log|param|x": "A number",
+ "Math.map": "Re-maps a number from one range to another. That is, a value of ``from low`` would get mapped to ``to low``, a value of ``from high`` to ``to high``, values in-between to values in-between, etc.",
+ "Math.map|param|fromHigh": "the upper bound of the value's current range, eg: 1023",
+ "Math.map|param|fromLow": "the lower bound of the value's current range",
+ "Math.map|param|toHigh": "the upper bound of the value's target range, eg: 4",
+ "Math.map|param|toLow": "the lower bound of the value's target range",
+ "Math.map|param|value": "value to map in ranges",
+ "Math.max": "Returns the larger of two supplied numeric expressions.",
+ "Math.min": "Returns the smaller of two supplied numeric expressions.",
+ "Math.pow": "Returns the value of a base expression taken to a specified power.",
+ "Math.pow|param|x": "The base value of the expression.",
+ "Math.pow|param|y": "The exponent value of the expression.",
+ "Math.random": "Returns a pseudorandom number between 0 and 1.",
+ "Math.randomRange": "Returns a pseudorandom number between min and max included. \nIf both numbers are integral, the result is integral.",
+ "Math.randomRange|param|max": "the upper inclusive bound, eg: 10",
+ "Math.randomRange|param|min": "the lower inclusive bound, eg: 0",
+ "Math.round": "Returns a supplied numeric expression rounded to the nearest number.",
+ "Math.round|param|x": "The value to be rounded to the nearest number.",
+ "Math.sign": "Returns the sign of the x, indicating whether x is positive, negative or zero.",
+ "Math.sign|param|x": "The numeric expression to test",
+ "Math.sin": "Returns the sine of a number.",
+ "Math.sin|param|x": "An angle in radians",
+ "Math.sqrt": "Returns the square root of a number.",
+ "Math.sqrt|param|x": "A numeric expression.",
+ "Math.tan": "Returns the tangent of a number.",
+ "Math.tan|param|x": "An angle in radians",
+ "Math.trunc": "Returns the number with the decimal part truncated.",
+ "Math.trunc|param|x": "A numeric expression.",
+ "String.charAt": "Returns the character at the specified index.",
+ "String.charAt|param|index": "The zero-based index of the desired character.",
+ "String.charCodeAt": "Returns the Unicode value of the character at the specified location.",
+ "String.charCodeAt|param|index": "The zero-based index of the desired character. If there is no character at the specified index, NaN is returned.",
+ "String.compare": "Determines whether relative order of two strings (in ASCII encoding).",
+ "String.compare|param|that": "String to compare to target string",
+ "String.concat": "Returns a string that contains the concatenation of two or more strings.",
+ "String.concat|param|other": "The string to append to the end of the string.",
+ "String.fromCharCode": "Make a string from the given ASCII character code.",
+ "String.isEmpty": "Returns a value indicating if the string is empty",
+ "String.length": "Returns the length of a String object.",
+ "String.substr": "Return substring of the current string.",
+ "String.substr|param|length": "number of characters to extract",
+ "String.substr|param|start": "first character index; can be negative from counting from the end, eg:0",
+ "control": "Program controls and events.",
+ "control.assert": "Display specified error code and stop the program.",
+ "control.deviceSerialNumber": "Derive a unique, consistent serial number of this device from internal data.",
+ "control.millis": "Gets the number of milliseconds elapsed since power on.",
+ "control.onEvent": "Run code when a registered event happens.",
+ "control.onEvent|param|value": "the event value to match",
+ "control.panic": "Display an error code and stop the program.",
+ "control.panic|param|code": "an error number to display. eg: 5",
+ "control.reset": "Reset the device.",
+ "control.runInBackground": "Run other code in the background.",
+ "control.waitForEvent": "Blocks the calling thread until the specified event is raised.",
+ "control.waitMicros": "Block the current fiber for the given microseconds",
+ "control.waitMicros|param|micros": "number of micro-seconds to wait. eg: 4",
+ "loops.forever": "Repeats the code forever in the background. On each iteration, allows other codes to run.",
+ "loops.pause": "Pause for the specified time in milliseconds",
+ "loops.pause|param|ms": "how long to pause for, eg: 100, 200, 500, 1000, 2000",
+ "serial": "Reading and writing data over a serial connection.",
+ "serial.writeBuffer": "Send a buffer across the serial connection.",
+ "serial.writeLine": "Write a line of text to the serial port.",
+ "serial.writeNumber": "Write a number to the serial port.",
+ "serial.writeString": "Write some text to the serial port.",
+ "serial.writeValue": "Write a name:value pair as a line of text to the serial port.",
+ "serial.writeValue|param|name": "name of the value stream, eg: x",
+ "serial.writeValue|param|value": "to write"
+}
\ No newline at end of file
diff --git a/libs/base/_locales/base-strings.json b/libs/base/_locales/base-strings.json
new file mode 100644
index 00000000..258f4397
--- /dev/null
+++ b/libs/base/_locales/base-strings.json
@@ -0,0 +1,36 @@
+{
+ "Math.constrain|block": "constrain %value|between %low|and %high",
+ "Math.map|block": "map %value|from low %fromLow|from high %fromHigh|to low %toLow|to high %toHigh",
+ "Math.randomRange|block": "pick random %min|to %limit",
+ "Math|block": "Math",
+ "String.charAt|block": "char from %this=text|at %pos",
+ "String.compare|block": "compare %this=text| to %that",
+ "String.fromCharCode|block": "text from char code %code",
+ "String.length|block": "length of %VALUE",
+ "String.substr|block": "substring of %this=text|from %start|of length %length",
+ "String|block": "String",
+ "control.deviceSerialNumber|block": "device serial number",
+ "control.millis|block": "millis (ms)",
+ "control.onEvent|block": "on event|from %src|with value %value",
+ "control.panic|block": "panic %code",
+ "control.reset|block": "reset",
+ "control.runInBackground|block": "run in background",
+ "control.waitForEvent|block": "wait for event|from %src|with value %value",
+ "control.waitMicros|block": "wait (µs)%micros",
+ "control|block": "control",
+ "loops.forever|block": "forever",
+ "loops.pause|block": "pause (ms) %pause",
+ "loops|block": "loops",
+ "serial.writeBuffer|block": "serial|write buffer %buffer",
+ "serial.writeLine|block": "serial|write line %text",
+ "serial.writeNumber|block": "serial|write number %value",
+ "serial.writeString|block": "serial|write string %text",
+ "serial.writeValue|block": "serial|write value %name|= %value",
+ "serial|block": "serial",
+ "{id:category}Control": "Control",
+ "{id:category}Loops": "Loops",
+ "{id:category}Math": "Math",
+ "{id:category}Serial": "Serial",
+ "{id:category}String": "String",
+ "{id:category}Text": "Text"
+}
\ No newline at end of file
diff --git a/libs/core/_locales/core-jsdoc-strings.json b/libs/core/_locales/core-jsdoc-strings.json
new file mode 100644
index 00000000..ee6004f7
--- /dev/null
+++ b/libs/core/_locales/core-jsdoc-strings.json
@@ -0,0 +1,30 @@
+{
+ "control": "Program controls and events.",
+ "control.allocateNotifyEvent": "Allocates the next user notification event",
+ "control.deviceFirmwareVersion": "Determine the version of system software currently running.",
+ "control.dmesg": "Write data to DMESG debugging buffer.",
+ "control.mmap": "Create new file mapping in memory",
+ "control.raiseEvent": "Announce that an event happened to registered handlers.",
+ "control.raiseEvent|param|src": "ID of the Component that generated the event",
+ "control.raiseEvent|param|value": "Component specific code indicating the cause of the event.",
+ "input.Button": "Generic button class, for device buttons and sensors.",
+ "input.buttonDown": "Down button.",
+ "input.buttonEnter": "Enter button.",
+ "input.buttonLeft": "Left button.",
+ "input.buttonRight": "Right button.",
+ "input.buttonUp": "Up button.",
+ "input.remoteBottomLeft": "Remote bottom-left button.",
+ "input.remoteBottomRight": "Remote bottom-right button.",
+ "input.remoteCenter": "Remote beacon (center) button.",
+ "input.remoteTopLeft": "Remote top-left button.",
+ "input.remoteTopRight": "Remote top-right button.",
+ "output.createBuffer": "Create a new zero-initialized buffer.",
+ "output.createBuffer|param|size": "number of bytes in the buffer",
+ "output.setLights": "Set lights.",
+ "screen.clear": "Clear screen and reset font to normal.",
+ "screen.drawText": "Draw text.",
+ "screen.scroll": "Scroll screen vertically.",
+ "screen.setFont": "Set font for drawText()",
+ "serial": "Reading and writing data over a serial connection.",
+ "serial.writeDmesg": "Send DMESG debug buffer over serial."
+}
\ No newline at end of file
diff --git a/libs/core/_locales/core-strings.json b/libs/core/_locales/core-strings.json
new file mode 100644
index 00000000..5744a682
--- /dev/null
+++ b/libs/core/_locales/core-strings.json
@@ -0,0 +1,28 @@
+{
+ "ButtonEvent.Click|block": "click",
+ "ButtonEvent.Down|block": "down",
+ "ButtonEvent.LongClick|block": "long click",
+ "ButtonEvent.Up|block": "up",
+ "control.raiseEvent|block": "raise event|from %src|with value value",
+ "control|block": "control",
+ "input.buttonDown|block": "button down",
+ "input.buttonEnter|block": "button enter",
+ "input.buttonLeft|block": "button left",
+ "input.buttonRight|block": "button right",
+ "input.buttonUp|block": "button up",
+ "input.remoteBottomLeft|block": "remote bottom-left",
+ "input.remoteBottomRight|block": "remote bottom-right",
+ "input.remoteCenter|block": "remote center",
+ "input.remoteTopLeft|block": "remote top-left",
+ "input.remoteTopRight|block": "remote top-right",
+ "input|block": "input",
+ "output.setLights|block": "set lights %pattern",
+ "output|block": "output",
+ "screen|block": "screen",
+ "serial|block": "serial",
+ "{id:category}Control": "Control",
+ "{id:category}Input": "Input",
+ "{id:category}Output": "Output",
+ "{id:category}Screen": "Screen",
+ "{id:category}Serial": "Serial"
+}
\ No newline at end of file
diff --git a/libs/core/sim/analogSensor.ts b/libs/core/sim/analogSensor.ts
new file mode 100644
index 00000000..39efaebb
--- /dev/null
+++ b/libs/core/sim/analogSensor.ts
@@ -0,0 +1,81 @@
+namespace pxsim {
+ enum ThresholdState {
+ High,
+ Low,
+ Normal
+ }
+
+ export class AnalogSensorState {
+ public sensorUsed: boolean = false;
+
+ private level: number;
+ private state = ThresholdState.Normal;
+
+ constructor(public id: number, private min = 0, private max = 255, private lowThreshold = 64, private highThreshold = 192) {
+ this.level = Math.ceil((max - min) / 2);
+ }
+
+ public setUsed() {
+ if (!this.sensorUsed) {
+ this.sensorUsed = true;
+ runtime.queueDisplayUpdate();
+ }
+ }
+
+ public setLevel(level: number) {
+ this.level = this.clampValue(level);
+
+ if (this.level >= this.highThreshold) {
+ this.setState(ThresholdState.High);
+ }
+ else if (this.level <= this.lowThreshold) {
+ this.setState(ThresholdState.Low);
+ }
+ else {
+ this.setState(ThresholdState.Normal);
+ }
+ }
+
+ public getLevel(): number {
+ return this.level;
+ }
+
+ public setLowThreshold(value: number) {
+ this.lowThreshold = this.clampValue(value);
+ this.highThreshold = Math.max(this.lowThreshold + 1, this.highThreshold);
+ }
+
+ public setHighThreshold(value: number) {
+ this.highThreshold = this.clampValue(value);
+ this.lowThreshold = Math.min(this.highThreshold - 1, this.lowThreshold);
+ }
+
+ private clampValue(value: number) {
+ if (value < this.min) {
+ return this.min;
+ }
+ else if (value > this.max) {
+ return this.max;
+ }
+ return value;
+ }
+
+ private setState(state: ThresholdState) {
+ if (this.state === state) {
+ return;
+ }
+
+ this.state = state;
+ switch (state) {
+ case ThresholdState.High:
+ board().bus.queue(this.id, DAL.ANALOG_THRESHOLD_HIGH);
+ break;
+ case ThresholdState.Low:
+ board().bus.queue(this.id, DAL.ANALOG_THRESHOLD_LOW);
+ break;
+ case ThresholdState.Normal:
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/core/sim/pins.ts b/libs/core/sim/pins.ts
new file mode 100644
index 00000000..2ba0961c
--- /dev/null
+++ b/libs/core/sim/pins.ts
@@ -0,0 +1,177 @@
+
+namespace pxsim.pins {
+ export class CommonPin extends Pin {
+ used: boolean;
+ }
+
+ export class DigitalPin extends CommonPin {
+ }
+
+ export class AnalogPin extends CommonPin {
+
+ }
+
+ export function markUsed(name: CommonPin) {
+ if (!name.used) {
+ name.used = true;
+ runtime.queueDisplayUpdate();
+ }
+ }
+}
+
+namespace pxsim.DigitalPinMethods {
+ export function digitalRead(name: pins.DigitalPin): number {
+ return name.digitalReadPin();
+ }
+
+ /**
+ * Set a pin or connector value to either 0 or 1.
+ * @param value value to set on the pin, 1 eg,0
+ */
+ export function digitalWrite(name: pins.DigitalPin, value: number): void {
+ name.digitalWritePin(value);
+ }
+
+ /**
+ * Configures this pin to a digital input, and generates events where the timestamp is the duration
+ * that this pin was either ``high`` or ``low``.
+ */
+ export function onPulsed(name: pins.DigitalPin, pulse: number, body: RefAction): void {
+ // TODO
+ }
+
+ /**
+ * Returns the duration of a pulse in microseconds
+ * @param value the value of the pulse (default high)
+ * @param maximum duration in micro-seconds
+ */
+ export function pulseIn(name: pins.DigitalPin, pulse: number, maxDuration = 2000000): number {
+ // TODO
+ return 500;
+ }
+
+ /**
+ * Configures the pull of this pin.
+ * @param pull one of the mbed pull configurations: PullUp, PullDown, PullNone
+ */
+ export function setPull(name: pins.DigitalPin, pull: number): void {
+ name.setPull(pull);
+ }
+
+ /**
+ * Do something when a pin is pressed.
+ * @param body the code to run when the pin is pressed
+ */
+ export function onPressed(name: pins.DigitalPin, body: RefAction): void {
+ }
+
+ /**
+ * Do something when a pin is released.
+ * @param body the code to run when the pin is released
+ */
+ export function onReleased(name: pins.DigitalPin, body: RefAction): void {
+ }
+
+ /**
+ * Get the pin state (pressed or not). Requires to hold the ground to close the circuit.
+ * @param name pin used to detect the touch
+ */
+ export function isPressed(name: pins.DigitalPin): boolean {
+ return name.isTouched();
+ }
+}
+
+namespace pxsim.AnalogPinMethods {
+ /**
+ * Read the connector value as analog, that is, as a value comprised between 0 and 1023.
+ */
+ export function analogRead(name: pins.AnalogPin): number {
+ pins.markUsed(name);
+ return name.analogReadPin();
+ }
+
+ /**
+ * Set the connector value as analog. Value must be comprised between 0 and 1023.
+ * @param value value to write to the pin between ``0`` and ``1023``. eg:1023,0
+ */
+ export function analogWrite(name: pins.AnalogPin, value: number): void {
+ pins.markUsed(name);
+ name.analogWritePin(value);
+
+ }
+
+ /**
+ * Configures the Pulse-width modulation (PWM) of the analog output to the given value in
+ * **microseconds** or `1/1000` milliseconds.
+ * If this pin is not configured as an analog output (using `analog write pin`), the operation has
+ * no effect.
+ * @param micros period in micro seconds. eg:20000
+ */
+ export function analogSetPeriod(name: pins.AnalogPin, micros: number): void {
+ pins.markUsed(name);
+ name.analogSetPeriod(micros);
+ }
+
+ /**
+ * Writes a value to the servo, controlling the shaft accordingly. On a standard servo, this will
+ * set the angle of the shaft (in degrees), moving the shaft to that orientation. On a continuous
+ * rotation servo, this will set the speed of the servo (with ``0`` being full-speed in one
+ * direction, ``180`` being full speed in the other, and a value near ``90`` being no movement).
+ * @param value angle or rotation speed, eg:180,90,0
+ */
+ export function servoWrite(name: pins.AnalogPin, value: number): void {
+ pins.markUsed(name);
+ name.servoWritePin(value);
+ }
+
+ /**
+ * Configures this IO pin as an analog/pwm output, configures the period to be 20 ms, and sets the
+ * pulse width, based on the value it is given **microseconds** or `1/1000` milliseconds.
+ * @param micros pulse duration in micro seconds, eg:1500
+ */
+ export function servoSetPulse(name: pins.AnalogPin, micros: number): void {
+ pins.markUsed(name);
+ // TODO fix pxt
+ // name.servoSetPulse(micros);
+ }
+}
+
+namespace pxsim.PwmPinMethods {
+ export function analogSetPeriod(name: pins.AnalogPin, micros: number): void {
+ name.analogSetPeriod(micros);
+ }
+
+ export function servoWrite(name: pins.AnalogPin, value: number): void {
+ name.servoWritePin(value);
+ }
+
+ export function servoSetPulse(name: pins.AnalogPin, micros: number): void {
+ name.servoSetPulse(name.id, micros);
+ }
+}
+
+namespace pxsim.pins {
+ export function pulseDuration(): number {
+ // bus last event timestamp
+ return 500;
+ }
+
+ export function createBuffer(sz: number) {
+ return pxsim.BufferMethods.createBuffer(sz)
+ }
+
+ export function spiWrite(value: number): number {
+ // TODO
+ return 0;
+ }
+
+ export function i2cReadBuffer(address: number, size: number, repeat?: boolean): RefBuffer {
+ // fake reading zeros
+ return createBuffer(size)
+ }
+
+ export function i2cWriteBuffer(address: number, buf: RefBuffer, repeat?: boolean): void {
+ // fake - noop
+ }
+}
+
diff --git a/libs/ev3/_locales/ev3-jsdoc-strings.json b/libs/ev3/_locales/ev3-jsdoc-strings.json
new file mode 100644
index 00000000..9e26dfee
--- /dev/null
+++ b/libs/ev3/_locales/ev3-jsdoc-strings.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/libs/ev3/_locales/ev3-strings.json b/libs/ev3/_locales/ev3-strings.json
new file mode 100644
index 00000000..9e26dfee
--- /dev/null
+++ b/libs/ev3/_locales/ev3-strings.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/libs/music/_locales/music-jsdoc-strings.json b/libs/music/_locales/music-jsdoc-strings.json
new file mode 100644
index 00000000..2ea11296
--- /dev/null
+++ b/libs/music/_locales/music-jsdoc-strings.json
@@ -0,0 +1,28 @@
+{
+ "music": "Generation of music tones.",
+ "music.beat": "Return the duration of a beat in milliseconds (the beat fraction).",
+ "music.beat|param|fraction": "the fraction of the current whole note, eg: BeatFraction.Half",
+ "music.changeTempoBy": "Change the tempo up or down by some amount of beats per minute (bpm).",
+ "music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20",
+ "music.noteFrequency": "Get the frequency of a note.",
+ "music.noteFrequency|param|name": "the note name, eg: Note.C",
+ "music.playSound": "Start playing a sound and don't wait for it to finish.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
+ "music.playSoundUntilDone": "Play a sound and wait until the sound is done.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
+ "music.playSoundUntilDone|param|sound": "the melody to play, eg: 'g5:1'",
+ "music.playSound|param|sound": "the melody to play, eg: 'g5:1'",
+ "music.playTone": "Play a tone through the speaker for some amount of time.",
+ "music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
+ "music.playTone|param|ms": "tone duration in milliseconds (ms)",
+ "music.rest": "Rest, or play silence, for some time (in milleseconds).",
+ "music.rest|param|ms": "rest duration in milliseconds (ms)",
+ "music.ringTone": "Play a tone.",
+ "music.ringTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
+ "music.setTempo": "Set the tempo a number of beats per minute (bpm).",
+ "music.setTempo|param|bpm": "The new tempo in beats per minute, eg: 120",
+ "music.setVolume": "Set the output volume of the sound synthesizer.",
+ "music.setVolume|param|volume": "the volume 0...256, eg: 128",
+ "music.sounds": "Get the melody string for a built-in melody.",
+ "music.sounds|param|name": "the note name, eg: Note.C",
+ "music.stopAllSounds": "Stop all sounds from playing.",
+ "music.tempo": "Return the tempo in beats per minute (bpm).\nTempo is the speed (bpm = beats per minute) at which notes play. The larger the tempo value, the faster the notes will play."
+}
\ No newline at end of file
diff --git a/libs/music/_locales/music-strings.json b/libs/music/_locales/music-strings.json
new file mode 100644
index 00000000..ee0d0e9d
--- /dev/null
+++ b/libs/music/_locales/music-strings.json
@@ -0,0 +1,46 @@
+{
+ "BeatFraction.Breve|block": "4",
+ "BeatFraction.Double|block": "2",
+ "BeatFraction.Eighth|block": "1/8",
+ "BeatFraction.Half|block": "1/2",
+ "BeatFraction.Quarter|block": "1/4",
+ "BeatFraction.Sixteenth|block": "1/16",
+ "BeatFraction.Whole|block": "1",
+ "Note.CSharp3|block": "C#3",
+ "Note.CSharp4|block": "C#4",
+ "Note.CSharp5|block": "C#5",
+ "Note.CSharp|block": "C#",
+ "Note.FSharp3|block": "F#3",
+ "Note.FSharp4|block": "F#4",
+ "Note.FSharp5|block": "F#5",
+ "Note.FSharp|block": "F#",
+ "Note.GSharp3|block": "G#3",
+ "Note.GSharp4|block": "G#4",
+ "Note.GSharp5|block": "G#5",
+ "Note.GSharp|block": "G#",
+ "SoundOutputDestination.Pin|block": "pin",
+ "SoundOutputDestination.Speaker|block": "speaker",
+ "Sounds.BaDing|block": "ba ding",
+ "Sounds.JumpDown|block": "jump down",
+ "Sounds.JumpUp|block": "jump up",
+ "Sounds.MagicWand|block": "magic wand",
+ "Sounds.PowerDown|block": "power down",
+ "Sounds.PowerUp|block": "power up",
+ "Sounds.Siren|block": "siren",
+ "Sounds.Wawawawaa|block": "wawawawaa",
+ "music.beat|block": "%fraction|beat",
+ "music.changeTempoBy|block": "change tempo by (bpm)|%value",
+ "music.noteFrequency|block": "%note",
+ "music.playSoundUntilDone|block": "play sound %sound=music_sounds|until done",
+ "music.playSound|block": "play sound %sound=music_sounds",
+ "music.playTone|block": "play tone|at %note=device_note|for %duration=device_beat",
+ "music.rest|block": "rest|for %duration=device_beat",
+ "music.ringTone|block": "ring tone|at %note=device_note",
+ "music.setTempo|block": "set tempo to (bpm)|%value",
+ "music.setVolume|block": "set volume %volume",
+ "music.sounds|block": "%name",
+ "music.stopAllSounds|block": "stop all sounds",
+ "music.tempo|block": "tempo (bpm)",
+ "music|block": "music",
+ "{id:category}Music": "Music"
+}
\ No newline at end of file
diff --git a/pxtarget.json b/pxtarget.json
index 6b9f1792..250107a9 100644
--- a/pxtarget.json
+++ b/pxtarget.json
@@ -13,14 +13,14 @@
"libs/ev3"
],
"simulator": {
- "headless": true
- },
- "DISABLED-simulator": {
"autoRun": true,
"streams": true,
- "aspectRatio": 1.0,
+ "aspectRatio": 0.67,
"parts": false,
- "enableTrace": true
+ "enableTrace": true,
+ "boardDefinition": {
+ "visual": "ev3"
+ }
},
"cloud": {
"workspace": false,
@@ -118,8 +118,6 @@
"invertedMonaco": false,
"monacoToolbox": true,
"invertedToolbox": true,
- "simAnimationEnter": "roll in",
- "simAnimationExit": "roll out",
"exampleGallery": "examples",
"hasAudio": true,
"usbHelp": [],
diff --git a/sim/dalboard.ts b/sim/dalboard.ts
new file mode 100644
index 00000000..726d002b
--- /dev/null
+++ b/sim/dalboard.ts
@@ -0,0 +1,197 @@
+///
+///
+///
+
+namespace pxsim {
+ export enum CPlayPinName {
+ A0,
+ A1,
+ A2,
+ A3,
+ A4,
+ A5,
+ A6,
+ A7,
+ A8,
+ A9,
+ D4,
+ D5,
+ D6,
+ D7,
+ D8,
+ D13
+ }
+
+ export class DalBoard extends CoreBoard implements
+ AccelerometerBoard,
+ CommonBoard,
+ LightBoard,
+ LightSensorBoard,
+ MicrophoneBoard,
+ MusicBoard,
+ SlideSwitchBoard,
+ TemperatureBoard,
+ InfraredBoard,
+ CapTouchBoard {
+ // state & update logic for component services
+ neopixelState: CommonNeoPixelState;
+ buttonState: EV3ButtonState;
+ slideSwitchState: SlideSwitchState;
+ lightSensorState: AnalogSensorState;
+ thermometerState: AnalogSensorState;
+ thermometerUnitState: number;
+ microphoneState: AnalogSensorState;
+ edgeConnectorState: EdgeConnectorState;
+ capacitiveSensorState: CapacitiveSensorState;
+ accelerometerState: AccelerometerState;
+ audioState: AudioState;
+ touchButtonState: TouchButtonState;
+ irState: InfraredState;
+ lightState: EV3LightState;
+
+ view: SVGSVGElement;
+
+ constructor() {
+ super()
+
+ this.bus.setNotify(DAL.DEVICE_ID_NOTIFY, DAL.DEVICE_ID_NOTIFY_ONE);
+
+ //components
+
+ this.builtinParts["buttons"] = this.buttonState = new EV3ButtonState();
+ this.builtinParts["light"] = this.lightState = new EV3LightState();
+ /*this.builtinParts["neopixel"] = this.neopixelState = new CommonNeoPixelState();
+ this.builtinParts["buttonpair"] = this.buttonState = new CommonButtonState();
+
+ this.builtinParts["switch"] = this.slideSwitchState = new SlideSwitchState();
+ this.builtinParts["audio"] = this.audioState = new AudioState();
+ this.builtinParts["lightsensor"] = this.lightSensorState = new AnalogSensorState(DAL.DEVICE_ID_LIGHT_SENSOR, 0, 255);
+ this.builtinParts["thermometer"] = this.thermometerState = new AnalogSensorState(DAL.DEVICE_ID_THERMOMETER, -5, 50);
+ this.builtinParts["soundsensor"] = this.microphoneState = new AnalogSensorState(DAL.DEVICE_ID_TOUCH_SENSOR + 1, 0, 255);
+ this.builtinParts["capacitivesensor"] = this.capacitiveSensorState = new CapacitiveSensorState({
+ 0: 0,
+ 1: 1,
+ 2: 2,
+ 3: 3,
+ 6: 4,
+ 9: 5,
+ 10: 6,
+ 12: 7
+ });
+
+ this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
+ this.builtinParts["edgeconnector"] = this.edgeConnectorState = new EdgeConnectorState({
+ pins: [
+ pxsim.CPlayPinName.A0,
+ pxsim.CPlayPinName.A1,
+ pxsim.CPlayPinName.A2,
+ pxsim.CPlayPinName.A3,
+ pxsim.CPlayPinName.A4,
+ pxsim.CPlayPinName.A5,
+ pxsim.CPlayPinName.A6,
+ pxsim.CPlayPinName.A7,
+ pxsim.CPlayPinName.A8,
+ pxsim.CPlayPinName.A9,
+ pxsim.CPlayPinName.D4,
+ pxsim.CPlayPinName.D5,
+ pxsim.CPlayPinName.D6,
+ pxsim.CPlayPinName.D7,
+ pxsim.CPlayPinName.D8,
+ pxsim.CPlayPinName.D13
+ ]
+ });
+ this.builtinParts["microservo"] = this.edgeConnectorState;
+
+ this.builtinVisuals["microservo"] = () => new visuals.MicroServoView();
+ this.builtinPartVisuals["microservo"] = (xy: visuals.Coord) => visuals.mkMicroServoPart(xy);
+ this.touchButtonState = new TouchButtonState([
+ pxsim.CPlayPinName.A1,
+ pxsim.CPlayPinName.A2,
+ pxsim.CPlayPinName.A3,
+ pxsim.CPlayPinName.A4,
+ pxsim.CPlayPinName.A5,
+ pxsim.CPlayPinName.A6,
+ pxsim.CPlayPinName.A7
+ ]);
+
+ this.builtinParts["ir"] = this.irState = new InfraredState();*/
+ }
+
+ receiveMessage(msg: SimulatorMessage) {
+ if (!runtime || runtime.dead) return;
+
+ switch (msg.type || "") {
+ case "eventbus": {
+ let ev = msg;
+ this.bus.queue(ev.id, ev.eventid, ev.value);
+ break;
+ }
+ case "serial": {
+ let data = (msg).data || "";
+ // TODO
+ break;
+ }
+ case "irpacket": {
+ let ev = msg;
+ this.irState.receive(new RefBuffer(ev.packet));
+ break;
+ }
+ }
+ }
+
+ initAsync(msg: SimulatorRunMessage): Promise {
+ super.initAsync(msg);
+
+ const options = (msg.options || {}) as pxt.RuntimeOptions;
+
+ const boardDef = msg.boardDefinition;
+ const cmpsList = msg.parts;
+ const cmpDefs = msg.partDefinitions || {};
+ const fnArgs = msg.fnArgs;
+
+ const opts: visuals.BoardHostOpts = {
+ state: this,
+ boardDef: boardDef,
+ partsList: cmpsList,
+ partDefs: cmpDefs,
+ fnArgs: fnArgs,
+ maxWidth: "100%",
+ maxHeight: "100%",
+ };
+ const viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
+ visual: boardDef.visual
+ }), opts);
+
+ document.body.innerHTML = ""; // clear children
+ document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement);
+
+ return Promise.resolve();
+ }
+
+ screenshot(): string {
+ return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
+ }
+
+ defaultNeopixelPin() {
+ return this.edgeConnectorState.getPin(CPlayPinName.D8);
+ }
+
+ getDefaultPitchPin() {
+ return this.edgeConnectorState.getPin(CPlayPinName.D6);
+ }
+ }
+
+ export function initRuntimeWithDalBoard() {
+ U.assert(!runtime.board);
+ let b = new DalBoard();
+ runtime.board = b;
+ runtime.postError = (e) => {
+ // TODO
+ runtime.updateDisplay();
+ }
+ }
+
+ if (!pxsim.initCurrentRuntime) {
+ pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
+ }
+}
\ No newline at end of file
diff --git a/sim/instructions.ts b/sim/instructions.ts
new file mode 100644
index 00000000..e3fb8a76
--- /dev/null
+++ b/sim/instructions.ts
@@ -0,0 +1,92 @@
+///
+///
+///
+
+//HACK: allows instructions.html to access pxtblocks without requiring simulator.html to import blocks as well
+if (!(window).pxt) (window).pxt = {};
+import pxtrunner = pxt.runner;
+import pxtdocs = pxt.docs;
+
+namespace pxsim.instructions {
+ export function drawInstructions() {
+ pxsim.visuals.mkBoardView = (opts: pxsim.visuals.BoardViewOptions): pxsim.visuals.BoardView => {
+ return new visuals.EV3BoardSvg({
+ runtime: runtime,
+ theme: visuals.randomTheme(),
+ disableTilt: false,
+ wireframe: opts.wireframe,
+ });
+ }
+
+ let getQsVal = parseQueryString();
+
+ //project name
+ let name = getQsVal("name") || "Untitled";
+
+ // board def
+ const boardDef = JSON.parse(getQsVal("board")) as pxsim.BoardDefinition;
+
+ //parts list
+ let parts = (getQsVal("parts") || "").split(" ");
+ parts.sort();
+
+ // parts definitions
+ let partDefinitions = JSON.parse(getQsVal("partdefs") || "{}") as pxsim.Map
+
+ //fn args
+ let fnArgs = JSON.parse((getQsVal("fnArgs") || "{}"));
+
+ //project code
+ let tsCode = getQsVal("code");
+ let tsPackage = getQsVal("package") || "";
+ let codeSpinnerDiv = document.getElementById("proj-code-spinner");
+ let codeContainerDiv = document.getElementById("proj-code-container");
+ if (tsCode) {
+ //we use the docs renderer to decompile the code to blocks and render it
+ //TODO: render the blocks code directly
+ let md =
+ `\`\`\`blocks
+${tsCode}
+\`\`\`
+\`\`\`package
+${tsPackage}
+\`\`\`
+`
+
+ pxtdocs.requireMarked = function () { return (window).marked; }
+ pxtrunner.renderMarkdownAsync(codeContainerDiv, md)
+ .done(function () {
+ let codeSvg = $("#proj-code-container svg");
+ if (codeSvg.length > 0) {
+ //code rendered successfully as blocks
+ codeSvg.css("width", "inherit");
+ codeSvg.css("height", "inherit");
+ //takes the svg out of the wrapper markdown
+ codeContainerDiv.innerHTML = "";
+ codeContainerDiv.appendChild(codeSvg[0]);
+ } else {
+ //code failed to convert to blocks, display as typescript instead
+ codeContainerDiv.innerText = tsCode;
+ }
+ $(codeContainerDiv).show();
+ $(codeSpinnerDiv).hide();
+ });
+ }
+
+
+ if (name)
+ $("#proj-title").text(name);
+
+ //init runtime
+ if (!pxsim.initCurrentRuntime)
+ pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
+
+ renderParts({
+ name,
+ boardDef,
+ parts,
+ partDefinitions,
+ fnArgs
+ })
+ }
+}
\ No newline at end of file
diff --git a/sim/public/parts/.gitignore b/sim/public/parts/.gitignore
new file mode 100644
index 00000000..74a7a76e
--- /dev/null
+++ b/sim/public/parts/.gitignore
@@ -0,0 +1,5 @@
+# don't check in until OSS request is approved
+sparkfun-*
+raspberrypi-*
+arduino-*
+max6675*
diff --git a/sim/public/sim.manifest b/sim/public/sim.manifest
new file mode 100644
index 00000000..bfa592ca
--- /dev/null
+++ b/sim/public/sim.manifest
@@ -0,0 +1,10 @@
+CACHE MANIFEST
+
+CACHE:
+/cdn/bluebird.min.js
+/cdn/pxtsim.js
+/sim/common-sim.js
+/sim/sim.js
+
+NETWORK:
+*
diff --git a/sim/public/siminstructions.html b/sim/public/siminstructions.html
new file mode 100644
index 00000000..e5fbdd60
--- /dev/null
+++ b/sim/public/siminstructions.html
@@ -0,0 +1,201 @@
+
+
+
+
+
+ Assembly Instructions
+
+
+
+
+
+