Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
4dfb77fcd7 | |||
70deffb665 | |||
41a148de28 | |||
886e071d7f | |||
8a58d664c3 | |||
71d1155f21 | |||
167c1d8fce | |||
e59ae37954 | |||
72a621ec8b | |||
d6ff930333 | |||
54a7ac81ea | |||
e5d985dbf1 | |||
9db91d89d6 | |||
1fa9bf12d5 | |||
61bab257eb | |||
2e90b351da | |||
801bd6c7a0 | |||
777ba40899 | |||
0d11c16ecf | |||
9c1628b977 | |||
953b362b34 | |||
8b40850a94 | |||
fc3a02cc41 | |||
80f2ff6757 | |||
6f790d167c | |||
ac7502074a | |||
50473255a8 | |||
910772d54e |
@ -19,10 +19,3 @@
|
||||
<link rel="shortcut icon" href="@appLogo@">
|
||||
<meta name="theme-color" content="@accentColor@">
|
||||
|
||||
<style>
|
||||
#root .avatar .avatar-image {
|
||||
background-image: url(https://az851932.vo.msecnd.net/pub/jovrytni/microbit.simplified.svg);
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
</style>
|
5
docfiles/target.css
Normal file
@ -0,0 +1,5 @@
|
||||
#root .avatar .avatar-image {
|
||||
background-image: url(https://az851932.vo.msecnd.net/pub/jovrytni/microbit.simplified.svg);
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
@ -1,3 +1,50 @@
|
||||

|
||||
|
||||
# About
|
||||
|
||||
### @description A Blocks / Javascript code editor for the micro:bit, a pocket-size computer with 5x5 display, sensors and Bluetooth.
|
||||
|
||||
The [BBC micro:bit](https://www.microbit.co.uk) is a [pocket-size computer](/device) with a 5x5 display of 25 LEDs, Bluetooth and sensors that can be programmed by anyone.
|
||||
The BBC micro:bit was made possible by many [partners](https://www.microbit.co.uk/partners).
|
||||
|
||||
The micro:bit provides an easy and fun introduction to programming and making – switch on, program it to do something fun – wear it, customize it.
|
||||
Just like Arduino, the micro:bit can be connected to and interact with sensors, displays, and other devices.
|
||||
|
||||
* [Read the docs](/docs)
|
||||
|
||||
## [Hardware: The Device](/device)
|
||||
|
||||
The BBC micro:bit is packaged with sensors, radio and other goodies. Learn about the [hardware components](/device) of the micro:bit to make the most of it!
|
||||
|
||||
## Programming: [Blocks](/blocks) or [JavaScript](/javascript)
|
||||
|
||||
You can program the micro:bit using [Blocks](/blocks) or [JavaScript](/javascript) in your web browser via the [micro:bit APIs](/reference):
|
||||
|
||||
```block
|
||||
input.onButtonPressed(Button.A, () => {
|
||||
basic.showString("Hi!");
|
||||
})
|
||||
```
|
||||
```typescript
|
||||
input.onButtonPressed(Button.A, () => {
|
||||
basic.showString("Hi!");
|
||||
})
|
||||
```
|
||||
|
||||
The editor work in [most modern browsers](/browsers), work [offline](/offline) once loaded and do not require any installation.
|
||||
|
||||
## [Compile and Flash: Your Program!](/device/usb)
|
||||
|
||||
When you have your code ready, you connect your micro:bit to a computer via a USB cable, so it appears as a mounted drive (named MICROBIT).
|
||||
|
||||
Compilation to ARM thumb machine code from [Blocks](/blocks) or [JavaScript](/javascript) happens in the browser. You save the ARM binary
|
||||
program to a file, which you then copy to the micro:bit drive, which flashes the micro:bit device with the new program.
|
||||
|
||||
## Simulator: Test Your Code
|
||||
|
||||
You can run your code using the micro:bit simulator, all within the confines of a web browser.
|
||||
The simulator has support for the LED screen, buttons, as well as compass, accelerometer, and digital I/O pins.
|
||||
|
||||
```sim
|
||||
basic.forever(() => {
|
||||
basic.showString("Hi!");
|
||||
@ -20,43 +67,8 @@ input.onButtonPressed(Button.B, () => {
|
||||
. # . # .
|
||||
. . # . .`);
|
||||
});
|
||||
```
|
||||
# About
|
||||
|
||||
### @description A Blocks / Javascript code editor for the micro:bit, a pocket-size computer with 5x5 display, sensors and Bluetooth.
|
||||
|
||||
The [BBC micro:bit](https://www.microbit.co.uk) is a [pocket-size computer](/device) with a 5x5 display of 25 LEDs, Bluetooth and sensors that can be programmed by anyone.
|
||||
The BBC micro:bit was made possible by many [partners](https://www.microbit.co.uk/partners).
|
||||
|
||||
The micro:bit provides an easy and fun introduction to programming and making – switch on, program it to do something fun – wear it, customize it.
|
||||
Just like Arduino, the micro:bit can be connected to and interact with sensors, displays, and other devices.
|
||||
|
||||
## Hardware: The Device
|
||||
|
||||
Learn about the [hardware components](/device) of the micro:bit to make the most of it!
|
||||
|
||||
## Programming: Blocks or JavaScript
|
||||
|
||||
You can program the micro:bit using [Blocks](/blocks) or [JavaScript](/javascript), via the [micro:bit APIs](/reference):
|
||||
|
||||
```blocks
|
||||
input.onButtonPressed(Button.A, () => {
|
||||
basic.showString("Hi!");
|
||||
})
|
||||
```
|
||||
|
||||
## Compile and Flash: Your Program!
|
||||
|
||||
When you have your code ready, you connect your micro:bit to a computer via a USB cable, so it appears as a mounted drive (named MICROBIT).
|
||||
|
||||
Compilation to ARM thumb machine code from [Blocks](/blocks) or [JavaScript](/javascript) happens in the browser. You save the ARM binary
|
||||
program to a file, which you then copy to the micro:bit drive, which flashes the micro:bit device with the new program.
|
||||
|
||||
## Simulator: Test Your Code
|
||||
|
||||
You can run your code using the micro:bit simulator, all within the confines of a web browser.
|
||||
The simulator has support for the LED screen, buttons, as well as compass, accelerometer, and digital I/O pins.
|
||||
|
||||
## C++ Runtime
|
||||
|
||||
The [C++ micro:bit runtime](http://lancaster-university.github.io/microbit-docs/), created at [Lancaster University](http://www.lancaster.ac.uk/), provides access to the hardware functions of the micro:bit,
|
||||
@ -65,6 +77,15 @@ as well as a set of helper functions (such as displaying a number/image/string o
|
||||
The [micro:bit library](/reference) mirrors the functions of the C++ library.
|
||||
When code is compiled to ARM machine code, the calls to JavaScript micro:bit functions are replaced with calls to the corresponding C++ functions.
|
||||
|
||||
## Open Source
|
||||
## [Command Line Tools](/cli)
|
||||
|
||||
Looking to use codethemicrobit.com in your favorite editor? Install the [command line tools](/cli) and get rolling!
|
||||
|
||||
## [Packages](/packages)
|
||||
|
||||
Create, edit and distribute your own blocks and JavaScript using [packages](/packages). Packages are hosted on GitHub and may be written
|
||||
using C++, JavaScript and/or ARM thumb.
|
||||
|
||||
## [Open Source](/open-source)
|
||||
|
||||
The code for the micro:bit is [open source](/open-source) on GitHub. Contributors are welcome!
|
||||
|
101
docs/browsers.md
Normal file
@ -0,0 +1,101 @@
|
||||
# Unsupported configuration
|
||||
|
||||
[codethemicrobit.com](https://codethemicrobit.com) doesn't currently support
|
||||
your browser or operating system. The following configurations are supported:
|
||||
|
||||
## Windows
|
||||
|
||||
You need one of these browsers running on Windows 7, Windows 8, Windows 8.1, or
|
||||
Windows 10:
|
||||
|
||||
* Internet Explorer 11
|
||||
* Microsoft Edge
|
||||
* Google Chrome
|
||||
* Mozilla Firefox
|
||||
|
||||
## Mac
|
||||
|
||||
You need one of these browsers running on OS X 10.9 Mavericks, OS X 10.10
|
||||
Yosemite, OS X 10.11 El Capitan, or macOS 10.12 Sierra:
|
||||
|
||||
* Safari
|
||||
* Google Chrome
|
||||
* Mozilla Firefox
|
||||
|
||||
## Linux
|
||||
|
||||
If you're using a Raspberry Pi, please see [the documentation
|
||||
here](/raspberry-pi).
|
||||
|
||||
You need to be running a Linux distribution recent enough to run the most recent
|
||||
version of one of the following:
|
||||
|
||||
* Google Chrome, or Chromium
|
||||
* Mozilla Firefox, Iceweasel, or Seamonkey
|
||||
|
||||
## How to check your OS or browser
|
||||
|
||||
### Windows
|
||||
|
||||
* Click on the Start menu
|
||||
* Type 'System'
|
||||
* Click on the app called 'System'
|
||||
* The version of Windows you are using will be displayed:
|
||||
|
||||

|
||||
|
||||
### Mac
|
||||
|
||||
* Click on the Apple icon in the top left
|
||||
* Click on 'About this Mac'
|
||||
* This window will be displayed:
|
||||
|
||||

|
||||
|
||||
### Internet Explorer
|
||||
|
||||
* Click on the Settings icon in the top right
|
||||
* Click 'About Internet Explorer'
|
||||
* This window will be displayed:
|
||||
|
||||

|
||||
|
||||
### Edge
|
||||
|
||||
Edge automatically updates, so you should always be using the latest version
|
||||
|
||||
* Click on the menu icon in the top right (three dots)
|
||||
* Scroll to the bottom
|
||||
* Information similar to the following will be displayed:
|
||||
|
||||

|
||||
|
||||
### Google Chrome
|
||||
|
||||
Google Chrome automatically updates, so you should always be using the latest version
|
||||
|
||||
* Click on the menu icon in the top right (three dots)
|
||||
* Click Help, and About Google Chrome
|
||||
* Information similar to the following will be displayed:
|
||||
|
||||

|
||||
|
||||
### Firefox
|
||||
|
||||
Firefox automatically updates, so you should always be using the latest version
|
||||
|
||||
* Click on the menu icon in the top right (three horizontal lines)
|
||||
* Click the question mark icon (help button)
|
||||
* Click 'About Firefox'
|
||||
|
||||

|
||||
|
||||
### Safari
|
||||
|
||||
Safari updates when your operating system updates, so if you are using the
|
||||
latest version of OS X then you'll be using the latest version of Safari.
|
||||
|
||||
* Click on the Safari menu in the top left
|
||||
* Click 'About Safari'
|
||||
|
||||

|
6
docs/browsers/linux.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Unsupported configuration
|
||||
|
||||
As you are using Linux, it is recommended that you use Mozilla Firefox or Google
|
||||
Chrome.
|
||||
|
||||
Please see [here](/browsers) for more information.
|
6
docs/browsers/mac.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Unsupported configuration
|
||||
|
||||
As you are using OS X, it is recommended that you use Safari. Alternatively,
|
||||
Google Chrome and Mozilla Firefox are also supported.
|
||||
|
||||
Please see [here](/browsers) for more information.
|
8
docs/browsers/windows.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Unsupported configuration
|
||||
|
||||
As you are using Windows, it is recommended that you use Microsoft Edge. If you
|
||||
are running a version of Windows prior to Windows 10, you can use Internet
|
||||
Explorer 11. Alternatively, Google Chrome and Mozilla Firefox are also
|
||||
supported.
|
||||
|
||||
Please see [here](/browsers) for more information.
|
@ -149,6 +149,10 @@ By copying the script onto the `MICROBIT` drive, you have programmed it into the
|
||||
flash memory on the micro:bit, which means even after you unplug the micro:bit,
|
||||
your program will still run if the micro:bit is powered by battery.
|
||||
|
||||
If you want to save time, you can use the [micro:bit uploader](/uploader) to
|
||||
automatically deploy hex files to your micro:bit. It works on Windows and is
|
||||
compatible with any browser.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
You can’t drag and drop more than one hex file at once onto your micro:bit. If
|
||||
|
BIN
docs/static/configurations/chrome-version.png
vendored
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/static/configurations/edge-version.png
vendored
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
docs/static/configurations/firefox-version.png
vendored
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
docs/static/configurations/ie-version.png
vendored
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/static/configurations/osx-version.png
vendored
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
docs/static/configurations/safari-version.png
vendored
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
docs/static/configurations/windows-version.png
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
docs/static/mb/device/pano.jpg
vendored
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
docs/static/mb/device/usb-generic.jpg
vendored
Normal file
After Width: | Height: | Size: 343 KiB |
BIN
docs/static/mb/device/usb-mac.jpg
vendored
Normal file
After Width: | Height: | Size: 593 KiB |
BIN
docs/static/mb/device/usb-osx-chrome.png
vendored
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 74 KiB |
BIN
docs/static/mb/device/usb-osx-firefox-1.png
vendored
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 201 KiB |
BIN
docs/static/mb/device/usb-windows-chrome.png
vendored
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 54 KiB |
BIN
docs/static/mb/device/usb-windows-edge-1.png
vendored
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 24 KiB |
BIN
docs/static/mb/device/usb-windows-edge-2.png
vendored
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 18 KiB |
BIN
docs/static/mb/device/usb-windows-firefox-1.png
vendored
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.9 KiB |
BIN
docs/static/mb/device/usb-windows-ie11-1.png
vendored
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 10 KiB |
BIN
docs/static/mb/device/usb-windows-ie11-2.png
vendored
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 10 KiB |
BIN
docs/static/mb/device/usb-windows-sendto.jpg
vendored
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 50 KiB |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pxt-microbit",
|
||||
"version": "0.3.88",
|
||||
"version": "0.4.4",
|
||||
"description": "micro:bit target for PXT",
|
||||
"keywords": [
|
||||
"JavaScript",
|
||||
@ -29,6 +29,6 @@
|
||||
"typescript": "^1.8.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"pxt-core": "0.3.102"
|
||||
"pxt-core": "0.4.8"
|
||||
}
|
||||
}
|
||||
|
181
pxtarget.json
@ -87,6 +87,95 @@
|
||||
"bluetooth": true,
|
||||
"thermometer": true,
|
||||
"compass": true
|
||||
},
|
||||
"boardDefinition": {
|
||||
"visual": "microbit",
|
||||
"gpioPinBlocks": [
|
||||
[
|
||||
"P0"
|
||||
],
|
||||
[
|
||||
"P1"
|
||||
],
|
||||
[
|
||||
"P2"
|
||||
],
|
||||
[
|
||||
"P3"
|
||||
],
|
||||
[
|
||||
"P4",
|
||||
"P5",
|
||||
"P6",
|
||||
"P7"
|
||||
],
|
||||
[
|
||||
"P8",
|
||||
"P9",
|
||||
"P10",
|
||||
"P11",
|
||||
"P12"
|
||||
],
|
||||
[
|
||||
"P16"
|
||||
]
|
||||
],
|
||||
"gpioPinMap": {
|
||||
"P0": "P0",
|
||||
"P1": "P1",
|
||||
"P2": "P2",
|
||||
"P3": "P3",
|
||||
"P4": "P4",
|
||||
"P5": "P5",
|
||||
"P6": "P6",
|
||||
"P7": "P7",
|
||||
"P8": "P8",
|
||||
"P9": "P9",
|
||||
"P10": "P10",
|
||||
"P11": "P11",
|
||||
"P12": "P12",
|
||||
"P13": "P13",
|
||||
"P14": "P14",
|
||||
"P15": "P15",
|
||||
"P16": "P16",
|
||||
"P19": "P19",
|
||||
"P20": "P20"
|
||||
},
|
||||
"spiPins": {
|
||||
"MOSI": "P15",
|
||||
"MISO": "P14",
|
||||
"SCK": "P13"
|
||||
},
|
||||
"i2cPins": {
|
||||
"SDA": "P20",
|
||||
"SCL": "P19"
|
||||
},
|
||||
"analogInPins": [
|
||||
"P0",
|
||||
"P1",
|
||||
"P2",
|
||||
"P3",
|
||||
"P10"
|
||||
],
|
||||
"groundPins": [
|
||||
"GND"
|
||||
],
|
||||
"threeVoltPins": [
|
||||
"+3v3"
|
||||
],
|
||||
"attachPowerOnRight": true,
|
||||
"onboardComponents": [
|
||||
"buttonpair",
|
||||
"ledmatrix",
|
||||
"speaker"
|
||||
],
|
||||
"useCrocClips": true,
|
||||
"marginWhenBreadboarding": [
|
||||
0,
|
||||
0,
|
||||
80,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"compileService": {
|
||||
@ -116,6 +205,33 @@
|
||||
"privacyUrl": "https://go.microsoft.com/fwlink/?LinkId=521839",
|
||||
"termsOfUseUrl": "https://go.microsoft.com/fwlink/?LinkID=206977",
|
||||
"githubUrl": "https://github.com/Microsoft/pxt-microbit",
|
||||
"browserSupport": [
|
||||
{
|
||||
"name": "unsupported",
|
||||
"os": "*",
|
||||
"path": "/browsers"
|
||||
},
|
||||
{
|
||||
"name": "unsupported",
|
||||
"os": "mac",
|
||||
"path": "/browsers/mac"
|
||||
},
|
||||
{
|
||||
"name": "unsupported",
|
||||
"os": "linux",
|
||||
"path": "browsers/linux"
|
||||
},
|
||||
{
|
||||
"name": "unsupported",
|
||||
"os": "rpi",
|
||||
"path": "/raspberry-pi"
|
||||
},
|
||||
{
|
||||
"name": "unsupported",
|
||||
"os": "windows",
|
||||
"path": "/browsers/windows"
|
||||
}
|
||||
],
|
||||
"boardName": "BBC micro:bit",
|
||||
"docMenu": [
|
||||
{
|
||||
@ -143,7 +259,70 @@
|
||||
"path": "/device"
|
||||
}
|
||||
],
|
||||
"sideDoc": "getting-started"
|
||||
"sideDoc": "getting-started",
|
||||
"usbDocs": "/device/usb",
|
||||
"usbHelp": [
|
||||
{
|
||||
"name": "connection",
|
||||
"os": "*",
|
||||
"browser": "*",
|
||||
"path": "/static/mb/device/usb-generic.jpg"
|
||||
},
|
||||
{
|
||||
"name": "connection",
|
||||
"os": "mac",
|
||||
"browser": "*",
|
||||
"path": "/static/mb/device/usb-mac.jpg"
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"os": "windows",
|
||||
"browser": "firefox",
|
||||
"path": "/static/mb/device/usb-windows-firefox-1.png"
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"os": "mac",
|
||||
"browser": "firefox",
|
||||
"path": "/static/mb/device/usb-osx-firefox-1.png"
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"os": "mac",
|
||||
"browser": "chrome",
|
||||
"path": "/static/mb/device/usb-osx-chrome.png"
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"os": "windows",
|
||||
"browser": "edge",
|
||||
"path": "/static/mb/device/usb-windows-edge-1.png"
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"os": "windows",
|
||||
"browser": "ie",
|
||||
"path": "/static/mb/device/usb-windows-ie11-1.png"
|
||||
},
|
||||
{
|
||||
"name": "save",
|
||||
"os": "windows",
|
||||
"browser": "chrome",
|
||||
"path": "/static/mb/device/usb-windows-chrome.png"
|
||||
},
|
||||
{
|
||||
"name": "copy",
|
||||
"os": "mac",
|
||||
"browser": "*",
|
||||
"path": "/static/mb/device/usb-osx-dnd.png"
|
||||
},
|
||||
{
|
||||
"name": "copy",
|
||||
"os": "windows",
|
||||
"browser": "*",
|
||||
"path": "/static/mb/device/usb-windows-sendto.jpg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"analytics": {
|
||||
"userVoiceApiKey": "WEkkIGaj1WtJnSUF59iwaA",
|
||||
|
@ -1,12 +1,7 @@
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
namespace pxsim {
|
||||
export class DalBoard extends BaseBoard {
|
||||
id: string;
|
||||
|
||||
// the bus
|
||||
bus: pxsim.EventBus;
|
||||
|
||||
export class DalBoard extends CoreBoard {
|
||||
// state & update logic for component services
|
||||
ledMatrixState: LedMatrixState;
|
||||
edgeConnectorState: EdgeConnectorState;
|
||||
@ -19,31 +14,58 @@ namespace pxsim {
|
||||
radioState: RadioState;
|
||||
neopixelState: NeoPixelState;
|
||||
|
||||
// updates
|
||||
updateSubscribers: (() => void)[];
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.id = "b" + Math_.random(2147483647);
|
||||
this.bus = new pxsim.EventBus(runtime);
|
||||
|
||||
// components
|
||||
this.ledMatrixState = new LedMatrixState(runtime);
|
||||
this.buttonPairState = new ButtonPairState();
|
||||
this.edgeConnectorState = new EdgeConnectorState();
|
||||
this.radioState = new RadioState(runtime);
|
||||
this.accelerometerState = new AccelerometerState(runtime);
|
||||
this.serialState = new SerialState();
|
||||
this.thermometerState = new ThermometerState();
|
||||
this.lightSensorState = new LightSensorState();
|
||||
this.compassState = new CompassState();
|
||||
this.neopixelState = new NeoPixelState();
|
||||
this.builtinParts["ledmatrix"] = this.ledMatrixState = new LedMatrixState(runtime);
|
||||
this.builtinParts["buttonpair"] = this.buttonPairState = new ButtonPairState({
|
||||
ID_BUTTON_A: DAL.MICROBIT_ID_BUTTON_A,
|
||||
ID_BUTTON_B: DAL.MICROBIT_ID_BUTTON_B,
|
||||
ID_BUTTON_AB: DAL.MICROBIT_ID_BUTTON_AB,
|
||||
BUTTON_EVT_UP: DAL.MICROBIT_BUTTON_EVT_UP,
|
||||
BUTTON_EVT_CLICK: DAL.MICROBIT_BUTTON_EVT_CLICK
|
||||
});
|
||||
this.builtinParts["edgeconnector"] = this.edgeConnectorState = new EdgeConnectorState({
|
||||
pins: [
|
||||
DAL.MICROBIT_ID_IO_P0,
|
||||
DAL.MICROBIT_ID_IO_P1,
|
||||
DAL.MICROBIT_ID_IO_P2,
|
||||
DAL.MICROBIT_ID_IO_P3,
|
||||
DAL.MICROBIT_ID_IO_P4,
|
||||
DAL.MICROBIT_ID_IO_P5,
|
||||
DAL.MICROBIT_ID_IO_P6,
|
||||
DAL.MICROBIT_ID_IO_P7,
|
||||
DAL.MICROBIT_ID_IO_P8,
|
||||
DAL.MICROBIT_ID_IO_P9,
|
||||
DAL.MICROBIT_ID_IO_P10,
|
||||
DAL.MICROBIT_ID_IO_P11,
|
||||
DAL.MICROBIT_ID_IO_P12,
|
||||
DAL.MICROBIT_ID_IO_P13,
|
||||
DAL.MICROBIT_ID_IO_P14,
|
||||
DAL.MICROBIT_ID_IO_P15,
|
||||
DAL.MICROBIT_ID_IO_P16,
|
||||
0,
|
||||
0,
|
||||
DAL.MICROBIT_ID_IO_P19,
|
||||
DAL.MICROBIT_ID_IO_P20
|
||||
]
|
||||
});
|
||||
this.builtinParts["radio"] = this.radioState = new RadioState(runtime);
|
||||
this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
|
||||
this.builtinParts["serial"] = this.serialState = new SerialState();
|
||||
this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState();
|
||||
this.builtinParts["lightsensor"] = this.lightSensorState = new LightSensorState();
|
||||
this.builtinParts["compass"] = this.compassState = new CompassState();
|
||||
this.builtinParts["neopixel"] = this.neopixelState = new NeoPixelState();
|
||||
|
||||
// updates
|
||||
this.updateSubscribers = []
|
||||
this.updateView = () => {
|
||||
this.updateSubscribers.forEach(sub => sub());
|
||||
}
|
||||
this.builtinVisuals["buttonpair"] = () => new visuals.ButtonPairView();
|
||||
this.builtinVisuals["ledmatrix"] = () => new visuals.LedMatrixView();
|
||||
this.builtinVisuals["neopixel"] = () => new visuals.NeoPixelView();
|
||||
|
||||
this.builtinPartVisuals["buttonpair"] = (xy: visuals.Coord) => visuals.mkBtnSvg(xy);
|
||||
this.builtinPartVisuals["ledmatrix"] = (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8);
|
||||
this.builtinPartVisuals["neopixel"] = (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy);
|
||||
}
|
||||
|
||||
receiveMessage(msg: SimulatorMessage) {
|
||||
@ -65,23 +87,17 @@ namespace pxsim {
|
||||
}
|
||||
}
|
||||
|
||||
kill() {
|
||||
super.kill();
|
||||
AudioContextManager.stop();
|
||||
}
|
||||
|
||||
initAsync(msg: SimulatorRunMessage): Promise<void> {
|
||||
super.initAsync(msg);
|
||||
|
||||
let options = (msg.options || {}) as RuntimeOptions;
|
||||
const options = (msg.options || {}) as RuntimeOptions;
|
||||
|
||||
let boardDef = CURRENT_BOARD; //TODO: read from pxt.json/pxttarget.json
|
||||
const boardDef = msg.boardDefinition;
|
||||
const cmpsList = msg.parts;
|
||||
const cmpDefs = msg.partDefinitions || {};
|
||||
const fnArgs = msg.fnArgs;
|
||||
|
||||
let cmpsList = msg.parts;
|
||||
let cmpDefs = msg.partDefinitions || {}; //TODO: read from pxt.json/pxttarget.json
|
||||
let fnArgs = msg.fnArgs;
|
||||
|
||||
let viewHost = new visuals.BoardHost({
|
||||
const opts : visuals.BoardHostOpts = {
|
||||
state: this,
|
||||
boardDef: boardDef,
|
||||
partsList: cmpsList,
|
||||
@ -89,7 +105,8 @@ namespace pxsim {
|
||||
fnArgs: fnArgs,
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%",
|
||||
});
|
||||
};
|
||||
const viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView(opts), opts);
|
||||
|
||||
document.body.innerHTML = ""; // clear children
|
||||
document.body.appendChild(viewHost.getView());
|
||||
@ -125,9 +142,9 @@ namespace pxsim {
|
||||
|
||||
if (!pxsim.initCurrentRuntime) {
|
||||
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
|
||||
}
|
||||
}
|
||||
|
||||
export function board() {
|
||||
return runtime.board as DalBoard;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/typings/bluebird/bluebird.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtparts.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
/// <reference path="../libs/microbit/dal.d.ts"/>
|
||||
/// <reference path="./visuals/neopixel.ts"/>
|
||||
|
||||
namespace pxsim {
|
||||
export const MICROBIT_DEF: BoardDefinition = {
|
||||
visual: "microbit",
|
||||
gpioPinBlocks: [
|
||||
["P0"], ["P1"], ["P2"],
|
||||
["P3"],
|
||||
["P4", "P5", "P6", "P7"],
|
||||
["P8", "P9", "P10", "P11", "P12"],
|
||||
["P16"],
|
||||
],
|
||||
gpioPinMap: {
|
||||
"P0": "P0",
|
||||
"P1": "P1",
|
||||
"P2": "P2",
|
||||
"P3": "P3",
|
||||
"P4": "P4",
|
||||
"P5": "P5",
|
||||
"P6": "P6",
|
||||
"P7": "P7",
|
||||
"P8": "P8",
|
||||
"P9": "P9",
|
||||
"P10": "P10",
|
||||
"P11": "P11",
|
||||
"P12": "P12",
|
||||
"P13": "P13",
|
||||
"P14": "P14",
|
||||
"P15": "P15",
|
||||
"P16": "P16",
|
||||
"P19": "P19",
|
||||
"P20": "P20",
|
||||
},
|
||||
spiPins: {
|
||||
MOSI: "P15",
|
||||
MISO: "P14",
|
||||
SCK: "P13",
|
||||
},
|
||||
i2cPins: {
|
||||
SDA: "P20",
|
||||
SCL: "P19",
|
||||
},
|
||||
analogInPins: ["P0", "P1", "P2", "P3", "P10"],
|
||||
groundPins: ["GND"],
|
||||
threeVoltPins: ["+3v3"],
|
||||
attachPowerOnRight: true,
|
||||
onboardComponents: ["buttonpair", "ledmatrix", "speaker"],
|
||||
useCrocClips: true,
|
||||
marginWhenBreadboarding: [0, 0, 80, 0],
|
||||
}
|
||||
|
||||
export const builtinComponentSimVisual: Map<() => visuals.IBoardPart<any>> = {
|
||||
"buttonpair": () => new visuals.ButtonPairView(),
|
||||
"ledmatrix": () => new visuals.LedMatrixView(),
|
||||
"neopixel": () => new visuals.NeoPixelView(),
|
||||
};
|
||||
export const builtinComponentSimState: Map<(d: DalBoard) => any> = {
|
||||
"buttonpair": (d: DalBoard) => d.buttonPairState,
|
||||
"ledmatrix": (d: DalBoard) => d.ledMatrixState,
|
||||
"edgeconnector": (d: DalBoard) => d.edgeConnectorState,
|
||||
"serial": (d: DalBoard) => d.serialState,
|
||||
"radio": (d: DalBoard) => d.radioState,
|
||||
"thermometer": (d: DalBoard) => d.thermometerState,
|
||||
"accelerometer": (d: DalBoard) => d.accelerometerState,
|
||||
"compass": (d: DalBoard) => d.compassState,
|
||||
"lightsensor": (d: DalBoard) => d.lightSensorState,
|
||||
"neopixel": (d: DalBoard) => d.neopixelState,
|
||||
};
|
||||
export const builtinComponentPartVisual: Map<(xy: visuals.Coord) => visuals.SVGElAndSize> = {
|
||||
"buttonpair": (xy: visuals.Coord) => visuals.mkBtnSvg(xy),
|
||||
"ledmatrix": (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8),
|
||||
"neopixel": (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy),
|
||||
};
|
||||
|
||||
//TODO: add multiple board support
|
||||
export const CURRENT_BOARD = MICROBIT_DEF;
|
||||
}
|
@ -282,13 +282,14 @@ namespace pxsim.instructions {
|
||||
return div;
|
||||
}
|
||||
function mkCmpDiv(cmp: "wire" | PartVisualDefinition, opts: mkCmpDivOpts): HTMLElement {
|
||||
let state = runtime.board as pxsim.CoreBoard;
|
||||
let el: visuals.SVGElAndSize;
|
||||
if (cmp == "wire") {
|
||||
el = visuals.mkWirePart([0, 0], opts.wireClr || "red", opts.crocClips);
|
||||
} else {
|
||||
let partVis = <PartVisualDefinition>cmp;
|
||||
if (typeof partVis.builtIn == "string") {
|
||||
let cnstr = builtinComponentPartVisual[partVis.builtIn];
|
||||
let cnstr = state.builtinPartVisuals[partVis.builtIn];
|
||||
el = cnstr([0, 0]);
|
||||
} else {
|
||||
el = visuals.mkGenericPartSVG(partVis);
|
||||
@ -353,8 +354,8 @@ namespace pxsim.instructions {
|
||||
};
|
||||
}
|
||||
function mkBlankBoardAndBreadboard(boardDef: BoardDefinition, cmpDefs: Map<PartDefinition>, fnArgs: any, width: number, buildMode: boolean = false): visuals.BoardHost {
|
||||
let state = runtime.board as pxsim.DalBoard;
|
||||
let boardHost = new visuals.BoardHost({
|
||||
const state = runtime.board as pxsim.CoreBoard;
|
||||
const opts : visuals.BoardHostOpts = {
|
||||
state: state,
|
||||
boardDef: boardDef,
|
||||
forceBreadboard: true,
|
||||
@ -362,7 +363,8 @@ namespace pxsim.instructions {
|
||||
maxWidth: `${width}px`,
|
||||
fnArgs: fnArgs,
|
||||
wireframe: buildMode,
|
||||
});
|
||||
};
|
||||
let boardHost = new visuals.BoardHost(pxsim.visuals.mkBoardView(opts), opts);
|
||||
let view = boardHost.getView();
|
||||
svg.addClass(view, "board-svg");
|
||||
|
||||
@ -613,6 +615,9 @@ ${tsPackage}
|
||||
});
|
||||
}
|
||||
|
||||
// board def
|
||||
const boardDef = JSON.parse(getQsVal("board")) as pxsim.BoardDefinition;
|
||||
|
||||
//parts list
|
||||
let parts = (getQsVal("parts") || "").split(" ");
|
||||
parts.sort();
|
||||
@ -636,7 +641,6 @@ ${tsPackage}
|
||||
|
||||
style.textContent += STYLE;
|
||||
|
||||
const boardDef = CURRENT_BOARD;
|
||||
const cmpDefs = partDefinitions;
|
||||
|
||||
//props
|
||||
|
@ -1,7 +1,7 @@
|
||||
namespace pxsim.input {
|
||||
export function onButtonPressed(button: number, handler: RefAction): void {
|
||||
let b = board().buttonPairState;
|
||||
if (button == DAL.MICROBIT_ID_BUTTON_AB && !b.usesButtonAB) {
|
||||
if (button == b.props.ID_BUTTON_AB && !b.usesButtonAB) {
|
||||
b.usesButtonAB = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
@ -10,32 +10,12 @@ namespace pxsim.input {
|
||||
|
||||
export function buttonIsPressed(button: number): boolean {
|
||||
let b = board().buttonPairState;
|
||||
if (button == DAL.MICROBIT_ID_BUTTON_AB && !b.usesButtonAB) {
|
||||
if (button == b.abBtn.id && !b.usesButtonAB) {
|
||||
b.usesButtonAB = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
if (button == DAL.MICROBIT_ID_BUTTON_A) return b.aBtn.pressed;
|
||||
if (button == DAL.MICROBIT_ID_BUTTON_B) return b.bBtn.pressed;
|
||||
if (button == b.aBtn.id) return b.aBtn.pressed;
|
||||
if (button == b.bBtn.id) return b.bBtn.pressed;
|
||||
return b.abBtn.pressed || (b.aBtn.pressed && b.bBtn.pressed);
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim {
|
||||
export class Button {
|
||||
constructor(public id: number) { }
|
||||
pressed: boolean;
|
||||
}
|
||||
|
||||
export class ButtonPairState {
|
||||
usesButtonAB: boolean = false;
|
||||
aBtn: Button;
|
||||
bBtn: Button;
|
||||
abBtn: Button;
|
||||
|
||||
constructor() {
|
||||
this.aBtn = new Button(DAL.MICROBIT_ID_BUTTON_A);
|
||||
this.bBtn = new Button(DAL.MICROBIT_ID_BUTTON_B);
|
||||
this.abBtn = new Button(DAL.MICROBIT_ID_BUTTON_AB);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,11 +12,4 @@ namespace pxsim.input {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim {
|
||||
export class CompassState {
|
||||
usesHeading = false;
|
||||
heading = 90;
|
||||
}
|
||||
}
|
@ -24,64 +24,6 @@ namespace pxsim {
|
||||
export function getPin(id: number) {
|
||||
return board().edgeConnectorState.getPin(id);
|
||||
}
|
||||
|
||||
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;
|
||||
mode = PinFlags.Unused;
|
||||
pitch = false;
|
||||
pull = 0; // PullDown
|
||||
|
||||
isTouched(): boolean {
|
||||
this.mode = PinFlags.Touch;
|
||||
return this.touched;
|
||||
}
|
||||
}
|
||||
|
||||
export class EdgeConnectorState {
|
||||
pins: Pin[];
|
||||
|
||||
constructor() {
|
||||
this.pins = [
|
||||
new Pin(DAL.MICROBIT_ID_IO_P0),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P1),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P2),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P3),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P4),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P5),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P6),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P7),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P8),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P9),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P10),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P11),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P12),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P13),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P14),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P15),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P16),
|
||||
null,
|
||||
null,
|
||||
new Pin(DAL.MICROBIT_ID_IO_P19),
|
||||
new Pin(DAL.MICROBIT_ID_IO_P20)
|
||||
];
|
||||
}
|
||||
|
||||
public getPin(id: number) {
|
||||
return this.pins.filter(p => p && p.id == id)[0] || null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.pins {
|
||||
|
@ -116,72 +116,6 @@ namespace pxsim {
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
export interface AnimationOptions {
|
||||
interval: number;
|
||||
// false means last frame
|
||||
frame: () => boolean;
|
||||
whenDone?: (cancelled: boolean) => void;
|
||||
}
|
||||
|
||||
export class AnimationQueue {
|
||||
private queue: AnimationOptions[] = [];
|
||||
private process: () => void;
|
||||
|
||||
constructor(private runtime: Runtime) {
|
||||
this.process = () => {
|
||||
let top = this.queue[0]
|
||||
if (!top) return
|
||||
if (this.runtime.dead) return
|
||||
runtime = this.runtime
|
||||
let res = top.frame()
|
||||
runtime.queueDisplayUpdate()
|
||||
runtime.maybeUpdateDisplay()
|
||||
if (res === false) {
|
||||
this.queue.shift();
|
||||
// if there is already something in the queue, start processing
|
||||
if (this.queue[0])
|
||||
setTimeout(this.process, this.queue[0].interval)
|
||||
// this may push additional stuff
|
||||
top.whenDone(false);
|
||||
} else {
|
||||
setTimeout(this.process, top.interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public cancelAll() {
|
||||
let q = this.queue
|
||||
this.queue = []
|
||||
for (let a of q) {
|
||||
a.whenDone(true)
|
||||
}
|
||||
}
|
||||
|
||||
public cancelCurrent() {
|
||||
let top = this.queue[0]
|
||||
if (top) {
|
||||
this.queue.shift();
|
||||
top.whenDone(true);
|
||||
}
|
||||
}
|
||||
|
||||
public enqueue(anim: AnimationOptions) {
|
||||
if (!anim.whenDone) anim.whenDone = () => { };
|
||||
this.queue.push(anim)
|
||||
// we start processing when the queue goes from 0 to 1
|
||||
if (this.queue.length == 1)
|
||||
this.process()
|
||||
}
|
||||
|
||||
public executeAsync(anim: AnimationOptions) {
|
||||
U.assert(!anim.whenDone)
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
anim.whenDone = resolve
|
||||
this.enqueue(anim)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.images {
|
||||
@ -337,6 +271,7 @@ namespace pxsim.led {
|
||||
|
||||
export function stopAnimation(): void {
|
||||
board().ledMatrixState.animationQ.cancelAll();
|
||||
board().ledMatrixState.image.clear();
|
||||
}
|
||||
|
||||
export function setDisplayMode(mode: DisplayMode): void {
|
||||
|
@ -1,10 +1,3 @@
|
||||
namespace pxsim {
|
||||
export class LightSensorState {
|
||||
usesLightLevel = false;
|
||||
lightLevel = 128;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.input {
|
||||
export function lightLevel(): number {
|
||||
let b = board().lightSensorState;
|
||||
|
@ -22,58 +22,6 @@ namespace pxsim {
|
||||
throw new Error("PANIC " + code)
|
||||
}
|
||||
|
||||
export namespace AudioContextManager {
|
||||
let _context: any; // AudioContext
|
||||
let _vco: any; // OscillatorNode;
|
||||
let _vca: any; // GainNode;
|
||||
|
||||
function context(): any {
|
||||
if (!_context) _context = freshContext();
|
||||
return _context;
|
||||
}
|
||||
|
||||
function freshContext(): any {
|
||||
(<any>window).AudioContext = (<any>window).AudioContext || (<any>window).webkitAudioContext;
|
||||
if ((<any>window).AudioContext) {
|
||||
try {
|
||||
// this call my crash.
|
||||
// SyntaxError: audio resources unavailable for AudioContext construction
|
||||
return new (<any>window).AudioContext();
|
||||
} catch (e) { }
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function stop() {
|
||||
if (_vca) _vca.gain.value = 0;
|
||||
}
|
||||
|
||||
export function tone(frequency: number, gain: number) {
|
||||
if (frequency <= 0) return;
|
||||
let ctx = context();
|
||||
if (!ctx) return;
|
||||
|
||||
gain = Math.max(0, Math.min(1, gain));
|
||||
if (!_vco) {
|
||||
try {
|
||||
_vco = ctx.createOscillator();
|
||||
_vca = ctx.createGain();
|
||||
_vco.connect(_vca);
|
||||
_vca.connect(ctx.destination);
|
||||
_vca.gain.value = gain;
|
||||
_vco.start(0);
|
||||
} catch (e) {
|
||||
_vco = undefined;
|
||||
_vca = undefined;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_vco.frequency.value = frequency;
|
||||
_vca.gain.value = gain;
|
||||
}
|
||||
}
|
||||
|
||||
export interface RuntimeOptions {
|
||||
theme: string;
|
||||
}
|
||||
|
@ -4,54 +4,10 @@ namespace pxsim {
|
||||
if (b) {
|
||||
let np = b.neopixelState;
|
||||
if (np) {
|
||||
np.updateBuffer(buffer, pin);
|
||||
let buf = <Uint8Array[]>(<any>buffer).data;
|
||||
np.updateBuffer(buf, pin);
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim {
|
||||
export enum NeoPixelMode {RGB, RGBW};
|
||||
export type RGBW = [number, number, number, number];
|
||||
|
||||
function readNeoPixelBuffer(inBuffer: Uint8Array[], outColors: RGBW[], mode: NeoPixelMode) {
|
||||
let buf = inBuffer;
|
||||
let stride = mode === NeoPixelMode.RGBW ? 4 : 3;
|
||||
let pixelCount = Math.floor(buf.length / stride);
|
||||
for (let i = 0; i < pixelCount; i++) {
|
||||
// NOTE: for whatever reason, NeoPixels pack GRB not RGB
|
||||
let r = buf[i * stride + 1] as any as number
|
||||
let g = buf[i * stride + 0] as any as number
|
||||
let b = buf[i * stride + 2] as any as number
|
||||
let w = 0;
|
||||
if (stride === 4)
|
||||
w = buf[i * stride + 3] as any as number
|
||||
outColors[i] = [r, g, b, w]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class NeoPixelState {
|
||||
private buffers: {[pin: number]: Uint8Array[]} = {};
|
||||
private colors: {[pin: number]: RGBW[]} = {};
|
||||
private dirty: {[pin: number]: boolean} = {};
|
||||
|
||||
public updateBuffer(buffer: Buffer, pin: DigitalPin) {
|
||||
//update buffers
|
||||
let buf = <Uint8Array[]>(<any>buffer).data;
|
||||
this.buffers[pin] = buf;
|
||||
this.dirty[pin] = true;
|
||||
}
|
||||
|
||||
public getColors(pin: number, mode: NeoPixelMode): RGBW[] {
|
||||
let outColors = this.colors[pin] || (this.colors[pin] = []);
|
||||
if (this.dirty[pin]) {
|
||||
let buf = this.buffers[pin] || (this.buffers[pin] = []);
|
||||
readNeoPixelBuffer(buf, outColors, mode);
|
||||
this.dirty[pin] = false;
|
||||
}
|
||||
return outColors;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
namespace pxsim.visuals {
|
||||
export interface BoardHostOpts {
|
||||
state: DalBoard,
|
||||
boardDef: BoardDefinition,
|
||||
partsList?: string[],
|
||||
partDefs: Map<PartDefinition>,
|
||||
fnArgs: any,
|
||||
forceBreadboard?: boolean,
|
||||
maxWidth?: string,
|
||||
maxHeight?: string
|
||||
wireframe?: boolean
|
||||
}
|
||||
export class BoardHost {
|
||||
private parts: IBoardPart<any>[] = [];
|
||||
private wireFactory: WireFactory;
|
||||
private breadboard: Breadboard;
|
||||
private fromBBCoord: (xy: Coord) => Coord;
|
||||
private fromMBCoord: (xy: Coord) => Coord;
|
||||
private boardView: BoardView;
|
||||
private view: SVGSVGElement;
|
||||
private partGroup: SVGGElement;
|
||||
private partOverGroup: SVGGElement;
|
||||
private style: SVGStyleElement;
|
||||
private defs: SVGDefsElement;
|
||||
private state: DalBoard;
|
||||
private useCrocClips: boolean;
|
||||
|
||||
constructor(opts: BoardHostOpts) {
|
||||
this.state = opts.state;
|
||||
let onboardCmps = opts.boardDef.onboardComponents || [];
|
||||
let activeComponents = (opts.partsList || []).filter(c => onboardCmps.indexOf(c) < 0);
|
||||
activeComponents.sort();
|
||||
this.useCrocClips = opts.boardDef.useCrocClips;
|
||||
|
||||
if (opts.boardDef.visual === "microbit") {
|
||||
this.boardView = new visuals.MicrobitBoardSvg({
|
||||
runtime: runtime,
|
||||
theme: visuals.randomTheme(),
|
||||
disableTilt: false,
|
||||
wireframe: opts.wireframe,
|
||||
});
|
||||
} else {
|
||||
let boardVis = opts.boardDef.visual as BoardImageDefinition;
|
||||
this.boardView = new visuals.GenericBoardSvg({
|
||||
visualDef: boardVis,
|
||||
wireframe: opts.wireframe,
|
||||
});
|
||||
}
|
||||
|
||||
let useBreadboard = 0 < activeComponents.length || opts.forceBreadboard;
|
||||
if (useBreadboard) {
|
||||
this.breadboard = new Breadboard({
|
||||
wireframe: opts.wireframe,
|
||||
});
|
||||
let bMarg = opts.boardDef.marginWhenBreadboarding || [0, 0, 40, 0];
|
||||
let composition = composeSVG({
|
||||
el1: this.boardView.getView(),
|
||||
scaleUnit1: this.boardView.getPinDist(),
|
||||
el2: this.breadboard.getSVGAndSize(),
|
||||
scaleUnit2: this.breadboard.getPinDist(),
|
||||
margin: [bMarg[0], bMarg[1], 20, bMarg[3]],
|
||||
middleMargin: bMarg[2],
|
||||
maxWidth: opts.maxWidth,
|
||||
maxHeight: opts.maxHeight,
|
||||
});
|
||||
let under = composition.under;
|
||||
let over = composition.over;
|
||||
this.view = composition.host;
|
||||
let edges = composition.edges;
|
||||
this.fromMBCoord = composition.toHostCoord1;
|
||||
this.fromBBCoord = composition.toHostCoord2;
|
||||
let pinDist = composition.scaleUnit;
|
||||
this.partGroup = over;
|
||||
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
|
||||
|
||||
this.style = <SVGStyleElement>svg.child(this.view, "style", {});
|
||||
this.defs = <SVGDefsElement>svg.child(this.view, "defs", {});
|
||||
|
||||
this.wireFactory = new WireFactory(under, over, edges, this.style, this.getLocCoord.bind(this));
|
||||
|
||||
let allocRes = allocateDefinitions({
|
||||
boardDef: opts.boardDef,
|
||||
partDefs: opts.partDefs,
|
||||
fnArgs: opts.fnArgs,
|
||||
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
|
||||
partsList: activeComponents,
|
||||
});
|
||||
|
||||
this.addAll(allocRes);
|
||||
} else {
|
||||
let el = this.boardView.getView().el;
|
||||
this.view = el;
|
||||
this.partGroup = <SVGGElement>svg.child(this.view, "g");
|
||||
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
|
||||
if (opts.maxWidth)
|
||||
svg.hydrate(this.view, { width: opts.maxWidth });
|
||||
if (opts.maxHeight)
|
||||
svg.hydrate(this.view, { height: opts.maxHeight });
|
||||
}
|
||||
|
||||
this.state.updateSubscribers.push(() => this.updateState());
|
||||
}
|
||||
|
||||
public highlightBoardPin(pinNm: string) {
|
||||
this.boardView.highlightPin(pinNm);
|
||||
}
|
||||
|
||||
public highlightBreadboardPin(rowCol: BBLoc) {
|
||||
this.breadboard.highlightLoc(rowCol);
|
||||
}
|
||||
|
||||
public highlightWire(wire: Wire) {
|
||||
//TODO: move to wiring.ts
|
||||
//underboard wires
|
||||
wire.wires.forEach(e => {
|
||||
svg.addClass(e, "highlight");
|
||||
(<any>e).style["visibility"] = "visible";
|
||||
});
|
||||
|
||||
//un greyed out
|
||||
svg.addClass(wire.endG, "highlight");
|
||||
}
|
||||
|
||||
public getView(): SVGElement {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
this.parts.forEach(c => c.updateState());
|
||||
}
|
||||
|
||||
private getBBCoord(rowCol: BBLoc) {
|
||||
let bbCoord = this.breadboard.getCoord(rowCol);
|
||||
return this.fromBBCoord(bbCoord);
|
||||
}
|
||||
private getPinCoord(pin: string) {
|
||||
let boardCoord = this.boardView.getCoord(pin);
|
||||
return this.fromMBCoord(boardCoord);
|
||||
}
|
||||
public getLocCoord(loc: Loc): Coord {
|
||||
let coord: Coord;
|
||||
if (loc.type === "breadboard") {
|
||||
let rowCol = (<BBLoc>loc);
|
||||
coord = this.getBBCoord(rowCol);
|
||||
} else {
|
||||
let pinNm = (<BoardLoc>loc).pin;
|
||||
coord = this.getPinCoord(pinNm);
|
||||
}
|
||||
if (!coord) {
|
||||
console.error("Unknown location: " + name)
|
||||
return [0, 0];
|
||||
}
|
||||
return coord;
|
||||
}
|
||||
|
||||
public addPart(partInst: PartInst): IBoardPart<any> {
|
||||
let part: IBoardPart<any> = null;
|
||||
let colOffset = 0;
|
||||
if (partInst.simulationBehavior) {
|
||||
//TODO: seperate simulation behavior from builtin visual
|
||||
let builtinBehavior = partInst.simulationBehavior;
|
||||
let cnstr = builtinComponentSimVisual[builtinBehavior];
|
||||
let stateFn = builtinComponentSimState[builtinBehavior];
|
||||
part = cnstr();
|
||||
part.init(this.state.bus, stateFn(this.state), this.view, partInst.params);
|
||||
} else {
|
||||
let vis = partInst.visual as PartVisualDefinition;
|
||||
part = new GenericPart(vis);
|
||||
}
|
||||
this.parts.push(part);
|
||||
this.partGroup.appendChild(part.element);
|
||||
if (part.overElement)
|
||||
this.partOverGroup.appendChild(part.overElement);
|
||||
if (part.defs)
|
||||
part.defs.forEach(d => this.defs.appendChild(d));
|
||||
this.style.textContent += part.style || "";
|
||||
let colIdx = partInst.startColumnIdx;
|
||||
let rowIdx = partInst.startRowIdx;
|
||||
let row = getRowName(rowIdx);
|
||||
let col = getColumnName(colIdx);
|
||||
let xOffset = partInst.bbFit.xOffset / partInst.visual.pinDistance;
|
||||
let yOffset = partInst.bbFit.yOffset / partInst.visual.pinDistance;
|
||||
let rowCol = <BBLoc>{
|
||||
type: "breadboard",
|
||||
row: row,
|
||||
col: col,
|
||||
xOffset: xOffset,
|
||||
yOffset: yOffset
|
||||
};
|
||||
let coord = this.getBBCoord(rowCol);
|
||||
part.moveToCoord(coord);
|
||||
let getCmpClass = (type: string) => `sim-${type}-cmp`;
|
||||
let cls = getCmpClass(partInst.name);
|
||||
svg.addClass(part.element, cls);
|
||||
svg.addClass(part.element, "sim-cmp");
|
||||
part.updateTheme();
|
||||
part.updateState();
|
||||
return part;
|
||||
}
|
||||
public addWire(inst: WireInst): Wire {
|
||||
return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
|
||||
}
|
||||
public addAll(allocRes: AllocatorResult) {
|
||||
allocRes.partsAndWires.forEach(pAndWs => {
|
||||
let part = pAndWs.part;
|
||||
if (part)
|
||||
this.addPart(part)
|
||||
let wires = pAndWs.wires;
|
||||
if (wires)
|
||||
wires.forEach(w => this.addWire(w));
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
19
sim/visuals/boardview.ts
Normal file
@ -0,0 +1,19 @@
|
||||
namespace pxsim.visuals {
|
||||
export function mkBoardView(opts: BoardHostOpts): BoardView {
|
||||
if (opts.boardDef.visual === "microbit") {
|
||||
return new visuals.MicrobitBoardSvg({
|
||||
runtime: runtime,
|
||||
theme: visuals.randomTheme(),
|
||||
disableTilt: false,
|
||||
wireframe: opts.wireframe,
|
||||
});
|
||||
} else {
|
||||
let boardVis = opts.boardDef.visual as BoardImageDefinition;
|
||||
return new visuals.GenericBoardSvg({
|
||||
visualDef: boardVis,
|
||||
wireframe: opts.wireframe,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
/// <reference path="../../node_modules/pxt-core/typings/bluebird/bluebird.d.ts"/>
|
||||
/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
/// <reference path="../../libs/microbit/dal.d.ts"/>
|
||||
|
||||
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");
|
||||
svg.addClass(btnTxt, "sim-text")
|
||||
svg.addClass(btnTxt, "sim-text-virtual");
|
||||
btng.appendChild(btnTxt);
|
||||
|
||||
return btng;
|
||||
}
|
||||
|
||||
this.abBtn = mkVirtualBtn();
|
||||
this.abBtn.style.visibility = "hidden";
|
||||
|
||||
let el = svg.elt("g");
|
||||
svg.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) => {
|
||||
btn.addEventListener(pointerEvents.down, 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, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.bus.queue(btnStates[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
})
|
||||
})
|
||||
let updateBtns = (s: boolean) => {
|
||||
btnStates.forEach(b => b.pressed = s)
|
||||
};
|
||||
this.abBtn.addEventListener(pointerEvents.down, 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, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.bus.queue(this.state.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -226,25 +226,6 @@ namespace pxsim.visuals {
|
||||
wireframe?: boolean;
|
||||
}
|
||||
|
||||
export interface IPointerEvents {
|
||||
up: string,
|
||||
down: string,
|
||||
move: string,
|
||||
leave: string
|
||||
}
|
||||
|
||||
export const pointerEvents = !!(window as any).PointerEvent ? {
|
||||
up: "pointerup",
|
||||
down: "pointerdown",
|
||||
move: "pointermove",
|
||||
leave: "pointerleave"
|
||||
} : {
|
||||
up: "mouseup",
|
||||
down: "mousedown",
|
||||
move: "mousemove",
|
||||
leave: "mouseleave"
|
||||
};
|
||||
|
||||
export class MicrobitBoardSvg implements BoardView {
|
||||
public element: SVGSVGElement;
|
||||
private style: SVGStyleElement;
|
@ -3,43 +3,6 @@
|
||||
/// <reference path="../../libs/microbit/shims.d.ts"/>
|
||||
/// <reference path="../../libs/microbit/enums.d.ts"/>
|
||||
|
||||
//TODO move to utils
|
||||
namespace pxsim.visuals {
|
||||
//expects rgb from 0,255, gives h in [0,360], s in [0, 100], l in [0, 100]
|
||||
export function rgbToHsl(rgb: [number, number, number]): [number, number, number] {
|
||||
let [r, g, b] = rgb;
|
||||
let [r$, g$, b$] = [r / 255, g / 255, b / 255];
|
||||
let cMin = Math.min(r$, g$, b$);
|
||||
let cMax = Math.max(r$, g$, b$);
|
||||
let cDelta = cMax - cMin;
|
||||
let h: number, s: number, l: number;
|
||||
let maxAndMin = cMax + cMin;
|
||||
|
||||
//lum
|
||||
l = (maxAndMin / 2) * 100
|
||||
|
||||
if (cDelta === 0)
|
||||
s = h = 0;
|
||||
else {
|
||||
//hue
|
||||
if (cMax === r$)
|
||||
h = 60 * (((g$ - b$) / cDelta) % 6);
|
||||
else if (cMax === g$)
|
||||
h = 60 * (((b$ - r$) / cDelta) + 2);
|
||||
else if (cMax === b$)
|
||||
h = 60 * (((r$ - g$) / cDelta) + 4);
|
||||
|
||||
//sat
|
||||
if (l > 50)
|
||||
s = 100 * (cDelta / (2 - maxAndMin));
|
||||
else
|
||||
s = 100 * (cDelta / maxAndMin);
|
||||
}
|
||||
|
||||
return [Math.floor(h), Math.floor(s), Math.floor(l)];
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.visuals {
|
||||
const PIXEL_SPACING = PIN_DIST * 3;
|
||||
const PIXEL_RADIUS = PIN_DIST;
|
||||
@ -115,10 +78,11 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
public setRgb(rgb: [number, number, number]) {
|
||||
let hsl = rgbToHsl(rgb);
|
||||
let hsl = visuals.rgbToHsl(rgb);
|
||||
let [h, s, l] = hsl;
|
||||
//We ignore luminosity since it doesn't map well to real-life brightness
|
||||
let fill = `hsl(${h}, ${s}%, 70%)`;
|
||||
// at least 70% luminosity
|
||||
l = Math.max(l, 60);
|
||||
let fill = `hsl(${h}, ${s}%, ${l}%)`;
|
||||
this.el.setAttribute("fill", fill);
|
||||
}
|
||||
}
|
||||
@ -203,18 +167,9 @@ namespace pxsim.visuals {
|
||||
function parseNeoPixelMode(modeStr: string): NeoPixelMode {
|
||||
const modeMap: Map<NeoPixelMode> = {
|
||||
"NeoPixelMode.RGB": NeoPixelMode.RGB,
|
||||
"NeoPixelMode.RGBW": NeoPixelMode.RGBW,
|
||||
"*": NeoPixelMode.RGB,
|
||||
"NeoPixelMode.RGBW": NeoPixelMode.RGBW
|
||||
};
|
||||
let mode: NeoPixelMode = null;
|
||||
for (let key in modeMap) {
|
||||
if (key == modeStr) {
|
||||
mode = modeMap[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
U.assert(mode != null, "Unknown NeoPixelMode: " + modeStr);
|
||||
return mode;
|
||||
return modeMap[modeStr] || NeoPixelMode.RGB;
|
||||
}
|
||||
|
||||
export class NeoPixelView implements IBoardPart<NeoPixelState> {
|
||||
|